Remove Let's Encrypt guide for now to focus on IP-only installs
Some checks are pending
CI/CD Pipeline (Fully Isolated DinD) / Run Tests (DinD) (push) Waiting to run
CI/CD Pipeline (Fully Isolated DinD) / Build and Push Docker Images (DinD) (push) Blocked by required conditions
CI/CD Pipeline (Fully Isolated DinD) / Deploy to Production (push) Blocked by required conditions

This commit is contained in:
continuist 2025-08-24 11:21:16 -04:00
parent 6c30dd20aa
commit 7525f936bf

View file

@ -51,11 +51,10 @@ This guide covers setting up a complete Continuous Integration/Continuous Deploy
- Two Ubuntu 24.04 LTS Linodes with root access - Two Ubuntu 24.04 LTS Linodes with root access
- Basic familiarity with Linux commands and SSH - Basic familiarity with Linux commands and SSH
- Forgejo repository with Actions enabled - Forgejo repository with Actions enabled
- **Optional**: Domain name for Production Linode (for SSL/TLS)
## Quick Start ## Quick Start
1. **Set up CI/CD Linode** (Steps 0-9) 1. **Set up CI/CD Linode** (Steps 0-8)
2. **Set up Production Linode** (Steps 10-16) 2. **Set up Production Linode** (Steps 10-16)
3. **Set up Forgejo repository secrets** (Step 17) 3. **Set up Forgejo repository secrets** (Step 17)
4. **Test the complete pipeline** (Step 18) 4. **Test the complete pipeline** (Step 18)
@ -74,7 +73,6 @@ This guide covers setting up a complete Continuous Integration/Continuous Deploy
### Production Linode Features ### Production Linode Features
- Podman-based application deployment - Podman-based application deployment
- **Optional SSL/TLS certificate management** (if domain is provided)
- Nginx reverse proxy with security headers - Nginx reverse proxy with security headers
- Automated backups and monitoring - Automated backups and monitoring
- Firewall and fail2ban protection - Firewall and fail2ban protection
@ -136,7 +134,6 @@ This guide assumes you have already:
2. **Set root passwords** for both Linodes 2. **Set root passwords** for both Linodes
3. **Have SSH client** installed on your local machine 3. **Have SSH client** installed on your local machine
4. **Have Forgejo repository** with Actions enabled 4. **Have Forgejo repository** with Actions enabled
5. **Optional**: Domain name pointing to Production Linode's IP addresses
### Step 0: Initial SSH Access and Verification ### Step 0: Initial SSH Access and Verification
@ -146,7 +143,7 @@ Before proceeding with the setup, you need to establish initial SSH access to bo
From your Linode dashboard, note the IP addresses for: From your Linode dashboard, note the IP addresses for:
- **CI/CD Linode**: `YOUR_CI_CD_IP` (IP address only, no domain needed) - **CI/CD Linode**: `YOUR_CI_CD_IP` (IP address only, no domain needed)
- **Production Linode**: `YOUR_PRODUCTION_IP` (IP address for SSH, domain for web access) - **Production Linode**: `YOUR_PRODUCTION_IP` (IP address for SSH and web access)
#### 0.2 Test Initial SSH Access #### 0.2 Test Initial SSH Access
@ -182,15 +179,14 @@ Before proceeding, decide on:
3. **Application Name**: Choose a name for your application (e.g., `sharenet`) 3. **Application Name**: Choose a name for your application (e.g., `sharenet`)
- Replace `APP_NAME` in this guide with your chosen name - Replace `APP_NAME` in this guide with your chosen name
4. **Domain Name** (Optional): If you have a domain, note it for SSL configuration
- Replace `your-domain.com` in this guide with your actual domain
**Example**: **Example**:
- If you choose `ci-service` as CI service account, `ci-deploy` as CI deployment user, and `sharenet` as application name: - If you choose `ci-service` as CI service account, `ci-deploy` as CI deployment user, and `sharenet` as application name:
- Replace all `CI_SERVICE_USER` with `ci-service` - Replace all `CI_SERVICE_USER` with `ci-service`
- Replace all `CI_DEPLOY_USER` with `ci-deploy` - Replace all `CI_DEPLOY_USER` with `ci-deploy`
- Replace all `APP_NAME` with `sharenet` - Replace all `APP_NAME` with `sharenet`
- If you have a domain `example.com`, replace `your-domain.com` with `example.com`
**Security Model**: **Security Model**:
- **CI Service Account (`CI_SERVICE_USER`)**: Runs CI pipeline and Docker operations, no sudo access - **CI Service Account (`CI_SERVICE_USER`)**: Runs CI pipeline and Docker operations, no sudo access
@ -766,13 +762,7 @@ sudo chmod 755 /var/lib/registry/data # Registry data
#### 4.2.4 Generate TLS Certificate and Install for Podman #### 4.2.4 Generate TLS Certificate and Install for Podman
**Choose one of the following options based on whether you have a domain name:** **Generate Self-Signed Certificate:**
---
#### **Option A: Self-Signed Certificate (No Domain Required)**
**Perform all of these steps if you do NOT have a domain name:**
```bash ```bash
# 1. Generate self-signed certificate with proper CA chain using FHS-compliant structure # 1. Generate self-signed certificate with proper CA chain using FHS-compliant structure
@ -830,80 +820,11 @@ sudo update-ca-certificates
--- ---
#### **Option B: Let's Encrypt Certificate (Domain Required)**
**Perform all of these steps if you DO have a domain name:**
```bash
# 1. Generate Let's Encrypt certificate
sudo apt update
sudo apt install -y certbot python3-certbot-nginx
sudo certbot certonly --standalone \
--email your-email@example.com \
--agree-tos \
--no-eff-email \
-d YOUR_DOMAIN_NAME
sudo certbot certificates
sudo cp /etc/letsencrypt/live/YOUR_DOMAIN_NAME/fullchain.pem /etc/registry/certs/registry.crt
sudo cp /etc/letsencrypt/live/YOUR_DOMAIN_NAME/privkey.pem /etc/registry/certs/registry.key
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /etc/registry/certs/registry.crt /etc/registry/certs/registry.key
sudo chmod 644 /etc/registry/certs/registry.crt
sudo chmod 600 /etc/registry/certs/registry.key
# 2. Install certificate in system trust store (for curl, wget, etc.)
# Note: Let's Encrypt certificates are already trusted by the system, but we install for consistency
sudo cp /etc/registry/certs/registry.crt /usr/local/share/ca-certificates/registry-ca.crt
sudo update-ca-certificates
```
---
**Note:** **Note:**
- For **Option A**: Replace `YOUR_ACTUAL_IP_ADDRESS` with your server's IP address. - Replace `YOUR_ACTUAL_IP_ADDRESS` with your server's IP address.
- For **Option B**: Replace `YOUR_DOMAIN_NAME` with your domain name and `your-email@example.com` with your email address.
--- ---
**After completing the steps for your chosen option, continue with Step 4.5 (Set Up Systemd Service for Docker Registry v2).**
#### 4.3 Install Certificate for Podman (Option B Only)
**Important**: This step adds the Let's Encrypt certificate to the system trust store. Since Let's Encrypt is a trusted CA, system tools will automatically trust this certificate.
```bash
# Install certificate in system trust store (for curl, wget, etc.)
sudo cp /etc/registry/certs/registry.crt /usr/local/share/ca-certificates/registry-ca.crt
sudo update-ca-certificates
# Verify certificate installation
if [ -f "/usr/local/share/ca-certificates/registry-ca.crt" ]; then
echo "✅ Let's Encrypt certificate installed in system trust store"
else
echo "❌ Failed to install certificate in system trust store"
exit 1
fi
echo "Certificate installation completed successfully!"
echo "System tools can now connect to the registry securely using your domain name"
```
#### 4.4 Set Up Automatic Certificate Renewal (Option B Only)
**Important**: Let's Encrypt certificates expire after 90 days, so we need to set up automatic renewal.
```bash
# Test automatic renewal
sudo certbot renew --dry-run
# Set up automatic renewal cron job
sudo crontab -e
# Add this line to renew certificates twice daily (Let's Encrypt allows renewal 30 days before expiry):
# 0 12,18 * * * /usr/bin/certbot renew --quiet --post-hook "cp /etc/letsencrypt/live/YOUR_DOMAIN_NAME/fullchain.pem /etc/registry/certs/registry.crt && cp /etc/letsencrypt/live/YOUR_DOMAIN_NAME/privkey.pem /etc/registry/certs/registry.key && chown CI_SERVICE_USER:CI_SERVICE_USER /etc/registry/certs/registry.* && chmod 644 /etc/registry/certs/registry.crt && chmod 600 /etc/registry/certs/registry.key && cp /etc/registry/certs/registry.crt /usr/local/share/ca-certificates/registry-ca.crt && update-ca-certificates && systemctl restart docker-registry.service"
echo "Automatic certificate renewal configured!"
echo "Certificates will be renewed automatically and the registry service will be restarted"
```
#### 4.5 Set Up Systemd Service for Docker Registry v2 #### 4.5 Set Up Systemd Service for Docker Registry v2
```bash ```bash
@ -1026,52 +947,17 @@ env PODMAN_ROOT=/var/tmp/podman-$(id -u)/root PODMAN_RUNROOT=/run/user/$(id -u)/
exit exit
``` ```
**For Option B (Let's Encrypt certificates):**
```bash
# Switch to CI_SERVICE_USER for testing (CI_SERVICE_USER runs CI pipeline and Docker operations)
sudo su - CI_SERVICE_USER
# Navigate to the application directory
cd /opt/APP_NAME
# Test Podman login and push (now using Let's Encrypt certificate with domain)
echo "your-secure-registry-password" | env PODMAN_ROOT=/var/tmp/podman-$(id -u)/root PODMAN_RUNROOT=/run/user/$(id -u)/podman-run PODMAN_TMPDIR=/var/tmp/podman-$(id -u)/tmp XDG_DATA_HOME=/var/tmp/podman-$(id -u)/xdg-data XDG_CONFIG_HOME=/var/tmp/podman-$(id -u)/xdg-config podman login YOUR_DOMAIN_NAME -u registry-user --password-stdin
# Create and push test image
echo "FROM alpine:latest" > /tmp/test.Dockerfile
env PODMAN_ROOT=/var/tmp/podman-$(id -u)/root PODMAN_RUNROOT=/run/user/$(id -u)/podman-run PODMAN_TMPDIR=/var/tmp/podman-$(id -u)/tmp XDG_DATA_HOME=/var/tmp/podman-$(id -u)/xdg-data XDG_CONFIG_HOME=/var/tmp/podman-$(id -u)/xdg-config podman build -f /tmp/test.Dockerfile -t YOUR_DOMAIN_NAME/APP_NAME/test:latest /tmp
env PODMAN_ROOT=/var/tmp/podman-$(id -u)/root PODMAN_RUNROOT=/run/user/$(id -u)/podman-run PODMAN_TMPDIR=/var/tmp/podman-$(id -u)/tmp XDG_DATA_HOME=/var/tmp/podman-$(id -u)/xdg-data XDG_CONFIG_HOME=/var/tmp/podman-$(id -u)/xdg-config podman push YOUR_DOMAIN_NAME/APP_NAME/test:latest
# Test public pull (no authentication)
env PODMAN_ROOT=/var/tmp/podman-$(id -u)/root PODMAN_RUNROOT=/run/user/$(id -u)/podman-run PODMAN_TMPDIR=/var/tmp/podman-$(id -u)/tmp XDG_DATA_HOME=/var/tmp/podman-$(id -u)/xdg-data XDG_CONFIG_HOME=/var/tmp/podman-$(id -u)/xdg-config podman logout YOUR_DOMAIN_NAME
env PODMAN_ROOT=/var/tmp/podman-$(id -u)/root PODMAN_RUNROOT=/run/user/$(id -u)/podman-run PODMAN_TMPDIR=/var/tmp/podman-$(id -u)/tmp XDG_DATA_HOME=/var/tmp/podman-$(id -u)/xdg-data XDG_CONFIG_HOME=/var/tmp/podman-$(id -u)/xdg-config podman pull YOUR_DOMAIN_NAME/APP_NAME/test:latest
# Test that unauthorized push is blocked
echo "FROM alpine:latest" > /tmp/unauthorized.Dockerfile
env PODMAN_ROOT=/var/tmp/podman-$(id -u)/root PODMAN_RUNROOT=/run/user/$(id -u)/podman-run PODMAN_TMPDIR=/var/tmp/podman-$(id -u)/tmp XDG_DATA_HOME=/var/tmp/podman-$(id -u)/xdg-data XDG_CONFIG_HOME=/var/tmp/podman-$(id -u)/xdg-config podman build -f /tmp/unauthorized.Dockerfile -t YOUR_DOMAIN_NAME/APP_NAME/unauthorized:latest /tmp
env PODMAN_ROOT=/var/tmp/podman-$(id -u)/root PODMAN_RUNROOT=/run/user/$(id -u)/podman-run PODMAN_TMPDIR=/var/tmp/podman-$(id -u)/tmp XDG_DATA_HOME=/var/tmp/podman-$(id -u)/xdg-data XDG_CONFIG_HOME=/var/tmp/podman-$(id -u)/xdg-config podman push YOUR_DOMAIN_NAME/APP_NAME/unauthorized:latest
# Expected: This should fail with authentication error
# Clean up
env PODMAN_ROOT=/var/tmp/podman-$(id -u)/root PODMAN_RUNROOT=/run/user/$(id -u)/podman-run PODMAN_TMPDIR=/var/tmp/podman-$(id -u)/tmp XDG_DATA_HOME=/var/tmp/podman-$(id -u)/xdg-data XDG_CONFIG_HOME=/var/tmp/podman-$(id -u)/xdg-config podman rmi YOUR_DOMAIN_NAME/APP_NAME/test:latest
env PODMAN_ROOT=/var/tmp/podman-$(id -u)/root PODMAN_RUNROOT=/run/user/$(id -u)/podman-run PODMAN_TMPDIR=/var/tmp/podman-$(id -u)/tmp XDG_DATA_HOME=/var/tmp/podman-$(id -u)/xdg-data XDG_CONFIG_HOME=/var/tmp/podman-$(id -u)/xdg-config podman rmi YOUR_DOMAIN_NAME/APP_NAME/unauthorized:latest
exit
```
**Important**: For Option B, the registry configuration is handled via environment variables in the Docker Compose file, so no additional configuration changes are needed.
**Expected behavior**: **Expected behavior**:
- ✅ Push requires authentication with `registry-user` credentials on port 4443 - ✅ Push requires authentication with `registry-user` credentials on port 4443
- ✅ Pull works without authentication (public read access) on port 443 - ✅ Pull works without authentication (public read access) on port 443
- ✅ Unauthorized push is blocked on authenticated endpoint - ✅ Unauthorized push is blocked on authenticated endpoint
- ✅ Registry accessible at `https://YOUR_ACTUAL_IP_ADDRESS:4443` for authenticated operations (Option A) - ✅ Registry accessible at `https://YOUR_ACTUAL_IP_ADDRESS:4443` for authenticated operations
- ✅ Registry accessible at `https://YOUR_ACTUAL_IP_ADDRESS` for unauthenticated pulls (Option A) - ✅ Registry accessible at `https://YOUR_ACTUAL_IP_ADDRESS` for unauthenticated pulls
- ✅ Registry accessible at `https://YOUR_DOMAIN_NAME` with valid Let's Encrypt certificate (Option B)
- ✅ Certificate automatically renews every 60 days (Option B only)
- ✅ Proper FHS-compliant certificate structure with secure permissions - ✅ Proper FHS-compliant certificate structure with secure permissions
**Troubleshooting TLS Errors (Option A only):** **Troubleshooting TLS Errors:**
If you get a TLS error like `remote error: tls: internal error` when using self-signed certificates, verify the certificate installation and Docker configuration: If you get a TLS error like `remote error: tls: internal error` when using self-signed certificates, verify the certificate installation and Docker configuration:
@ -1398,6 +1284,7 @@ podman exec ci-pip podman pull YOUR_CI_CD_IP/APP_NAME/test:latest
# Clean up test images # Clean up test images
podman exec ci-pip podman rmi YOUR_CI_CD_IP:4443/APP_NAME/test:latest podman exec ci-pip podman rmi YOUR_CI_CD_IP:4443/APP_NAME/test:latest
podman exec ci-pip podman rmi YOUR_CI_CD_IP/APP_NAME/test:latest podman exec ci-pip podman rmi YOUR_CI_CD_IP/APP_NAME/test:latest
```
#### 6.3 Set Up Workspace Directory #### 6.3 Set Up Workspace Directory
@ -1460,7 +1347,6 @@ The Docker Registry setup now follows the Filesystem Hierarchy Standard (FHS) fo
- **Log management**: Logs in `/var/log/nginx/` for centralized logging - **Log management**: Logs in `/var/log/nginx/` for centralized logging
- **Configuration separation**: App configs in app directory, system data in system directories - **Configuration separation**: App configs in app directory, system data in system directories
- **Policy enforcement**: Container policies for image signature verification - **Policy enforcement**: Container policies for image signature verification
```
**What this does**: **What this does**:
- **Configures certificate trust**: Properly sets up Docker Registry certificate trust in DinD - **Configures certificate trust**: Properly sets up Docker Registry certificate trust in DinD
@ -1869,11 +1755,10 @@ exit
```bash ```bash
# Install Podman and related tools # Install Podman and related tools
sudo apt install -y podman podman-compose sudo apt install -y podman
# Verify installation # Verify installation
podman --version podman --version
podman-compose --version
``` ```
#### 12.2 Configure Podman for Production Service Account #### 12.2 Configure Podman for Production Service Account
@ -2213,7 +2098,6 @@ sudo fail2ban-client status
```bash ```bash
podman --version podman --version
podman-compose --version
``` ```
#### 16.2 Test Docker Registry v2 Access #### 16.2 Test Docker Registry v2 Access
@ -2227,7 +2111,7 @@ podman pull YOUR_CI_CD_IP/APP_NAME/test:latest
**Note**: Production uses unauthenticated pulls from the standard HTTPS port (443) for deployment operations. **Note**: Production uses unauthenticated pulls from the standard HTTPS port (443) for deployment operations.
**Note**: Application deployment testing will be done in Step 20 after the complete CI/CD pipeline is set up. **Note**: Application deployment testing will be done in Step 19 after the complete CI/CD pipeline is set up.
--- ---
@ -2249,9 +2133,7 @@ Go to your Forgejo repository and add these secrets in **Settings → Secrets an
- `REGISTRY_PUSH_URL`: Docker Registry v2 URL for authenticated pushes (e.g., `YOUR_CI_CD_IP:4443`) - `REGISTRY_PUSH_URL`: Docker Registry v2 URL for authenticated pushes (e.g., `YOUR_CI_CD_IP:4443`)
- `REGISTRY_PULL_URL`: Docker Registry v2 URL for unauthenticated pulls (e.g., `YOUR_CI_CD_IP`) - `REGISTRY_PULL_URL`: Docker Registry v2 URL for unauthenticated pulls (e.g., `YOUR_CI_CD_IP`)
**Optional Secrets (for domain users):**
- `DOMAIN`: Your domain name (e.g., `example.com`)
- `EMAIL`: Your email for SSL certificate notifications
**Note**: This setup uses custom Dockerfiles for testing environments with base images stored in Docker Registry. The CI pipeline automatically checks if base images exist in Docker Registry and pulls them from Docker Hub only when needed, eliminating rate limiting issues and providing better control over the testing environment. **Note**: This setup uses custom Dockerfiles for testing environments with base images stored in Docker Registry. The CI pipeline automatically checks if base images exist in Docker Registry and pulls them from Docker Hub only when needed, eliminating rate limiting issues and providing better control over the testing environment.
@ -2317,40 +2199,14 @@ podman logs sharenet-production-pod-frontend
#### 18.5 Test Application Functionality #### 18.5 Test Application Functionality
1. **Frontend**: Visit your production URL (IP or domain) 1. **Frontend**: Visit your production URL (IP address)
2. **Backend API**: Test API endpoints 2. **Backend API**: Test API endpoints
3. **Database**: Verify database connections 3. **Database**: Verify database connections
4. **Logs**: Check for any errors in application logs 4. **Logs**: Check for any errors in application logs
### Step 19: Set Up SSL/TLS (Optional - Domain Users) ### Step 19: Final Verification
#### 19.1 Install SSL Certificate #### 19.1 Security Check
If you have a domain pointing to your Production Linode:
```bash
# On Production Linode
sudo certbot --nginx -d your-domain.com
# Verify certificate
sudo certbot certificates
```
#### 19.2 Configure Auto-Renewal
```bash
# Test auto-renewal
sudo certbot renew --dry-run
# Add to crontab for automatic renewal
sudo crontab -e
# Add this line:
# 0 12 * * * /usr/bin/certbot renew --quiet
```
### Step 20: Final Verification
#### 20.1 Security Check
```bash ```bash
# Check firewall status # Check firewall status
@ -2363,7 +2219,7 @@ sudo systemctl status fail2ban
sudo grep "PasswordAuthentication" /etc/ssh/sshd_config sudo grep "PasswordAuthentication" /etc/ssh/sshd_config
``` ```
#### 20.2 Performance Check #### 19.2 Performance Check
```bash ```bash
# Check system resources # Check system resources
@ -2376,7 +2232,7 @@ df -h
docker system df docker system df
``` ```
#### 20.3 Backup Verification #### 19.3 Backup Verification
```bash ```bash
# Test backup script # Test backup script
@ -2387,16 +2243,16 @@ cd /opt/APP_NAME
./scripts/backup.sh ./scripts/backup.sh
``` ```
### Step 21: Documentation and Maintenance ### Step 20: Documentation and Maintenance
#### 21.1 Update Documentation #### 20.1 Update Documentation
1. **Update README.md** with deployment information 1. **Update README.md** with deployment information
2. **Document environment variables** and their purposes 2. **Document environment variables** and their purposes
3. **Create troubleshooting guide** for common issues 3. **Create troubleshooting guide** for common issues
4. **Document backup and restore procedures** 4. **Document backup and restore procedures**
#### 21.2 Set Up Monitoring Alerts #### 20.2 Set Up Monitoring Alerts
```bash ```bash
# Set up monitoring cron job # Set up monitoring cron job
@ -2406,7 +2262,7 @@ cd /opt/APP_NAME
tail -f /tmp/monitor.log tail -f /tmp/monitor.log
``` ```
#### 21.3 Regular Maintenance Tasks #### 20.3 Regular Maintenance Tasks
**Daily:** **Daily:**
- Check application logs for errors - Check application logs for errors
@ -2420,7 +2276,6 @@ tail -f /tmp/monitor.log
**Monthly:** **Monthly:**
- Review and rotate logs - Review and rotate logs
- Update SSL certificates
- Review and update documentation - Review and update documentation
--- ---
@ -2435,7 +2290,7 @@ You have successfully set up a complete CI/CD pipeline with:
- ✅ **Health monitoring** and logging - ✅ **Health monitoring** and logging
- ✅ **Backup and cleanup** automation - ✅ **Backup and cleanup** automation
- ✅ **Security hardening** with proper user separation - ✅ **Security hardening** with proper user separation
- ✅ **SSL/TLS support** for production (optional) - ✅ **SSL/TLS support** with self-signed certificates
- ✅ **Zero resource contention** between CI/CD and Docker Registry - ✅ **Zero resource contention** between CI/CD and Docker Registry
- ✅ **FHS-compliant directory structure** for better organization and security - ✅ **FHS-compliant directory structure** for better organization and security