Files
railiance-infra/workplans/RAIL-HO-WP-0001-hosteurope-bootstrap.md
tegwick 703c57d91c chore(rename): railiance-hosts → railiance-infra
Update all operational references to reflect the new repo name per
ADR-003 (OAS S1 Infrastructure Substrate). Historical text in ADRs
and state-hub-inbox files preserved as-is. Gitea remote URL updated
locally (Gitea repo rename is a manual step).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 00:34:18 +01:00

222 lines
6.0 KiB
Markdown

---
id: RAIL-HO-WP-0001
type: workplan
title: "Secure Single-Server Bootstrap at HostEurope"
domain: railiance
repo: railiance-infra
status: completed
owner: railiance
topic_slug: railiance
repo_goal_id: 9e835b82-acca-493a-943f-2553ffe0bf54
state_hub_workstream_id: "bf40b47e-be5b-4930-a7d2-362e76b943bb"
created: "2026-03-08"
updated: "2026-03-09"
completed: "2026-03-09"
handoff_note: >
T01 and T02 (inventory entry and SSH tunnel setup) are prerequisites to run
T03 onwards from the HostEurope server itself. The ansible work previously
done in railiance-bootstrap (harden.yml, bootstrap.yml) is superseded by
the roles/base and roles/sops_agent structure in this repo. The HostEurope
server IP is 92.205.62.239 (hosts.ini is gitignored — recreate from
inventory/servers.yaml when working on the host).
---
# Secure Single-Server Bootstrap at HostEurope
## Goal
Provision and converge the HostEurope server so that it becomes a secure,
hardened node ready to join the ThreePhoenix Kubernetes cluster. This workplan
covers everything from establishing SSH access and tunnel connectivity through a
fully converged, verified server — all secrets managed via SOPS/age, all
services secured from the start.
Scope is deliberately narrow: one server, secure from day one. Automated
provisioning of additional server resources and the full three-node setup
are deferred.
## Boundary conditions
- Ubuntu 24.04 LTS at HostEurope (92.205.62.239), manually provisioned
- All remote access key-based only (no password auth)
- Firewall active before k3s is installed
- Secrets managed via SOPS/age — nothing committed in plaintext
- This workplan is **run from the HostEurope server** via SSH, not from WSL
## Remote execution setup
This repo is intended to run from the HostEurope server itself. To retain
State Hub connectivity during Claude sessions on that server, set up an SSH
reverse tunnel from your local machine before connecting:
```bash
# On your local machine — forward local state-hub to the remote
ssh -R 8000:127.0.0.1:8000 <user>@92.205.62.239
```
Then Claude on the remote host can reach `http://127.0.0.1:8000` as normal.
See also: the tunnel setup note in `CLAUDE.md`.
---
## Tasks
### T01 — Add HostEurope host to inventory
```task
id: T01
status: done
completed: "2026-03-08"
priority: high
state_hub_task_id: "9b2222a3-0f9f-4543-9321-e4cd5f87a457"
```
Add the HostEurope host to `inventory/servers.yaml` under a `hosteurope` group.
Create `ansible/hosts.ini` from the inventory (gitignored — recreate on each
new working machine):
```bash
python3 ansible/inventory_from_yaml.py > ansible/hosts.ini
# or add manually: [hosteurope]\n92.205.62.239 ansible_user=...
```
Verify connectivity:
```bash
ansible -i ansible/hosts.ini hosteurope -m ping
```
**Done when:** ping succeeds from a control node with network access to the host.
---
### T02 — Set up SSH tunnel for State Hub access
```task
id: T02
status: done
completed: "2026-03-09"
priority: high
state_hub_task_id: "e4dda416-19bc-4672-b9ba-8ddb1b9e9659"
```
On your local machine, establish a reverse tunnel before SSH-ing to the
HostEurope server so the State Hub MCP server is reachable from Claude
sessions on that host:
```bash
make tunnel # reads host from inventory/servers.yaml
```
Or manually:
```bash
ssh -R 8000:127.0.0.1:8000 tegwick@92.205.62.239
```
Verify the tunnel is working from the remote:
```bash
curl http://127.0.0.1:8000/state/health
```
**Done when:** `make tunnel` target implemented; procedure documented.
---
### T03 — Extend base role with security hardening
```task
id: T03
status: done
completed: "2026-03-08"
priority: high
state_hub_task_id: "6eda6875-1301-4794-a07e-3e13ff1d92bf"
```
Extend `ansible/roles/base/tasks/main.yml` to cover security hardening that
must run before any service installation:
- Disable root SSH login (`PermitRootLogin no`)
- Disable password authentication (`PasswordAuthentication no`)
- Enable and configure UFW: deny all inbound, allow SSH (22), k3s API (6443),
Flannel VXLAN (8472/UDP)
- Install and enable `fail2ban` with SSH jail
- Set `HISTCONTROL=ignorespace` in `/etc/profile.d/`
Verify with:
```bash
ansible-playbook -i ansible/hosts.ini -l hosteurope ansible/playbooks/bootstrap.yaml --check
```
**Done when:** dry-run produces no errors and hardening tasks are visible in
the play recap.
---
### T04 — Run bootstrap on the HostEurope host
```task
id: T04
status: done
completed: "2026-03-08"
priority: high
state_hub_task_id: "77921431-3a45-45b2-a0b0-cf0c43262205"
```
Execute the full bootstrap playbook from the HostEurope server (tunnel must
be active per T02):
```bash
ansible-playbook -i ansible/hosts.ini -l hosteurope ansible/playbooks/bootstrap.yaml
```
**Done when:**
- Playbook completes with no failed tasks
- UFW is active and SSH still works after the run
- SOPS agent role applied successfully
---
### T05 — Smoke test and record
```task
id: T05
status: done
completed: "2026-03-08"
priority: medium
state_hub_task_id: "c573c200-bf22-49d1-86f9-dca1fc71743c"
```
Verify the converged state:
```bash
# Confirm UFW active
ansible -i ansible/hosts.ini hosteurope -m shell -a "ufw status"
# Confirm fail2ban running
ansible -i ansible/hosts.ini hosteurope -m shell -a "systemctl is-active fail2ban"
# Confirm SSH hardening applied
ansible -i ansible/hosts.ini hosteurope -m shell -a "sshd -T | grep -E 'permitrootlogin|passwordauthentication'"
```
Add `docs/hosteurope-bootstrap.md` recording:
- Server specs (vCPU, RAM, disk)
- Public IP (no credentials)
- Date bootstrapped
- Roles applied
Log completion to the State Hub:
```
add_progress_event(summary="HostEurope server bootstrapped and hardened",
event_type="milestone", workstream_id="bf40b47e-be5b-4930-a7d2-362e76b943bb")
```
**Done when:** all checks pass and the progress event is logged.
---
## References
- Repo goal: `9e835b82-acca-493a-943f-2553ffe0bf54`
- Domain goal: `6f96c712-60e6-4ea9-ab06-168878eafbce` (Three-Phoenix Secure Kubernetes Infrastructure)
- Previous ansible work: `railiance-bootstrap/ansible/` (harden.yml, bootstrap.yml — superseded by this repo's role structure)