morphit/ops/ansible/roles/ipfs/tasks/main.yml
Morphit Team 51b1d1ba77
Some checks failed
morphit-release / Build + publish release tarball (push) Has been cancelled
Morphit v1.10.1
2026-08-06 20:44:17 -07:00

235 lines
8.6 KiB
YAML

---
# Every Morphit instance runs a small Kubo node that pins the signed release.
- name: Create the ipfs group
ansible.builtin.group:
name: "{{ morphit_ipfs_group }}"
system: true
- name: Create the unprivileged ipfs user
ansible.builtin.user:
name: "{{ morphit_ipfs_user }}"
group: "{{ morphit_ipfs_group }}"
home: "{{ morphit_ipfs_home }}"
shell: /usr/sbin/nologin
system: true
create_home: true
- name: Check the currently-installed Kubo version (idempotent install)
ansible.builtin.command: ipfs --version
register: morphit_ipfs_have
changed_when: false
failed_when: false
- name: Install Kubo (download + verify + place binary)
when: morphit_kubo_version | regex_replace('^v', '') not in (morphit_ipfs_have.stdout | default(''))
block:
- name: Make a scratch dir for the Kubo download
ansible.builtin.tempfile:
state: directory
suffix: kubo
register: morphit_kubo_tmp
- name: Detect a bundled Kubo tarball (offline appliance)
ansible.builtin.stat:
path: "{{ morphit_repo_path }}/vendor/kubo/kubo_{{ morphit_kubo_version }}_{{ morphit_kubo_arch }}.tar.gz"
register: morphit_kubo_vendor
- name: Use the bundled Kubo tarball (offline — still SHA-512 verified below)
ansible.builtin.copy:
src: "{{ morphit_repo_path }}/vendor/kubo/kubo_{{ morphit_kubo_version }}_{{ morphit_kubo_arch }}.tar.gz"
dest: "{{ morphit_kubo_tmp.path }}/kubo.tar.gz"
remote_src: true
mode: "0644"
when: morphit_kubo_vendor.stat.exists
- name: Download the Kubo tarball
ansible.builtin.get_url:
url: "{{ morphit_kubo_dist_base }}/{{ morphit_kubo_version }}/kubo_{{ morphit_kubo_version }}_{{ morphit_kubo_arch }}.tar.gz"
dest: "{{ morphit_kubo_tmp.path }}/kubo.tar.gz"
mode: "0644"
when: not morphit_kubo_vendor.stat.exists
- name: Download the official Kubo checksum (used when no explicit pin is set)
ansible.builtin.get_url:
url: "{{ morphit_kubo_dist_base }}/{{ morphit_kubo_version }}/kubo_{{ morphit_kubo_version }}_{{ morphit_kubo_arch }}.tar.gz.sha512"
dest: "{{ morphit_kubo_tmp.path }}/kubo.tar.gz.sha512"
mode: "0644"
when: morphit_kubo_sha512 | length == 0
- name: Compute the download's SHA-512
ansible.builtin.stat:
path: "{{ morphit_kubo_tmp.path }}/kubo.tar.gz"
checksum_algorithm: sha512
get_checksum: true
register: morphit_kubo_dl
- name: Verify against the operator-pinned SHA-512 (strong pin)
ansible.builtin.assert:
that:
- morphit_kubo_dl.stat.checksum | lower == morphit_kubo_sha512 | lower
fail_msg: >-
Kubo tarball SHA-512 mismatch — expected the pinned {{ morphit_kubo_sha512 }},
got {{ morphit_kubo_dl.stat.checksum }}. Refusing to install.
when: morphit_kubo_sha512 | length > 0
- name: Verify against the official published SHA-512
ansible.builtin.assert:
that:
- morphit_kubo_dl.stat.checksum | lower
== (lookup('file', morphit_kubo_tmp.path + '/kubo.tar.gz.sha512') | regex_search('[0-9a-fA-F]{128}')) | lower
fail_msg: "Kubo tarball did not match its published .sha512 — refusing to install."
when: morphit_kubo_sha512 | length == 0
- name: Extract Kubo
ansible.builtin.unarchive:
src: "{{ morphit_kubo_tmp.path }}/kubo.tar.gz"
dest: "{{ morphit_kubo_tmp.path }}"
remote_src: true
- name: Install the ipfs binary
ansible.builtin.copy:
src: "{{ morphit_kubo_tmp.path }}/kubo/ipfs"
dest: /usr/local/bin/ipfs
remote_src: true
owner: root
group: root
mode: "0755"
notify: Restart ipfs
- name: Remove the scratch dir
ansible.builtin.file:
path: "{{ morphit_kubo_tmp.path }}"
state: absent
- name: Initialise the Kubo repo (once; applies the low-footprint profile)
ansible.builtin.command:
cmd: "ipfs init --profile {{ morphit_ipfs_profile }}"
creates: "{{ morphit_ipfs_repo }}/config"
become: true
become_user: "{{ morphit_ipfs_user }}"
environment:
IPFS_PATH: "{{ morphit_ipfs_repo }}"
- name: Keep the node small — bind API/gateway to loopback + cap connections
ansible.builtin.command:
cmd: "ipfs config --json {{ item.key }} {{ item.value }}"
become: true
become_user: "{{ morphit_ipfs_user }}"
environment:
IPFS_PATH: "{{ morphit_ipfs_repo }}"
loop:
- { key: "Addresses.API", value: "'\"{{ morphit_ipfs_api_addr }}\"'" }
- { key: "Addresses.Gateway", value: "'\"{{ morphit_ipfs_gateway_addr }}\"'" }
- { key: "Swarm.ConnMgr.HighWater", value: "{{ morphit_ipfs_connmgr_high }}" }
- { key: "Swarm.ConnMgr.LowWater", value: "{{ morphit_ipfs_connmgr_low }}" }
- { key: "Routing.Type", value: "'\"auto\"'" }
changed_when: false
notify: Restart ipfs
- name: Ensure /etc/morphit exists (env dir)
ansible.builtin.file:
path: /etc/morphit
state: directory
owner: root
group: root
mode: "0755"
- name: Install /usr/local/lib/morphit (script dir)
ansible.builtin.file:
path: /usr/local/lib/morphit
state: directory
owner: root
group: root
mode: "0755"
- name: Install the release-pinning script
ansible.builtin.copy:
src: "{{ playbook_dir }}/../ipfs/morphit-ipfs-pin.sh"
dest: /usr/local/lib/morphit/morphit-ipfs-pin.sh
owner: root
group: root
mode: "0755"
- name: Install the IPNS-rebroadcast script
# Re-announces @morphit's on-chain signed IPNS record to the public DHT from
# this box (no key required), so ipns://<name> stays resolvable as long as ANY
# instance is alive. The hand-managed path (morphit-ipfs-setup.sh) installs the
# same script + timer; this wires it for Ansible/wizard installs too.
ansible.builtin.copy:
src: "{{ playbook_dir }}/../ipfs/morphit-ipns-rebroadcast.sh"
dest: /usr/local/lib/morphit/morphit-ipns-rebroadcast.sh
owner: root
group: root
mode: "0755"
- name: Install the pin service env file
ansible.builtin.template:
src: ipfs-pin.env.j2
dest: /etc/morphit/ipfs-pin.env
owner: root
group: "{{ morphit_ipfs_group }}"
mode: "0640"
- name: Install the Kubo daemon unit
ansible.builtin.template:
src: ipfs.service.j2
dest: /etc/systemd/system/ipfs.service
owner: root
group: root
mode: "0644"
notify:
- Reload systemd
- Restart ipfs
- name: Install the release-pinning + IPNS-rebroadcast services + timers
ansible.builtin.template:
src: "{{ item.src }}"
dest: "/etc/systemd/system/{{ item.dest }}"
owner: root
group: root
mode: "0644"
loop:
- { src: "morphit-ipfs-pin.service.j2", dest: "morphit-ipfs-pin.service" }
- { src: "morphit-ipfs-pin.timer.j2", dest: "morphit-ipfs-pin.timer" }
- { src: "morphit-ipns-rebroadcast.service.j2", dest: "morphit-ipns-rebroadcast.service" }
- { src: "morphit-ipns-rebroadcast.timer.j2", dest: "morphit-ipns-rebroadcast.timer" }
notify: Reload systemd
- name: Flush handlers so the daemon is running before we enable the timer
ansible.builtin.meta: flush_handlers
- name: Enable + start the Kubo daemon (best-effort — enabled always, started if it can)
ansible.builtin.systemd:
name: ipfs
state: started
enabled: true
# Same rationale as the handler: keep it ENABLED so it persists + retries, but
# don't let an offline-Phase-1 box (no network yet) fail the whole install here.
register: morphit_ipfs_started
failed_when: false
- name: Note that IPFS release-hosting is deferred if the daemon isn't up yet
ansible.builtin.debug:
msg: >-
The IPFS (Kubo) daemon didn't start just now — this is expected on an
offline/first-boot box. It's enabled with Restart=on-failure, so it comes up
once this machine has network (or on the next reboot); `sudo systemctl status
ipfs` shows why if it's still down after you're online.
when: morphit_ipfs_started is failed or (morphit_ipfs_started.status is defined and morphit_ipfs_started.status.ActiveState | default('') != 'active')
- name: Enable + start the release-pinning timer
ansible.builtin.systemd:
name: morphit-ipfs-pin.timer
state: started
enabled: true
- name: Enable + start the IPNS-rebroadcast timer
# Same best-effort posture as the daemon: a fresh offline box may not have the
# DHT reachable yet, but the timer stays enabled and fires once it's online, so
# ipns://<name> gets re-announced from this instance without operator action.
ansible.builtin.systemd:
name: morphit-ipns-rebroadcast.timer
state: started
enabled: true
failed_when: false