--- # description: Provision a fresh host — create the steward account + managed key + passwordless sudo, then install the agent. # steward:category: host-agent # First-contact provisioning: bring a fresh host under Steward management, then # install the host agent — all in one idempotent run. # # Run this ONCE per host with bootstrap credentials (an existing login + sudo, # typically password auth via the "Provision host" button). It: # 1. creates a dedicated `steward` login account, # 2. installs Steward's managed public key into its authorized_keys, # 3. grants it passwordless sudo, # 4. installs/updates the host agent. # After this, every future run (agent updates, maintenance) connects as # `steward` with the managed key — no operator credentials needed. # # Extra-vars (injected by the Provision card): # steward_url, steward_token — agent → Steward auth (per-host) # steward_pubkey — managed public key to authorize # steward_user (default steward), agent_interval (default 30) - name: Provision host for Steward + install agent hosts: all become: true gather_facts: false # steward_token is injected as a per-host inventory var (it differs per host), # so it must NOT be declared here — a play var would outrank the inventory var # and shadow it. The globals below arrive as extra-vars (highest precedence), # which override these defaults. vars: steward_url: "" steward_pubkey: "" steward_user: steward agent_interval: 30 tasks: - name: Require provisioning vars ansible.builtin.assert: that: - steward_url | default('') | length > 0 - steward_token | default('') | length > 0 - steward_pubkey | default('') | length > 0 fail_msg: "Pass steward_url, steward_token and steward_pubkey (Steward injects these)." # ── Managed login account ──────────────────────────────────────────────── - name: Create the steward management account ansible.builtin.user: name: "{{ steward_user }}" shell: /bin/sh create_home: true state: present - name: Ensure ~/.ssh exists ansible.builtin.file: path: "/home/{{ steward_user }}/.ssh" state: directory owner: "{{ steward_user }}" group: "{{ steward_user }}" mode: "0700" - name: Authorize the managed public key (replace any prior steward-managed key) ansible.builtin.lineinfile: path: "/home/{{ steward_user }}/.ssh/authorized_keys" # Match on the ' steward-managed' comment so a rotated key REPLACES the # old one in place rather than stacking a second authorized key. Any # keys the operator added by hand (different comment) are left untouched. regexp: ' steward-managed$' line: "{{ steward_pubkey }}" create: true owner: "{{ steward_user }}" group: "{{ steward_user }}" mode: "0600" - name: Grant passwordless sudo to the steward account ansible.builtin.copy: dest: "/etc/sudoers.d/steward" content: "{{ steward_user }} ALL=(ALL) NOPASSWD:ALL\n" owner: root group: root mode: "0440" validate: "visudo -cf %s" # ── Host agent (same as install.yml) ───────────────────────────────────── - name: Create steward-agent system user ansible.builtin.user: name: steward-agent system: true shell: /usr/sbin/nologin create_home: false # Let the agent read the local Docker socket so it can report this host's # containers (the per-host docker collection path). Best-effort: skipped on # hosts without Docker. Restart picks up the new supplementary group. - name: Detect docker group ansible.builtin.command: getent group docker register: _docker_grp changed_when: false failed_when: false - name: Add steward-agent to the docker group ansible.builtin.user: name: steward-agent groups: docker append: true when: _docker_grp.rc == 0 notify: restart steward-agent - name: Create agent directory ansible.builtin.file: path: /usr/local/lib/steward-agent state: directory mode: "0755" - name: Download agent.py ansible.builtin.get_url: url: "{{ steward_url }}/plugins/host_agent/agent.py" dest: /usr/local/lib/steward-agent/agent.py mode: "0755" force: true notify: restart steward-agent - name: Write agent config ansible.builtin.copy: dest: /etc/steward-agent.conf owner: root group: steward-agent mode: "0640" content: | url = {{ steward_url }} token = {{ steward_token }} interval_seconds = {{ agent_interval }} notify: restart steward-agent - name: Install systemd unit ansible.builtin.copy: dest: /etc/systemd/system/steward-agent.service mode: "0644" content: | [Unit] Description=Steward host agent After=network-online.target Wants=network-online.target [Service] Type=simple User=steward-agent Environment=STEWARD_AGENT_CONFIG=/etc/steward-agent.conf ExecStart=/usr/bin/env python3 /usr/local/lib/steward-agent/agent.py Restart=on-failure RestartSec=5 NoNewPrivileges=true ProtectSystem=strict ProtectHome=true PrivateTmp=true [Install] WantedBy=multi-user.target notify: restart steward-agent - name: Enable and start the agent ansible.builtin.systemd: name: steward-agent enabled: true state: started daemon_reload: true handlers: - name: restart steward-agent ansible.builtin.systemd: name: steward-agent state: restarted daemon_reload: true