# Provision a drone on the bench: identity, peer trust, WireGuard, data plane. # Idempotent; never runs over the mission radio (bench network only). --- - name: Provision drone data plane hosts: drones become: true vars: swarm_user: swarm swarm_home: /opt/swarm forced_command: "{{ swarm_home }}/bin/peer-query.sh" tasks: - name: Create swarm service user ansible.builtin.user: name: "{{ swarm_user }}" home: "{{ swarm_home }}" shell: /usr/sbin/nologin system: true # --- 1. Identity ----------------------------------------------------- - name: Generate drone ed25519 identity key community.crypto.openssh_keypair: path: "{{ swarm_home }}/.ssh/id_ed25519" type: ed25519 comment: "{{ inventory_hostname }}" owner: "{{ swarm_user }}" mode: "0600" register: identity - name: Collect fleet public keys ansible.builtin.slurp: src: "{{ swarm_home }}/.ssh/id_ed25519.pub" register: pubkey # --- 2. Peer trust: forced command only ------------------------------ # Every peer may connect, but the only thing it can execute is the # read-only SQL wrapper (docs/04). No shell, no forwarding, no pty. - name: Authorize fleet peers with read-only forced command ansible.posix.authorized_key: user: "{{ swarm_user }}" key: "{{ hostvars[item].pubkey.content | b64decode }}" key_options: >- command="{{ forced_command }}",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty loop: "{{ groups['drones'] | difference([inventory_hostname]) }}" when: hostvars[item].pubkey is defined - name: Install peer query wrapper ansible.builtin.copy: dest: "{{ forced_command }}" mode: "0755" content: | #!/bin/sh # Forced command: read-only DuckDB SQL over the sealed lake. # SSH_ORIGINAL_COMMAND carries the statement; the gate rejects # anything that is not a single read statement. exec {{ swarm_home }}/bin/sql-gate --read-only --data /data "$SSH_ORIGINAL_COMMAND" # --- 3. WireGuard swarm plane ---------------------------------------- - name: Render WireGuard configuration ansible.builtin.template: src: templates/wg0.conf.j2 dest: /etc/wireguard/wg0.conf mode: "0600" notify: Restart wireguard - name: Enable WireGuard interface ansible.builtin.systemd: name: wg-quick@wg0 enabled: true state: started # --- 4. Data plane from the fleet release manifest -------------------- - name: Read fleet release manifest ansible.builtin.include_vars: file: "{{ fleet_manifest }}" name: manifest - name: Lay down Compose bundle for release {{ manifest.release }} ansible.builtin.unarchive: src: "{{ playbook_dir }}/../../.tmp/{{ manifest.artifacts[0].name }}" dest: "{{ swarm_home }}/release" owner: "{{ swarm_user }}" - name: Enable data plane unit ansible.builtin.template: src: templates/swarm-data-plane.service.j2 dest: /etc/systemd/system/swarm-data-plane.service mode: "0644" notify: Restart data plane handlers: - name: Restart wireguard ansible.builtin.systemd: name: wg-quick@wg0 state: restarted - name: Restart data plane ansible.builtin.systemd: name: swarm-data-plane state: restarted daemon_reload: true