Skip to content

Commit 7724f3b

Browse files
committed
completely reworked caching, now cache by exception (/blobs/ only essentially)
- now only /v2/.../blobs/... URIs are actually cached (together with their redirect catchers) - /manifests/, /token, and /v2/ are not cached anymore, which should solve a lot of problems - better messages for /v1 attempts - fix usage of $connect_host:443 (which is hostname:port and causes errors to be logged) to $connect_addr (which returns an IP:port) in the proxy layer
1 parent 8ff06e3 commit 7724f3b

File tree

3 files changed

+47
-57
lines changed

3 files changed

+47
-57
lines changed

Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ ENV AUTH_REGISTRIES="some.authenticated.registry:oneuser:onepassword another.reg
4747
ENV VERIFY_SSL="true"
4848
# Enable debugging mode; this inserts mitmproxy/mitmweb between the CONNECT proxy and the caching layer
4949
ENV DEBUG="true"
50+
# Enable nginx debugging mode; this uses nginx-debug binary and enabled debug logging, which is VERY verbose so separate setting
51+
ENV DEBUG_NGINX="false"
5052

51-
# Did you want a shell? Sorry. This only does one job; use exec /bin/bash if you wanna inspect stuff
53+
# Did you want a shell? Sorry, the entrypoint never returns, because it runs nginx itself. Use 'docker exec' if you need to mess around internally.
5254
ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,8 @@ NGINX_BIN="nginx"
5858
if [[ "a${DEBUG}" == "atrue" ]]; then
5959
# in debug mode, change caching layer to listen on 444, so that mitmproxy can sit in the middle.
6060
echo " listen 444 ssl default_server;" > /etc/nginx/caching.layer.listen
61-
echo "error_log /var/log/nginx/error.log debug;" > /etc/nginx/error.log.debug.warn
62-
63-
# use debug binary
64-
NGINX_BIN="nginx-debug"
6561

66-
echo "Starting in DEBUG MODE."
62+
echo "Starting in DEBUG MODE (mitmproxy)."
6763
echo "Run mitmproxy with reverse pointing to the same certs..."
6864
mitmweb --no-web-open-browser --web-iface 0.0.0.0 --web-port 8081 \
6965
--set keep_host_header=true --set ssl_insecure=true \
@@ -73,6 +69,12 @@ if [[ "a${DEBUG}" == "atrue" ]]; then
7369
echo "Access mitmweb via http://127.0.0.1:8081/ "
7470
fi
7571

72+
if [[ "a${DEBUG_NGINX}" == "atrue" ]]; then
73+
echo "Starting in DEBUG MODE (nginx)."
74+
echo "error_log /var/log/nginx/error.log debug;" > /etc/nginx/error.log.debug.warn
75+
# use debug binary
76+
NGINX_BIN="nginx-debug"
77+
fi
7678

7779
echo "Testing nginx config..."
7880
${NGINX_BIN} -t

nginx.conf

