Resolves #14 Each client now gets a dedicated SSH key pair, ensuring that compromise of one client server does not grant access to other client servers. ## Changes ### Infrastructure (OpenTofu) - Replace shared `hcloud_ssh_key.default` with per-client `hcloud_ssh_key.client` - Each client key read from `keys/ssh/<client_name>.pub` - Server recreated with new key (dev server only, acceptable downtime) ### Key Management - Created `keys/ssh/` directory for SSH keys - Added `.gitignore` to protect private keys from git - Generated ED25519 key pair for dev client - Private key gitignored, public key committed ### Scripts - **`scripts/generate-client-keys.sh`** - Generate SSH key pairs for clients - Updated `scripts/deploy-client.sh` to check for client SSH key ### Documentation - **`docs/ssh-key-management.md`** - Complete SSH key management guide - **`keys/ssh/README.md`** - Quick reference for SSH keys directory ### Configuration - Removed `ssh_public_key` variable from `variables.tf` - Updated `terraform.tfvars` to remove shared SSH key reference - Updated `terraform.tfvars.example` with new key generation instructions ## Security Improvements ✅ Client isolation: Each client has dedicated SSH key ✅ Granular rotation: Rotate keys per-client without affecting others ✅ Defense in depth: Minimize blast radius of key compromise ✅ Proper key storage: Private keys gitignored, backups documented ## Testing - ✅ Generated new SSH key for dev client - ✅ Applied OpenTofu changes (server recreated) - ✅ Tested SSH access: `ssh -i keys/ssh/dev root@78.47.191.38` - ✅ Verified key isolation: Old shared key removed from Hetzner ## Migration Notes For existing clients: 1. Generate key: `./scripts/generate-client-keys.sh <client>` 2. Apply OpenTofu: `cd tofu && tofu apply` (will recreate server) 3. Deploy: `./scripts/deploy-client.sh <client>` For new clients: 1. Generate key first 2. Deploy as normal 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
87 lines
1.8 KiB
HCL
87 lines
1.8 KiB
HCL
# Provider Configuration
|
|
provider "hcloud" {
|
|
token = var.hcloud_token
|
|
}
|
|
|
|
# hcloud provider handles both Cloud and DNS resources
|
|
|
|
# Per-Client SSH Keys
|
|
resource "hcloud_ssh_key" "client" {
|
|
for_each = var.clients
|
|
name = "client-${each.key}-deploy-key"
|
|
public_key = file("${path.module}/../keys/ssh/${each.key}.pub")
|
|
}
|
|
|
|
# Firewall Rules
|
|
resource "hcloud_firewall" "client_firewall" {
|
|
name = "client-default-firewall"
|
|
|
|
# SSH (restricted - add your management IPs here)
|
|
rule {
|
|
direction = "in"
|
|
protocol = "tcp"
|
|
port = "22"
|
|
source_ips = [
|
|
"0.0.0.0/0", # CHANGE THIS: Replace with your management IP
|
|
"::/0"
|
|
]
|
|
}
|
|
|
|
# HTTP (for Let's Encrypt challenge)
|
|
rule {
|
|
direction = "in"
|
|
protocol = "tcp"
|
|
port = "80"
|
|
source_ips = [
|
|
"0.0.0.0/0",
|
|
"::/0"
|
|
]
|
|
}
|
|
|
|
# HTTPS
|
|
rule {
|
|
direction = "in"
|
|
protocol = "tcp"
|
|
port = "443"
|
|
source_ips = [
|
|
"0.0.0.0/0",
|
|
"::/0"
|
|
]
|
|
}
|
|
}
|
|
|
|
# Client VPS Instances
|
|
resource "hcloud_server" "client" {
|
|
for_each = var.clients
|
|
|
|
name = each.key
|
|
server_type = each.value.server_type
|
|
image = "ubuntu-24.04"
|
|
location = each.value.location
|
|
ssh_keys = [hcloud_ssh_key.client[each.key].id]
|
|
firewall_ids = [hcloud_firewall.client_firewall.id]
|
|
|
|
labels = {
|
|
client = each.key
|
|
role = "app-server"
|
|
# Note: labels can't contain special chars, store apps list separately if needed
|
|
}
|
|
|
|
# Enable backups if requested
|
|
backups = var.enable_snapshots
|
|
|
|
# User data for initial setup
|
|
user_data = <<-EOF
|
|
#cloud-config
|
|
package_update: true
|
|
package_upgrade: true
|
|
packages:
|
|
- curl
|
|
- wget
|
|
- git
|
|
- python3
|
|
- python3-pip
|
|
runcmd:
|
|
- hostnamectl set-hostname ${each.key}
|
|
EOF
|
|
}
|