Improve security of docker registry install
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
9103f53673
commit
95331c2d11
1 changed files with 78 additions and 115 deletions
|
@ -1,6 +1,6 @@
|
||||||
# Docker Registry Install Guide
|
# Docker Registry Install Guide
|
||||||
|
|
||||||
This guide covers setting up a rootless Docker Registry v2 with host TLS reverse proxy for secure image storage. The registry runs rootless via Podman with loopback-only access, while a host nginx reverse proxy provides TLS termination and authentication.
|
This guide covers setting up a rootless Docker Registry v2 with host TLS reverse proxy for secure image storage. The registry runs rootless via Podman with loopback-only access, while a host nginx reverse proxy provides TLS termination and mTLS authentication.
|
||||||
|
|
||||||
## Architecture Overview
|
## Architecture Overview
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ This guide covers setting up a rootless Docker Registry v2 with host TLS reverse
|
||||||
│ │ │
|
│ │ │
|
||||||
│ │ │
|
│ │ │
|
||||||
└─── HTTPS :443 ────────┼───────────────────────┘
|
└─── HTTPS :443 ────────┼───────────────────────┘
|
||||||
│
|
(Unauthenticated pulls) │
|
||||||
└─── HTTPS :4443 ───────┘
|
└─── HTTPS :4443 ───────┘
|
||||||
(mTLS Authentication)
|
(mTLS Authentication)
|
||||||
```
|
```
|
||||||
|
@ -26,6 +26,8 @@ This guide covers setting up a rootless Docker Registry v2 with host TLS reverse
|
||||||
- **Loopback Isolation**: Registry only accessible via `127.0.0.1:5000`
|
- **Loopback Isolation**: Registry only accessible via `127.0.0.1:5000`
|
||||||
- **mTLS Authentication**: Client certificates required for push operations
|
- **mTLS Authentication**: Client certificates required for push operations
|
||||||
- **No $HOME I/O**: All Podman state outside user home directory
|
- **No $HOME I/O**: All Podman state outside user home directory
|
||||||
|
- **Port 443**: Unauthenticated pulls only (GET requests)
|
||||||
|
- **Port 4443**: Authenticated pushes only (mTLS required)
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
|
@ -36,7 +38,7 @@ This guide covers setting up a rootless Docker Registry v2 with host TLS reverse
|
||||||
|
|
||||||
## Step 1: Install Podman (if not already installed)
|
## Step 1: Install Podman (if not already installed)
|
||||||
|
|
||||||
### 1.5 Install Podman
|
### 1.1 Install Podman
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install Podman and related tools
|
# Install Podman and related tools
|
||||||
|
@ -54,7 +56,7 @@ sudo usermod --add-subuids 100000-165535 CI_SERVICE_USER
|
||||||
sudo usermod --add-subgids 100000-165535 CI_SERVICE_USER
|
sudo usermod --add-subgids 100000-165535 CI_SERVICE_USER
|
||||||
```
|
```
|
||||||
|
|
||||||
## Step 2: Set Up Rootless Docker Registry v2 with Host TLS Reverse Proxy
|
## Step 2: Set Up Rootless Docker Registry v2
|
||||||
|
|
||||||
### 2.1 Create System-wide Podman Configuration
|
### 2.1 Create System-wide Podman Configuration
|
||||||
|
|
||||||
|
@ -102,6 +104,11 @@ sudo mkdir -p /etc/registry/certs/private /etc/registry/certs/clients
|
||||||
sudo chown root:root /etc/registry/certs/private /etc/registry/certs/clients
|
sudo chown root:root /etc/registry/certs/private /etc/registry/certs/clients
|
||||||
sudo chmod 750 /etc/registry/certs/private
|
sudo chmod 750 /etc/registry/certs/private
|
||||||
sudo chmod 755 /etc/registry/certs/clients
|
sudo chmod 755 /etc/registry/certs/clients
|
||||||
|
|
||||||
|
# Create registry data directory
|
||||||
|
sudo mkdir -p /var/lib/registry/data
|
||||||
|
sudo chown CI_SERVICE_USER:CI_SERVICE_USER /var/lib/registry/data
|
||||||
|
sudo chmod 755 /var/lib/registry/data
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2.4 Install Systemd Services
|
### 2.4 Install Systemd Services
|
||||||
|
@ -112,27 +119,26 @@ sudo chmod 755 /etc/registry/certs/clients
|
||||||
# Install systemd user service for rootless registry
|
# Install systemd user service for rootless registry
|
||||||
sudo tee /etc/systemd/user/registry.service > /dev/null << 'EOF'
|
sudo tee /etc/systemd/user/registry.service > /dev/null << 'EOF'
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Rootless Docker Registry v2
|
Description=Rootless Docker Registry v2 (loopback only)
|
||||||
After=network.target
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
|
||||||
Environment=PODMAN_ROOT=/var/tmp/podman-%U/root
|
Environment=PODMAN_ROOT=/var/tmp/podman-%U/root
|
||||||
Environment=PODMAN_RUNROOT=/run/user/%U/podman-run
|
Environment=PODMAN_RUNROOT=/run/user/%U/podman-run
|
||||||
Environment=PODMAN_TMPDIR=/var/tmp/podman-%U/tmp
|
Environment=PODMAN_TMPDIR=/var/tmp/podman-%U/tmp
|
||||||
Environment=XDG_DATA_HOME=/var/tmp/podman-%U/xdg-data
|
Environment=XDG_DATA_HOME=/var/tmp/podman-%U/xdg-data
|
||||||
Environment=XDG_CONFIG_HOME=/var/tmp/podman-%U/xdg-config
|
Environment=XDG_CONFIG_HOME=/var/tmp/podman-%U/xdg-config
|
||||||
Environment=REGISTRY_HTTP_ADDR=0.0.0.0:5000
|
ExecStart=/usr/bin/podman --root=${PODMAN_ROOT} --runroot=${PODMAN_RUNROOT} --tmpdir=${PODMAN_TMPDIR} --events-backend=file \
|
||||||
Environment=REGISTRY_STORAGE_DELETE_ENABLED=false
|
run --rm --name registry \
|
||||||
ExecStart=/usr/bin/podman run --rm --name registry \
|
|
||||||
--root=${PODMAN_ROOT} --runroot=${PODMAN_RUNROOT} --tmpdir=${PODMAN_TMPDIR} --events-backend=file \
|
|
||||||
-p 127.0.0.1:5000:5000 \
|
-p 127.0.0.1:5000:5000 \
|
||||||
--cap-drop=ALL --read-only --tmpfs /tmp:size=64m --security-opt=no-new-privileges \
|
--read-only --tmpfs /tmp:size=64m --cap-drop=ALL --security-opt=no-new-privileges \
|
||||||
-v /var/lib/registry:/var/lib/registry:Z \
|
-e REGISTRY_HTTP_ADDR=0.0.0.0:5000 \
|
||||||
|
-e REGISTRY_STORAGE_DELETE_ENABLED=false \
|
||||||
|
-v /var/lib/registry/data:/var/lib/registry:z \
|
||||||
registry:2
|
registry:2
|
||||||
ExecStop=/usr/bin/podman stop --root=${PODMAN_ROOT} --runroot=${PODMAN_RUNROOT} --tmpdir=${PODMAN_TMPDIR} --events-backend=file registry
|
ExecStop=/usr/bin/podman --root=${PODMAN_ROOT} --runroot=${PODMAN_RUNROOT} --tmpdir=${PODMAN_TMPDIR} stop -t 10 registry
|
||||||
Restart=always
|
Restart=on-failure
|
||||||
RestartSec=10
|
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=default.target
|
WantedBy=default.target
|
||||||
|
@ -145,12 +151,11 @@ EOF
|
||||||
# Install systemd system service for TLS reverse proxy
|
# Install systemd system service for TLS reverse proxy
|
||||||
sudo tee /etc/systemd/system/registry-proxy.service > /dev/null << 'EOF'
|
sudo tee /etc/systemd/system/registry-proxy.service > /dev/null << 'EOF'
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Docker Registry TLS Reverse Proxy
|
Description=TLS proxy for Docker Registry (443 pulls / 4443 pushes)
|
||||||
After=network.target registry.service
|
After=network-online.target
|
||||||
Requires=registry.service
|
Wants=network-online.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
|
||||||
User=registry-proxy
|
User=registry-proxy
|
||||||
Group=registry-proxy
|
Group=registry-proxy
|
||||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||||
|
@ -159,13 +164,16 @@ NoNewPrivileges=yes
|
||||||
PrivateTmp=yes
|
PrivateTmp=yes
|
||||||
ProtectSystem=strict
|
ProtectSystem=strict
|
||||||
ProtectHome=yes
|
ProtectHome=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
LockPersonality=yes
|
||||||
|
MemoryDenyWriteExecute=yes
|
||||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||||
IPAddressDeny=any
|
IPAddressDeny=any
|
||||||
IPAddressAllow=127.0.0.1/8 ::1
|
IPAddressAllow=127.0.0.1/8 ::1
|
||||||
ExecStart=/usr/sbin/nginx -c /etc/registry/nginx.conf
|
ExecStart=/usr/sbin/nginx -g 'daemon off;' -c /etc/registry/nginx.conf
|
||||||
ExecReload=/usr/sbin/nginx -c /etc/registry/nginx.conf -s reload
|
Restart=on-failure
|
||||||
Restart=always
|
|
||||||
RestartSec=10
|
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
@ -177,97 +185,48 @@ EOF
|
||||||
```bash
|
```bash
|
||||||
# Create nginx configuration for TLS reverse proxy
|
# Create nginx configuration for TLS reverse proxy
|
||||||
sudo tee /etc/registry/nginx.conf > /dev/null << 'EOF'
|
sudo tee /etc/registry/nginx.conf > /dev/null << 'EOF'
|
||||||
events {
|
worker_processes auto;
|
||||||
worker_connections 1024;
|
events { worker_connections 1024; }
|
||||||
}
|
|
||||||
|
|
||||||
http {
|
http {
|
||||||
include /etc/nginx/mime.types;
|
limit_req_zone $binary_remote_addr zone=reg_read:10m rate=10r/s;
|
||||||
default_type application/octet-stream;
|
limit_req_zone $binary_remote_addr zone=reg_write:10m rate=5r/s;
|
||||||
|
client_max_body_size 2g;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
upstream reg { server 127.0.0.1:5000; }
|
||||||
|
|
||||||
# Rate limiting
|
# 443: unauthenticated pulls only
|
||||||
limit_req_zone $binary_remote_addr zone=read_limit:10m rate=10r/s;
|
|
||||||
limit_req_zone $binary_remote_addr zone=write_limit:10m rate=5r/s;
|
|
||||||
|
|
||||||
# Upstream registry
|
|
||||||
upstream registry {
|
|
||||||
server 127.0.0.1:5000;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Port 443: Unauthenticated pulls only
|
|
||||||
server {
|
server {
|
||||||
listen 443 ssl http2;
|
listen 443 ssl http2;
|
||||||
server_name _;
|
|
||||||
|
|
||||||
# TLS configuration
|
|
||||||
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 Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
||||||
add_header X-Frame-Options DENY always;
|
|
||||||
add_header X-Content-Type-Options nosniff always;
|
|
||||||
|
|
||||||
# Certificate files
|
|
||||||
ssl_certificate /etc/registry/certs/registry.crt;
|
ssl_certificate /etc/registry/certs/registry.crt;
|
||||||
ssl_certificate_key /etc/registry/certs/private/registry.key;
|
ssl_certificate_key /etc/registry/certs/private/registry.key;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
# Block write methods (PUT, PATCH, POST, DELETE)
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
if ($request_method ~ ^(PUT|PATCH|POST|DELETE)$) {
|
if ($request_method ~* ^(PUT|PATCH|POST|DELETE)$) { return 403; }
|
||||||
return 403;
|
location /v2/ {
|
||||||
}
|
limit_req zone=reg_read burst=20 nodelay;
|
||||||
|
proxy_pass http://reg;
|
||||||
# Rate limiting for reads
|
proxy_set_header Host $host;
|
||||||
limit_req zone=read_limit burst=20 nodelay;
|
proxy_set_header X-Forwarded-Proto https;
|
||||||
|
|
||||||
# Proxy to registry
|
|
||||||
location / {
|
|
||||||
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-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Port 4443: Authenticated pushes only (mTLS)
|
# 4443: authenticated pushes only (mTLS)
|
||||||
server {
|
server {
|
||||||
listen 4443 ssl http2;
|
listen 4443 ssl http2;
|
||||||
server_name _;
|
|
||||||
|
|
||||||
# TLS configuration
|
|
||||||
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;
|
|
||||||
|
|
||||||
# mTLS configuration
|
|
||||||
ssl_client_certificate /etc/registry/certs/clients/ca.crt;
|
|
||||||
ssl_verify_client on;
|
|
||||||
|
|
||||||
# Security headers
|
|
||||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
||||||
add_header X-Frame-Options DENY always;
|
|
||||||
add_header X-Content-Type-Options nosniff always;
|
|
||||||
|
|
||||||
# Certificate files
|
|
||||||
ssl_certificate /etc/registry/certs/registry.crt;
|
ssl_certificate /etc/registry/certs/registry.crt;
|
||||||
ssl_certificate_key /etc/registry/certs/private/registry.key;
|
ssl_certificate_key /etc/registry/certs/private/registry.key;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
# Rate limiting for writes
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
limit_req zone=write_limit burst=10 nodelay;
|
ssl_client_certificate /etc/registry/certs/clients/ca.crt;
|
||||||
|
ssl_verify_client on;
|
||||||
# Proxy to registry (full API access)
|
location /v2/ {
|
||||||
location / {
|
limit_req zone=reg_write burst=10;
|
||||||
proxy_pass http://registry;
|
proxy_pass http://reg;
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $host:4443;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Forwarded-Proto https;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -390,7 +349,9 @@ sudo ufw allow 4443/tcp # Docker Registry via nginx (authenticated pushes with m
|
||||||
# Enable and start services
|
# Enable and start services
|
||||||
sudo systemctl --global enable registry.service
|
sudo systemctl --global enable registry.service
|
||||||
sudo systemctl enable registry-proxy.service
|
sudo systemctl enable registry-proxy.service
|
||||||
sudo -u CI_SERVICE_USER -H sh -lc 'systemctl --user daemon-reload; systemctl --user enable --now registry.service'
|
|
||||||
|
# Start as the service user
|
||||||
|
sudo -u CI_SERVICE_USER sh -lc 'systemctl --user daemon-reload && systemctl --user enable --now registry.service'
|
||||||
sudo systemctl start registry-proxy.service
|
sudo systemctl start registry-proxy.service
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -537,6 +498,7 @@ The project uses a two-port configuration:
|
||||||
5. **🔧 Robust Boot**: User manager guaranteed to exist at boot time
|
5. **🔧 Robust Boot**: User manager guaranteed to exist at boot time
|
||||||
6. **🎯 Clear Separation**: Unauthenticated pulls vs authenticated pushes
|
6. **🎯 Clear Separation**: Unauthenticated pulls vs authenticated pushes
|
||||||
7. **⚡ Low-Port Binding**: Via minimal capability (`CAP_NET_BIND_SERVICE`)
|
7. **⚡ Low-Port Binding**: Via minimal capability (`CAP_NET_BIND_SERVICE`)
|
||||||
|
8. **🛡️ Comprehensive Hardening**: Multiple security layers and restrictions
|
||||||
|
|
||||||
## 🎉 Congratulations!
|
## 🎉 Congratulations!
|
||||||
|
|
||||||
|
@ -549,5 +511,6 @@ You have successfully set up a rootless Docker Registry v2 with host TLS reverse
|
||||||
- ✅ **FHS-compliant directory structure** for better organization and security
|
- ✅ **FHS-compliant directory structure** for better organization and security
|
||||||
- ✅ **No $HOME I/O** policy with all state outside user home directory
|
- ✅ **No $HOME I/O** policy with all state outside user home directory
|
||||||
- ✅ **Port 443 allows unauthenticated Docker Registry v2 pulls; Port 4443 is for authenticated pushes (mTLS)**
|
- ✅ **Port 443 allows unauthenticated Docker Registry v2 pulls; Port 4443 is for authenticated pushes (mTLS)**
|
||||||
|
- ✅ **Comprehensive security hardening** with multiple protection layers
|
||||||
|
|
||||||
Your Docker Registry is now ready for secure image storage with proper authentication and isolation!
|
Your Docker Registry is now ready for secure image storage with proper authentication and isolation!
|
||||||
|
|
Loading…
Add table
Reference in a new issue