feat: Automate SSH key and secrets generation in deployment scripts

Simplify client deployment workflow by automating SSH key generation and
secrets file creation. No more manual preparation steps!

## Changes

### Deploy Script Automation
**`scripts/deploy-client.sh`**:
- Auto-generates SSH key pair if missing (calls generate-client-keys.sh)
- Auto-creates secrets file from template if missing
- Opens SOPS editor for user to customize secrets
- Continues with deployment after setup complete

### Rebuild Script Automation
**`scripts/rebuild-client.sh`**:
- Same automation as deploy script
- Ensures SSH key and secrets exist before rebuild

### Documentation Updates
- **`README.md`** - Updated quick start workflow
- **`scripts/README.md`** - Updated script descriptions and examples

## Workflow: Before vs After

### Before (Manual)
```bash
# 1. Generate SSH key
./scripts/generate-client-keys.sh newclient

# 2. Create secrets file
cp secrets/clients/template.sops.yaml secrets/clients/newclient.sops.yaml
sops secrets/clients/newclient.sops.yaml

# 3. Add to terraform.tfvars
vim tofu/terraform.tfvars

# 4. Deploy
./scripts/deploy-client.sh newclient
```

### After (Automated)
```bash
# 1. Add to terraform.tfvars
vim tofu/terraform.tfvars

# 2. Deploy (everything else is automatic!)
./scripts/deploy-client.sh newclient
# Script automatically:
# - Generates SSH key if missing
# - Creates secrets file from template if missing
# - Opens editor for you to customize
# - Continues with deployment
```

## Benefits

 **Fewer manual steps**: 4 steps → 2 steps
 **Less error-prone**: Can't forget to generate SSH key
 **Better UX**: Script guides you through setup
 **Still flexible**: Can pre-create SSH key/secrets if desired
 **Idempotent**: Won't regenerate if already exists

## Backward Compatible

Existing workflows still work:
- If SSH key already exists, script uses it
- If secrets file already exists, script uses it
- Can still use generate-client-keys.sh manually if preferred

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Pieter 2026-01-17 20:04:29 +01:00
parent 071ed083f7
commit ac4187d041
4 changed files with 156 additions and 52 deletions

View file

