Post-Tyranny-Tech-Infrastru.../scripts
Pieter 071ed083f7 feat: Implement per-client SSH key isolation
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>
2026-01-17 19:50:30 +01:00
..
deploy-client.sh feat: Implement per-client SSH key isolation 2026-01-17 19:50:30 +01:00
destroy-client.sh fix: Resolve Authentik email delivery issues 2026-01-13 09:52:23 +01:00
generate-client-keys.sh feat: Implement per-client SSH key isolation 2026-01-17 19:50:30 +01:00
README.md feat: Complete Authentik SSO integration with automated OIDC setup 2026-01-08 16:56:19 +01:00
rebuild-client.sh feat: Complete Authentik SSO integration with automated OIDC setup 2026-01-08 16:56:19 +01:00

Management Scripts

Automated scripts for managing client infrastructure.

Prerequisites

Set required environment variables:

export HCLOUD_TOKEN="your-hetzner-cloud-api-token"
export SOPS_AGE_KEY_FILE="./keys/age-key.txt"

Scripts

1. Deploy Fresh Client

Purpose: Deploy a brand new client from scratch

Usage:

./scripts/deploy-client.sh <client_name>

What it does:

  1. Provisions VPS server (if not exists)
  2. Sets up base system (Docker, Traefik)
  3. Deploys Authentik + Nextcloud
  4. Configures SSO integration automatically

Time: ~10-15 minutes

Example:

./scripts/deploy-client.sh test

Requirements:

  • Secrets file must exist: secrets/clients/<client_name>.sops.yaml
  • Client must be defined in tofu/terraform.tfvars

2. Rebuild Client

Purpose: Destroy and recreate a client's infrastructure from scratch

Usage:

./scripts/rebuild-client.sh <client_name>

What it does:

  1. Destroys existing infrastructure (asks for confirmation)
  2. Provisions new VPS server
  3. Sets up base system
  4. Deploys applications
  5. Configures SSO

Time: ~10-15 minutes

Example:

./scripts/rebuild-client.sh test

Warning: This is destructive - all data on the server will be lost!


3. Destroy Client

Purpose: Completely remove a client's infrastructure

Usage:

./scripts/destroy-client.sh <client_name>

What it does:

  1. Stops and removes all Docker containers
  2. Removes all Docker volumes
  3. Destroys VPS server via OpenTofu
  4. Removes DNS records

Time: ~2-3 minutes

Example:

./scripts/destroy-client.sh test

Warning: This is destructive and irreversible! All data will be lost.

Note: Secrets file is preserved after destruction.


Workflow Examples

Deploy a New Client

# 1. Create secrets file
cp secrets/clients/test.sops.yaml secrets/clients/newclient.sops.yaml
sops secrets/clients/newclient.sops.yaml
# Edit: client_name, domains, regenerate passwords

# 2. Add to terraform.tfvars
vim tofu/terraform.tfvars
# Add client definition

# 3. Deploy
./scripts/deploy-client.sh newclient

Test Changes (Rebuild)

# Make changes to Ansible roles/playbooks

# Test by rebuilding
./scripts/rebuild-client.sh test

# Verify changes worked

Clean Up

# Remove test infrastructure
./scripts/destroy-client.sh test

Script Output

All scripts provide:

  • ✓ Colored output (green = success, yellow = warning, red = error)
  • Progress indicators for each step
  • Total time taken
  • Service URLs and credentials
  • Next steps guidance

Error Handling

Scripts will exit if:

  • Required environment variables not set
  • Secrets file doesn't exist
  • Confirmation not provided (for destructive operations)
  • Any command fails (set -e)

Safety Features

Destroy Script

  • Requires typing client name to confirm
  • Shows what will be deleted
  • Preserves secrets file

Rebuild Script

  • Asks for confirmation before destroying
  • 10-second delay after destroy before rebuilding
  • Shows existing infrastructure before proceeding

Deploy Script

  • Checks for existing infrastructure
  • Skips provisioning if server exists
  • Validates secrets file exists

Integration with CI/CD

These scripts can be used in automation:

# Non-interactive deployment
export HCLOUD_TOKEN="..."
export SOPS_AGE_KEY_FILE="..."

./scripts/deploy-client.sh production

For rebuild (skip confirmation):

# Modify rebuild-client.sh to accept --yes flag
./scripts/rebuild-client.sh production --yes

Troubleshooting

Script fails with "HCLOUD_TOKEN not set"

export HCLOUD_TOKEN="your-token-here"

Script fails with "Secrets file not found"

Create the secrets file:

cp secrets/clients/test.sops.yaml secrets/clients/<client>.sops.yaml
sops secrets/clients/<client>.sops.yaml

Server not reachable during destroy

This is normal if server is already destroyed. The script will skip Docker cleanup and proceed to OpenTofu destroy.

OpenTofu state conflicts

If multiple people are managing infrastructure:

cd tofu
tofu state pull
tofu state push

Consider using remote state (S3, Terraform Cloud, etc.)

Performance

Typical timings:

Operation Time
Deploy fresh 10-15 min
Rebuild 10-15 min
Destroy 2-3 min

Breakdown:

  • Infrastructure provisioning: 2 min
  • Server initialization: 1 min
  • Base system setup: 3 min
  • Application deployment: 5-7 min

See Also