Lines changed: 37 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ http {
2121
'"HOST: $host" "UPSTREAM: $upstream_addr" '
2222
'"UPSTREAM-STATUS: $upstream_status" '
2323
'"SSL-PROTO: $ssl_protocol" '
24+
'"CONNECT-HOST: $connect_host" "CONNECT-PORT: $connect_port" "CONNECT-ADDR: $connect_addr" '
25+
'"PROXY-HOST: $proxy_host" "UPSTREAM-REDIRECT: $upstream_http_location" "CACHE-STATUS: $upstream_cache_status" '
26+
'"AUTH: $http_authorization" ' ;
27+
28+
log_format debug_proxy 'CONNECTPROXY: $remote_addr - $remote_user [$time_local] "$request" '
29+
'$status $body_bytes_sent '
30+
'"HOST: $host" "UPSTREAM: $upstream_addr" '
31+
'"UPSTREAM-STATUS: $upstream_status" '
32+
'"SSL-PROTO: $ssl_protocol" '
2433
'"CONNECT-HOST: $connect_host" "CONNECT-PORT: $connect_port" "CONNECT-ADDR: $connect_addr" "INTERCEPTED: $interceptedHost" '
2534
'"PROXY-HOST: $proxy_host" "UPSTREAM-REDIRECT: $upstream_http_location" "CACHE-STATUS: $upstream_cache_status" '
2635
'"AUTH: $http_authorization" ' ;
@@ -51,18 +60,23 @@ http {
5160
default "";
5261
}
5362

63+
# @TODO: actually for auth.docker.io, if we want to support multiple authentications, we'll need to decide
64+
# @TODO: based not only on the hostname, but also URI (/token) and query string (?scope)
65+
# @TODO: I wonder if this would help gcr.io and quay.io with authentication also....
66+
67+
map $dockerAuth $finalAuth {
68+
"" "$http_authorization"; # if empty, keep the original passed-in from the docker client.
69+
default "Basic $dockerAuth"; # if not empty, add the Basic preamble to the auth
70+
}
71+
72+
5473
# Map to decide which hosts get directed to the caching portion.
5574
# This is automatically generated from the list of cached registries, plus a few fixed hosts
5675
# By default, we don't intercept, allowing free flow of non-registry traffic
5776
map $connect_host $interceptedHost {
5877
hostnames;
5978
include /etc/nginx/docker.intercept.map;
60-
default "$connect_host:443";
61-
}
62-
63-
map $dockerAuth $finalAuth {
64-
"" "$http_authorization"; # if empty, keep the original passed-in from the client
65-
default "Basic $dockerAuth"; # if not empty, add the Basic preamble to the auth
79+
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.'
6680
}
6781

6882

@@ -86,6 +100,7 @@ http {
86100
server_name _;
87101

88102
# dont log the CONNECT proxy.
103+
#access_log /var/log/nginx/access.log debug_proxy;
89104
access_log off;
90105

91106
proxy_connect;
@@ -97,7 +112,8 @@ http {
97112

98113
# forward proxy for non-CONNECT request
99114
location / {
100-
return 403 "The docker caching proxy is working!";
115+
add_header "Content-type" "text/plain" always;
116+
return 200 "docker-registry-proxy: The docker caching proxy is working!";
101117
}
102118

103119
location /ca.crt {
@@ -145,7 +161,7 @@ http {
145161
# Use cache locking, with a huge timeout, so that multiple Docker clients asking for the same blob at the same time
146162
# will wait for the first to finish instead of doing multiple upstream requests.
147163
proxy_cache_lock on;
148-
proxy_cache_lock_timeout 120s;
164+
proxy_cache_lock_timeout 880s;
149165

150166
# Cache all 200, 301, 302, and 307 (emitted by private registries) for 60 days.
151167
proxy_cache_valid 200 301 302 307 60d;
@@ -165,55 +181,18 @@ http {
165181
# This comes from a include file generated by the entrypoint.
166182
include /etc/nginx/docker.verify.ssl.conf;
167183

168-
# Some debugging info
169-
# add_header X-Docker-Caching-Proxy-Real-Host $realHost;
170-
# add_header X-Docker-Caching-Proxy-Real-Path $realPath;
171-
# add_header X-Docker-Caching-Proxy-Auth $finalAuth;
172-
173184
# Block API v1. We dont know how to handle these.
174185
# Docker-client should start with v2 and fallback to v1 if something fails, for example, if authentication failed to a protected v2 resource.
175186
location /v1 {
176-
return 405 "API v1 is invalid. Either the image does not exist upstream, or you need auth to get a v2 endpoint working against $host";
187+
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";
177188
}
178189

179-
# don't cache mutable entity /v2/<name>/manifests/<reference> (unless the reference is a digest)
180-
location ~ ^/v2/[^\/]+/manifests/(?![A-Fa-f0-9_+.-]+:) {
181-
proxy_pass https://$targetHost;
182-
add_header X-Docker-Caching-Proxy-Debug-Cache "no:manifests";
183-
}
184190

185-
# don't cache mutable entity /v2/<name>/tags/list
186-
location ~ ^/v2/[^\/]+/tags/list {
187-
proxy_pass https://$targetHost;
188-
proxy_cache off;
189-
add_header X-Docker-Caching-Proxy-Debug-Cache "no:tagslist";
190-
}
191-
192-
# don't cache mutable entity /v2/_catalog
193-
location ~ ^/v2/_catalog$ {
194-
proxy_pass https://$targetHost;
195-
proxy_cache off;
196-
add_header X-Docker-Caching-Proxy-Debug-Cache "no:catalog";
197-
}
198-
199-
# dont cache the first hit which is always /v2/
200-
location = /v2/ {
201-
proxy_pass https://$targetHost;
202-
proxy_cache off;
203-
add_header X-Docker-Caching-Proxy-Debug-Cache "no:rootv2";
204-
}
205-
206-
# dont cache /token (done against auth servers)
207-
location = /token {
208-
proxy_pass https://$targetHost;
209-
proxy_cache off;
210-
add_header X-Docker-Caching-Proxy-Debug-Cache "no:token";
211-
}
212-
213-
# cache everything else
214-
location / {
191+
# for the /v2/..../blobs/.... URIs, do cache, and treat redirects.
192+
location ~ ^/v2/(.*)/blobs/ {
215193
proxy_pass https://$targetHost;
216194
proxy_cache cache;
195+
add_header X-Docker-Caching-Proxy-Debug-Cache "yes:blobs";
217196

218197
# Handling of redirects.
219198
# Many registries (eg, quay.io, or k8s.gcr.io) emit a Location redirect
@@ -223,10 +202,9 @@ http {
223202
# We to it twice, one for http and another for https.
224203
proxy_redirect ~^https://([^:/]+)(/.+)$ https://docker.caching.proxy.internal/forcecachesecure/$1/originalwas$2;
225204
proxy_redirect ~^http://([^:/]+)(/.+)$ http://docker.caching.proxy.internal/forcecacheinsecure/$1/originalwas$2;
226-
227-
add_header X-Docker-Caching-Proxy-Debug-Cache "yes:everythingelse";
228205
}
229206

207+
230208
# handling for the redirect case explained above, with https.
231209
# The $realHost and $realPath variables come from a map defined at the top of this file.
232210
location /forcecachesecure {
@@ -251,5 +229,13 @@ http {
251229

252230
add_header X-Docker-Caching-Proxy-Debug-Cache "yes:forcecacheinsecure";
253231
}
232+
233+
# by default, dont cache anything.
234+
location / {
235+
proxy_pass https://$targetHost;
236+
proxy_cache off;
237+
add_header X-Docker-Caching-Proxy-Debug-Cache "no:default";
238+
}
239+
254240
}
255241
}

0 commit comments

Comments
 (0)