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:
parent
071ed083f7
commit
ac4187d041
4 changed files with 156 additions and 52 deletions
30
README.md
30
README.md
|
|
@ -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!
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 -e "${GREEN}✓ SSH key generated${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
exit 1
|
|
||||||
fi
|
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"
|
SECRETS_FILE="$PROJECT_ROOT/secrets/clients/${CLIENT_NAME}.sops.yaml"
|
||||||
|
TEMPLATE_FILE="$PROJECT_ROOT/secrets/clients/template.sops.yaml"
|
||||||
|
|
||||||
if [ ! -f "$SECRETS_FILE" ]; then
|
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 ""
|
||||||
echo "Create a secrets file first:"
|
|
||||||
echo " 1. Copy the template:"
|
# Check if template exists
|
||||||
echo " cp secrets/clients/template.sops.yaml secrets/clients/${CLIENT_NAME}.sops.yaml"
|
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 ""
|
||||||
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 ""
|
||||||
echo " 3. Update the following fields:"
|
echo "Please 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
|
||||||
|
|
|
||||||
|
|
@ -36,15 +36,64 @@ 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
|
||||||
|
|
||||||
|
# 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
|
fi
|
||||||
|
|
||||||
# Check required environment variables
|
# Check required environment variables
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue