Improve security #11
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 17:18:48 -04:00
parent 61acecc570
commit e28c94f955
5 changed files with 96 additions and 27 deletions

View file

@ -53,6 +53,10 @@ jobs:
timeout 15 bash -c 'until docker exec ci-dind docker version > /dev/null 2>&1; do echo "Waiting for Docker daemon inside DinD..."; sleep 5; done' timeout 15 bash -c 'until docker exec ci-dind docker version > /dev/null 2>&1; do echo "Waiting for Docker daemon inside DinD..."; sleep 5; done'
echo "DinD container is ready" echo "DinD container is ready"
# Install Cosign in DinD container
echo "Installing Cosign..."
docker exec ci-dind sh -c "wget -O /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 && chmod +x /usr/local/bin/cosign"
# Login to Docker Registry (using HTTPS port 443) # Login to Docker Registry (using HTTPS port 443)
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker exec -i ci-dind docker login ${{ secrets.CI_HOST }}:443 -u ${{ secrets.REGISTRY_USER }} --password-stdin echo "${{ secrets.REGISTRY_PASSWORD }}" | docker exec -i ci-dind docker login ${{ secrets.CI_HOST }}:443 -u ${{ secrets.REGISTRY_USER }} --password-stdin
@ -111,6 +115,15 @@ jobs:
# Push to Docker Registry # Push to Docker Registry
if docker exec ci-dind docker push "$registry_image"; then if docker exec ci-dind docker push "$registry_image"; then
echo "✓ Successfully pushed $registry_image to Docker Registry" echo "✓ Successfully pushed $registry_image to Docker Registry"
# Sign the image with Cosign (keyless OIDC)
echo "Signing image with Cosign..."
if docker exec ci-dind sh -c "COSIGN_EXPERIMENTAL=1 cosign sign --keyless $registry_image"; then
echo "✓ Successfully signed $registry_image with Cosign"
else
echo "✗ Failed to sign $registry_image with Cosign"
exit 1
fi
else else
echo "✗ Failed to push $registry_image to Docker Registry" echo "✗ Failed to push $registry_image to Docker Registry"
exit 1 exit 1
@ -252,6 +265,10 @@ jobs:
-f /workspace/backend/Dockerfile \ -f /workspace/backend/Dockerfile \
/workspace/backend /workspace/backend
# Sign the backend image with Cosign (keyless OIDC)
echo "Signing backend image with Cosign..."
docker exec ci-dind sh -c "COSIGN_EXPERIMENTAL=1 cosign sign --keyless ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/backend:${{ gitea.sha }}"
- name: Build and push frontend image - name: Build and push frontend image
run: | run: |
# Build and push frontend image using DinD # Build and push frontend image using DinD
@ -264,6 +281,10 @@ jobs:
-f /workspace/frontend/Dockerfile \ -f /workspace/frontend/Dockerfile \
/workspace/frontend /workspace/frontend
# Sign the frontend image with Cosign (keyless OIDC)
echo "Signing frontend image with Cosign..."
docker exec ci-dind sh -c "COSIGN_EXPERIMENTAL=1 cosign sign --keyless ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/frontend:${{ gitea.sha }}"
- name: Cleanup Testing Environment - name: Cleanup Testing Environment
if: always() if: always()
run: | run: |
@ -335,6 +356,11 @@ jobs:
# Wait for Docker to be ready # Wait for Docker to be ready
timeout 30 bash -c 'until docker info; do sleep 1; done' timeout 30 bash -c 'until docker info; do sleep 1; done'
# Verify signed images before deployment
echo "Verifying signed images..."
cosign verify --key /etc/containers/keys/org-cosign.pub ${{ secrets.CI_HOST }}:443/${{ secrets.APP_NAME || 'sharenet' }}/backend:${{ gitea.sha }}
cosign verify --key /etc/containers/keys/org-cosign.pub ${{ secrets.CI_HOST }}:443/${{ secrets.APP_NAME || 'sharenet' }}/frontend:${{ gitea.sha }}
- name: Validate migration files - name: Validate migration files
run: | run: |
echo "Validating migration files before deployment..." echo "Validating migration files before deployment..."

10
.gitignore vendored
View file

@ -38,6 +38,16 @@ yarn-error.log*
Thumbs.db Thumbs.db
*.pem *.pem
# Cosign keys and certificates
*.key
*.pem
*.crt
*.pub
cosign.key
cosign.pub
org-cosign.key
org-cosign.pub
# Build outputs # Build outputs
dist/ dist/
build/ build/

View file

@ -1808,8 +1808,8 @@ Go to your Forgejo repository and add these secrets in **Settings → Secrets an
- `POSTGRES_PASSWORD`: A strong password for the PostgreSQL database - `POSTGRES_PASSWORD`: A strong password for the PostgreSQL database
- `REGISTRY_CLIENT_CERT`: Path to client certificate for mTLS authentication (e.g., `/etc/registry/certs/clients/client.crt`) - `REGISTRY_CLIENT_CERT`: Path to client certificate for mTLS authentication (e.g., `/etc/registry/certs/clients/client.crt`)
- `REGISTRY_CLIENT_KEY`: Path to client private key for mTLS authentication (e.g., `/etc/registry/certs/private/client.key`) - `REGISTRY_CLIENT_KEY`: Path to client private key for mTLS authentication (e.g., `/etc/registry/certs/private/client.key`)
- `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`) **Note**: The CI pipeline now uses mTLS authentication for pushes (port 4443) and Cosign for image signing. The registry policy enforces Sigstore signatures for all images consumed from the registry.

View file

