Docker Private Registry
dockerStructure
- Nginx web server as proxy server and using
htpasswd
to verify access - Registry as docker registry server
- Registry-ui as Web GUI
Setup Registry
Without htpasswd
and running behind Nginx server
Setup Nginx
- Generate SSL Server certificate
#!/usr/bin/env bash
# ssl location
mkdir ssl
# my domain name
mycn="my domain name"
# Specify where we will install
# the xip.io certificate
SSL_DIR="./ssl/${mycn}"
# Set the wildcarded domain
# we want to use
DOMAIN="*.${mycn}"
# A blank passphrase
PASSPHRASE=""
# Set our CSR variables
SUBJ="
C=US
ST=New York
O=
localityName=New York
commonName=$DOMAIN
organizationalUnitName=my org name
emailAddress=web.info@my domain name
"
# Create our SSL directory
# in case it doesn't exist
mkdir -p "$SSL_DIR"
# Generate our Private Key, CSR and Certificate
openssl genrsa -out "$SSL_DIR/${mycn}.key" 4096
openssl req -new -subj "$(echo -n "$SUBJ" | tr "\n" "/")" -key "$SSL_DIR/${mycn}.key" -out "$SSL_DIR/${mycn}.csr" -passin pass:$PASSPHRASE
openssl x509 -req -days 3650 -in "$SSL_DIR/${mycn}.csr" -signkey "$SSL_DIR/${mycn}.key" -out "$SSL_DIR/${mycn}.crt"
- Generate user and password
mkdir conf
htpasswd -b conf/passwordfile username password
- Nginx configuration file
conf/nginx.conf
error_log logs/error.log;
pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
client_body_temp_path /tmp/client_temp;
proxy_temp_path /tmp/proxy_temp_path;
fastcgi_temp_path /tmp/fastcgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
scgi_temp_path /tmp/scgi_temp;
include mime.types;
default_type application/octet-stream;
gzip on;
gzip_min_length 5000;
gzip_buffers 4 8k;
gzip_types text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_proxied any;
gzip_comp_level 2;
ignore_invalid_headers on;
include sites-enabled/*;
}
conf/fcgi.conf
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
fastcgi_index index.php ;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name ;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;
sites-enabled/my domain.conf
map $upstream_http_docker_distribution_api_version $docker_distribution_api_version {
'' 'registry/2.0';
}
server {
listen 443 ssl;
server_name hub.mydomain.name;
# SSL
ssl_certificate /etc/nginx/ssl/hub.mydomain.name/domain.crt;
ssl_certificate_key /etc/nginx/ssl/hub.mydomain.name/domain.key;
# Recommendations from https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
# disable any limits to avoid HTTP 413 for large image uploads
client_max_body_size 0;
# required to avoid HTTP 411: see Issue #1486 (https://github.com/moby/moby/issues/1486)
chunked_transfer_encoding on;
location /v2/ {
# Do not allow connections from docker 1.5 and earlier
# docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
return 404;
}
# To add basic authentication to v2 use auth_basic setting.
auth_basic "Registry realm";
auth_basic_user_file /etc/nginx/conf.d/registry.password;
## If $docker_distribution_api_version is empty, the header is not added.
## See the map directive above where this variable is defined.
add_header 'Docker-Distribution-Api-Version' $docker_distribution_api_version always;
proxy_pass http://172.20.128.11:5000;
proxy_set_header Host $http_host; # required for docker client's sake
proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
location / {
# To add basic authentication to v2 use auth_basic setting.
auth_basic "Registry realm";
auth_basic_user_file /etc/nginx/conf.d/registry.password;
proxy_pass http://172.20.128.10;
proxy_set_header Host $http_host; # required for docker client's sake
proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
}
- Nginx Docker file
FROM nginx:alpine
RUN adduser -u 1000 --disabled-password ngUser
USER ngUser
- Nginx docker-compose.yaml
version: '3'
services:
nginx:
build:
context: .
dockerfile: Dockerfile-nginx
image: "cisdd-nginx:alpine"
ports:
- "443:443"
- "80:80"
volumes:
- ./conf:/etc/nginx/conf.d
- ./conf/nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl
- ./sites-enabled:/etc/nginx/sites-enabled
- ./sites-static-files:/var/www
- ./runtime/logs:/etc/nginx/logs
#- ./runtime/cache:/var/cache/nginx
restart: always
networks:
default:
ipv4_address: 172.20.128.12
networks:
default:
external:
name: my-network
Registry server without authentication
- conf/config.yaml
version: 0.1
log:
fields:
service: registry
storage:
delete:
enabled: true
cache:
blobdescriptor: inmemory
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
Access-Control-Allow-Origin: ['*']
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
Access-Control-Allow-Headers: ['Authorization', 'Accept']
Access-Control-Max-Age: [1728000]
Access-Control-Allow-Credentials: [true]
Access-Control-Expose-Headers: ['Docker-Content-Digest']
- Dockerfile
FROM registry:2
RUN adduser -u 1000 --disabled-password regDockerUser
USER regDockerUser
- Docker-compose file
version: '3'
services:
registry:
build:
context: .
dockerfile: Dockerfile-registry
image: my-registry:2
#ports:
# - "5000:5000"
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
volumes:
- ./data:/data
- ./conf/config.yaml:/etc/docker/registry/config.yml
restart: always
networks:
default:
ipv4_address: 172.20.128.11
registry-ui:
image: joxit/docker-registry-ui:static
#ports:
# - 5001:80
environment:
- REGISTRY_TITLE=CISDD Private Docker Registry
- DELETE_IMAGES=true
- URL=https://hub.mydomain.name
depends_on:
- registry
restart: always
networks:
default:
ipv4_address: 172.20.128.10
networks:
default:
external:
name: my-network
Registry server with user authentication
- Docker file is same as above
- Generate user and password
mkdir auth
htpasswd -b auth/passwordfile username password
- config-secure.yaml
version: 0.1
log:
fields:
service: registry
storage:
delete:
enabled: true
cache:
blobdescriptor: inmemory
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
Access-Control-Allow-Origin: ['http://hub.mydomain.name:5001']
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
Access-Control-Allow-Headers: ['Authorization', 'Accept']
Access-Control-Max-Age: [1728000]
Access-Control-Allow-Credentials: [true]
Access-Control-Expose-Headers: ['Docker-Content-Digest']
- docker-compose-secure.yaml
version: '3'
services:
registry:
build:
context: .
dockerfile: Dockerfile-registry
image: my-registry:1
ports:
- "5000:5000"
environment:
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry
REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
volumes:
- ./auth:/auth
- ./data:/data
- ./conf/config-secure.yaml:/etc/docker/registry/config.yml
restart: always
ui:
image: joxit/docker-registry-ui:static
ports:
- 5001:80
environment:
- REGISTRY_TITLE=CISDD Private Docker Registry
- DELETE_IMAGES=true
- URL=http://hub.mydomain.name:5000
depends_on:
- registry
networks:
default:
external:
name: my-network
Written on March 27, 2022