Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Little cleanup #79

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ LABEL org.opencontainers.image.source https://github.com/rpardini/docker-registr
RUN apk add --no-cache --update bash ca-certificates-bundle coreutils openssl

# If set to 1, enables building mitmproxy, which helps a lot in debugging, but is super heavy to build.
ARG DEBUG_BUILD="1"
ARG DEBUG_BUILD="0"
ENV DO_DEBUG_BUILD="$DEBUG_BUILD"

# Build mitmproxy via pip. This is heavy, takes minutes do build and creates a 90mb+ layer. Oh well.
RUN [[ "a$DO_DEBUG_BUILD" == "a1" ]] && { echo "Debug build ENABLED." \
RUN [ "$DO_DEBUG_BUILD" = "1" ] && { echo "Debug build ENABLED." \
&& apk add --no-cache --update su-exec git g++ libffi libffi-dev libstdc++ openssl-dev python3 python3-dev py3-pip py3-wheel py3-six py3-idna py3-certifi py3-setuptools \
&& LDFLAGS=-L/lib pip install mitmproxy==5.2 \
&& apk del --purge git g++ libffi-dev openssl-dev python3-dev py3-pip py3-wheel \
Expand All @@ -28,7 +28,7 @@ RUN [[ "a$DO_DEBUG_BUILD" == "a1" ]] && { echo "Debug build ENABLED." \
ENV LANG=en_US.UTF-8

# Check the installed mitmproxy version, if built.
RUN [[ "a$DO_DEBUG_BUILD" == "a1" ]] && { mitmproxy --version && mitmweb --version ; } || { echo "Debug build disabled."; }
RUN [ "$DO_DEBUG_BUILD" = "1" ] && { mitmproxy --version && mitmweb --version ; } || { echo "Debug build disabled."; }

# Create the cache directory and CA directory
RUN mkdir -p /docker_mirror_cache /ca
Expand Down Expand Up @@ -60,17 +60,24 @@ EXPOSE 8082

## Default envs.
# A space delimited list of registries we should proxy and cache; this is in addition to the central DockerHub.
ENV REGISTRIES="k8s.gcr.io gcr.io quay.io"
ENV REGISTRIES="auth.docker.io registry-1.docker.io docker.caching.proxy.internal k8s.gcr.io gcr.io quay.io gitlab.com registry.gitlab.com"
# A space delimited list of registry:user:password to inject authentication for
ENV AUTH_REGISTRIES="some.authenticated.registry:oneuser:onepassword another.registry:user:password"
# (e.g. AUTH_REGISTRIES="auth.docker.io:dhuser:dhpass gitlab.com:gluser:glpass")
ENV AUTH_REGISTRIES=""
# Should we verify upstream's certificates? Default to true.
ENV VERIFY_SSL="true"

# Enable debugging mode; this inserts mitmproxy/mitmweb between the CONNECT proxy and the caching layer
ENV DEBUG="false"
# Enable debugging mode; this inserts mitmproxy/mitmweb between the caching layer and DockerHub's registry
ENV DEBUG_HUB="false"
# Enable nginx debugging mode; this uses nginx-debug binary and enabled debug logging, which is VERY verbose so separate setting
ENV DEBUG_NGINX="false"
# Enable debugging mode for creating CA certificate
ENV DEBUG_CA_CERT="false"

# Set Docker Registry cache size, by default, 32 GB ('32g')
ENV CACHE_MAX_SIZE="32g"

# Manifest caching tiers. Disabled by default, to mimick 0.4/0.5 behaviour.
# Setting it to true enables the processing of the ENVs below.
Expand All @@ -94,8 +101,13 @@ ENV MANIFEST_CACHE_SECONDARY_TIME="60d"
# In the default config, :latest and other frequently-used tags will get this value.
ENV MANIFEST_CACHE_DEFAULT_TIME="1h"

# Should we allow overridding with own authentication, default to false.
ENV ALLOW_OWN_AUTH="false"

# Should we allow actions different than pull, default to false.
ENV ALLOW_PUSH="false"
# Should we allow push only with own authentication, default to false.
ENV ALLOW_PUSH_WITH_OWN_AUTH="false"

# Timeouts
# ngx_http_core_module
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ for this to work it requires inserting a root CA certificate into system trusted
- Expose port 3128 to the network
- Map volume `/docker_mirror_cache` for up to `CACHE_MAX_SIZE` (32gb by default) of cached images across all cached registries
- Map volume `/ca`, the proxy will store the CA certificate here across restarts. **Important** this is security sensitive.
- Env `ALLOW_PUSH` : This bypasses the proxy when pushing, default to false - if kept to false, pushing will not work. For more info see this [commit](https://github.com/rpardini/docker-registry-proxy/commit/536f0fc8a078d03755f1ae8edc19a86fc4b37fcf).
- Env `ALLOW_OWN_AUTH` (default `false`): Allow overridding the `AUTH_REGISTRIES` authentication with own Docker credentials if provided (to support `docker login` as another user).
- Env `ALLOW_PUSH` (default `false`): This bypasses the proxy when pushing, default to false - if kept to false, pushing will not work. For more info see this [commit](https://github.com/rpardini/docker-registry-proxy/commit/536f0fc8a078d03755f1ae8edc19a86fc4b37fcf).
- Env `ALLOW_PUSH_WITH_OWN_AUTH` (default `false`): Allow bypassing the proxy when pushing only if own authentication is provided.
- Env `CACHE_MAX_SIZE` (default `32g`): set the max size to be used for caching local Docker image layers. Use [Nginx sizes](http://nginx.org/en/docs/syntax.html).
- Env `ENABLE_MANIFEST_CACHE`, see the section on pull rate limiting.
- Env `REGISTRIES`: space separated list of registries to cache; no need to include DockerHub, its already done internally.
Expand Down
24 changes: 11 additions & 13 deletions create_ca_cert.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

set -Eeuo pipefail

declare -i DEBUG=0

logInfo() {
echo "INFO: $@"
}
Expand Down Expand Up @@ -53,10 +51,10 @@ subjectKeyIdentifier = hash
EOF
)

[[ ${DEBUG} -gt 0 ]] && logInfo "show the CA cert details"
[[ ${DEBUG} -gt 0 ]] && openssl x509 -noout -text -in ${CA_CRT_FILE}
[ "${DEBUG_CA_CERT}" = "true" ] && logInfo "show the CA cert details"
[ "${DEBUG_CA_CERT}" = "true" ] && openssl x509 -noout -text -in ${CA_CRT_FILE}

echo 01 > ${CA_SRL_FILE}
echo "01" > ${CA_SRL_FILE}

fi

Expand All @@ -78,8 +76,8 @@ subjectKeyIdentifier = hash
EOF
)

[[ ${DEBUG} -gt 0 ]] && logInfo "Show the singing request, to make sure extensions are there"
[[ ${DEBUG} -gt 0 ]] && openssl req -in ia.csr -noout -text
[ "${DEBUG_CA_CERT}" = "true" ] && logInfo "Show the singing request, to make sure extensions are there"
[ "${DEBUG_CA_CERT}" = "true" ] && openssl req -in ia.csr -noout -text

logInfo "Sign the IA request with the CA cert and key, producing the IA cert"
openssl x509 -req -days 730 -in ia.csr -CA ${CA_CRT_FILE} -CAkey ${CA_KEY_FILE} -CAserial ${CA_SRL_FILE} -out ia.crt -passin pass:foobar -extensions IA -extfile <(
Expand All @@ -95,8 +93,8 @@ EOF
) &> /dev/null


[[ ${DEBUG} -gt 0 ]] && logInfo "show the IA cert details"
[[ ${DEBUG} -gt 0 ]] && openssl x509 -noout -text -in ia.crt
[ "${DEBUG_CA_CERT}" = "true" ] && logInfo "show the IA cert details"
[ "${DEBUG_CA_CERT}" = "true" ] && openssl x509 -noout -text -in ia.crt

logInfo "Initialize the serial number for signed certificates"
echo 01 > ia.srl
Expand All @@ -108,14 +106,14 @@ openssl rsa -passin pass:foobar -in web.orig.key -out web.key &> /dev/null
logInfo "Create the signing request, using extensions"
openssl req -new -key web.key -sha256 -out web.csr -passin pass:foobar -subj "/C=NL/ST=Noord Holland/L=Amsterdam/O=ME/OU=IT/CN=${CN_WEB}" -reqexts SAN -config <(cat <(printf "[req]\ndistinguished_name = dn\n[dn]\n[SAN]\nsubjectAltName=${ALLDOMAINS}"))

[[ ${DEBUG} -gt 0 ]] && logInfo "Show the singing request, to make sure extensions are there"
[[ ${DEBUG} -gt 0 ]] && openssl req -in web.csr -noout -text
[ "${DEBUG_CA_CERT}" = "true" ] && logInfo "Show the singing request, to make sure extensions are there"
[ "${DEBUG_CA_CERT}" = "true" ] && openssl req -in web.csr -noout -text

logInfo "Sign the request, using the intermediate cert and key"
openssl x509 -req -days 365 -in web.csr -CA ia.crt -CAkey ia.key -out web.crt -passin pass:foobar -extensions SAN -extfile <(cat <(printf "[req]\ndistinguished_name = dn\n[dn]\n[SAN]\nsubjectAltName=${ALLDOMAINS}")) &> /dev/null

[[ ${DEBUG} -gt 0 ]] && logInfo "Show the final cert details"
[[ ${DEBUG} -gt 0 ]] && openssl x509 -noout -text -in web.crt
[ "${DEBUG_CA_CERT}" = "true" ] && logInfo "Show the final cert details"
[ "${DEBUG_CA_CERT}" = "true" ] && openssl x509 -noout -text -in web.crt

logInfo "Concatenating fullchain.pem..."
cat web.crt ia.crt ${CA_CRT_FILE} > fullchain.pem
Expand Down
96 changes: 64 additions & 32 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ trap "echo TRAPed signal" HUP INT QUIT TERM

#configure nginx DNS settings to match host, why must we do that nginx?
export RESOLVERS=$(awk '$1 == "nameserver" {print ($2 ~ ":")? "["$2"]": $2}' ORS=' ' /etc/resolv.conf | sed 's/ *$//g')
if [ "x$RESOLVERS" = "x" ]; then
if [ -z "$RESOLVERS" ]; then
echo "Warning: unable to determine DNS resolvers for nginx" >&2
exit 66
fi
Expand All @@ -20,7 +20,7 @@ done

echo "Final chosen resolver: $conf"
confpath=/etc/nginx/resolvers.conf
if [ ! -e $confpath ] || [ "$conf" != "$(cat $confpath)" ]
if [ ! -e "$confpath" ] || [ "$conf" != "$(cat "$confpath")" ]
then
echo "Using auto-determined resolver '$conf' via '$confpath'"
echo "$conf" > $confpath
Expand All @@ -36,7 +36,7 @@ ALLDOMAINS=""
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
for ONEREGISTRYIN in ${REGISTRIES}; do
ONEREGISTRY=$(echo ${ONEREGISTRYIN} | xargs) # Remove whitespace
echo "Adding certificate for registry: $ONEREGISTRY"
ALLDOMAINS="${ALLDOMAINS},DNS:${ONEREGISTRY}"
Expand All @@ -55,7 +55,7 @@ echo -n "" > /etc/nginx/docker.targetHost.map
echo -n "" > /etc/nginx/docker.auth.map

# Only configure auth registries if the env var contains values
if [ "$AUTH_REGISTRIES" ]; then
if [ -n "$AUTH_REGISTRIES" ]; then
# Ref: https://stackoverflow.com/a/47633817/219530
AUTH_REGISTRIES_DELIMITER=${AUTH_REGISTRIES_DELIMITER:-" "}
s=$AUTH_REGISTRIES$AUTH_REGISTRIES_DELIMITER
Expand Down Expand Up @@ -87,64 +87,96 @@ fi
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}

# 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 /docker_mirror_cache levels=1:2 max_size=$CACHE_MAX_SIZE inactive=60d keys_zone=cache:10m use_temp_path=off;" > /etc/nginx/conf.d/cache_max_size.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
if [ "${ENABLE_MANIFEST_CACHE}" = "true" ]; then
[ -n "${MANIFEST_CACHE_PRIMARY_REGEX}" ] && cat <<EOF >>/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";
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
EOF
[ -n "${MANIFEST_CACHE_SECONDARY_REGEX}" ] && cat <<EOF >>/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";
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
EOF
cat <<EOF >>/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";
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
EOF
else
cat <<EOF >>/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";
proxy_cache_valid 0s;
include "/etc/nginx/nginx.manifest.stale.conf";
}
EOD
EOF
fi

echo -e "\nManifest caching config: ---\n"
cat /etc/nginx/nginx.manifest.caching.config.conf
echo "---"

if [[ "a${ALLOW_PUSH}" == "atrue" ]]; then
echo -n "" > /etc/nginx/conf.d/allowed_override_auth.conf
if [ "${ALLOW_OWN_AUTH}" = "true" ]; 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
fi

if [ "${ALLOW_PUSH}" = "true" ]; 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 [ "${ALLOW_PUSH_WITH_OWN_AUTH}" = "true" ]; 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.
Expand All @@ -163,8 +195,8 @@ fi
# normally use non-debug version of nginx
NGINX_BIN="/usr/sbin/nginx"

if [[ "a${DEBUG}" == "atrue" ]]; then
if [[ ! -f /usr/bin/mitmweb ]]; then
if [ "${DEBUG}" = "true" ]; then
if [ ! -f /usr/bin/mitmweb ]; then
echo "To debug, you need the -debug version of this image, eg: :latest-debug"
exit 3
fi
Expand All @@ -182,8 +214,8 @@ if [[ "a${DEBUG}" == "atrue" ]]; then
echo "Access mitmweb via http://127.0.0.1:8081/ "
fi

if [[ "a${DEBUG_HUB}" == "atrue" ]]; then
if [[ ! -f /usr/bin/mitmweb ]]; then
if [ "${DEBUG_HUB}" = "true" ]; then
if [ ! -f /usr/bin/mitmweb ]; then
echo "To debug, you need the -debug version of this image, eg: :latest-debug"
exit 3
fi
Expand All @@ -205,8 +237,8 @@ if [[ "a${DEBUG_HUB}" == "atrue" ]]; then
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
if [ "${DEBUG_NGINX}" = "true" ]; 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
Expand All @@ -219,8 +251,8 @@ fi


# Timeout configurations
echo "" > /etc/nginx/nginx.timeouts.config.conf
cat <<EOD >>/etc/nginx/nginx.timeouts.config.conf
echo -n "" > /etc/nginx/nginx.timeouts.config.conf
cat <<EOF >>/etc/nginx/nginx.timeouts.config.conf
# Timeouts

# ngx_http_core_module
Expand All @@ -238,23 +270,23 @@ cat <<EOD >>/etc/nginx/nginx.timeouts.config.conf
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
EOF

echo -e "\nTimeout configs: ---"
cat /etc/nginx/nginx.timeouts.config.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
echo -n "" > /etc/nginx/docker.verify.ssl.conf
if [ "${VERIFY_SSL}" = "true" ]; then
cat <<EOF > /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
EOF
echo "Upstream SSL certificate verification enabled."
else
echo "Upstream SSL certificate verification is DISABLED."
Expand Down
1 change: 1 addition & 0 deletions nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ echo "Docker configured with HTTPS_PROXY=$scheme://$http_host/"
proxy_ignore_headers X-Accel-Expires Expires Cache-Control Set-Cookie;

# Add the authentication info, if the map matched the target domain.
include "/etc/nginx/conf.d/allowed_override_auth.conf";
proxy_set_header Authorization $finalAuth;

# Use SNI during the TLS handshake with the upstream.
Expand Down