feat(deploy): add Gitea CI/CD workflows and production Docker Compose config
- Add staging deployment workflow (deploy-staging.yml) - Add production deployment workflow (deploy-production.yml) - Add workflow documentation (README.md) - Add secrets setup guide (SECRETS_SETUP_GUIDE.md) - Add production Docker Compose configuration (docker-compose.prod.yml) Workflows implement automated deployment with SSH-based remote execution, health checks, rollback on failure, and smoke testing.
This commit is contained in:
481
.gitea/workflows/SECRETS_SETUP_GUIDE.md
Normal file
481
.gitea/workflows/SECRETS_SETUP_GUIDE.md
Normal file
@@ -0,0 +1,481 @@
|
||||
# Gitea Secrets Configuration Guide
|
||||
|
||||
**Purpose:** Step-by-step guide to configure all required secrets for staging and production deployments.
|
||||
|
||||
**Prerequisites:**
|
||||
- Admin access to Gitea repository
|
||||
- SSH access to staging and production servers
|
||||
- OpenSSH installed locally
|
||||
|
||||
---
|
||||
|
||||
## Quick Start Checklist
|
||||
|
||||
- [ ] Generate SSH key pair
|
||||
- [ ] Distribute public key to target servers
|
||||
- [ ] Configure staging secrets in Gitea
|
||||
- [ ] Configure production secrets in Gitea
|
||||
- [ ] Test SSH connection manually
|
||||
- [ ] Verify secrets are accessible to workflows
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Generate SSH Keys
|
||||
|
||||
### Option A: Generate New Deployment Keys (Recommended)
|
||||
|
||||
```bash
|
||||
# Navigate to project root
|
||||
cd /home/michael/dev/michaelschiemer
|
||||
|
||||
# Create SSH keys directory
|
||||
mkdir -p .gitea/ssh-keys
|
||||
cd .gitea/ssh-keys
|
||||
|
||||
# Generate staging key
|
||||
ssh-keygen -t rsa -b 4096 -f gitea-staging-deploy \
|
||||
-C "gitea-staging-deployment" -N ""
|
||||
|
||||
# Generate production key
|
||||
ssh-keygen -t rsa -b 4096 -f gitea-production-deploy \
|
||||
-C "gitea-production-deployment" -N ""
|
||||
|
||||
# Verify keys created
|
||||
ls -la
|
||||
# Expected output:
|
||||
# gitea-staging-deploy (private key)
|
||||
# gitea-staging-deploy.pub (public key)
|
||||
# gitea-production-deploy (private key)
|
||||
# gitea-production-deploy.pub (public key)
|
||||
```
|
||||
|
||||
**⚠️ Security Note:**
|
||||
- Private keys should NEVER be committed to git
|
||||
- Add `.gitea/ssh-keys/` to `.gitignore` if not already present
|
||||
- Store private keys securely (e.g., password manager, Vault)
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Distribute Public Keys to Servers
|
||||
|
||||
### Staging Server Setup
|
||||
|
||||
```bash
|
||||
# Copy public key to staging server
|
||||
ssh-copy-id -i .gitea/ssh-keys/gitea-staging-deploy.pub deploy@YOUR_STAGING_HOST
|
||||
|
||||
# Or manually (if ssh-copy-id not available):
|
||||
cat .gitea/ssh-keys/gitea-staging-deploy.pub | \
|
||||
ssh deploy@YOUR_STAGING_HOST "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
|
||||
|
||||
# Set proper permissions on server
|
||||
ssh deploy@YOUR_STAGING_HOST "chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"
|
||||
|
||||
# Test connection
|
||||
ssh -i .gitea/ssh-keys/gitea-staging-deploy deploy@YOUR_STAGING_HOST "echo 'Staging SSH connection successful'"
|
||||
```
|
||||
|
||||
### Production Server Setup
|
||||
|
||||
```bash
|
||||
# Copy public key to production server
|
||||
ssh-copy-id -i .gitea/ssh-keys/gitea-production-deploy.pub deploy@YOUR_PRODUCTION_HOST
|
||||
|
||||
# Or manually:
|
||||
cat .gitea/ssh-keys/gitea-production-deploy.pub | \
|
||||
ssh deploy@YOUR_PRODUCTION_HOST "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
|
||||
|
||||
# Set proper permissions
|
||||
ssh deploy@YOUR_PRODUCTION_HOST "chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"
|
||||
|
||||
# Test connection
|
||||
ssh -i .gitea/ssh-keys/gitea-production-deploy deploy@YOUR_PRODUCTION_HOST "echo 'Production SSH connection successful'"
|
||||
```
|
||||
|
||||
**Deployment User Requirements:**
|
||||
- User must exist on target server (e.g., `deploy`, `www-data`, `ubuntu`)
|
||||
- User must have sudo privileges for Docker commands
|
||||
- User must have write access to deployment directories:
|
||||
- Staging: `/opt/framework-staging/`
|
||||
- Production: `/opt/framework-production/`
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Prepare Secret Values
|
||||
|
||||
### Extract Private Key Content
|
||||
|
||||
```bash
|
||||
cd /home/michael/dev/michaelschiemer/.gitea/ssh-keys
|
||||
|
||||
# Display staging private key (copy entire output)
|
||||
echo "=== STAGING_SSH_KEY ==="
|
||||
cat gitea-staging-deploy
|
||||
echo ""
|
||||
|
||||
# Display production private key (copy entire output)
|
||||
echo "=== PRODUCTION_SSH_KEY ==="
|
||||
cat gitea-production-deploy
|
||||
echo ""
|
||||
```
|
||||
|
||||
**Important:** Copy the **entire key content** including:
|
||||
```
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
[key content here]
|
||||
-----END RSA PRIVATE KEY-----
|
||||
```
|
||||
|
||||
### Determine Server Details
|
||||
|
||||
**Staging Server:**
|
||||
```bash
|
||||
# Get staging hostname/IP (replace with your actual server)
|
||||
STAGING_HOST="staging.michaelschiemer.de" # or IP: 203.0.113.42
|
||||
|
||||
# Get SSH port (default: 22)
|
||||
STAGING_SSH_PORT="22"
|
||||
|
||||
# Get deployment user
|
||||
STAGING_USER="deploy"
|
||||
```
|
||||
|
||||
**Production Server:**
|
||||
```bash
|
||||
# Get production hostname/IP
|
||||
PRODUCTION_HOST="michaelschiemer.de" # or IP: 198.51.100.10
|
||||
|
||||
# Get SSH port (default: 22)
|
||||
PRODUCTION_SSH_PORT="22"
|
||||
|
||||
# Get deployment user
|
||||
PRODUCTION_USER="deploy"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Configure Secrets in Gitea
|
||||
|
||||
### Access Gitea Secrets Configuration
|
||||
|
||||
1. **Navigate to Repository:**
|
||||
- Open Gitea web interface
|
||||
- Go to your framework repository
|
||||
|
||||
2. **Access Secrets Settings:**
|
||||
- Click `Settings` (⚙️ icon)
|
||||
- Click `Secrets` in left sidebar
|
||||
- Or direct URL: `https://git.michaelschiemer.de/michael/framework/settings/secrets`
|
||||
|
||||
3. **Add New Secret:**
|
||||
- Click `Add Secret` button
|
||||
- Fill in `Name` and `Value` fields
|
||||
- Click `Add Secret` to save
|
||||
|
||||
---
|
||||
|
||||
### Staging Secrets Configuration
|
||||
|
||||
**Secret 1: STAGING_HOST**
|
||||
- **Name:** `STAGING_HOST`
|
||||
- **Value:** `staging.michaelschiemer.de` (or your staging server hostname/IP)
|
||||
- **Description:** Staging server hostname or IP address
|
||||
|
||||
**Secret 2: STAGING_USER**
|
||||
- **Name:** `STAGING_USER`
|
||||
- **Value:** `deploy` (or your deployment user)
|
||||
- **Description:** SSH username for staging deployments
|
||||
|
||||
**Secret 3: STAGING_SSH_KEY**
|
||||
- **Name:** `STAGING_SSH_KEY`
|
||||
- **Value:** [Paste entire content of `gitea-staging-deploy` private key]
|
||||
- **Description:** Private SSH key for staging authentication
|
||||
- **⚠️ Important:** Include `-----BEGIN RSA PRIVATE KEY-----` and `-----END RSA PRIVATE KEY-----` lines
|
||||
|
||||
**Secret 4: STAGING_SSH_PORT**
|
||||
- **Name:** `STAGING_SSH_PORT`
|
||||
- **Value:** `22` (or your custom SSH port)
|
||||
- **Description:** SSH port for staging server (optional, defaults to 22)
|
||||
|
||||
---
|
||||
|
||||
### Production Secrets Configuration
|
||||
|
||||
**Secret 5: PRODUCTION_HOST**
|
||||
- **Name:** `PRODUCTION_HOST`
|
||||
- **Value:** `michaelschiemer.de` (or your production server hostname/IP)
|
||||
- **Description:** Production server hostname or IP address
|
||||
|
||||
**Secret 6: PRODUCTION_USER**
|
||||
- **Name:** `PRODUCTION_USER`
|
||||
- **Value:** `deploy` (or your deployment user)
|
||||
- **Description:** SSH username for production deployments
|
||||
|
||||
**Secret 7: PRODUCTION_SSH_KEY**
|
||||
- **Name:** `PRODUCTION_SSH_KEY`
|
||||
- **Value:** [Paste entire content of `gitea-production-deploy` private key]
|
||||
- **Description:** Private SSH key for production authentication
|
||||
- **⚠️ Important:** Include `-----BEGIN RSA PRIVATE KEY-----` and `-----END RSA PRIVATE KEY-----` lines
|
||||
|
||||
**Secret 8: PRODUCTION_SSH_PORT**
|
||||
- **Name:** `PRODUCTION_SSH_PORT`
|
||||
- **Value:** `22` (or your custom SSH port)
|
||||
- **Description:** SSH port for production server (optional, defaults to 22)
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Verify Secrets Configuration
|
||||
|
||||
### Via Gitea UI
|
||||
|
||||
1. Navigate to `Settings` → `Secrets`
|
||||
2. Verify all 8 secrets are listed:
|
||||
- ✅ STAGING_HOST
|
||||
- ✅ STAGING_USER
|
||||
- ✅ STAGING_SSH_KEY
|
||||
- ✅ STAGING_SSH_PORT
|
||||
- ✅ PRODUCTION_HOST
|
||||
- ✅ PRODUCTION_USER
|
||||
- ✅ PRODUCTION_SSH_KEY
|
||||
- ✅ PRODUCTION_SSH_PORT
|
||||
|
||||
3. Check that secrets show "Last Updated" timestamp
|
||||
|
||||
**Note:** Secret values are masked in the UI for security (you cannot view them after saving).
|
||||
|
||||
---
|
||||
|
||||
### Manual SSH Connection Test
|
||||
|
||||
Test SSH connections using the same credentials that workflows will use:
|
||||
|
||||
```bash
|
||||
cd /home/michael/dev/michaelschiemer/.gitea/ssh-keys
|
||||
|
||||
# Test staging connection
|
||||
ssh -i gitea-staging-deploy deploy@YOUR_STAGING_HOST \
|
||||
"echo 'Staging SSH test successful'; docker --version"
|
||||
|
||||
# Test production connection
|
||||
ssh -i gitea-production-deploy deploy@YOUR_PRODUCTION_HOST \
|
||||
"echo 'Production SSH test successful'; docker --version"
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
Staging SSH test successful
|
||||
Docker version 24.0.7, build afdd53b
|
||||
```
|
||||
|
||||
**If Connection Fails:**
|
||||
- Verify hostname/IP is correct
|
||||
- Check SSH port (try specifying: `ssh -p 2222 ...`)
|
||||
- Verify public key is in `~/.ssh/authorized_keys` on server
|
||||
- Check server firewall allows SSH connections
|
||||
- Verify user has Docker permissions: `ssh user@host "docker ps"`
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Test Workflow Access to Secrets
|
||||
|
||||
### Trigger Test Workflow
|
||||
|
||||
Create a minimal test workflow to verify secrets are accessible:
|
||||
|
||||
**File:** `.gitea/workflows/test-secrets.yml`
|
||||
```yaml
|
||||
name: Test Secrets Configuration
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test-secrets:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Test staging secrets
|
||||
run: |
|
||||
echo "Testing staging secrets..."
|
||||
echo "STAGING_HOST: ${{ secrets.STAGING_HOST }}"
|
||||
echo "STAGING_USER: ${{ secrets.STAGING_USER }}"
|
||||
echo "STAGING_SSH_PORT: ${{ secrets.STAGING_SSH_PORT }}"
|
||||
echo "STAGING_SSH_KEY length: ${#STAGING_SSH_KEY}"
|
||||
env:
|
||||
STAGING_SSH_KEY: ${{ secrets.STAGING_SSH_KEY }}
|
||||
|
||||
- name: Test production secrets
|
||||
run: |
|
||||
echo "Testing production secrets..."
|
||||
echo "PRODUCTION_HOST: ${{ secrets.PRODUCTION_HOST }}"
|
||||
echo "PRODUCTION_USER: ${{ secrets.PRODUCTION_USER }}"
|
||||
echo "PRODUCTION_SSH_PORT: ${{ secrets.PRODUCTION_SSH_PORT }}"
|
||||
echo "PRODUCTION_SSH_KEY length: ${#PRODUCTION_SSH_KEY}"
|
||||
env:
|
||||
PRODUCTION_SSH_KEY: ${{ secrets.PRODUCTION_SSH_KEY }}
|
||||
|
||||
- name: Test SSH connection to staging
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.STAGING_HOST }}
|
||||
username: ${{ secrets.STAGING_USER }}
|
||||
key: ${{ secrets.STAGING_SSH_KEY }}
|
||||
port: ${{ secrets.STAGING_SSH_PORT || 22 }}
|
||||
script: |
|
||||
echo "Staging SSH connection successful"
|
||||
docker --version
|
||||
docker-compose --version
|
||||
|
||||
- name: Test SSH connection to production
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.PRODUCTION_HOST }}
|
||||
username: ${{ secrets.PRODUCTION_USER }}
|
||||
key: ${{ secrets.PRODUCTION_SSH_KEY }}
|
||||
port: ${{ secrets.PRODUCTION_SSH_PORT || 22 }}
|
||||
script: |
|
||||
echo "Production SSH connection successful"
|
||||
docker --version
|
||||
docker-compose --version
|
||||
```
|
||||
|
||||
**Run Test:**
|
||||
1. Commit and push test workflow: `git add .gitea/workflows/test-secrets.yml && git commit -m "Add secrets test workflow" && git push`
|
||||
2. Go to Gitea Actions tab
|
||||
3. Select "Test Secrets Configuration" workflow
|
||||
4. Click "Run workflow"
|
||||
5. Monitor execution logs
|
||||
|
||||
**Expected Result:**
|
||||
- All secrets should be accessible
|
||||
- SSH connections should succeed
|
||||
- Docker/docker-compose versions should be displayed
|
||||
|
||||
---
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### SSH Key Management
|
||||
|
||||
**✅ Do:**
|
||||
- Use 4096-bit RSA keys (or Ed25519)
|
||||
- Generate separate keys for staging and production
|
||||
- Rotate keys quarterly
|
||||
- Store private keys in secure password manager
|
||||
- Use dedicated deployment user (not root)
|
||||
- Restrict deployment user permissions
|
||||
|
||||
**❌ Don't:**
|
||||
- Commit private keys to git
|
||||
- Share keys between environments
|
||||
- Use personal SSH keys for deployments
|
||||
- Store keys in plain text files
|
||||
- Reuse keys across projects
|
||||
|
||||
---
|
||||
|
||||
### Secret Rotation Schedule
|
||||
|
||||
**Quarterly (Every 3 Months):**
|
||||
1. Generate new SSH key pairs
|
||||
2. Add new public keys to servers (keep old keys active)
|
||||
3. Update Gitea secrets with new private keys
|
||||
4. Test deployments with new keys
|
||||
5. Remove old public keys from servers
|
||||
6. Delete old private keys securely
|
||||
|
||||
**Template for Rotation:**
|
||||
```bash
|
||||
# Generate new keys with date suffix
|
||||
ssh-keygen -t rsa -b 4096 -f gitea-staging-deploy-2025-04 \
|
||||
-C "gitea-staging-deployment-2025-04" -N ""
|
||||
|
||||
# Repeat process from Step 2 onwards
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Access Control
|
||||
|
||||
**Gitea Repository Permissions:**
|
||||
- Limit "Secrets" access to repository admins only
|
||||
- Require 2FA for admin accounts
|
||||
- Audit secret access logs regularly
|
||||
|
||||
**Server Access Control:**
|
||||
- Deployment user should have minimal required permissions
|
||||
- Use sudo configuration for Docker commands only
|
||||
- Monitor SSH access logs
|
||||
- Implement IP whitelisting if possible
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: Secret Not Found in Workflow
|
||||
|
||||
**Symptoms:**
|
||||
- Workflow fails with "secret not found" error
|
||||
- Secret value is empty in workflow logs
|
||||
|
||||
**Solutions:**
|
||||
1. Verify secret name matches exactly (case-sensitive)
|
||||
2. Check secret is created at repository level (not organization or user level)
|
||||
3. Ensure workflow has access to repository secrets
|
||||
4. Try re-creating the secret
|
||||
|
||||
---
|
||||
|
||||
### Issue: SSH Authentication Failed
|
||||
|
||||
**Symptoms:**
|
||||
- Workflow fails: "Permission denied (publickey)"
|
||||
- Cannot connect to server via SSH
|
||||
|
||||
**Solutions:**
|
||||
1. Verify entire private key content is in secret (including BEGIN/END lines)
|
||||
2. Check public key is in `~/.ssh/authorized_keys` on server
|
||||
3. Verify SSH key format (PEM vs OpenSSH format)
|
||||
4. Check server SSH configuration allows public key authentication
|
||||
5. Test manual connection: `ssh -i key-file user@host`
|
||||
|
||||
---
|
||||
|
||||
### Issue: Docker Permission Denied
|
||||
|
||||
**Symptoms:**
|
||||
- SSH connection succeeds but Docker commands fail
|
||||
- Error: "permission denied while trying to connect to Docker daemon"
|
||||
|
||||
**Solutions:**
|
||||
```bash
|
||||
# Add deployment user to docker group (on server)
|
||||
ssh deploy@server
|
||||
sudo usermod -aG docker deploy
|
||||
|
||||
# Log out and back in for group changes to take effect
|
||||
exit
|
||||
ssh deploy@server
|
||||
|
||||
# Verify Docker access
|
||||
docker ps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
After secrets are configured and tested:
|
||||
|
||||
1. ✅ Secrets configured in Gitea
|
||||
2. ⏳ Test staging deployment workflow
|
||||
3. ⏳ Test production deployment workflow
|
||||
4. ⏳ Update main deployment documentation
|
||||
5. ⏳ Set up monitoring and alerting
|
||||
|
||||
**Continue to:** `Testing Deployment Workflows` section in main README.md
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-01-28
|
||||
**Guide Version:** 1.0
|
||||
**Security Review Date:** 2025-01-28
|
||||
Reference in New Issue
Block a user