Change to use nginx instead of Caddy for docker registry
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
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:
parent
bf41839b8c
commit
0b4fb89e77
5 changed files with 163 additions and 90 deletions
|
@ -65,10 +65,10 @@ This guide covers setting up a complete Continuous Integration/Continuous Deploy
|
|||
### CI/CD Linode Features
|
||||
- Forgejo Actions runner for automated builds
|
||||
- **Docker-in-Docker (DinD) container** for isolated CI operations
|
||||
- Docker Registry with Caddy reverse proxy for image storage
|
||||
- Docker Registry with nginx reverse proxy for image storage
|
||||
- **FHS-compliant directory structure** for data, certificates, and logs
|
||||
- Unauthenticated pulls, authenticated pushes
|
||||
- Automatic HTTPS with Caddy
|
||||
- Automatic HTTPS with nginx
|
||||
- Secure SSH communication with production
|
||||
- **Simplified cleanup** - just restart DinD container
|
||||
|
||||
|
@ -654,9 +654,9 @@ sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
|||
sudo usermod -aG docker CI_SERVICE_USER
|
||||
```
|
||||
|
||||
### Step 5: Set Up Docker Registry with Caddy
|
||||
### Step 5: Set Up Docker Registry with nginx
|
||||
|
||||
We'll set up a basic Docker Registry with Caddy as a reverse proxy, configured to allow unauthenticated pulls but require authentication for pushes.
|
||||
We'll set up a basic Docker Registry with nginx as a reverse proxy, configured to allow unauthenticated pulls but require authentication for pushes.
|
||||
|
||||
#### 5.1 Configure FHS-Compliant Registry Directories
|
||||
|
||||
|
@ -679,38 +679,34 @@ sudo chmod 755 /var/log/registry
|
|||
# Navigate to the cloned application directory
|
||||
cd /opt/APP_NAME/registry
|
||||
|
||||
# Update Caddyfile with your actual IP address
|
||||
sudo sed -i "s/YOUR_CI_CD_IP/YOUR_ACTUAL_IP_ADDRESS/g" /opt/APP_NAME/registry/Caddyfile
|
||||
# Update nginx.conf with your actual IP address
|
||||
sudo sed -i "s/YOUR_CI_CD_IP/YOUR_ACTUAL_IP_ADDRESS/g" /opt/APP_NAME/registry/nginx.conf
|
||||
|
||||
# Update openssl.conf with your actual IP address and registry name
|
||||
sudo sed -i "s/YOUR_CI_CD_IP/YOUR_ACTUAL_IP_ADDRESS/g" /opt/APP_NAME/registry/openssl.conf
|
||||
sudo sed -i "s/YOUR_REGISTRY_NAME/APP_NAME-Registry/g" /opt/APP_NAME/registry/openssl.conf
|
||||
|
||||
# Create FHS-compliant environment directory
|
||||
sudo mkdir -p /etc/registry/env
|
||||
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /etc/registry/env
|
||||
sudo chmod 755 /etc/registry/env
|
||||
# Create FHS-compliant authentication directory
|
||||
sudo mkdir -p /etc/registry/auth
|
||||
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /etc/registry/auth
|
||||
sudo chmod 755 /etc/registry/auth
|
||||
|
||||
# Create secure environment file for registry authentication
|
||||
# First, create a secure password hash
|
||||
# Create htpasswd file for nginx authentication
|
||||
# Save this password somewhere safe
|
||||
REGISTRY_PASSWORD="your-secure-registry-password"
|
||||
REGISTRY_PASSWORD_HASH=$(htpasswd -nbB registry-user "$REGISTRY_PASSWORD" | cut -d: -f2)
|
||||
|
||||
# Create the .env file in FHS-compliant location
|
||||
sudo tee /etc/registry/env/.env > /dev/null <<EOF
|
||||
REGISTRY_PASSWORD_HASH=$REGISTRY_PASSWORD_HASH
|
||||
EOF
|
||||
# Create htpasswd file in FHS-compliant location
|
||||
sudo htpasswd -cb /etc/registry/auth/.htpasswd registry-user "$REGISTRY_PASSWORD"
|
||||
|
||||
# Set secure permissions on .env file (owner read/write only)
|
||||
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /etc/registry/env/.env
|
||||
sudo chmod 600 /etc/registry/env/.env
|
||||
# Set secure permissions on htpasswd file (owner read/write only)
|
||||
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /etc/registry/auth/.htpasswd
|
||||
sudo chmod 600 /etc/registry/auth/.htpasswd
|
||||
|
||||
# Set proper permissions for configuration files
|
||||
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/APP_NAME/registry/Caddyfile
|
||||
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/APP_NAME/registry/nginx.conf
|
||||
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/APP_NAME/registry/openssl.conf
|
||||
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/APP_NAME/registry/docker-compose.registry.yml
|
||||
sudo chmod 644 /opt/APP_NAME/registry/Caddyfile
|
||||
sudo chmod 644 /opt/APP_NAME/registry/nginx.conf
|
||||
sudo chmod 644 /opt/APP_NAME/registry/openssl.conf
|
||||
sudo chmod 644 /opt/APP_NAME/registry/docker-compose.registry.yml
|
||||
```
|
||||
|
@ -928,8 +924,8 @@ sudo systemctl status docker-registry.service
|
|||
# Check that containers are running
|
||||
sudo su - CI_SERVICE_USER -c "cd /opt/APP_NAME/registry && docker compose -f docker-compose.registry.yml ps"
|
||||
|
||||
# Check Caddy logs
|
||||
sudo su - CI_SERVICE_USER -c "cd /opt/APP_NAME/registry && docker compose -f docker-compose.registry.yml logs caddy"
|
||||
# Check nginx logs
|
||||
sudo su - CI_SERVICE_USER -c "cd /opt/APP_NAME/registry && docker compose -f docker-compose.registry.yml logs nginx"
|
||||
|
||||
# Check Registry logs
|
||||
sudo su - CI_SERVICE_USER -c "cd /opt/APP_NAME/registry && docker compose -f docker-compose.registry.yml logs registry"
|
||||
|
@ -1031,8 +1027,8 @@ openssl verify -CAfile /etc/registry/certs/ca/ca.crt /etc/registry/certs/registr
|
|||
# Test the certificate connection
|
||||
openssl s_client -connect YOUR_ACTUAL_IP_ADDRESS:4443 -servername YOUR_ACTUAL_IP_ADDRESS < /dev/null
|
||||
|
||||
# Verify Caddy is using the correct certificates
|
||||
docker exec caddy ls -la /etc/certs/
|
||||
# Verify nginx is using the correct certificates
|
||||
docker exec nginx ls -la /etc/registry/certs/
|
||||
|
||||
# If issues persist, restart Docker daemon to reload certificates
|
||||
sudo systemctl restart docker
|
||||
|
@ -1383,7 +1379,7 @@ The Docker Registry setup now follows the Filesystem Hierarchy Standard (FHS) fo
|
|||
|
||||
**Application Files** (in `/opt/APP_NAME/registry/`):
|
||||
- `docker-compose.registry.yml` - Docker Compose configuration from project repository
|
||||
- `Caddyfile` - Caddy reverse proxy configuration from project repository
|
||||
- `nginx.conf` - nginx reverse proxy configuration from project repository
|
||||
- `openssl.conf` - OpenSSL configuration for certificate generation from project repository
|
||||
- `docker-registry.service` - Systemd service file for Docker Registry
|
||||
|
||||
|
@ -1394,10 +1390,9 @@ The Docker Registry setup now follows the Filesystem Hierarchy Standard (FHS) fo
|
|||
- `/etc/registry/certs/ca/` - CA certificates (mode 644)
|
||||
- `/etc/registry/certs/requests/` - Certificate requests and configs (mode 644)
|
||||
- `/etc/registry/certs/registry.crt` - Server certificate (mode 644)
|
||||
- `/etc/registry/env/` - Environment variables and secrets:
|
||||
- `/etc/registry/env/.env` - Registry authentication secrets (mode 600)
|
||||
- `/etc/registry/auth/.htpasswd` - nginx authentication file (mode 600)
|
||||
- `/etc/systemd/system/docker-registry.service` - Systemd service configuration
|
||||
- `/var/log/registry/` - Registry and Caddy logs
|
||||
- `/var/log/registry/` - Registry and nginx logs
|
||||
|
||||
**Benefits of FHS Compliance**:
|
||||
- **Data persistence**: Registry data stored in `/var/lib/registry/data/` survives container restarts
|
||||
|
@ -1538,7 +1533,7 @@ sudo ufw --force enable
|
|||
sudo ufw default deny incoming
|
||||
sudo ufw default allow outgoing
|
||||
sudo ufw allow ssh
|
||||
sudo ufw allow 443/tcp # Docker Registry via Caddy (public read access)
|
||||
sudo ufw allow 443/tcp # Docker Registry via nginx (public read access)
|
||||
```
|
||||
|
||||
**Security Model**:
|
||||
|
@ -1852,7 +1847,7 @@ ls -la /opt/APP_NAME
|
|||
|
||||
### Step 13: Configure Docker for Docker Registry Access
|
||||
|
||||
**Important**: The Production Linode needs to be able to pull Docker images from the Docker Registry on the CI/CD Linode. Since we're using Caddy with automatic HTTPS, no additional certificate configuration is needed.
|
||||
**Important**: The Production Linode needs to be able to pull Docker images from the Docker Registry on the CI/CD Linode. Since we're using nginx with automatic HTTPS, no additional certificate configuration is needed.
|
||||
|
||||
```bash
|
||||
# Change to the PROD_SERVICE_USER
|
||||
|
@ -1873,7 +1868,7 @@ exit
|
|||
|
||||
**What this does**:
|
||||
- **Tests Docker Registry access**: Verifies that Docker can successfully pull images from the Docker Registry
|
||||
- **No certificate configuration needed**: Caddy handles HTTPS automatically
|
||||
- **No certificate configuration needed**: nginx handles HTTPS automatically
|
||||
- **Simple setup**: No complex certificate management required
|
||||
|
||||
### Step 14: Set Up Forgejo Runner for Production Deployment
|
||||
|
@ -2195,7 +2190,7 @@ Go to your Forgejo repository and add these secrets in **Settings → Secrets an
|
|||
- `APP_NAME`: Your application name (e.g., `sharenet`)
|
||||
- `POSTGRES_PASSWORD`: A strong password for the PostgreSQL database
|
||||
- `REGISTRY_USER`: Docker Registry username for CI operations (e.g., `registry-user`)
|
||||
- `REGISTRY_PASSWORD`: Docker Registry password for CI operations (the password you set in the Caddyfile, default: `your-secure-registry-password`)
|
||||
- `REGISTRY_PASSWORD`: Docker Registry password for CI operations (the password you set in the nginx configuration, default: `your-secure-registry-password`)
|
||||
- `REGISTRY_PUSH_URL`: Docker Registry URL for authenticated pushes (e.g., `YOUR_CI_CD_IP:4443`)
|
||||
- `REGISTRY_PULL_URL`: Docker Registry URL for unauthenticated pulls (e.g., `YOUR_CI_CD_IP`)
|
||||
|
||||
|
@ -2402,17 +2397,17 @@ sudo rm -rf /opt/APP_NAME/registry/certs/requests/openssl.conf
|
|||
|
||||
# Note: DO NOT remove these files as they are needed for operation:
|
||||
# - /opt/APP_NAME/registry/docker-compose.registry.yml
|
||||
# - /opt/APP_NAME/registry/Caddyfile
|
||||
# - /opt/APP_NAME/registry/nginx.conf
|
||||
# - /opt/APP_NAME/registry/docker-registry.service
|
||||
# - /etc/registry/env/.env (contains the actual secrets)
|
||||
# - /etc/registry/auth/.htpasswd (contains the actual secrets)
|
||||
# - /etc/systemd/system/docker-registry.service
|
||||
```
|
||||
|
||||
**Security Note**: The `.env` file in `/etc/registry/env/.env` contains sensitive authentication data and should be:
|
||||
**Security Note**: The `.htpasswd` file in `/etc/registry/auth/.htpasswd` contains sensitive authentication data and should be:
|
||||
- **Backed up securely** if needed for disaster recovery
|
||||
- **Never committed to version control**
|
||||
- **Protected with proper permissions** (600 - owner read/write only)
|
||||
- **Rotated regularly** by updating the password and regenerating the hash
|
||||
- **Rotated regularly** by updating the password and regenerating the htpasswd file
|
||||
|
||||
### Step 8.6 CI/CD Workflow Summary Table
|
||||
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
# Unauthenticated pulls on 443 (GET requests only)
|
||||
:443 {
|
||||
tls /etc/registry/certs/registry.crt /etc/registry/certs/private/registry.key
|
||||
log
|
||||
|
||||
# Block all write operations explicitly
|
||||
@writes method PUT POST PATCH DELETE
|
||||
respond @writes "Method Not Allowed" 405
|
||||
|
||||
# Allow all GET requests to v2 API (Docker Registry itself will handle security)
|
||||
reverse_proxy /v2/* registry:5000
|
||||
}
|
||||
|
||||
# Auth-required pushes on 4443
|
||||
:4443 {
|
||||
tls /etc/registry/certs/registry.crt /etc/registry/certs/private/registry.key
|
||||
log
|
||||
|
||||
# require auth on writes
|
||||
@writes method PUT POST PATCH DELETE
|
||||
basic_auth @writes {
|
||||
registry-user {env.REGISTRY_PASSWORD_HASH}
|
||||
}
|
||||
|
||||
# also require auth on the /v2/ ping so Docker sends creds
|
||||
@v2ping {
|
||||
path /v2/
|
||||
method GET
|
||||
}
|
||||
basic_auth @v2ping {
|
||||
registry-user {env.REGISTRY_PASSWORD_HASH}
|
||||
}
|
||||
|
||||
reverse_proxy /v2/* registry:5000
|
||||
}
|
||||
|
||||
|
||||
# TODO: Add Option B: Let's Encrypt certificates (Domain name)
|
|
@ -4,8 +4,8 @@ This folder contains the configuration files for the Docker Registry setup used
|
|||
|
||||
## Files
|
||||
|
||||
- **`docker-compose.registry.yml`**: Docker Compose configuration for the registry and Caddy reverse proxy
|
||||
- **`Caddyfile`**: Caddy configuration for HTTPS and authentication
|
||||
- **`docker-compose.registry.yml`**: Docker Compose configuration for the registry and nginx reverse proxy
|
||||
- **`nginx.conf`**: nginx configuration for HTTPS and authentication
|
||||
- **`docker-registry.service`**: Systemd service file for Docker Registry
|
||||
- **`README.md`**: This documentation file
|
||||
|
||||
|
@ -13,9 +13,9 @@ This folder contains the configuration files for the Docker Registry setup used
|
|||
|
||||
The registry setup uses:
|
||||
- **Docker Registry**: Basic registry for storing Docker images
|
||||
- **Caddy**: Reverse proxy with automatic HTTPS and authentication
|
||||
- **nginx**: Reverse proxy with automatic HTTPS and authentication
|
||||
- **Environment Variables**: For authentication credentials and registry configuration
|
||||
- **Service User**: The registry and Caddy services run as the existing `CI_SERVICE_USER` (not a separate registry user)
|
||||
- **Service User**: The registry and nginx services run as the existing `CI_SERVICE_USER` (not a separate registry user)
|
||||
|
||||
## Authentication Model
|
||||
|
||||
|
@ -40,8 +40,8 @@ The registry setup uses:
|
|||
The setup is configured through:
|
||||
1. **Environment Variables**: Stored in `.env` file (created during setup) for authentication
|
||||
2. **Docker Compose Environment**: Registry configuration via environment variables
|
||||
3. **Caddyfile**: Handles HTTPS and authentication
|
||||
4. **Docker Compose**: Orchestrates the registry and Caddy services
|
||||
3. **nginx.conf**: Handles HTTPS and authentication
|
||||
4. **Docker Compose**: Orchestrates the registry and nginx services
|
||||
5. **Systemd Service**: Manages the Docker Registry service lifecycle
|
||||
6. **User/Permissions**: All files and services are owned and run by `CI_SERVICE_USER` for consistency and security
|
||||
|
||||
|
@ -51,8 +51,8 @@ The registry is automatically set up during the CI/CD pipeline setup process. Th
|
|||
|
||||
## Security
|
||||
|
||||
- Authentication is handled by Caddy using environment variables
|
||||
- HTTPS is automatically managed by Caddy
|
||||
- Authentication is handled by nginx using htpasswd file
|
||||
- HTTPS is automatically managed by nginx
|
||||
- Registry data is persisted in Docker volumes
|
||||
- Environment file contains sensitive credentials and should be properly secured
|
||||
- All files and services are owned by `CI_SERVICE_USER` (not a separate registry user)
|
|
@ -14,9 +14,9 @@ services:
|
|||
expose:
|
||||
- "5000" # internal only, not published
|
||||
|
||||
caddy:
|
||||
image: caddy:2
|
||||
container_name: caddy
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: nginx
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- registry
|
||||
|
@ -25,7 +25,8 @@ services:
|
|||
- "4443:4443"
|
||||
# deliberately no "80:80" – no HTTP
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- /etc/registry/certs:/etc/registry/certs:ro
|
||||
environment:
|
||||
- REGISTRY_PASSWORD_HASH=${REGISTRY_PASSWORD_HASH}
|
||||
- /etc/registry/auth/.htpasswd:/etc/nginx/.htpasswd:ro
|
||||
- /var/log/nginx:/var/log/nginx
|
||||
|
||||
|
|
115
registry/nginx.conf
Normal file
115
registry/nginx.conf
Normal file
|
@ -0,0 +1,115 @@
|
|||
# Docker Registry Nginx Configuration
|
||||
# Port 443: Unauthenticated pulls (GET requests only)
|
||||
# Port 4443: Authenticated operations (login, logout, push, delete, etc.)
|
||||
|
||||
# Upstream Docker Registry
|
||||
upstream registry {
|
||||
server registry:5000;
|
||||
}
|
||||
|
||||
# HTTP server for unauthenticated pulls on port 443
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name _;
|
||||
|
||||
# SSL Configuration
|
||||
ssl_certificate /etc/registry/certs/registry.crt;
|
||||
ssl_certificate_key /etc/registry/certs/private/registry.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
|
||||
# Block all write operations explicitly
|
||||
if ($request_method !~ ^(GET|HEAD)$) {
|
||||
return 405 "Method Not Allowed";
|
||||
}
|
||||
|
||||
# Allow all GET requests to v2 API (Docker Registry itself will handle security)
|
||||
location /v2/ {
|
||||
proxy_pass http://registry;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 900;
|
||||
proxy_connect_timeout 60;
|
||||
proxy_send_timeout 60;
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
|
||||
# Default location - deny all
|
||||
location / {
|
||||
return 404;
|
||||
}
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/registry_access.log;
|
||||
error_log /var/log/nginx/registry_error.log;
|
||||
}
|
||||
|
||||
# HTTPS server for authenticated operations on port 4443
|
||||
server {
|
||||
listen 4443 ssl http2;
|
||||
server_name _;
|
||||
|
||||
# SSL Configuration
|
||||
ssl_certificate /etc/registry/certs/registry.crt;
|
||||
ssl_certificate_key /etc/registry/certs/private/registry.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
|
||||
# Basic authentication for write operations
|
||||
location ~ ^/v2/.*$ {
|
||||
# Require auth for all v2 API operations
|
||||
auth_basic "Docker Registry";
|
||||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||||
|
||||
proxy_pass http://registry;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 900;
|
||||
proxy_connect_timeout 60;
|
||||
proxy_send_timeout 60;
|
||||
}
|
||||
|
||||
# Default location - deny all
|
||||
location / {
|
||||
return 404;
|
||||
}
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/registry_auth_access.log;
|
||||
error_log /var/log/nginx/registry_auth_error.log;
|
||||
}
|
||||
|
||||
# Redirect HTTP to HTTPS (optional - for port 80 if needed)
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
Loading…
Add table
Reference in a new issue