From 783c8cebbdd60bf57eeae51efeb45a5466c9dca8 Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 9 Mar 2026 19:53:22 +0100 Subject: [PATCH] feat(boundary): remove OS-hardening overlap; add k3s baseline workplan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per ADR-002 (railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md): - ansible/harden.yml: replaced with tombstone pointing to railiance-hosts - ansible/bootstrap.yml: remove `import_playbook: harden.yml`; add pre-condition comment; OS hardening is no longer this repo's concern - docs/first_host.md: rewritten to reflect 3-step flow: converge railiance-hosts → railiance-bootstrap k3s install → smoke test - workplans/RAIL-BS-WP-0002-k3s-baseline.md: new workplan for k3s + Helm + Kubernetes platform baseline; linked to repo goal 70ab2379 Co-Authored-By: Claude Sonnet 4.6 --- ansible/bootstrap.yml | 7 +- ansible/harden.yml | 138 ++---------------- docs/first_host.md | 45 ++++-- workplans/RAIL-BS-WP-0002-k3s-baseline.md | 169 ++++++++++++++++++++++ 4 files changed, 219 insertions(+), 140 deletions(-) create mode 100644 workplans/RAIL-BS-WP-0002-k3s-baseline.md diff --git a/ansible/bootstrap.yml b/ansible/bootstrap.yml index e0d2101..0a236b4 100644 --- a/ansible/bootstrap.yml +++ b/ansible/bootstrap.yml @@ -1,8 +1,9 @@ --- -# Stage 1: Harden the server before anything else is installed. -- import_playbook: harden.yml +# Pre-condition: the target host must already be converged by railiance-hosts +# (`make converge` in that repo) before running this playbook. +# OS hardening (SSH, UFW, fail2ban) is owned by railiance-hosts — see ADR-002. -# Stage 2: Install base packages and k3s. +# Install base packages and k3s. - name: Railiance host bootstrap hosts: all become: true diff --git a/ansible/harden.yml b/ansible/harden.yml index 917d4f6..e4bb7f5 100644 --- a/ansible/harden.yml +++ b/ansible/harden.yml @@ -1,124 +1,16 @@ --- -- name: Server hardening - hosts: all - become: true - - vars: - ssh_port: 22 - k3s_api_port: 6443 - flannel_vxlan_port: 8472 - - tasks: - # ── SSH hardening ──────────────────────────────────────────────────────── - - - name: Disable root SSH login - lineinfile: - path: /etc/ssh/sshd_config - regexp: '^#?PermitRootLogin' - line: 'PermitRootLogin no' - state: present - notify: Restart sshd - - - name: Disable password authentication - lineinfile: - path: /etc/ssh/sshd_config - regexp: '^#?PasswordAuthentication' - line: 'PasswordAuthentication no' - state: present - notify: Restart sshd - - - name: Disable challenge-response authentication - lineinfile: - path: /etc/ssh/sshd_config - regexp: '^#?ChallengeResponseAuthentication' - line: 'ChallengeResponseAuthentication no' - state: present - notify: Restart sshd - - # ── UFW firewall ───────────────────────────────────────────────────────── - - - name: Install ufw - apt: - name: ufw - state: present - update_cache: yes - - - name: Set UFW default inbound policy to deny - ufw: - default: deny - direction: incoming - - - name: Set UFW default outbound policy to allow - ufw: - default: allow - direction: outgoing - - - name: Allow SSH - ufw: - rule: allow - port: "{{ ssh_port }}" - proto: tcp - - - name: Allow k3s API server - ufw: - rule: allow - port: "{{ k3s_api_port }}" - proto: tcp - - - name: Allow Flannel VXLAN (inter-node) - ufw: - rule: allow - port: "{{ flannel_vxlan_port }}" - proto: udp - - - name: Enable UFW - ufw: - state: enabled - - # ── fail2ban ───────────────────────────────────────────────────────────── - - - name: Install fail2ban - apt: - name: fail2ban - state: present - - - name: Configure fail2ban SSH jail - copy: - dest: /etc/fail2ban/jail.d/sshd.conf - content: | - [sshd] - enabled = true - port = {{ ssh_port }} - maxretry = 5 - bantime = 3600 - findtime = 600 - mode: '0644' - notify: Restart fail2ban - - - name: Enable and start fail2ban - systemd: - name: fail2ban - enabled: true - state: started - - # ── Shell hygiene ───────────────────────────────────────────────────────── - - - name: Set HISTCONTROL to suppress space-prefixed commands from history - copy: - dest: /etc/profile.d/histcontrol.sh - content: | - # Commands prefixed with a space are not recorded in shell history. - # Use this when typing secrets interactively. - export HISTCONTROL=ignorespace - mode: '0644' - - handlers: - - name: Restart sshd - systemd: - name: sshd - state: restarted - - - name: Restart fail2ban - systemd: - name: fail2ban - state: restarted +# SUPERSEDED — do not use for new work. +# +# OS security hardening (SSH, UFW, fail2ban, HISTCONTROL) is now owned by +# the railiance-hosts repository: +# +# railiance-hosts/ansible/roles/base/ +# railiance-hosts/spec/server-baseline.yaml ← authoritative spec +# railiance-hosts/goss/baseline.yaml ← automated assertions +# +# Run `make converge` in railiance-hosts before deploying anything from +# this repo. See ADR-002 for the full boundary definition: +# railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md +# +# This file is retained to avoid breaking git history references. +# It must not be imported or executed. diff --git a/docs/first_host.md b/docs/first_host.md index 1e5838a..8caed08 100644 --- a/docs/first_host.md +++ b/docs/first_host.md @@ -1,22 +1,39 @@ -# First Railiance Host — Provider Guide +# First Railiance Host — Setup Guide -## 1) Generate SSH key -``` -bin/railiance gen-ssh-key +## Prerequisites + +The target server must be a converged `railiance-hosts` node before running +anything from this repo. The OS baseline (SSH hardening, UFW, fail2ban, SOPS +agent) is owned by `railiance-hosts`. + +**Step 0 — Converge the host OS (railiance-hosts)** +```bash +# In railiance-hosts/ +make converge # apply OS baseline roles +make verify # confirm all Goss assertions pass ``` -## 2) Choose a VM -Ubuntu 24.04 LTS, 2 vCPU, 4–8GB RAM, 60+GB SSD, open 22/80/443. +See `railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md` +for the boundary definition between the two repos. -## 3) Cloud-init -``` -bin/railiance cloudinit > user-data.yaml +--- + +## Kubernetes bootstrap (this repo) + +### 1) Inventory + +Add the host to `ansible/hosts.ini` (gitignored — recreate on each machine): +```ini +[hosteurope] +92.205.62.239 ansible_user=tegwick ``` -## 4) Seed -Copy Spore or clone directly, then run seed script on the host. +### 2) Install k3s +```bash +ansible-playbook -i ansible/hosts.ini ansible/bootstrap.yml +``` -## 5) Bootstrap (optional now) -``` -ansible-playbook -i ansible/inventory/hosts.ini ansible/bootstrap.yml +### 3) Smoke test +```bash +tests/smoke_kube.sh ``` diff --git a/workplans/RAIL-BS-WP-0002-k3s-baseline.md b/workplans/RAIL-BS-WP-0002-k3s-baseline.md new file mode 100644 index 0000000..84050f7 --- /dev/null +++ b/workplans/RAIL-BS-WP-0002-k3s-baseline.md @@ -0,0 +1,169 @@ +--- +id: RAIL-BS-WP-0002 +type: workplan +title: "k3s and Kubernetes Platform Baseline" +domain: railiance +repo: railiance-bootstrap +status: active +owner: railiance +topic_slug: railiance +repo_goal_id: "70ab2379-fb9d-4fec-a09d-b2a717e4ace8" +state_hub_workstream_id: "4c63dfc6-9eac-4e79-9f77-8f644ad7147d" +created: "2026-03-09" +updated: "2026-03-09" +--- + +# k3s and Kubernetes Platform Baseline + +## Goal + +Install k3s, Helm, and the baseline Kubernetes services on the converged +HostEurope node. This workplan picks up exactly where `railiance-hosts` +leaves off: a hardened, verified OS node that is ready for Kubernetes. + +## Pre-condition + +`railiance-hosts` converge + Goss verify must pass before any task here +is executed: + +```bash +# In railiance-hosts/ +make converge +make verify # must exit 0 +``` + +## Boundary + +This repo owns everything from k3s upward. It must not re-configure items +defined in `railiance-hosts/spec/server-baseline.yaml`. See +`railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md`. + +**Out of scope here:** application-layer deployments (Gitea, monitoring +stack, user-facing services). Those belong in `railiance-apps` once that +repo is established (decision pending). + +--- + +## Tasks + +### T01 — Ansible playbook: install k3s (server mode) + +```task +id: T01 +status: todo +priority: high +state_hub_task_id: "3f042630-eab0-4c6a-9167-e2b28ff20e40" +``` + +Harden `ansible/bootstrap.yml` to a production-ready k3s install: + +- Use the official k3s install script pinned to a specific version + (`INSTALL_K3S_VERSION=vX.Y.Z+k3s1`) +- `INSTALL_K3S_EXEC="server --cluster-init --write-kubeconfig-mode=644"` + (cluster-init enables embedded etcd for future HA expansion) +- Wait for node `Ready` before proceeding: + ```bash + k3s kubectl wait node --all --for=condition=Ready --timeout=120s + ``` +- Fetch kubeconfig to the control node as `~/.kube/config-hosteurope` + +**Done when:** `k3s kubectl get nodes` returns `Ready` from both the server +and the control node (via kubeconfig). + +--- + +### T02 — Helm installation + +```task +id: T02 +status: todo +priority: high +state_hub_task_id: "e8510646-46ed-4697-a345-f3d3009eea78" +``` + +Add a task (or a role `roles/helm/`) that: + +1. Downloads the Helm binary (pinned version) to `/usr/local/bin/helm` +2. Verifies the checksum +3. Confirms `helm version` succeeds + +**Done when:** `helm version` succeeds on the HostEurope node. + +--- + +### T03 — Smoke test: k3s + Helm + +```task +id: T03 +status: todo +priority: high +state_hub_task_id: "dab2c07f-8aa0-4635-8df6-857e87e93fc5" +``` + +Extend `tests/smoke_kube.sh` to assert: + +- `k3s kubectl get nodes` → node in Ready state +- `helm version` exits 0 +- CoreDNS pod running in `kube-system` +- Traefik ingress controller pod running (default in k3s) + +Run via: +```bash +ansible-playbook -i ansible/hosts.ini ansible/smoke.yml +``` +or directly over SSH if the kubeconfig is available locally. + +**Done when:** all assertions pass and the script exits 0. + +--- + +### T04 — Commit kubeconfig management notes + +```task +id: T04 +status: todo +priority: medium +state_hub_task_id: "5c3d40e4-239b-488e-9519-6f7a38d2325f" +``` + +Document in `docs/kubeconfig.md`: + +- Where the kubeconfig is fetched to (`~/.kube/config-hosteurope`) +- How to merge it into `~/.kube/config` +- How to switch context: `kubectl config use-context default` +- Security note: kubeconfig is gitignored (contains cluster CA + client cert) + +**Done when:** doc written and committed. + +--- + +### T05 — Add `make k3s-install` and `make smoke` targets + +```task +id: T05 +status: todo +priority: medium +state_hub_task_id: "7f9e0e58-a130-467a-a2d0-b3f2564e496f" +``` + +Add to Makefile (create one if none exists): + +```makefile +k3s-install: ## Install k3s and Helm on all inventory hosts + ansible-playbook -i ansible/hosts.ini ansible/bootstrap.yml + +smoke: ## Run Kubernetes smoke tests + bash tests/smoke_kube.sh +``` + +**Done when:** both targets work and are listed in `make help`. + +--- + +## References + +- Repo goal: `70ab2379-fb9d-4fec-a09d-b2a717e4ace8` (Install k3s and Kubernetes Baseline) +- Domain goal: `6f96c712-60e6-4ea9-ab06-168878eafbce` (Three-Phoenix Secure Kubernetes Infrastructure) +- Pre-condition: railiance-hosts WP-0001 (Secure Single-Server Bootstrap) — completed 2026-03-09 +- Boundary ADR: `railiance-hosts/docs/adr/ADR-002-repo-boundary-hosts-vs-bootstrap.md` +- k3s releases: https://github.com/k3s-io/k3s/releases