feat(tf/offsite): retire askari's WAN :22 (mesh-only SSH)

The Hetzner Cloud Firewall SSH rule is now conditional on a non-empty
ssh_admin_cidrs (default []); askari sets it empty so the WAN :22 rule is
removed on the next apply. SSH is reached over wt0; break-glass is the Hetzner
console. Apply is the live cutover (Task 5). Mesh-hardening 1/3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
sjat 2026-06-17 20:51:24 +02:00
parent cc21344ab1
commit b0511179cb
3 changed files with 14 additions and 8 deletions

View file

@ -11,7 +11,7 @@ module "askari" {
location = "hel1" # Helsinki
image = "debian-13"
ansible_ssh_pubkey = var.ansible_ssh_pubkey
ssh_admin_cidrs = var.ssh_admin_cidrs
ssh_admin_cidrs = [] # mesh-only: SSH is reached over wt0; WAN :22 retired (mesh-hardening 1/3)
public_web = true # Caddy 80/443 + NetBird 3478 (M4)
labels = {
env = "offsite"

View file

@ -26,12 +26,17 @@ resource "hcloud_ssh_key" "ansible" {
resource "hcloud_firewall" "this" {
name = "${var.name}-fw"
# SSH from the control node only.
rule {
direction = "in"
protocol = "tcp"
port = "22"
source_ips = var.ssh_admin_cidrs
# SSH from the control node only and only when admin CIDRs are set. An empty
# ssh_admin_cidrs removes the WAN :22 rule entirely (mesh-only SSH; reach the host over
# wt0, break-glass = Hetzner console). Mesh-hardening 1/3.
dynamic "rule" {
for_each = length(var.ssh_admin_cidrs) > 0 ? [1] : []
content {
direction = "in"
protocol = "tcp"
port = "22"
source_ips = var.ssh_admin_cidrs
}
}
# Public web (Caddy 80/443) + NetBird STUN/TURN (3478/udp) only when public_web

View file

@ -24,8 +24,9 @@ variable "ansible_ssh_pubkey" {
}
variable "ssh_admin_cidrs" {
description = "Source CIDRs allowed to reach SSH (e.g. ubongo's address/32)"
description = "Source CIDRs allowed to reach SSH over the WAN. Empty = no WAN SSH rule (mesh-only)."
type = list(string)
default = []
}
variable "public_web" {