@ -42,17 +42,29 @@ infrastructure/
export HCLOUD_TOKEN="your-hetzner-api-token"
export SOPS_AGE_KEY_FILE="./keys/age-key.txt"
# 2. Deploy client (fully automated, ~10-15 minutes)
./scripts/deploy-client.sh <client_name>
# 2. Add client to terraform.tfvars
# clients = {
# newclient = {
# server_type = "cx22"
# location = "fsn1"
# subdomain = "newclient"
# apps = ["authentik", "nextcloud"]
# }
# }
# 3. Deploy client (fully automated, ~10-15 minutes)
./scripts/deploy-client.sh newclient
```
This automatically:
- ✅ Provisions VPS on Hetzner Cloud
- ✅ Deploys Authentik (SSO/identity provider)
- ✅ Deploys Nextcloud (file storage)
- ✅ Configures OAuth2/OIDC integration
- ✅ Sets up SSL certificates
- ✅ Creates admin accounts
The script will automatically:
- ✅ Generate unique SSH key pair (if missing)
- ✅ Create secrets file from template (if missing, opens in editor)
- ✅ Provision VPS on Hetzner Cloud
- ✅ Deploy Authentik (SSO/identity provider)
- ✅ Deploy Nextcloud (file storage)
- ✅ Configure OAuth2/OIDC integration
- ✅ Set up SSL certificates
- ✅ Create admin accounts
**Result**: Fully functional system, ready to use immediately!

View file

@ -22,22 +22,31 @@ export SOPS_AGE_KEY_FILE="./keys/age-key.txt"
./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
**What it does** (automatically):
1. **Generates SSH key** (if missing) - Unique per-client key pair
2. **Creates secrets file** (if missing) - From template, opens in editor
3. Provisions VPS server (if not exists)
4. Sets up base system (Docker, Traefik)
5. Deploys Authentik + Nextcloud
6. Configures SSO integration automatically
**Time**: ~10-15 minutes
**Example**:
```bash
./scripts/deploy-client.sh test
# Just run the script - it handles everything!
./scripts/deploy-client.sh newclient
# Script will:
# 1. Generate keys/ssh/newclient + keys/ssh/newclient.pub
# 2. Copy secrets/clients/template.sops.yaml → secrets/clients/newclient.sops.yaml
# 3. Open SOPS editor for you to customize secrets
# 4. Continue with deployment
```
**Requirements**:
- Secrets file must exist: `secrets/clients/<client_name>.sops.yaml`
- Client must be defined in `tofu/terraform.tfvars`
- Environment variables: `HCLOUD_TOKEN`, `SOPS_AGE_KEY_FILE` (optional)
---
@ -98,20 +107,26 @@ export SOPS_AGE_KEY_FILE="./keys/age-key.txt"
## Workflow Examples
### Deploy a New Client
### Deploy a New Client (Fully Automated)
```bash
# 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
# 1. Add to terraform.tfvars
vim tofu/terraform.tfvars
# Add client definition
# Add:
# newclient = {
# server_type = "cx22"
# location = "fsn1"
# subdomain = "newclient"
# apps = ["authentik", "nextcloud"]
# }
# 3. Deploy
# 2. Deploy (script handles SSH key + secrets automatically)
./scripts/deploy-client.sh newclient
# That's it! Script will:
# - Generate SSH key if missing
# - Create secrets file from template if missing (opens editor)
# - Deploy everything
```
### Test Changes (Rebuild)

View file

@ -36,36 +36,64 @@ fi
CLIENT_NAME="$1"
# Check if SSH key exists
# Check if SSH key exists, generate if missing
SSH_KEY_FILE="$PROJECT_ROOT/keys/ssh/${CLIENT_NAME}"
if [ ! -f "$SSH_KEY_FILE" ]; then
echo -e "${RED}Error: SSH key not found: $SSH_KEY_FILE${NC}"
echo -e "${YELLOW}SSH key not found for client: $CLIENT_NAME${NC}"
echo "Generating SSH key pair automatically..."
echo ""
echo "Generate SSH key for client first:"
echo " ./scripts/generate-client-keys.sh ${CLIENT_NAME}"
# Generate SSH key
"$SCRIPT_DIR/generate-client-keys.sh" "$CLIENT_NAME"
echo ""
echo -e "${GREEN}✓ SSH key generated${NC}"
echo ""
exit 1
fi
# Check if secrets file exists
# Check if secrets file exists, create from template if missing
SECRETS_FILE="$PROJECT_ROOT/secrets/clients/${CLIENT_NAME}.sops.yaml"
TEMPLATE_FILE="$PROJECT_ROOT/secrets/clients/template.sops.yaml"
if [ ! -f "$SECRETS_FILE" ]; then
echo -e "${RED}Error: Secrets file not found: $SECRETS_FILE${NC}"
echo -e "${YELLOW}Secrets file not found for client: $CLIENT_NAME${NC}"
echo "Creating from template and opening for editing..."
echo ""
echo "Create a secrets file first:"
echo " 1. Copy the template:"
echo " cp secrets/clients/template.sops.yaml secrets/clients/${CLIENT_NAME}.sops.yaml"
# Check if template exists
if [ ! -f "$TEMPLATE_FILE" ]; then
echo -e "${RED}Error: Template file not found: $TEMPLATE_FILE${NC}"
exit 1
fi
# Copy template
cp "$TEMPLATE_FILE" "$SECRETS_FILE"
echo -e "${GREEN}✓ Copied template to $SECRETS_FILE${NC}"
echo ""
echo " 2. Edit with SOPS:"
echo " sops secrets/clients/${CLIENT_NAME}.sops.yaml"
# Open in SOPS for editing
echo -e "${BLUE}Opening secrets file in SOPS for editing...${NC}"
echo ""
echo "Please update the following fields:"
echo " - client_name: $CLIENT_NAME"
echo " - client_domain: ${CLIENT_NAME}.vrije.cloud"
echo " - authentik_domain: auth.${CLIENT_NAME}.vrije.cloud"
echo " - nextcloud_domain: nextcloud.${CLIENT_NAME}.vrije.cloud"
echo " - REGENERATE all passwords and tokens!"
echo ""
echo "Press Enter to open editor..."
read -r
# Open in SOPS
if [ -z "${SOPS_AGE_KEY_FILE:-}" ]; then
export SOPS_AGE_KEY_FILE="$PROJECT_ROOT/keys/age-key.txt"
fi
sops "$SECRETS_FILE"
echo ""
echo -e "${GREEN}✓ Secrets file configured${NC}"
echo ""
echo " 3. Update the following fields:"
echo " - client_name: $CLIENT_NAME"
echo " - client_domain: ${CLIENT_NAME}.vrije.cloud"
echo " - authentik_domain: auth.${CLIENT_NAME}.vrije.cloud"
echo " - nextcloud_domain: nextcloud.${CLIENT_NAME}.vrije.cloud"
echo " - All passwords and tokens (regenerate for security)"
exit 1
fi
# Check required environment variables

View file

@ -36,15 +36,64 @@ fi
CLIENT_NAME="$1"
# Check if secrets file exists
SECRETS_FILE="$PROJECT_ROOT/secrets/clients/${CLIENT_NAME}.sops.yaml"
if [ ! -f "$SECRETS_FILE" ]; then
echo -e "${RED}Error: Secrets file not found: $SECRETS_FILE${NC}"
# Check if SSH key exists, generate if missing
SSH_KEY_FILE="$PROJECT_ROOT/keys/ssh/${CLIENT_NAME}"
if [ ! -f "$SSH_KEY_FILE" ]; then
echo -e "${YELLOW}SSH key not found for client: $CLIENT_NAME${NC}"
echo "Generating SSH key pair automatically..."
echo ""
# Generate SSH key
"$SCRIPT_DIR/generate-client-keys.sh" "$CLIENT_NAME"
echo ""
echo -e "${GREEN}✓ SSH key generated${NC}"
echo ""
fi
# Check if secrets file exists, create from template if missing
SECRETS_FILE="$PROJECT_ROOT/secrets/clients/${CLIENT_NAME}.sops.yaml"
TEMPLATE_FILE="$PROJECT_ROOT/secrets/clients/template.sops.yaml"
if [ ! -f "$SECRETS_FILE" ]; then
echo -e "${YELLOW}Secrets file not found for client: $CLIENT_NAME${NC}"
echo "Creating from template and opening for editing..."
echo ""
# Check if template exists
if [ ! -f "$TEMPLATE_FILE" ]; then
echo -e "${RED}Error: Template file not found: $TEMPLATE_FILE${NC}"
exit 1
fi
# Copy template
cp "$TEMPLATE_FILE" "$SECRETS_FILE"
echo -e "${GREEN}✓ Copied template to $SECRETS_FILE${NC}"
echo ""
# Open in SOPS for editing
echo -e "${BLUE}Opening secrets file in SOPS for editing...${NC}"
echo ""
echo "Please update the following fields:"
echo " - client_name: $CLIENT_NAME"
echo " - client_domain: ${CLIENT_NAME}.vrije.cloud"
echo " - authentik_domain: auth.${CLIENT_NAME}.vrije.cloud"
echo " - nextcloud_domain: nextcloud.${CLIENT_NAME}.vrije.cloud"
echo " - REGENERATE all passwords and tokens!"
echo ""
echo "Press Enter to open editor..."
read -r
# Open in SOPS
if [ -z "${SOPS_AGE_KEY_FILE:-}" ]; then
export SOPS_AGE_KEY_FILE="$PROJECT_ROOT/keys/age-key.txt"
fi
sops "$SECRETS_FILE"
echo ""
echo -e "${GREEN}✓ Secrets file configured${NC}"
echo ""
echo "Create a secrets file first:"
echo " cp secrets/clients/test.sops.yaml secrets/clients/${CLIENT_NAME}.sops.yaml"
echo " sops secrets/clients/${CLIENT_NAME}.sops.yaml"
exit 1
fi
# Check required environment variables