Configure for self-signed TLS
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-07-13 11:35:11 -04:00
parent 838078f896
commit df7386c60d
4 changed files with 183 additions and 38 deletions

View file

@ -714,9 +714,163 @@ sudo sed -i "s/YOUR_CI_CD_IP/YOUR_ACTUAL_IP_ADDRESS/g" /opt/registry/config.yml
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry/config.yml
```
#### 5.4 Generate TLS Certificate
**Choose one of the following options based on whether you have a domain name:**
#### 5.5 Start Docker Registry with Docker Compose
### **Option A: Self-Signed Certificate (No Domain Required)**
This option works with IP addresses and is suitable for development/testing environments.
```bash
# Create certificate directory
sudo mkdir -p /opt/registry/certs
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry/certs
cd /opt/registry/certs
# Generate private key (4096-bit RSA)
sudo -u CI_SERVICE_USER openssl genrsa -out registry.key 4096
# Generate certificate signing request
sudo -u CI_SERVICE_USER openssl req -new -key registry.key \
-out registry.csr \
-subj "/C=US/ST=State/L=City/O=Organization/OU=IT/CN=YOUR_ACTUAL_IP_ADDRESS"
# Create certificate configuration for Subject Alternative Names (SAN)
sudo -u CI_SERVICE_USER tee registry.conf > /dev/null << EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = US
ST = State
L = City
O = Organization
OU = IT
CN = YOUR_ACTUAL_IP_ADDRESS
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = YOUR_ACTUAL_IP_ADDRESS
DNS.2 = localhost
IP.1 = YOUR_ACTUAL_IP_ADDRESS
IP.2 = 127.0.0.1
EOF
# Generate self-signed certificate (valid for 365 days)
sudo -u CI_SERVICE_USER openssl x509 -req -in registry.csr \
-signkey registry.key \
-out registry.crt \
-days 365 \
-extensions v3_req \
-extfile registry.conf
# Set proper permissions
sudo chmod 600 registry.key
sudo chmod 644 registry.crt
# Verify certificate
sudo -u CI_SERVICE_USER openssl x509 -in registry.crt -text -noout
echo "Self-signed TLS certificate generated successfully!"
echo "Certificate: /opt/registry/certs/registry.crt"
echo "Private key: /opt/registry/certs/registry.key"
echo "Note: This certificate will need to be renewed manually in 365 days"
```
### **Option B: Let's Encrypt Certificate (Domain Required)**
If you have a domain name pointing to your server, use this option for production-ready certificates.
```bash
# Install Certbot and Nginx plugin
sudo apt update
sudo apt install -y certbot python3-certbot-nginx
# Generate certificate using standalone mode
sudo certbot certonly --standalone \
--email your-email@example.com \
--agree-tos \
--no-eff-email \
-d YOUR_DOMAIN_NAME
# Verify certificate generation
sudo certbot certificates
# Create certificate directory for Caddy
sudo mkdir -p /opt/registry/certs
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry/certs
# Copy Let's Encrypt certificates to registry directory
sudo cp /etc/letsencrypt/live/YOUR_DOMAIN_NAME/fullchain.pem /opt/registry/certs/registry.crt
sudo cp /etc/letsencrypt/live/YOUR_DOMAIN_NAME/privkey.pem /opt/registry/certs/registry.key
# Set proper permissions
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry/certs/registry.crt
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry/certs/registry.key
sudo chmod 644 /opt/registry/certs/registry.crt
sudo chmod 600 /opt/registry/certs/registry.key
echo "Let's Encrypt certificate generated successfully!"
echo "Certificate: /opt/registry/certs/registry.crt"
echo "Private key: /opt/registry/certs/registry.key"
echo "Certificate will auto-renew every 60 days"
```
**Note**:
- For **Option A**: 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
#### 5.5 Install Certificate into Docker Trust Store
**Important**: This step adds the Let's Encrypt certificate to Docker's trust store. Since Let's Encrypt is a trusted CA, Docker will automatically trust this certificate.
```bash
# Create Docker certificates directory for your domain
sudo mkdir -p /etc/docker/certs.d/YOUR_DOMAIN_NAME
# Copy certificate to Docker trust store
sudo cp /opt/registry/certs/registry.crt /etc/docker/certs.d/YOUR_DOMAIN_NAME/ca.crt
# Restart Docker daemon to pick up the new certificate
sudo systemctl restart docker
# Verify certificate installation
if [ -f "/etc/docker/certs.d/YOUR_DOMAIN_NAME/ca.crt" ]; then
echo "✅ Let's Encrypt certificate installed in Docker trust store"
else
echo "❌ Failed to install certificate in Docker trust store"
exit 1
fi
echo "Certificate installation completed successfully!"
echo "Docker can now connect to the registry securely using your domain name"
```
#### 5.6 Set Up Automatic Certificate Renewal
**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 /opt/registry/certs/registry.crt && cp /etc/letsencrypt/live/YOUR_DOMAIN_NAME/privkey.pem /opt/registry/certs/registry.key && chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry/certs/registry.* && chmod 644 /opt/registry/certs/registry.crt && chmod 600 /opt/registry/certs/registry.key && systemctl restart docker-registry.service"
echo "Automatic certificate renewal configured!"
echo "Certificates will be renewed automatically and the registry service will be restarted"
```
#### 5.7 Start Docker Registry with Docker Compose
```bash
# Switch to CI_SERVICE_USER
@ -725,6 +879,13 @@ sudo su - CI_SERVICE_USER
# Navigate to registry directory
cd /opt/registry
# Copy updated Docker Compose and Caddy configuration with certificate support
sudo cp /opt/APP_NAME/registry/docker-compose.registry.yml docker-compose.yml
sudo cp /opt/APP_NAME/registry/Caddyfile Caddyfile
# Update Caddyfile with your actual IP address
sudo sed -i "s/YOUR_CI_CD_IP/YOUR_ACTUAL_IP_ADDRESS/g" Caddyfile
# Start the Docker Registry and Caddy services
docker compose up -d
@ -735,7 +896,7 @@ docker compose ps
exit
```
#### 5.6 Create Systemd Service for Docker Compose
#### 5.8 Create Systemd Service for Docker Compose
```bash
# Create systemd service file for Docker Registry with Docker Compose
@ -768,55 +929,33 @@ sudo systemctl start docker-registry.service
sudo journalctl -u docker-registry.service -f
```
#### 5.7 Configure Registry Access
The Docker Registry is now configured with the following access model:
**Authentication Model:**
- **Pulls**: Unauthenticated (public read access) - `/v2/*/blobs/*`, `/v2/*/manifests/*`, `/v2/_catalog`, `/v2/*/tags/list`
- **Pushes**: Require authentication with `registry-user` credentials - `/v2/*/blobs/uploads/*`, `/v2/*/manifests/*` (PUT/POST/PATCH/DELETE)
**Registry Credentials:**
- **Username**: `registry-user`
- **Password**: The password you set in the environment file (default: `your-secure-registry-password`)
**Registry URL**: `https://YOUR_CI_CD_IP`
**Security Features:**
- **URL-based access control**: Different paths require different authentication levels
- **Method-based restrictions**: Push operations (PUT/POST/PATCH/DELETE) require authentication
- **Path validation**: Prevents method spoofing by validating both URL patterns and HTTP methods
- **Security headers**: X-Content-Type-Options, X-Frame-Options for additional protection
**Note**: The authentication is handled by Caddy using the environment variables in the `.env` file. The Docker Registry itself runs without authentication, but Caddy enforces authentication for push operations based on URL patterns and HTTP methods.
#### 5.8 Test Registry Setup
#### 5.9 Test Registry Setup
```bash
# Switch to CI_SERVICE_USER for testing (CI_SERVICE_USER runs CI pipeline and Docker operations)
sudo su - CI_SERVICE_USER
# Test Docker login and push
echo "your-secure-registry-password" | docker login YOUR_CI_CD_IP -u registry-user --password-stdin
# Test Docker login and push (now using Let's Encrypt certificate with domain)
echo "your-secure-registry-password" | docker login YOUR_DOMAIN_NAME -u registry-user --password-stdin
# Create and push test image
echo "FROM alpine:latest" > /tmp/test.Dockerfile
docker build -f /tmp/test.Dockerfile -t YOUR_CI_CD_IP/APP_NAME/test:latest /tmp
docker push YOUR_CI_CD_IP/APP_NAME/test:latest
docker build -f /tmp/test.Dockerfile -t YOUR_DOMAIN_NAME/APP_NAME/test:latest /tmp
docker push YOUR_DOMAIN_NAME/APP_NAME/test:latest
# Test public pull (no authentication)
docker logout YOUR_CI_CD_IP
docker pull YOUR_CI_CD_IP/APP_NAME/test:latest
docker logout YOUR_DOMAIN_NAME
docker pull YOUR_DOMAIN_NAME/APP_NAME/test:latest
# Test that unauthorized push is blocked
echo "FROM alpine:latest" > /tmp/unauthorized.Dockerfile
docker build -f /tmp/unauthorized.Dockerfile -t YOUR_CI_CD_IP/APP_NAME/unauthorized:latest /tmp
docker push YOUR_CI_CD_IP/APP_NAME/unauthorized:latest
docker build -f /tmp/unauthorized.Dockerfile -t YOUR_DOMAIN_NAME/APP_NAME/unauthorized:latest /tmp
docker push YOUR_DOMAIN_NAME/APP_NAME/unauthorized:latest
# Expected: This should fail with authentication error
# Clean up
docker rmi YOUR_CI_CD_IP/APP_NAME/test:latest
docker rmi YOUR_CI_CD_IP/APP_NAME/unauthorized:latest
docker rmi YOUR_DOMAIN_NAME/APP_NAME/test:latest
docker rmi YOUR_DOMAIN_NAME/APP_NAME/unauthorized:latest
exit
```
@ -824,7 +963,9 @@ exit
- ✅ Push requires authentication with `registry-user` credentials
- ✅ Pull works without authentication (public read access)
- ✅ Unauthorized push is blocked
- ✅ Registry accessible at `https://YOUR_CI_CD_IP`
- ✅ Registry accessible at `https://YOUR_DOMAIN_NAME` with valid Let's Encrypt certificate
- ✅ No insecure registry configuration needed
- ✅ Certificate automatically renews every 60 days
### Step 6: Install Forgejo Actions Runner

View file

@ -4,7 +4,10 @@
}
}
YOUR_CI_CD_IP {
YOUR_DOMAIN_NAME {
# Use generated TLS certificate
tls /etc/caddy/certs/registry.crt /etc/caddy/certs/registry.key
# Security headers
header {
X-Content-Type-Options nosniff

View file

@ -16,4 +16,4 @@ middleware:
storage:
- name: Redirect
options:
baseurl: https://YOUR_CI_CD_IP
baseurl: https://YOUR_DOMAIN_NAME

View file

@ -23,6 +23,7 @@ services:
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./certs:/etc/caddy/certs
- caddy_data:/data
- caddy_config:/config
env_file: