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
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
838078f896
commit
df7386c60d
4 changed files with 183 additions and 38 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -16,4 +16,4 @@ middleware:
|
|||
storage:
|
||||
- name: Redirect
|
||||
options:
|
||||
baseurl: https://YOUR_CI_CD_IP
|
||||
baseurl: https://YOUR_DOMAIN_NAME
|
|
@ -23,6 +23,7 @@ services:
|
|||
- "443:443"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile
|
||||
- ./certs:/etc/caddy/certs
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
env_file:
|
||||
|
|
Loading…
Add table
Reference in a new issue