feat(base): NetBird agent enrollment concern (mesh)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
sjat 2026-06-17 16:04:46 +02:00
parent 98eb09d8ba
commit 44c4978b5f
4 changed files with 111 additions and 0 deletions

View file

@ -27,3 +27,30 @@ render + validate without applying (used by Molecule).
- `make test ROLE=base` — Molecule renders + `nft -c` syntax-checks (never applies; it
shares the host kernel). Enforcement + the apply/rollback path are verified at ADR-008
Level 2 on staging VMs.
## Mesh enrollment (NetBird agent)
Enrols the host as a NetBird *agent* on the self-hosted mesh (ADR-016): installs the
pinned `netbird` daemon from the upstream APT repo (keyring in `/etc/apt/keyrings`,
mirroring the `docker_host` repo idiom) and runs `netbird up` against the coordinator
with a setup key. Tagged `mesh`.
**Additive only — this concern makes no firewall change.** SSH is already gated to the
NetBird overlay interface by the `firewall` concern (`base__firewall_mgmt_interface`,
default `wt0`); enrolling a host simply brings that interface up. No port is opened here.
Enrolment is **opt-in**: `base__mesh_enabled` defaults to `false`, so applying `base` to
a host not on the mesh is a no-op for this concern. Re-enrolment is guarded on
`netbird status` reporting `Management: Connected`, so re-runs are idempotent. The setup
key is sourced from `vault.netbird.setup_key` and passed with `no_log` (it lands on the
process argv).
### Variables
| Variable | Default | Purpose |
|------------------------------|--------------------------------------|---------|
| `base__mesh_enabled` | `false` | Opt-in switch — include the concern at all. Set per-host/group to enrol. |
| `base__mesh_manage` | `true` | Test gate — when `false`, skips the live network/daemon tasks (apt install, status check, `netbird up`) so Molecule can exercise the wiring without a coordinator. |
| `base__mesh_management_url` | `https://netbird.askari.wingu.me` | Coordinator (management) URL. |
| `base__mesh_setup_key` | `{{ vault.netbird.setup_key }}` | Enrolment setup key, from vault. |
| `base__mesh_version` | `"0.72.4"` | Pinned agent version (matches the coordinator). The exact apt version string is confirmed on-host at deploy. |

View file

@ -20,3 +20,13 @@ base__fail2ban_bantime: 1h
base__fail2ban_findtime: 10m
# base__ssh_authorised_keys lives in group_vars/all/vars.yml (per-person control keys).
base__ssh_authorised_keys: []
# NetBird mesh agent enrollment (ADR-016). Opt-in: default off so applying `base` to a
# host not on the mesh is a no-op for this concern. The live actions (apt install over
# the network, `netbird up` against the coordinator) are additionally gated by
# base__mesh_manage so Molecule can exercise the wiring without a coordinator.
base__mesh_enabled: false
base__mesh_manage: true
base__mesh_management_url: "https://netbird.askari.wingu.me"
base__mesh_setup_key: "{{ vault.netbird.setup_key }}"
base__mesh_version: "0.72.4" # match the coordinator; exact apt pin confirmed on-host at deploy

View file

@ -22,3 +22,11 @@
apply:
tags: [hardening]
tags: [hardening]
- name: NetBird mesh enrollment
ansible.builtin.include_tasks:
file: mesh.yml
apply:
tags: [mesh]
when: base__mesh_enabled | bool
tags: [mesh]

66
roles/base/tasks/mesh.yml Normal file
View file

@ -0,0 +1,66 @@
---
# NetBird agent enrollment (ADR-016). Additive only — no firewall change here.
- name: Install NetBird apt prerequisites
ansible.builtin.apt:
name: [ca-certificates, curl, gnupg]
state: present
update_cache: true
when: base__mesh_manage | bool
tags: [mesh]
- name: Ensure /etc/apt/keyrings exists
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
when: base__mesh_manage | bool
tags: [mesh]
- name: Add the NetBird APT GPG key
ansible.builtin.get_url:
url: https://pkgs.netbird.io/debian/public.key
dest: /etc/apt/keyrings/netbird.asc
mode: "0644"
when: base__mesh_manage | bool
tags: [mesh]
- name: Add the NetBird APT repository
ansible.builtin.apt_repository:
repo: >-
deb [signed-by=/etc/apt/keyrings/netbird.asc]
https://pkgs.netbird.io/debian stable main
filename: netbird
state: present
when: base__mesh_manage | bool
tags: [mesh]
# The apt pin string can't be confirmed from docs — it might be a bare "0.72.4" or
# carry a packaging suffix. The live deploy task confirms the exact on-host string.
- name: Install the NetBird agent (pinned)
ansible.builtin.apt:
name: "netbird={{ base__mesh_version }}"
state: present
update_cache: true
when: base__mesh_manage | bool
tags: [mesh]
- name: Check current NetBird connection status
ansible.builtin.command: netbird status
register: _netbird_status
changed_when: false
failed_when: false
when: base__mesh_manage | bool
tags: [mesh]
- name: Enrol this host in the mesh
ansible.builtin.command: >-
netbird up
--management-url {{ base__mesh_management_url }}
--setup-key {{ base__mesh_setup_key }}
register: _netbird_up
changed_when: _netbird_up.rc == 0
when:
- base__mesh_manage | bool
- "'Management: Connected' not in (_netbird_status.stdout | default(''))"
no_log: true # setup key is on the argv
tags: [mesh]