morphit/ops/ansible/roles/base/tasks/main.yml
Morphit Team 277a072d7a
Some checks failed
morphit-release / Build + publish release tarball (push) Has been cancelled
Morphit v1.10.7
2026-08-08 20:33:33 -07:00

192 lines
6.7 KiB
YAML

# Role: base
#
# OS-level prep: service user, base packages, NTP, hostname.
# Does NOT do hardening (that's the `hardening` role). Does NOT
# install Postgres or Node (those are their own roles).
#
# Reference: scripts/vps-bootstrap.sh in the morphit repo —
# this role re-implements its base-package install + service
# user creation in idempotent Ansible form.
---
# On a LOCAL install inventory_hostname is 'localhost' (the inline `-i localhost,`
# inventory), so these would rename the operator's own machine to "localhost" —
# skip them locally and leave the box's existing hostname alone. On a REMOTE
# install inventory_hostname is the real host name from the inventory, so they run.
- name: Set hostname
ansible.builtin.hostname:
name: "{{ inventory_hostname }}"
when: not (morphit_local_install | default(false) | bool)
- name: Ensure /etc/hosts has the hostname
ansible.builtin.lineinfile:
path: /etc/hosts
regexp: '^127\.0\.1\.1\s'
line: "127.0.1.1 {{ inventory_hostname }}"
when: not (morphit_local_install | default(false) | bool)
- name: Set timezone to UTC
community.general.timezone:
name: Etc/UTC
- name: Update apt cache
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
- name: Install base packages
ansible.builtin.apt:
name:
# Core
- build-essential
- git
- curl
- wget
- ca-certificates
- gnupg
- lsb-release
- jq
# Cron — the aide + rkhunter scheduled scans (hardening role) install
# into /etc/cron.daily and /etc/cron.weekly, which the `cron` package
# owns. Near-universal, but pinned here so those dirs are guaranteed to
# exist even on a minimal box that shipped without cron.
- cron
# Time sync (auditd timestamps + chain-time skew depend on it)
- chrony
# Hardening packages (referenced by hardening role)
- ufw
- fail2ban
- auditd
- audispd-plugins
- aide
- aide-common
- apparmor
- apparmor-utils
- unattended-upgrades
- apt-listchanges
- rkhunter
- libpam-pwquality
# Backup tooling
- age
- rsync
# Postfix for outbound alerts (configured by morphit role)
- postfix
- libsasl2-modules
state: present
- name: Enable chrony for NTP
ansible.builtin.systemd:
name: chrony
enabled: true
state: started
- name: Create morphit service group
ansible.builtin.group:
name: "{{ morphit_service_group }}"
system: true
state: present
- name: Create morphit service user
ansible.builtin.user:
name: "{{ morphit_service_user }}"
group: "{{ morphit_service_group }}"
home: "{{ morphit_service_home }}"
shell: /usr/sbin/nologin
system: true
create_home: true
state: present
- name: Ensure the morphit service user owns its home (recursively)
# `create_home: true` only chowns the home when useradd CREATES it; if the dir
# pre-existed (a partial/earlier run, or a re-run on the same box) it is left
# root-owned, and then `npm exec`/`npm run` (which write $HOME/.npm) fail EACCES
# and Ansible's become-user temp dir (/var/lib/morphit/.ansible) is unwritable.
# This makes the home — and any stale root-owned dotfiles under it — the service
# user's, so those writes succeed on a fresh AND a re-used box.
ansible.builtin.file:
path: "{{ morphit_service_home }}"
state: directory
owner: "{{ morphit_service_user }}"
group: "{{ morphit_service_group }}"
recurse: true
# Part 122 cp5 F12 — the shipped morphit-relay.service specifies
# `User=morphit-relay / Group=morphit-relay`. Without this task,
# `systemctl start morphit-relay` fails with "user morphit-relay
# does not exist" on a first deploy, AND any earlier `chown
# morphit-relay:morphit-relay /var/lib/morphit-relay` step fails
# with "invalid user". Pre-cp5 the playbook would never reach
# "relay running" on a fresh box.
- name: Create morphit-relay system group
ansible.builtin.group:
name: morphit-relay
system: true
state: present
- name: Create morphit-relay system user
ansible.builtin.user:
name: morphit-relay
group: morphit-relay
# Add to morphit_service_group so the relay can read
# /etc/morphit/relay.env (chowned root:morphit_service_group
# mode 0640 by the morphit role). Without this membership
# the relay daemon can't read its own config.
groups: "{{ morphit_service_group }}"
append: true
home: /var/lib/morphit-relay
create_home: false
shell: /usr/sbin/nologin
system: true
state: present
# cp167 — MCP server (ops/systemd/morphit-mcp.service). Same shape
# as morphit-relay: dedicated system group + user, no shell, no home,
# member of morphit_service_group so it can read /etc/morphit/*.env.
# The MCP server holds no keys (it's read-only over the indexer's
# orderbook) so the user needs no privileged group memberships
# beyond config-file read access.
# NOTE: the morphit-mcp user/group is created by the `morphit` role (gated on
# morphit_mcp_enabled, home /opt/morphit-mcp, isolated — NOT in the service group),
# not here. base used to create it too, with a DIFFERENT home (/var/lib/morphit-mcp)
# and — wrongly — membership in morphit_service_group, which (a) forced a `usermod`
# on every converge as the two definitions fought, failing on a re-run/upgrade with
# "user morphit-mcp is currently used by process …" once morphit-mcp.service is up,
# and (b) broke the MCP's isolation (the whole point is morphit-mcp CANNOT read the
# main install's secrets). Single-owner now: the morphit role.
- name: Create /etc/morphit directory for env files + keystore
ansible.builtin.file:
path: /etc/morphit
state: directory
owner: root
group: "{{ morphit_service_group }}"
mode: '0750'
# cp676 — shared helper-script dir used by several roles (ddns, backup, mcp,
# ipfs). It was previously created only inside the ipfs role (which runs LATE),
# so an earlier consumer — e.g. the ddns role when DDNS is enabled — failed with
# "Destination directory /usr/local/lib/morphit does not exist". Create it here
# in base (runs before every consumer) so the install never depends on ipfs.
- name: Create /usr/local/lib/morphit shared script directory
ansible.builtin.file:
path: /usr/local/lib/morphit
state: directory
owner: root
group: root
mode: '0755'
- name: Create /opt/morphit parent directory for the repo clone
ansible.builtin.file:
path: "{{ morphit_repo_path | dirname }}"
state: directory
owner: root
group: root
mode: '0755'
- name: Create /var/log/morphit for service logs
ansible.builtin.file:
path: /var/log/morphit
state: directory
owner: "{{ morphit_service_user }}"
group: "{{ morphit_service_group }}"
mode: '0750'