2018-06-27 11:08:09 +00:00
user nginx ;
worker_processes auto ;
2018-11-04 10:23:52 +00:00
# error log config comes from external file created by entrypoint, to toggle debug on/off.
include /etc/nginx/error.log.debug.warn ;
2018-06-27 11:08:09 +00:00
pid /var/run/nginx.pid ;
events {
worker_connections 1024 ;
}
http {
2018-07-03 09:19:29 +00:00
map_hash_bucket_size 128 ;
2018-06-27 11:08:09 +00:00
include /etc/nginx/mime.types ;
default_type application/octet-stream ;
2018-11-04 10:23:52 +00:00
# Use a debug-oriented logging format.
2020-10-08 14:35:25 +00:00
log_format debugging escape=json
'{'
'"access_time":"$time_local",'
'"remote_addr":"$remote_addr",'
'"remote_user":"$remote_user",'
'"request":"$request",'
'"status":"$status",'
'"bytes_sent":"$body_bytes_sent",'
'"host":"$host",'
'"proxy_host":"$proxy_host",'
'"upstream":"$upstream_addr"'
'"upstream_status":"$upstream_status",'
'"ssl_protocol":"$ssl_protocol",'
'"connect_host":"$connect_host",'
'"connect_port":"$connect_port",'
'"connect_addr":"$connect_addr",'
'"upstream_http_location":"$upstream_http_location",'
'"upstream_cache_status":"$upstream_cache_status",'
'"http_authorization":"$http_authorization",'
'}' ;
log_format debug_proxy escape=json
'{'
'"access_time":"$time_local",'
'"remote_addr":"$remote_addr",'
'"remote_user":"$remote_user",'
'"request":"$request",'
'"status":"$status",'
'"bytes_sent":"$body_bytes_sent",'
'"host":"$host",'
'"proxy_host":"$proxy_host",'
'"upstream":"$upstream_addr"'
'"upstream_status":"$upstream_status",'
'"ssl_protocol":"$ssl_protocol",'
'"connect_host":"$connect_host",'
'"connect_port":"$connect_port",'
'"connect_addr":"$connect_addr",'
'"upstream_http_location":"$upstream_http_location",'
'"upstream_cache_status":"$upstream_cache_status",'
'"http_authorization":"$http_authorization",'
'}' ;
log_format tweaked escape=json
'{'
'"access_time":"$time_local",'
'"upstream_cache_status":"$upstream_cache_status",'
'"uri":"$uri",'
'"status":"$status",'
'"bytes_sent":"$body_bytes_sent",'
'"host":"$host",'
'"proxy_host":"$proxy_host",'
'"upstream":"$upstream_addr"'
'}' ;
2018-06-27 11:08:09 +00:00
keepalive_timeout 300 ;
gzip off ;
2020-04-03 12:05:01 +00:00
# Entrypoint generates the proxy_cache_path here, so it is configurable externally.
include /etc/nginx/conf.d/cache_max_size.conf ;
2018-06-27 11:08:09 +00:00
# Just in case you want to rewrite some hosts. Default maps directly.
map $host $targetHost {
hostnames ;
default $host ;
}
2018-06-28 23:39:02 +00:00
# A map to enable authentication to some specific docker registries.
# This is auto-generated by the entrypoint.sh based on environment variables
2018-06-27 13:18:25 +00:00
map $host $dockerAuth {
2018-06-28 23:39:02 +00:00
hostnames ;
include /etc/nginx/docker.auth.map ;
2018-06-27 13:18:25 +00:00
default "" ;
}
2018-11-04 10:23:52 +00:00
2018-11-04 15:43:53 +00:00
# @TODO: actually for auth.docker.io, if we want to support multiple authentications, we'll need to decide
# @TODO: based not only on the hostname, but also URI (/token) and query string (?scope)
# @TODO: I wonder if this would help gcr.io and quay.io with authentication also....
map $dockerAuth $finalAuth {
"" " $http_authorization" ; # if empty, keep the original passed-in from the docker client.
default "Basic $dockerAuth" ; # if not empty, add the Basic preamble to the auth
}
2018-06-28 23:39:02 +00:00
# Map to decide which hosts get directed to the caching portion.
# This is automatically generated from the list of cached registries, plus a few fixed hosts
# By default, we don't intercept, allowing free flow of non-registry traffic
map $connect_host $interceptedHost {
hostnames ;
include /etc/nginx/docker.intercept.map ;
2018-11-04 15:43:53 +00:00
default " $connect_addr" ; # $connect_addr is 'IP address and port of the remote host, e.g. "192.168.1.5:12345". IP address is resolved from host name of CONNECT request line.'
2018-06-28 23:39:02 +00:00
}
2018-11-04 10:23:52 +00:00
2018-06-27 11:08:09 +00:00
# These maps parse the original Host and URI from a /forcecache redirect.
map $request_uri $realHost {
~/forcecacheinsecure/([^:/]+)/originalwas(/.+) $1 ;
~/forcecachesecure/([^:/]+)/originalwas(/.+) $1 ;
default "DID_NOT_MATCH_HOST" ;
}
map $request_uri $realPath {
~/forcecacheinsecure/([^:/]+)/originalwas(/.+) $2 ;
~/forcecachesecure/([^:/]+)/originalwas(/.+) $2 ;
default "DID_NOT_MATCH_PATH" ;
}
2018-11-04 10:23:52 +00:00
2018-06-28 23:39:02 +00:00
# The proxy director layer, listens on 3128
server {
listen 3128 ;
server_name _ ;
2018-11-04 10:23:52 +00:00
2018-06-28 23:39:02 +00:00
# dont log the CONNECT proxy.
2018-11-04 15:43:53 +00:00
#access_log /var/log/nginx/access.log debug_proxy;
2018-06-28 23:39:02 +00:00
access_log off ;
2018-11-04 10:23:52 +00:00
2018-06-28 23:39:02 +00:00
proxy_connect ;
proxy_connect_address $interceptedHost ;
proxy_max_temp_file_size 0 ;
2018-11-04 10:23:52 +00:00
2018-06-28 23:39:02 +00:00
# We need to resolve the real names of our proxied servers.
2018-12-12 08:18:34 +00:00
#resolver 8.8.8.8 4.2.2.2 ipv6=off; # Avoid ipv6 addresses for now
include /etc/nginx/resolvers.conf ;
2018-06-28 23:39:02 +00:00
# forward proxy for non-CONNECT request
location / {
2018-11-04 15:43:53 +00:00
add_header "Content-type" "text/plain" always ;
return 200 "docker-registry-proxy: The docker caching proxy is working!" ;
2018-06-28 23:39:02 +00:00
}
2018-11-04 10:23:52 +00:00
2018-06-28 23:39:02 +00:00
location /ca.crt {
2018-11-04 10:23:52 +00:00
alias /ca/ca.crt ;
2018-06-28 23:39:02 +00:00
}
2018-12-14 20:18:28 +00:00
location /setup/systemd {
2018-12-12 08:42:40 +00:00
add_header "Content-type" "text/plain" always ;
return 200 '
2019-01-16 20:43:01 +00:00
set -e
2018-12-14 20:18:28 +00:00
2019-01-16 20:43:01 +00:00
if [ ! -d /etc/systemd ] ; then
2018-12-14 20:18:28 +00:00
echo "Not a systemd system"
exit 1
fi
2018-12-12 08:42:40 +00:00
mkdir -p /etc/systemd/system/docker.service.d
cat << EOD > /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTPS_PROXY=$scheme://$http_host/"
EOD
# Get the CA certificate from the proxy and make it a trusted root.
curl $scheme://$http_host/ca.crt > /usr/share/ca-certificates/docker_registry_proxy.crt
2018-12-14 20:18:28 +00:00
if fgrep -q "docker_registry_proxy.crt" /etc/ca-certificates.conf ; then
echo "certificate refreshed"
else
echo "docker_registry_proxy.crt" >> /etc/ca-certificates.conf
fi
2018-12-12 08:42:40 +00:00
update-ca-certificates --fresh
# Reload systemd
systemctl daemon-reload
# Restart dockerd
systemctl restart docker.service
2018-12-14 20:18:28 +00:00
echo "Docker configured with HTTPS_PROXY= $scheme://$http_host/"
2018-12-12 08:42:40 +00:00
' ;
2018-12-14 20:18:28 +00:00
} # end location /setup/systemd
} # end server
2018-11-04 10:23:52 +00:00
2018-06-27 11:08:09 +00:00
2018-06-28 23:39:02 +00:00
# The caching layer
2018-06-27 11:08:09 +00:00
server {
# Listen on both 80 and 443, for all hostnames.
2018-11-04 10:23:52 +00:00
# actually could be 443 or 444, depending on debug. this is now generated by the entrypoint.
2018-06-27 11:08:09 +00:00
listen 80 default_server ;
2018-11-04 10:23:52 +00:00
include /etc/nginx/caching.layer.listen ;
2018-06-27 11:08:09 +00:00
server_name _ ;
2018-11-04 10:23:52 +00:00
2018-06-28 23:39:02 +00:00
# Do some tweaked logging.
access_log /var/log/nginx/access.log tweaked ;
2018-06-27 11:08:09 +00:00
2018-06-28 23:39:02 +00:00
# Use the generated certificates, they contain names for all the proxied registries.
ssl_certificate /certs/fullchain.pem ;
ssl_certificate_key /certs/web.key ;
2018-06-27 11:08:09 +00:00
# We need to resolve the real names of our proxied servers.
2018-12-12 08:18:34 +00:00
#resolver 8.8.8.8 4.2.2.2 ipv6=off; # Avoid ipv6 addresses for now
include /etc/nginx/resolvers.conf ;
2018-06-27 11:08:09 +00:00
# Docker needs this. Don't ask.
chunked_transfer_encoding on ;
# Block POST/PUT/DELETE. Don't use this proxy for pushing.
if ( $request_method = POST) {
2018-06-28 23:39:02 +00:00
return 405 "POST method is not allowed" ;
2018-06-27 11:08:09 +00:00
}
if ( $request_method = PUT) {
2018-06-28 23:39:02 +00:00
return 405 "PUT method is not allowed" ;
2018-06-27 11:08:09 +00:00
}
if ( $request_method = DELETE) {
2018-06-28 23:39:02 +00:00
return 405 "DELETE method is not allowed" ;
2018-06-27 11:08:09 +00:00
}
2018-11-04 10:23:52 +00:00
2018-06-27 11:08:09 +00:00
proxy_read_timeout 900 ;
2018-11-04 10:23:52 +00:00
# Use cache locking, with a huge timeout, so that multiple Docker clients asking for the same blob at the same time
2018-06-27 11:08:09 +00:00
# will wait for the first to finish instead of doing multiple upstream requests.
proxy_cache_lock on ;
2018-11-04 15:43:53 +00:00
proxy_cache_lock_timeout 880s ;
2018-06-27 13:18:25 +00:00
2020-03-23 09:07:19 +00:00
# Cache all 200, 206 for 60 days.
proxy_cache_valid 200 206 60d ;
2018-11-04 10:23:52 +00:00
2018-06-27 13:18:25 +00:00
# Some extra settings to maximize cache hits and efficiency
2018-06-27 11:08:09 +00:00
proxy_force_ranges on ;
proxy_ignore_client_abort on ;
proxy_cache_revalidate on ;
2018-06-27 13:18:25 +00:00
# Hide/ignore headers from caching. S3 especially likes to send Expires headers in the past in some situations.
proxy_hide_header Set-Cookie ;
2018-06-28 23:39:02 +00:00
proxy_ignore_headers X-Accel-Expires Expires Cache-Control Set-Cookie ;
2018-11-04 10:23:52 +00:00
2018-06-28 23:39:02 +00:00
# Add the authentication info, if the map matched the target domain.
proxy_set_header Authorization $finalAuth ;
2018-11-04 10:23:52 +00:00
2019-11-21 09:23:46 +00:00
# Use SNI during the TLS handshake with the upstream.
proxy_ssl_server_name on ;
2018-06-28 23:39:02 +00:00
# This comes from a include file generated by the entrypoint.
include /etc/nginx/docker.verify.ssl.conf ;
2018-11-04 10:23:52 +00:00
2018-06-27 13:18:25 +00:00
# Block API v1. We dont know how to handle these.
# Docker-client should start with v2 and fallback to v1 if something fails, for example, if authentication failed to a protected v2 resource.
location /v1 {
2018-11-04 15:43:53 +00:00
return 405 "docker-registry-proxy: docker is trying to use v1 API. Either the image does not exist upstream, or you need to configure docker-registry-proxy to authenticate against $host" ;
2018-06-27 13:18:25 +00:00
}
2020-10-08 14:18:02 +00:00
# For blob requests by digest, do cache, and treat redirects.
location ~ ^/v2/(.*)/blobs/sha256:(.*) {
proxy_pass https:// $targetHost ;
proxy_cache cache ;
proxy_cache_key $uri ;
proxy_intercept_errors on ;
error_page 301 302 307 = @handle_redirects ;
}
# For manifest requests by digest, do cache, and treat redirects.
location ~ ^/v2/(.*)/manifests/sha256:(.*) {
proxy_pass https:// $targetHost ;
proxy_cache cache ;
proxy_cache_key $uri ;
proxy_intercept_errors on ;
error_page 301 302 307 = @handle_redirects ;
}
# Cache manifest requests that are not by digest (e.g. tags)
# Since these are mutable, we invalidate them immediately and keep them only in case the backend is down
location ~ ^/v2/(.*)/manifests/ {
proxy_pass https:// $targetHost ;
proxy_cache cache ;
proxy_cache_key $uri ;
proxy_intercept_errors on ;
proxy_cache_use_stale error timeout http_500 http_502 http_504 http_403 ;
proxy_cache_valid 1s ;
error_page 301 302 307 = @handle_redirects ;
}
# Cache blobs requests that are not by digest
# Since these are mutable, we invalidate them immediately and keep them only in case the backend is down
2018-11-04 15:43:53 +00:00
location ~ ^/v2/(.*)/blobs/ {
2018-06-27 11:08:09 +00:00
proxy_pass https:// $targetHost ;
proxy_cache cache ;
2020-03-23 09:07:19 +00:00
proxy_cache_key $uri ;
proxy_intercept_errors on ;
2020-10-08 14:18:02 +00:00
proxy_cache_use_stale error timeout http_500 http_502 http_504 http_403 ;
proxy_cache_valid 1s ;
2020-03-23 09:07:19 +00:00
error_page 301 302 307 = @handle_redirects ;
2018-06-27 11:08:09 +00:00
}
2020-10-08 14:18:02 +00:00
2020-03-23 09:07:19 +00:00
location @handle_redirects {
#store the current state of the world so we can reuse it in a minute
# We need to capture these values now, because as soon as we invoke
# the proxy_* directives, these will disappear
set $original_uri $uri ;
set $orig_loc $upstream_http_location ;
2018-11-04 15:43:53 +00:00
2020-04-03 13:33:46 +00:00
# during this process, nginx will preserve the headers intended for the original destination.
# in most cases thats okay, but for some (eg: google storage), passing an Authorization
# header can cause problems. Also, that would leak the credentials for the registry
# into the storage system (unrelated).
proxy_set_header Authorization "" ;
2020-03-23 09:07:19 +00:00
# nginx goes to fetch the value from the upstream Location header
proxy_pass $orig_loc ;
2018-06-27 11:08:09 +00:00
proxy_cache cache ;
2020-03-23 09:07:19 +00:00
# But we store the result with the cache key of the original request URI
# so that future clients don't need to follow the redirect too
proxy_cache_key $original_uri ;
2018-06-27 11:08:09 +00:00
}
2018-11-04 15:43:53 +00:00
# by default, dont cache anything.
location / {
proxy_pass https:// $targetHost ;
proxy_cache off ;
add_header X-Docker-Caching-Proxy-Debug-Cache "no:default" ;
}
2018-06-27 11:08:09 +00:00
}
2018-12-12 08:18:34 +00:00
}