141 lines
4.9 KiB
Markdown
141 lines
4.9 KiB
Markdown
# Ansible control project
|
|
|
|
This directory contains the reproducible control project for `ghostnet-ki`.
|
|
The read-only `playbooks/preflight.yml` verifies the documented host identity
|
|
and hardware. The first writing milestone, `playbooks/host.yml`, applies a
|
|
small shared host baseline through the dedicated deployment account.
|
|
|
|
## Control-node setup
|
|
|
|
Use Python 3.12 through 3.14 and create a virtual environment at the repository
|
|
root:
|
|
|
|
```bash
|
|
python3 -m venv .venv
|
|
.venv/bin/python -m pip install --upgrade pip
|
|
.venv/bin/python -m pip install -r ansible/requirements-control.txt
|
|
```
|
|
|
|
Load the private key for the read-only `ghost-agent` account into an SSH agent.
|
|
Do not store private keys or passphrases in this repository:
|
|
|
|
```bash
|
|
ssh-add /path/to/ghost-agent-private-key
|
|
```
|
|
|
|
The workstation must already trust the verified SSH host key for `10.0.0.90`.
|
|
An unexpected host-key change must be checked through a trusted console before
|
|
continuing.
|
|
|
|
## Read-only preflight
|
|
|
|
Run the commands from the `ansible/` directory:
|
|
|
|
```bash
|
|
cd ansible
|
|
../.venv/bin/ansible-inventory --graph
|
|
../.venv/bin/ansible-playbook playbooks/preflight.yml --syntax-check
|
|
../.venv/bin/ansible-playbook playbooks/preflight.yml
|
|
```
|
|
|
|
The final recap must report `changed=0` and `failed=0`. The playbook verifies:
|
|
|
|
- hostname `ghostnet-ki`;
|
|
- Debian major version 13;
|
|
- default IPv4 address `10.0.0.90`;
|
|
- remote Python availability; and
|
|
- two AMD Navi 21 PCI devices with ID `1002:73bf`.
|
|
|
|
The inventory deliberately contains no private-key path. Each operator supplies
|
|
their authorized key through `ssh-agent` or a local Ansible override.
|
|
|
|
## One-time deployment-account bootstrap
|
|
|
|
Status 2026-09-19: The initial bootstrap has been applied to `ghostnet-ki`, and
|
|
the separate verification playbook confirmed SSH access and non-interactive
|
|
effective UID 0 with `changed=0` and `failed=0`. Run the bootstrap again only
|
|
for an intentional repair, rotation or idempotence check.
|
|
|
|
The bootstrap creates the dedicated `ansible-deploy` account. Its password is
|
|
locked, its committed public key is restricted to the LAN and WireGuard source
|
|
networks, and `/etc/sudoers.d/90-ansible-deploy` is validated with `visudo`
|
|
before installation. The account receives `NOPASSWD` root escalation because
|
|
the planned host, package, systemd and container management cannot be expressed
|
|
as a small stable command allowlist.
|
|
|
|
The matching private key stays outside this repository. On the workstation on
|
|
which the initial key was created, it is stored as:
|
|
|
|
```text
|
|
~/.ssh/id_ed25519_ghostnet_ai_ansible_deploy
|
|
```
|
|
|
|
The automation key has no interactive passphrase so a future runner can use it
|
|
non-interactively. It is protected by filesystem mode `0600`, SSH source-network
|
|
restrictions and the dedicated account. Compromise of this private key would
|
|
nevertheless grant root-equivalent deployment access; rotate it when moving the
|
|
deployment identity to the runner or after any suspected disclosure.
|
|
|
|
Load the existing personal administrator key into an SSH agent, then run the
|
|
bootstrap interactively from the `ansible/` directory. Ansible will ask for the
|
|
personal administrator's existing sudo password:
|
|
|
|
```bash
|
|
ssh-add /path/to/personal-ghost-private-key
|
|
../.venv/bin/ansible-playbook \
|
|
--inventory inventories/bootstrap/hosts.yml \
|
|
playbooks/bootstrap-deploy-account.yml \
|
|
--ask-become-pass
|
|
```
|
|
|
|
Do not send the password through chat, store it in Git, or place it in a shell
|
|
history entry. The bootstrap is idempotent; a second run should report no
|
|
changes.
|
|
|
|
After a successful bootstrap, verify the new account independently:
|
|
|
|
```bash
|
|
../.venv/bin/ansible-playbook \
|
|
playbooks/verify-deploy-access.yml \
|
|
--extra-vars ansible_user=ansible-deploy \
|
|
--private-key ~/.ssh/id_ed25519_ghostnet_ai_ansible_deploy
|
|
```
|
|
|
|
The verification must report `changed=0` and `failed=0`. It confirms both the
|
|
unprivileged SSH identity and non-interactive escalation to effective UID 0.
|
|
|
|
## Shared host baseline
|
|
|
|
The host baseline is common to Production and Testing. It verifies the target
|
|
identity and at least 100 GiB free on the root filesystem, then manages these
|
|
root-owned directory roots:
|
|
|
|
```text
|
|
/etc/ghostnet-ai/{production,testing}
|
|
/var/lib/ghostnet-ai/{production,testing,shared}
|
|
```
|
|
|
|
It does not install software or start services. Use the dedicated host
|
|
inventory and supply the private deployment key outside the repository:
|
|
|
|
```bash
|
|
../.venv/bin/ansible-playbook \
|
|
--inventory inventories/host/hosts.yml \
|
|
playbooks/host.yml \
|
|
--syntax-check
|
|
|
|
../.venv/bin/ansible-playbook \
|
|
--inventory inventories/host/hosts.yml \
|
|
playbooks/host.yml \
|
|
--check --diff \
|
|
--private-key ~/.ssh/id_ed25519_ghostnet_ai_ansible_deploy
|
|
|
|
../.venv/bin/ansible-playbook \
|
|
--inventory inventories/host/hosts.yml \
|
|
playbooks/host.yml \
|
|
--private-key ~/.ssh/id_ed25519_ghostnet_ai_ansible_deploy
|
|
```
|
|
|
|
Run the final command a second time. The second recap must report `changed=0`
|
|
and `failed=0`; otherwise the role has not demonstrated convergence.
|