## template: jinja-free Go text/template — rendered by ## server/internal/provision (CloudInitRenderer). Fields: ## {{.NodeUUID}} {{.BootstrapToken}} {{.Region}} {{.Role}} {{.Tier}} {{.ControlPlaneURL}} ## ## "Nodes are cattle, not pets" (doc/04 §2): this is the ENTIRE persistent ## footprint of a data-plane node — sing-box + agent binaries + a one-time ## bootstrap token. Zero user DB, zero logs, zero persistent state. ## ## SECURITY: the rendered output carries a one-time bootstrap token. It MUST NOT ## be logged. The token is consumed exactly once during mTLS enrollment (task #5) ## and is useless afterwards. #cloud-config write_files: # Node identity + enrollment parameters consumed by the agent on first boot. - path: /etc/pangolin/bootstrap.env permissions: "0600" owner: root:root content: | PANGOLIN_NODE_UUID={{.NodeUUID}} PANGOLIN_BOOTSTRAP_TOKEN={{.BootstrapToken}} PANGOLIN_CONTROL_PLANE_URL={{.ControlPlaneURL}} PANGOLIN_REGION={{.Region}} PANGOLIN_ROLE={{.Role}} PANGOLIN_TIER={{.Tier}} # systemd unit: agent enrolls (mTLS) then pulls runtime config from control plane. - path: /etc/systemd/system/pangolin-agent.service permissions: "0644" owner: root:root content: | [Unit] Description=Pangolin node agent (self-register + config pull) After=network-online.target Wants=network-online.target [Service] Type=simple EnvironmentFile=/etc/pangolin/bootstrap.env ExecStart=/usr/local/bin/pangolin-agent run # No-log node: agent does not persist connection logs. Restart=always RestartSec=5 [Install] WantedBy=multi-user.target runcmd: # 1. Harden: key-only SSH, no root password (doc/06 §3). - sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config - sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config - systemctl restart sshd || true # 2. Install the agent binary (pinned, checksum-verified by the install script, # delivered out-of-band by task #6). Placeholder URL resolved at build time. - install -d -m 0755 /etc/pangolin - /usr/local/bin/pangolin-agent-install || true # 3. Enroll + start. The agent reads bootstrap.env, performs mTLS enrollment, # and the control plane flips the node provisioning → probing → up. - systemctl daemon-reload - systemctl enable --now pangolin-agent.service # 4. Shred the one-time token from disk after enrollment (defence in depth). - bash -c 'sleep 60; shred -u /etc/pangolin/bootstrap.env 2>/dev/null || rm -f /etc/pangolin/bootstrap.env' # No swap file written to disk, no persistent data dirs — a destroyed node leaks nothing.