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 HCLOUD_TOKEN="your-hetzner-api-token"
export SOPS_AGE_KEY_FILE="./keys/age-key.txt" export SOPS_AGE_KEY_FILE="./keys/age-key.txt"
# 2. Deploy client (fully automated, ~10-15 minutes) # 2. Add client to terraform.tfvars
./scripts/deploy-client.sh <client_name> # 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: The script will automatically:
- ✅ Provisions VPS on Hetzner Cloud - ✅ Generate unique SSH key pair (if missing)
- ✅ Deploys Authentik (SSO/identity provider) - ✅ Create secrets file from template (if missing, opens in editor)
- ✅ Deploys Nextcloud (file storage) - ✅ Provision VPS on Hetzner Cloud
- ✅ Configures OAuth2/OIDC integration - ✅ Deploy Authentik (SSO/identity provider)
- ✅ Sets up SSL certificates - ✅ Deploy Nextcloud (file storage)
- ✅ Creates admin accounts - ✅ Configure OAuth2/OIDC integration
- ✅ Set up SSL certificates
- ✅ Create admin accounts
**Result**: Fully functional system, ready to use immediately! **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> ./scripts/deploy-client.sh <client_name>
``` ```
**What it does**: **What it does** (automatically):
1. Provisions VPS server (if not exists) 1. **Generates SSH key** (if missing) - Unique per-client key pair
2. Sets up base system (Docker, Traefik) 2. **Creates secrets file** (if missing) - From template, opens in editor
3. Deploys Authentik + Nextcloud 3. Provisions VPS server (if not exists)
4. Configures SSO integration automatically 4. Sets up base system (Docker, Traefik)
5. Deploys Authentik + Nextcloud
6. Configures SSO integration automatically
**Time**: ~10-15 minutes **Time**: ~10-15 minutes
**Example**: **Example**:
```bash ```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**: **Requirements**:
- Secrets file must exist: `secrets/clients/<client_name>.sops.yaml`
- Client must be defined in `tofu/terraform.tfvars` - 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 ## Workflow Examples
### Deploy a New Client ### Deploy a New Client (Fully Automated)
```bash ```bash
# 1. Create secrets file # 1. Add to terraform.tfvars
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 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 ./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) ### Test Changes (Rebuild)

View file

@ -36,36 +36,64 @@ fi
CLIENT_NAME="$1" 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}" SSH_KEY_FILE="$PROJECT_ROOT/keys/ssh/${CLIENT_NAME}"
if [ ! -f "$SSH_KEY_FILE" ]; then 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 ""
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 ""
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 exit 1
fi fi
# Check if secrets file exists # Copy template
SECRETS_FILE="$PROJECT_ROOT/secrets/clients/${CLIENT_NAME}.sops.yaml" cp "$TEMPLATE_FILE" "$SECRETS_FILE"
if [ ! -f "$SECRETS_FILE" ]; then echo -e "${GREEN}✓ Copied template to $SECRETS_FILE${NC}"
echo -e "${RED}Error: Secrets file not found: $SECRETS_FILE${NC}"
echo "" echo ""
echo "Create a secrets file first:"
echo " 1. Copy the template:" # Open in SOPS for editing
echo " cp secrets/clients/template.sops.yaml secrets/clients/${CLIENT_NAME}.sops.yaml" echo -e "${BLUE}Opening secrets file in SOPS for editing...${NC}"
echo "" echo ""
echo " 2. Edit with SOPS:" echo "Please update the following fields:"
echo " sops secrets/clients/${CLIENT_NAME}.sops.yaml"
echo ""
echo " 3. Update the following fields:"
echo " - client_name: $CLIENT_NAME" echo " - client_name: $CLIENT_NAME"
echo " - client_domain: ${CLIENT_NAME}.vrije.cloud" echo " - client_domain: ${CLIENT_NAME}.vrije.cloud"
echo " - authentik_domain: auth.${CLIENT_NAME}.vrije.cloud" echo " - authentik_domain: auth.${CLIENT_NAME}.vrije.cloud"
echo " - nextcloud_domain: nextcloud.${CLIENT_NAME}.vrije.cloud" echo " - nextcloud_domain: nextcloud.${CLIENT_NAME}.vrije.cloud"
echo " - All passwords and tokens (regenerate for security)" echo " - REGENERATE all passwords and tokens!"
exit 1 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 ""
fi fi
# Check required environment variables # Check required environment variables

View file

@ -36,17 +36,66 @@ fi
CLIENT_NAME="$1" CLIENT_NAME="$1"
# Check if secrets file exists # Check if SSH key exists, generate if missing
SECRETS_FILE="$PROJECT_ROOT/secrets/clients/${CLIENT_NAME}.sops.yaml" SSH_KEY_FILE="$PROJECT_ROOT/keys/ssh/${CLIENT_NAME}"
if [ ! -f "$SECRETS_FILE" ]; then if [ ! -f "$SSH_KEY_FILE" ]; then
echo -e "${RED}Error: Secrets file not found: $SECRETS_FILE${NC}" echo -e "${YELLOW}SSH key not found for client: $CLIENT_NAME${NC}"
echo "Generating SSH key pair automatically..."
echo "" echo ""
echo "Create a secrets file first:"
echo " cp secrets/clients/test.sops.yaml secrets/clients/${CLIENT_NAME}.sops.yaml" # Generate SSH key
echo " sops secrets/clients/${CLIENT_NAME}.sops.yaml" "$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 exit 1
fi 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 ""
fi
# Check required environment variables # Check required environment variables
if [ -z "${HCLOUD_TOKEN:-}" ]; then if [ -z "${HCLOUD_TOKEN:-}" ]; then
echo -e "${RED}Error: HCLOUD_TOKEN environment variable not set${NC}" echo -e "${RED}Error: HCLOUD_TOKEN environment variable not set${NC}"