forked from rpardini/docker-registry-proxy
-
Notifications
You must be signed in to change notification settings - Fork 9
/
entrypoint.sh
402 lines (335 loc) · 15.5 KB
/
entrypoint.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
#! /bin/bash
echo "Entrypoint starting."
set -Eeuo pipefail
trap "echo TRAPed signal" HUP INT QUIT TERM
# configure nginx DNS settings to match host, why must we do that nginx?
# this leads to a world of problems. ipv6 format being different, etc.
# below is a collection of hacks contributed over the years.
echo "-- resolv.conf:"
cat /etc/resolv.conf
echo "-- end resolv"
# Podman adds a "%3" to the end of the last resolver? I don't get it. Strip it out.
export RESOLVERS=$(cat /etc/resolv.conf | sed -e 's/%3//g' | awk '$1 == "nameserver" {print ($2 ~ ":")? "["$2"]": $2}' ORS=' ' | sed 's/ *$//g')
if [ "x$RESOLVERS" = "x" ]; then
echo "Warning: unable to determine DNS resolvers for nginx" >&2
exit 66
fi
echo "DEBUG, determined RESOLVERS from /etc/resolv.conf: '$RESOLVERS'"
conf=""
for ONE_RESOLVER in ${RESOLVERS}; do
echo "Possible resolver: $ONE_RESOLVER"
conf="resolver $ONE_RESOLVER ipv6=off; "
done
echo "Final chosen resolver: $conf"
confpath=/etc/nginx/resolvers.conf
if [ ! -e $confpath ] || [ "$conf" != "$(cat $confpath)" ]
then
echo "Using auto-determined resolver '$conf' via '$confpath'"
echo "$conf" > $confpath
else
echo "Not using resolver config, keep existing '$confpath' -- mounted by user?"
fi
# The list of SAN (Subject Alternative Names) for which we will create a TLS certificate.
ALLDOMAINS=""
# Interceptions map, which are the hosts that will be handled by the caching part.
# It should list exactly the same hosts we have created certificates for -- if not, Docker will get TLS errors, of course.
echo -n "" > /etc/nginx/docker.intercept.map
# Some hosts/registries are always needed, but others can be configured in env var REGISTRIES
for ONEREGISTRYIN in docker.caching.proxy.internal registry-1.docker.io auth.docker.io ${REGISTRIES}; do
ONEREGISTRY=$(echo ${ONEREGISTRYIN} | xargs) # Remove whitespace
echo "Adding certificate for registry: $ONEREGISTRY"
ALLDOMAINS="${ALLDOMAINS},DNS:${ONEREGISTRY}"
echo "${ONEREGISTRY} 127.0.0.1:443;" >> /etc/nginx/docker.intercept.map
done
# Clean the list and generate certificates.
export ALLDOMAINS=${ALLDOMAINS:1} # remove the first comma and export
/create_ca_cert.sh # This uses ALLDOMAINS to generate the certificates.
# Target host interception. Empty by default. Used to intercept outgoing requests
# from the proxy to the registries.
echo -n "" > /etc/nginx/docker.targetHost.map
# Now handle the auth part.
echo -n "" > /etc/nginx/docker.auth.map
# Only configure auth registries if the env var contains values
if [ "$AUTH_REGISTRIES" ]; then
# Ref: https://stackoverflow.com/a/47633817/219530
AUTH_REGISTRIES_DELIMITER=${AUTH_REGISTRIES_DELIMITER:-" "}
s=$AUTH_REGISTRIES$AUTH_REGISTRIES_DELIMITER
auth_array=();
while [[ $s ]]; do
auth_array+=( "${s%%"$AUTH_REGISTRIES_DELIMITER"*}" );
s=${s#*"$AUTH_REGISTRIES_DELIMITER"};
done
AUTH_REGISTRY_DELIMITER=${AUTH_REGISTRY_DELIMITER:-":"}
for ONEREGISTRY in "${auth_array[@]}"; do
s=$ONEREGISTRY$AUTH_REGISTRY_DELIMITER
registry_array=();
while [[ $s ]]; do
registry_array+=( "${s%%"$AUTH_REGISTRY_DELIMITER"*}" );
s=${s#*"$AUTH_REGISTRY_DELIMITER"};
done
AUTH_HOST="${registry_array[0]}"
AUTH_USER="${registry_array[1]}"
AUTH_PASS="${registry_array[2]}"
AUTH_BASE64=$(echo -n ${AUTH_USER}:${AUTH_PASS} | base64 -w0 | xargs)
echo "Adding Auth for registry '${AUTH_HOST}' with user '${AUTH_USER}'."
echo "\"${AUTH_HOST}\" \"${AUTH_BASE64}\";" >> /etc/nginx/docker.auth.map
done
fi
# create default config for the caching layer to listen on 443.
echo " listen 443 ssl default_server;" > /etc/nginx/caching.layer.listen
echo "error_log /var/log/nginx/error.log warn;" > /etc/nginx/error.log.debug.warn
# Set Docker Registry cache size, by default, 32 GB ('32g')
CACHE_MAX_SIZE=${CACHE_MAX_SIZE:-32g}
# Set cache directory location, by default, /docker_mirror_cache
CACHE_DIRECTORY=${CACHE_DIRECTORY:-/docker_mirror_cache}
# The cache directory. This can get huge. Better to use a Docker volume pointing here!
# Set to 32gb which should be enough
echo "proxy_cache_path ${CACHE_DIRECTORY} levels=1:2 max_size=${CACHE_MAX_SIZE:-15g} min_free=${CACHE_MIN_FREE:-1g} inactive=${CACHE_INACTIVE_TIME:-60d} keys_zone=cache:${CACHE_KEYS_ZONE:-15m} use_temp_path=off manager_threshold=${CACHE_MANAGER_THRESHOLD:-1000ms} manager_sleep=${CACHE_MANAGER_SLEEP:-250ms} manager_files=${CACHE_MANAGER_FILES:-100} loader_files=${CACHE_LOADER_FILES:-100} loader_threshold=${CACHE_LOADER_THRESHOLD:-200ms} loader_sleep=${CACHE_LOADER_SLEEP:-50ms};" > /etc/nginx/conf.d/cache_max_size.conf
if [[ "a${SLOW_TIER_ENABLED}" == "atrue" ]]; then
{
echo ""
echo "proxy_cache_path ${SLOW_CACHE_DIRECTORY} levels=1:2 max_size=${SLOW_CACHE_MAX_SIZ:-15g} min_free=${SLOW_CACHE_MIN_FREE:-1g} inactive=${SLOW_CACHE_INACTIVE_TIME:-120d} keys_zone=slow_cache:${SLOW_CACHE_KEYS_ZONE:-150m} use_temp_path=off manager_threshold=${SLOW_CACHE_MANAGER_THRESHOLD:-1000ms} manager_sleep=${SLOW_CACHE_MANAGER_SLEEP:-250ms} manager_files=${SLOW_CACHE_MANAGER_FILES:-100} loader_files=${SLOW_CACHE_LOADER_FILES:-100} loader_threshold=${SLOW_CACHE_LOADER_THRESHOLD:-200ms} loader_sleep=${SLOW_CACHE_LOADER_SLEEP:-50ms};"
echo ""
echo "map \$request_uri \$cache {"
echo " ${SLOW_TIER_URIS%;};" | sed 's/;/ slow_cache;\n /g'
echo " default cache;"
echo "}"
echo ""
} >> /etc/nginx/conf.d/cache_max_size.conf; else
{
echo ""
echo "map \$request_uri \$cache {"
echo " default cache;"
echo "}"
echo ""
} >> /etc/nginx/conf.d/cache_max_size.conf;
fi
# Clear the cache directory if the free space is less than the threshold
# Get the available space in the directory
free_space=$(df -BG "${CACHE_DIRECTORY}" | awk 'NR==2 {print $4}' | tr -d 'G')
min_free=$(sed 's/g//I' <<< "${CACHE_MIN_FREE:-1g}")
# Compare available space with the threshold
if [[ ! ("${free_space}" -gt "${min_free}") ]]; then
echo "Free space in ${CACHE_DIRECTORY} is $free_space; less than defined CACHE_MIN_FREE $min_free; attempting clean-up before starting"
rm -rf "${CACHE_DIRECTORY:?}"/*
fi
# Set Docker Registry cache valid time, by default, 60 day ('60d')
CACHE_VALID_TIME=${CACHE_VALID_TIME:-60d}
# Set default cache valid time for 200 and 205 response.
sed -i "/# Cache all 200, 206 for 60 days default./a\ proxy_cache_valid 200 206 ${CACHE_VALID_TIME};" /etc/nginx/nginx.conf
# Manifest caching configuration. We generate config based on the environment vars.
echo -n "" >/etc/nginx/nginx.manifest.caching.config.conf
[[ "a${ENABLE_MANIFEST_CACHE}" == "atrue" ]] && [[ "a${MANIFEST_CACHE_PRIMARY_REGEX}" != "a" ]] && cat <<EOD >>/etc/nginx/nginx.manifest.caching.config.conf
# First tier caching of manifests; configure via MANIFEST_CACHE_PRIMARY_REGEX and MANIFEST_CACHE_PRIMARY_TIME
location ~ ^/v2/(.*)/manifests/${MANIFEST_CACHE_PRIMARY_REGEX} {
set \$docker_proxy_request_type "manifest-primary";
set \$cache_key \$uri;
proxy_cache_valid ${MANIFEST_CACHE_PRIMARY_TIME};
include "/etc/nginx/nginx.manifest.stale.conf";
}
EOD
[[ "a${ENABLE_MANIFEST_CACHE}" == "atrue" ]] && [[ "a${MANIFEST_CACHE_SECONDARY_REGEX}" != "a" ]] && cat <<EOD >>/etc/nginx/nginx.manifest.caching.config.conf
# Secondary tier caching of manifests; configure via MANIFEST_CACHE_SECONDARY_REGEX and MANIFEST_CACHE_SECONDARY_TIME
location ~ ^/v2/(.*)/manifests/${MANIFEST_CACHE_SECONDARY_REGEX} {
set \$docker_proxy_request_type "manifest-secondary";
set \$cache_key \$uri;
proxy_cache_valid ${MANIFEST_CACHE_SECONDARY_TIME};
include "/etc/nginx/nginx.manifest.stale.conf";
}
EOD
[[ "a${ENABLE_MANIFEST_CACHE}" == "atrue" ]] && cat <<EOD >>/etc/nginx/nginx.manifest.caching.config.conf
# Default tier caching for manifests. Caches for ${MANIFEST_CACHE_DEFAULT_TIME} (from MANIFEST_CACHE_DEFAULT_TIME)
location ~ ^/v2/(.*)/manifests/ {
set \$docker_proxy_request_type "manifest-default";
set \$cache_key \$uri;
proxy_cache_valid ${MANIFEST_CACHE_DEFAULT_TIME};
include "/etc/nginx/nginx.manifest.stale.conf";
}
EOD
[[ "a${ENABLE_MANIFEST_CACHE}" != "atrue" ]] && cat <<EOD >>/etc/nginx/nginx.manifest.caching.config.conf
# Manifest caching is disabled. Enable it with ENABLE_MANIFEST_CACHE=true
location ~ ^/v2/(.*)/manifests/ {
set \$docker_proxy_request_type "manifest-default-disabled";
set \$cache_key \$uri;
proxy_cache_valid 0s;
include "/etc/nginx/nginx.manifest.stale.conf";
}
EOD
echo -e "\nManifest caching config: ---\n"
cat /etc/nginx/nginx.manifest.caching.config.conf
echo "---"
if [[ "a${ALLOW_OWN_AUTH}" == "atrue" ]]; then
cat << 'EOF' > /etc/nginx/conf.d/allowed_override_auth.conf
if ($http_authorization != "") {
# override with own authentication if provided
set $finalAuth $http_authorization;
}
EOF
else
echo '' > /etc/nginx/conf.d/allowed_override_auth.conf
fi
if [[ "a${ALLOW_PUSH}" == "atrue" ]]; then
cat <<EOF > /etc/nginx/conf.d/allowed.methods.conf
# allow to upload big layers
client_max_body_size 0;
# only cache GET requests
proxy_cache_methods GET;
EOF
elif [[ "a${ALLOW_PUSH_WITH_OWN_AUTH}" == "atrue" ]]; then
cat << 'EOF' > /etc/nginx/conf.d/allowed.methods.conf
# Block POST/PUT/DELETE if own authentication is not provided.
set $combined_ha_rm "$http_authorization$request_method";
if ($combined_ha_rm = POST) {
return 405 "POST method is not allowed";
}
if ($combined_ha_rm = PUT) {
return 405 "PUT method is not allowed";
}
if ($combined_ha_rm = DELETE) {
return 405 "DELETE method is not allowed";
}
if ($http_authorization != "") {
# override with own authentication if provided
set $finalAuth $http_authorization;
}
# allow to upload big layers
client_max_body_size 0;
# only cache GET requests
proxy_cache_methods GET;
EOF
else
cat << 'EOF' > /etc/nginx/conf.d/allowed.methods.conf
# Block POST/PUT/DELETE. Don't use this proxy for pushing.
if ($request_method = POST) {
return 405 "POST method is not allowed";
}
if ($request_method = PUT) {
return 405 "PUT method is not allowed";
}
if ($request_method = DELETE) {
return 405 "DELETE method is not allowed";
}
EOF
fi
# normally use non-debug version of nginx
NGINX_BIN="/usr/sbin/nginx"
if [[ "a${DEBUG}" == "atrue" ]]; then
if [[ ! -f /usr/bin/mitmweb ]]; then
echo "To debug, you need the -debug version of this image, eg: :latest-debug"
exit 3
fi
# in debug mode, change caching layer to listen on 444, so that mitmproxy can sit in the middle.
echo " listen 444 ssl default_server;" > /etc/nginx/caching.layer.listen
echo "Starting in DEBUG MODE (mitmproxy)." >&2
echo "Run mitmproxy with reverse pointing to the same certs..."
mitmweb --no-web-open-browser --set web_host=0.0.0.0 --set confdir=~/.mitmproxy-incoming \
--set termlog_verbosity=error --set stream_large_bodies=128k --web-port 8081 \
--set keep_host_header=true --set ssl_insecure=true \
--mode reverse:https://127.0.0.1:444 --listen-host 0.0.0.0 \
--listen-port 443 --certs /certs/fullchain_with_key.pem &
echo "Access mitmweb via http://127.0.0.1:8081/ "
fi
if [[ "a${DEBUG_HUB}" == "atrue" ]]; then
if [[ ! -f /usr/bin/mitmweb ]]; then
echo "To debug, you need the -debug version of this image, eg: :latest-debug"
exit 3
fi
# in debug hub mode, we remap targetHost to point to mitmproxy below
echo "\"registry-1.docker.io\" \"127.0.0.1:445\";" > /etc/nginx/docker.targetHost.map
echo "Debugging outgoing DockerHub connections via mitmproxy on 8082." >&2
# this one has keep_host_header=false so we don't need to modify nginx config
mitmweb --no-web-open-browser --set web_host=0.0.0.0 --set confdir=~/.mitmproxy-outgoing-hub \
--set termlog_verbosity=error --set stream_large_bodies=128k --web-port 8082 \
--set keep_host_header=false --set ssl_insecure=true \
--mode reverse:https://registry-1.docker.io --listen-host 0.0.0.0 \
--listen-port 445 --certs /certs/fullchain_with_key.pem &
echo "Warning, DockerHub outgoing debugging disables upstream SSL verification for all upstreams." >&2
VERIFY_SSL=false
echo "Access mitmweb for outgoing DockerHub requests via http://127.0.0.1:8082/ "
fi
if [[ "a${DEBUG_NGINX}" == "atrue" ]]; then
if [[ ! -f /usr/sbin/nginx-debug ]]; then
echo "To debug, you need the -debug version of this image, eg: :latest-debug"
exit 4
fi
echo "Starting in DEBUG MODE (nginx)."
echo "error_log /var/log/nginx/error.log debug;" > /etc/nginx/error.log.debug.warn
# use debug binary
NGINX_BIN="/usr/sbin/nginx-debug"
fi
# Timeout configurations
echo "" > /etc/nginx/nginx.timeouts.config.conf
cat <<EOD >>/etc/nginx/nginx.timeouts.config.conf
# Timeouts
# ngx_http_core_module
keepalive_timeout ${KEEPALIVE_TIMEOUT};
send_timeout ${SEND_TIMEOUT};
client_body_timeout ${CLIENT_BODY_TIMEOUT};
client_header_timeout ${CLIENT_HEADER_TIMEOUT};
# ngx_http_proxy_module
proxy_read_timeout ${PROXY_READ_TIMEOUT};
proxy_connect_timeout ${PROXY_CONNECT_TIMEOUT};
proxy_send_timeout ${PROXY_SEND_TIMEOUT};
# ngx_http_proxy_connect_module - external module
proxy_connect_read_timeout ${PROXY_CONNECT_READ_TIMEOUT};
proxy_connect_connect_timeout ${PROXY_CONNECT_CONNECT_TIMEOUT};
proxy_connect_send_timeout ${PROXY_CONNECT_SEND_TIMEOUT};
EOD
echo -e "\nTimeout configs: ---"
cat /etc/nginx/nginx.timeouts.config.conf
echo -e "---\n"
# Request buffering
echo "" > /etc/nginx/proxy.buffering.conf
if [[ "a${PROXY_BUFFERING}" == "afalse" ]]; then
cat << EOD > /etc/nginx/proxy.buffering.conf
proxy_buffering off;
EOD
fi
echo -e "\nBuffering: ---"
cat /etc/nginx/proxy.buffering.conf
echo -e "---\n"
# Request buffering
echo "" > /etc/nginx/proxy.request.buffering.conf
if [[ "a${PROXY_REQUEST_BUFFERING}" == "afalse" ]]; then
cat << EOD > /etc/nginx/proxy.request.buffering.conf
proxy_max_temp_file_size 0;
proxy_request_buffering off;
proxy_http_version 1.1;
EOD
fi
echo -e "\nRequest buffering: ---"
cat /etc/nginx/proxy.request.buffering.conf
echo -e "---\n"
# force upstream to use http 1.1
echo "" > /etc/nginx/http1.1.upstream.conf
if [[ "a${FORCE_UPSTREAM_HTTP_1_1}" == "atrue" ]]; then
cat << EOD > /etc/nginx/http1.1.upstream.conf
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
EOD
fi
echo -e "\nConfigure upstream http version support: ---"
cat /etc/nginx/http1.1.upstream.conf
echo -e "---\n"
# Upstream SSL verification.
echo "" > /etc/nginx/docker.verify.ssl.conf
if [[ "a${VERIFY_SSL}" == "atrue" ]]; then
cat << EOD > /etc/nginx/docker.verify.ssl.conf
# We actually wanna be secure and avoid mitm attacks.
# Fitting, since this whole thing is a mitm...
# We'll accept any cert signed by a CA trusted by Mozilla (ca-certificates-bundle in alpine)
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
proxy_ssl_verify_depth 2;
EOD
echo "Upstream SSL certificate verification enabled."
else
echo "Upstream SSL certificate verification is DISABLED."
fi
# Set worker processes if provided
sed -i "s/worker_processes auto;/worker_processes ${WORKER_PROCESSES};/g" /etc/nginx/nginx.conf
echo "Testing nginx config..."
${NGINX_BIN} -t
echo "Starting nginx! Have a nice day."
${NGINX_BIN} -g "daemon off;"