@ -382,6 +382,23 @@ sudo cp /etc/registry/certs/ca/ca.crt /usr/local/share/ca-certificates/registry-
# This step should complete with "1 added, 0 removed". If it does not, there could be a problem with the certificate you generated, or you might already have a certificate in the trust store # This step should complete with "1 added, 0 removed". If it does not, there could be a problem with the certificate you generated, or you might already have a certificate in the trust store
sudo update-ca-certificates sudo update-ca-certificates
# 4. Generate Cosign key pair for image signing
# Install Cosign if not already installed
wget -O /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
chmod +x /usr/local/bin/cosign
# Generate Cosign key pair (or use keyless OIDC in CI)
cosign generate-key-pair
# Create directory for Cosign public key distribution
sudo mkdir -p /etc/containers/keys
sudo cp cosign.pub /etc/containers/keys/org-cosign.pub
sudo chmod 644 /etc/containers/keys/org-cosign.pub
# Secure the private key (keep this safe and distribute to CI systems)
sudo chmod 600 cosign.key
echo "IMPORTANT: Secure cosign.key and distribute to CI systems for image signing"
``` ```
## Step 4: Configure Firewall and Start Services ## Step 4: Configure Firewall and Start Services
@ -410,13 +427,17 @@ sudo ufw allow from 192.168.0.0/16 to any port 4443 proto tcp
## Client Trust Configuration ## Client Trust Configuration
For clients to trust your registry certificates, they should install the server CA certificate: For clients to trust your registry certificates and enforce image signatures, they should install the server CA certificate and Cosign public key:
**For pulls (port 443):** **For pulls (port 443):**
```bash ```bash
# On client systems - use the actual FQDN/IP from your certificates # On client systems - use the actual FQDN/IP from your certificates
sudo mkdir -p /etc/containers/certs.d/REGISTRY_HOST sudo mkdir -p /etc/containers/certs.d/REGISTRY_HOST
sudo cp /path/to/registry-ca.crt /etc/containers/certs.d/REGISTRY_HOST/ca.crt sudo cp /path/to/registry-ca.crt /etc/containers/certs.d/REGISTRY_HOST/ca.crt
# Install Cosign public key for signature verification
sudo mkdir -p /etc/containers/keys
sudo cp /path/to/org-cosign.pub /etc/containers/keys/org-cosign.pub
``` ```
**For pushes (port 4443, mTLS):** **For pushes (port 4443, mTLS):**
@ -426,9 +447,23 @@ sudo mkdir -p /etc/containers/certs.d/REGISTRY_HOST:4443
sudo cp /path/to/registry-ca.crt /etc/containers/certs.d/REGISTRY_HOST:4443/ca.crt sudo cp /path/to/registry-ca.crt /etc/containers/certs.d/REGISTRY_HOST:4443/ca.crt
sudo cp /path/to/client.crt /etc/containers/certs.d/REGISTRY_HOST:4443/client.cert sudo cp /path/to/client.crt /etc/containers/certs.d/REGISTRY_HOST:4443/client.cert
sudo cp /path/to/client.key /etc/containers/certs.d/REGISTRY_HOST:4443/client.key sudo cp /path/to/client.key /etc/containers/certs.d/REGISTRY_HOST:4443/client.key
# Install Cosign public key for signature verification
sudo mkdir -p /etc/containers/keys
sudo cp /path/to/org-cosign.pub /etc/containers/keys/org-cosign.pub
``` ```
**Note:** Replace `REGISTRY_HOST` with the actual FQDN or IP address that matches your certificate's Subject Alternative Name (SAN). For pushes, both the server CA certificate and client certificate/key are required for mTLS authentication. **Note:** Replace `REGISTRY_HOST` with the actual FQDN or IP address that matches your certificate's Subject Alternative Name (SAN). For pushes, both the server CA certificate and client certificate/key are required for mTLS authentication. The Cosign public key is required for signature verification on both ports.
## Security Model
This setup implements a multi-layered security approach:
1. **mTLS on port 4443**: Controls **who can push** to the registry
2. **Cosign signatures**: Controls **what content is trusted** - all images must be signed
3. **Policy enforcement**: The `containers-policy.json` rejects unsigned images from the registry
4. **Port separation**: Unauthenticated pulls (443) vs authenticated pushes (4443)
5. **Rootless registry**: Registry runs as non-root user with minimal privileges
``` ```
### 4.2 Enable and Start Services ### 4.2 Enable and Start Services
@ -548,6 +583,10 @@ openssl s_client -connect YOUR_ACTUAL_IP_ADDRESS:4443 -servername YOUR_ACTUAL_IP
-cert /etc/registry/certs/clients/client.crt \ -cert /etc/registry/certs/clients/client.crt \
-key /etc/registry/certs/private/client.key < /dev/null -key /etc/registry/certs/private/client.key < /dev/null
# Test Cosign signature verification
echo "Testing Cosign signature verification..."
cosign verify --key /etc/containers/keys/org-cosign.pub YOUR_ACTUAL_IP_ADDRESS/APP_NAME/test:latest
# Verify nginx is using the correct certificates # Verify nginx is using the correct certificates
sudo ls -la /etc/registry/certs/registry.crt /etc/registry/certs/private/registry.key /etc/registry/certs/clients/ca.crt sudo ls -la /etc/registry/certs/registry.crt /etc/registry/certs/private/registry.key /etc/registry/certs/clients/ca.crt

View file

@ -1,19 +1,19 @@
{ {
"default": [ "default": [{ "type": "reject" }],
{
"type": "reject"
}
],
"transports": { "transports": {
"docker": { "docker": {
"localhost:443": [ "REGISTRY_HOST": [
{ {
"type": "insecureAcceptAnything" "type": "sigstoreSigned",
"keyPath": "/etc/containers/keys/org-cosign.pub",
"signedIdentity": { "type": "matchRepository" }
} }
], ],
"localhost:4443": [ "REGISTRY_HOST:4443": [
{ {
"type": "insecureAcceptAnything" "type": "sigstoreSigned",
"keyPath": "/etc/containers/keys/org-cosign.pub",
"signedIdentity": { "type": "matchRepository" }
} }
], ],
"docker.io": [ "docker.io": [
@ -22,12 +22,6 @@
} }
] ]
}, },
"docker-daemon": { "docker-daemon": { "": [{ "type": "reject" }] }
"": [
{
"type": "insecureAcceptAnything"
}
]
}
} }
} }