diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 36e6b31..a341f60 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,16 +1,16 @@ name: test on: - workflow_dispatch: - pull_request: - branches: - - master + # workflow_dispatch: + # pull_request: + # branches: + # - master + push: jobs: sanity-check: runs-on: ubuntu-latest steps: - - name: Get machine's first IPv4 address for eth0 id: ip run: | @@ -25,56 +25,31 @@ jobs: # We use buildx instead of regular build so we can take advantage of Docker layer cache via Github Actions' cache - name: Set up Docker Buildx - id: buildx uses: docker/setup-buildx-action@v1 - # Setup the Github Actions cache. - - name: Cache Docker layers - uses: actions/cache@v2 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildxarch-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildxarch- - - name: Build amd64 release image locally to Docker uses: docker/build-push-action@v2 with: build-args: | DEBUG_BUILD=0 BASE_IMAGE_SUFFIX= - builder: ${{ steps.buildx.outputs.name }} context: . - file: ./Dockerfile platforms: linux/amd64 tags: sanity-check/docker-registry-proxy:latest - push: false load: true - cache-from: type=local,src=/tmp/.buildx-cache/release - # this only reads from the cache + cache-from: type=gha + cache-to: type=gha,mode=max # this only reads from the cache - name: Start proxy instance in docker (ENABLE_MANIFEST_CACHE=false) run: | - docker run -d --rm --name docker_registry_proxy \ + docker run -d --name docker_registry_proxy \ -p 0.0.0.0:3128:3128 -e ENABLE_MANIFEST_CACHE=false \ -v $(pwd)/docker_mirror_cache:/docker_mirror_cache \ -v $(pwd)/docker_mirror_certs:/ca \ sanity-check/docker-registry-proxy:latest - - name: Get the initial logs for the container into a file after 10s - run: | - echo "Sleeping 10s..." - sleep 10 - docker logs docker_registry_proxy > initial_logs.txt - - - name: Upload artifact initial_logs - uses: actions/upload-artifact@v2 - with: - name: initial_logs - path: initial_logs.txt - - name: Wait for container to be up - timeout-minutes: 1 + timeout-minutes: 2 run: | declare -i IS_UP=0 while [[ $IS_UP -lt 1 ]]; do @@ -84,14 +59,18 @@ jobs: done echo "Container is up..." + - name: Sleep to startup nginx + run: | + echo "Sleeping 10s..." + sleep 20 + - name: Grab the CA cert from running container via curl run: | curl http://${{ steps.ip.outputs.ETHER }}:3128/ca.crt | sudo tee /usr/share/ca-certificates/docker_registry_proxy.crt - name: Stop proxy instance in docker - timeout-minutes: 1 run: | - timeout 58 docker stop docker_registry_proxy + docker rm -f docker_registry_proxy - name: Refresh system-wide CA store run: | @@ -107,17 +86,14 @@ jobs: Environment="HTTPS_PROXY=http://${{ steps.ip.outputs.ETHER }}:3128/" EOD - - name: Reload systemd from disk + - name: Reload systemd from disk & dockerd via systemd run: | sudo systemctl daemon-reload - - - name: Restart dockerd via systemd - run: | sudo systemctl restart docker.service - name: Start proxy instance in docker again (ENABLE_MANIFEST_CACHE=true) run: | - docker run -d --rm --name docker_registry_proxy \ + docker run -d --name docker_registry_proxy \ -p 0.0.0.0:3128:3128 -e ENABLE_MANIFEST_CACHE=true \ -v $(pwd)/docker_mirror_cache:/docker_mirror_cache \ -v $(pwd)/docker_mirror_certs:/ca \ @@ -135,49 +111,27 @@ jobs: echo "Container is up again..." # This can be quite slow, since Github Actions runner Docker comes preloaded with a lot of images. - - name: Initial prune of all unused images from docker cache (slow) - timeout-minutes: 2 + - name: Remove images run: | - docker image prune --all --force + docker rmi -f alpine:3.6 + docker rmi -f k8s.gcr.io/pause:3.3 - name: First round of pulls - timeout-minutes: 2 run: | docker pull alpine:3.6 docker pull k8s.gcr.io/pause:3.3 - - name: Get the cold cache logs for the container into a file + - name: Remove images 2 run: | - docker logs docker_registry_proxy > cold_cache.txt - - - name: Upload artifact cold_cache - uses: actions/upload-artifact@v2 - with: - name: cold_cache - path: cold_cache.txt - - - name: prune all unused images from docker cache again - timeout-minutes: 1 - run: | - docker image prune --all --force + docker rmi -f alpine:3.6 + docker rmi -f k8s.gcr.io/pause:3.3 - name: sleep 2s to allow cache to stale a bit run: | sleep 2 - - name: Second round of pulls - timeout-minutes: 2 + - name: Get the logs + if: ${{ always() }} run: | - docker pull alpine:3.6 - docker pull k8s.gcr.io/pause:3.3 - - - name: Get the warm cache docker logs for the container into a file - run: | - docker logs docker_registry_proxy > warm_cache.txt - - - name: Upload artifact warm_cache - uses: actions/upload-artifact@v2 - with: - name: warm_cache - path: warm_cache.txt - + docker logs docker_registry_proxy || true + docker ps -a diff --git a/Dockerfile b/Dockerfile index 925cb87..5fc00aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,8 +47,7 @@ ADD nginx.manifest.stale.conf /etc/nginx/nginx.manifest.stale.conf # Add our very hackish entrypoint and ca-building scripts, make them executable ADD entrypoint.sh /entrypoint.sh -ADD create_ca_cert.sh /create_ca_cert.sh -RUN chmod +x /create_ca_cert.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh # Clients should only use 3128, not anything else. EXPOSE 3128 diff --git a/create_ca_cert.sh b/create_ca_cert.sh deleted file mode 100644 index b7c9352..0000000 --- a/create_ca_cert.sh +++ /dev/null @@ -1,124 +0,0 @@ -#! /bin/bash - -set -Eeuo pipefail - -declare -i DEBUG=0 - -logInfo() { - echo "INFO: $@" -} - -PROJ_NAME=DockerMirrorBox -logInfo "Will create certificate with names $ALLDOMAINS" - -CADATE=$(date "+%Y.%m.%d %H:%M") -CAID="$(hostname -f) ${CADATE}" - -CN_CA="${PROJ_NAME} CA Root ${CAID}" -CN_IA="${PROJ_NAME} Intermediate IA ${CAID}" -CN_WEB="${PROJ_NAME} Web Cert ${CAID}" - -CN_CA=${CN_CA:0:64} -CN_IA=${CN_IA:0:64} -CN_WEB=${CN_WEB:0:64} - -mkdir -p /certs /ca -cd /ca - -CA_KEY_FILE=${CA_KEY_FILE:-/ca/ca.key} -CA_CRT_FILE=${CA_CRT_FILE:-/ca/ca.crt} -CA_SRL_FILE=${CA_SRL_FILE:-/ca/ca.srl} - -if [ -f "$CA_CRT_FILE" ] ; then - logInfo "CA already exists. Good. We'll reuse it." - if [ ! -f "$CA_SRL_FILE" ] ; then - echo 01 > ${CA_SRL_FILE} - fi -else - logInfo "No CA was found. Generating one." - logInfo "*** Please *** make sure to mount /ca as a volume -- if not, everytime this container starts, it will regenerate the CA and nothing will work." - - openssl genrsa -des3 -passout pass:foobar -out ${CA_KEY_FILE} 4096 - - logInfo "generate CA cert with key and self sign it: ${CAID}" - openssl req -new -x509 -days 1300 -sha256 -key ${CA_KEY_FILE} -out ${CA_CRT_FILE} -passin pass:foobar -subj "/C=NL/ST=Noord Holland/L=Amsterdam/O=ME/OU=IT/CN=${CN_CA}" -extensions IA -config <( -cat <<-EOF -[req] -distinguished_name = dn -[dn] -[IA] -basicConstraints = critical,CA:TRUE -keyUsage = critical, digitalSignature, cRLSign, keyCertSign -subjectKeyIdentifier = hash -EOF -) - - [[ ${DEBUG} -gt 0 ]] && logInfo "show the CA cert details" - [[ ${DEBUG} -gt 0 ]] && openssl x509 -noout -text -in ${CA_CRT_FILE} - - echo 01 > ${CA_SRL_FILE} - -fi - -cd /certs - -logInfo "Generate IA key" -openssl genrsa -des3 -passout pass:foobar -out ia.key 4096 &> /dev/null - -logInfo "Create a signing request for the IA: ${CAID}" -openssl req -new -key ia.key -out ia.csr -passin pass:foobar -subj "/C=NL/ST=Noord Holland/L=Amsterdam/O=ME/OU=IT/CN=${CN_IA}" -reqexts IA -config <( -cat <<-EOF -[req] -distinguished_name = dn -[dn] -[IA] -basicConstraints = critical,CA:TRUE,pathlen:0 -keyUsage = critical, digitalSignature, cRLSign, keyCertSign -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 - -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 <( -cat <<-EOF -[req] -distinguished_name = dn -[dn] -[IA] -basicConstraints = critical,CA:TRUE,pathlen:0 -keyUsage = critical, digitalSignature, cRLSign, keyCertSign -subjectKeyIdentifier = hash -EOF -) &> /dev/null - - -[[ ${DEBUG} -gt 0 ]] && logInfo "show the IA cert details" -[[ ${DEBUG} -gt 0 ]] && openssl x509 -noout -text -in ia.crt - -logInfo "Initialize the serial number for signed certificates" -echo 01 > ia.srl - -logInfo "Create the key (w/o passphrase..)" -openssl genrsa -des3 -passout pass:foobar -out web.orig.key 2048 &> /dev/null -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 - -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 - -logInfo "Concatenating fullchain.pem..." -cat web.crt ia.crt ${CA_CRT_FILE} > fullchain.pem - -logInfo "Concatenating fullchain_with_key.pem" -cat fullchain.pem web.key > fullchain_with_key.pem diff --git a/entrypoint.sh b/entrypoint.sh index 98574ed..3833ac6 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,41 +1,188 @@ -#! /bin/bash +#!/bin/bash echo "Entrypoint starting." set -Eeuo pipefail trap "echo TRAPed signal" HUP INT QUIT TERM +logInfo() { + echo "INFO: $*" +} + +logErr() { + echo "ERR: $*" >&2 +} + +function creatCa() { + declare -i DEBUG=0 + + PROJ_NAME=DockerMirrorBox + logInfo "Will create certificate with names ${1}" + + CADATE=$(date "+%Y.%m.%d %H:%M") + CAID="$(hostname -f) ${CADATE}" + + CN_CA="${PROJ_NAME} CA Root ${CAID}" + CN_IA="${PROJ_NAME} Intermediate IA ${CAID}" + CN_WEB="${PROJ_NAME} Web Cert ${CAID}" + + CN_CA=${CN_CA:0:64} + CN_IA=${CN_IA:0:64} + CN_WEB=${CN_WEB:0:64} + + mkdir -p /certs /ca + cd /ca + + CA_KEY_FILE=${CA_KEY_FILE:-/ca/ca.key} + CA_CRT_FILE=${CA_CRT_FILE:-/ca/ca.crt} + CA_SRL_FILE=${CA_SRL_FILE:-/ca/ca.srl} + + if [[ -f "${CA_CRT_FILE}" ]]; then + logInfo "CA already exists. Good. We'll reuse it." + if [[ ! -f "${CA_SRL_FILE}" ]]; then + echo 01 >"${CA_SRL_FILE}" + fi + else + logInfo "No CA was found. Generating one." + logInfo "*** Please *** make sure to mount /ca as a volume -- if not, everytime this container starts, it will regenerate the CA and nothing will work." + + openssl genrsa -des3 -passout pass:foobar -out "${CA_KEY_FILE}" 4096 + + logInfo "generate CA cert with key and self sign it: ${CAID}" + openssl req \ + -new \ + -x509 \ + -days 1300 \ + -sha256 \ + -key "${CA_KEY_FILE}" \ + -out "${CA_CRT_FILE}" \ + -passin pass:foobar \ + -subj "/C=NL/ST=Noord Holland/L=Amsterdam/O=ME/OU=IT/CN=${CN_CA}" \ + -extensions IA \ + -config <(printf '[req]\ndistinguished_name = dn\n[dn]\n[IA]\nbasicConstraints = critical,CA:TRUE\nkeyUsage = critical, digitalSignature, cRLSign, keyCertSign\nsubjectKeyIdentifier = hash') + + if [[ "${DEBUG}" ]]; then + logInfo "show the CA cert details" + openssl x509 -noout -text -in "${CA_CRT_FILE}" + fi + echo 01 >"${CA_SRL_FILE}" + fi + + cd /certs + + logInfo "Generate IA key" + openssl genrsa -des3 -passout pass:foobar -out ia.key 4096 &>/dev/null + + logInfo "Create a signing request for the IA: ${CAID}" + openssl req \ + -new \ + -key ia.key \ + -out ia.csr \ + -passin pass:foobar \ + -subj "/C=NL/ST=Noord Holland/L=Amsterdam/O=ME/OU=IT/CN=${CN_IA}" \ + -reqexts IA \ + -config <(printf '[req]\ndistinguished_name = dn\n[dn]\n[IA]\nbasicConstraints = critical,CA:TRUE,pathlen:0\nkeyUsage = critical, digitalSignature, cRLSign, keyCertSign\nsubjectKeyIdentifier = hash') + + if [[ "${DEBUG}" ]]; then + logInfo "Show the singing request, to make sure extensions are there" + openssl req -in ia.csr -noout -text + fi + + 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 <(printf '[req]\ndistinguished_name = dn\n[dn]\n[IA]\nbasicConstraints = critical,CA:TRUE,pathlen:0\nkeyUsage = critical, digitalSignature, cRLSign, keyCertSign\nsubjectKeyIdentifier = hash') &>/dev/null + + if [[ "${DEBUG}" ]]; then + logInfo "show the IA cert details" + openssl x509 -noout -text -in ia.crt + fi + + logInfo "Initialize the serial number for signed certificates" + echo 01 >ia.srl + + logInfo "Create the key (w/o passphrase..)" + openssl genrsa -des3 -passout pass:foobar -out web.orig.key 2048 &>/dev/null + 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 <(printf '[req]\ndistinguished_name = dn\n[dn]\n[SAN]\nsubjectAltName=%s' "${1}") + + if [[ "${DEBUG}" ]]; then + logInfo "Show the singing request, to make sure extensions are there" + openssl req -in web.csr -noout -text + fi + + 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 <(printf '[req]\ndistinguished_name = dn\n[dn]\n[SAN]\nsubjectAltName=%s' "${1}") &>/dev/null + + if [[ "${DEBUG}" ]]; then + logInfo "Show the final cert details" + openssl x509 -noout -text -in web.crt + fi + + logInfo "Concatenating fullchain.pem..." + cat web.crt ia.crt "${CA_CRT_FILE}" >fullchain.pem + + logInfo "Concatenating fullchain_with_key.pem" + cat fullchain.pem web.key >fullchain_with_key.pem + +} # 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:" +logInfo "-- resolv.conf:" cat /etc/resolv.conf -echo "-- end resolv" +logInfo "-- 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 +RESOLVERS=$(sed -e 's/%3//g' /etc/resolv.conf | awk '$1 == "nameserver" {print ($2 ~ ":")? "["$2"]": $2}' ORS=' ' | sed 's/ *$//g') +if [[ -z "${RESOLVERS}" ]]; then + logErr "Unable to determine DNS resolvers for nginx" exit 66 fi -echo "DEBUG, determined RESOLVERS from /etc/resolv.conf: '$RESOLVERS'" +logInfo "DEBUG, determined RESOLVERS from /etc/resolv.conf: '${RESOLVERS}'" conf="" -for ONE_RESOLVER in ${RESOLVERS}; do - echo "Possible resolver: $ONE_RESOLVER" - conf="resolver $ONE_RESOLVER; " +for RESOLVER in ${RESOLVERS}; do + logInfo "Possible resolver: ${RESOLVER}" + conf="resolver ${RESOLVER}; " done -echo "Final chosen resolver: $conf" -confpath=/etc/nginx/resolvers.conf -if [ ! -e $confpath ] -then - echo "Using auto-determined resolver '$conf' via '$confpath'" - echo "$conf" > $confpath +logInfo "Final chosen resolver: ${conf}" +confpath="/etc/nginx/resolvers.conf" +if [[ ! -f ${confpath} ]]; then + logInfo "Using auto-determined resolver '${conf}' via '${confpath}'" + echo "${conf}" >${confpath} else - echo "Not using resolver config, keep existing '$confpath' -- mounted by user?" + logInfo "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. @@ -43,71 +190,74 @@ 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 +touch /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" + ONEREGISTRY=$(echo "${ONEREGISTRYIN}" | xargs) # Remove whitespace + logInfo "Adding certificate for registry: ${ONEREGISTRY}" ALLDOMAINS="${ALLDOMAINS},DNS:${ONEREGISTRY}" - echo "${ONEREGISTRY} 127.0.0.1:443;" >> /etc/nginx/docker.intercept.map + 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. +# export ALLDOMAINS=${ALLDOMAINS:1} # remove the first comma and export +creatCa "${ALLDOMAINS:1}" # 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 +touch /etc/nginx/docker.targetHost.map # Now handle the auth part. -echo -n "" > /etc/nginx/docker.auth.map +touch /etc/nginx/docker.auth.map # Only configure auth registries if the env var contains values -if [ "$AUTH_REGISTRIES" ]; then +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"}; + 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"}; + 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 + AUTH_BASE64=$(echo -n "${AUTH_USER}:${AUTH_PASS}" | base64 -w0 | xargs) + logInfo "Adding Auth for registry '${AUTH_HOST}' with user '${AUTH_USER}'." + printf '"%s" "%s";' "${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 +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 +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 +touch /etc/nginx/nginx.manifest.caching.config.conf -[[ "a${ENABLE_MANIFEST_CACHE}" == "atrue" ]] && [[ "a${MANIFEST_CACHE_PRIMARY_REGEX}" != "a" ]] && cat <>/etc/nginx/nginx.manifest.caching.config.conf +if [[ "${ENABLE_MANIFEST_CACHE}" == true ]]; then + if [[ -n "${MANIFEST_CACHE_PRIMARY_REGEX}" ]]; then + cat >>/etc/nginx/nginx.manifest.caching.config.conf </etc/nginx/nginx.manifest.caching.config.conf include "/etc/nginx/nginx.manifest.stale.conf"; } EOD - -[[ "a${ENABLE_MANIFEST_CACHE}" == "atrue" ]] && [[ "a${MANIFEST_CACHE_SECONDARY_REGEX}" != "a" ]] && cat <>/etc/nginx/nginx.manifest.caching.config.conf + fi + if [[ -n "${MANIFEST_CACHE_SECONDARY_REGEX}" ]]; then + cat >>/etc/nginx/nginx.manifest.caching.config.conf <>/etc/nginx/nginx.manifest.caching.config.conf + fi + cat >>/etc/nginx/nginx.manifest.caching.config.conf <>/etc/nginx/nginx.manifest.caching.config.conf +else + cat >>/etc/nginx/nginx.manifest.caching.config.conf < /etc/nginx/conf.d/allowed.methods.conf +if [[ "${ALLOW_PUSH}" == true ]]; then + cat </etc/nginx/conf.d/allowed.methods.conf # allow to upload big layers client_max_body_size 0; @@ -156,7 +308,7 @@ if [[ "a${ALLOW_PUSH}" == "atrue" ]]; then proxy_cache_methods GET; EOF else - cat << 'EOF' > /etc/nginx/conf.d/allowed.methods.conf + 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"; @@ -173,63 +325,59 @@ 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 +if [[ "${DEBUG}" == true ]]; then + if [[ ! -f /usr/bin/mitmweb ]]; then + logErr "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 + # 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/ " + logErr "Starting in DEBUG MODE (mitmproxy)." + logInfo "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 & + logInfo "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 +if [[ "${DEBUG_HUB}" == true ]]; then + if [[ ! -f /usr/bin/mitmweb ]]; then + logErr "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 + # 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 & + logErr "Debugging outgoing DockerHub connections via mitmproxy on 8082." + # 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 + logErr "Warning, DockerHub outgoing debugging disables upstream SSL verification for all upstreams." + VERIFY_SSL=false - echo "Access mitmweb for outgoing DockerHub requests via http://127.0.0.1:8082/ " + logInfo "Access mitmweb for outgoing DockerHub requests via http://127.0.0.1:8082/" + if [[ ! -f /usr/sbin/nginx-debug ]]; then + logErr "To debug, you need the -debug version of this image, eg: :latest-debug" + exit 4 + fi + + logInfo "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 -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 +touch /etc/nginx/nginx.timeouts.config.conf cat <>/etc/nginx/nginx.timeouts.config.conf # Timeouts @@ -250,9 +398,9 @@ cat <>/etc/nginx/nginx.timeouts.config.conf proxy_connect_send_timeout ${PROXY_CONNECT_SEND_TIMEOUT}; EOD -echo -e "\nTimeout configs: ---" +logInfo "Timeout configs: ---" cat /etc/nginx/nginx.timeouts.config.conf -echo -e "---\n" +logInfo "---" # Request buffering echo "" > /etc/nginx/proxy.request.buffering.conf @@ -269,9 +417,9 @@ cat /etc/nginx/proxy.request.buffering.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 +touch /etc/nginx/docker.verify.ssl.conf +if [[ "${VERIFY_SSL}" == true ]]; then + cat </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) @@ -279,13 +427,13 @@ if [[ "a${VERIFY_SSL}" == "atrue" ]]; then proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; proxy_ssl_verify_depth 2; EOD - echo "Upstream SSL certificate verification enabled." + logInfo "Upstream SSL certificate verification enabled." else - echo "Upstream SSL certificate verification is DISABLED." + logInfo "Upstream SSL certificate verification is DISABLED." fi -echo "Testing nginx config..." +logInfo "Testing nginx config..." ${NGINX_BIN} -t -echo "Starting nginx! Have a nice day." +logInfo "Starting nginx! Have a nice day." ${NGINX_BIN} -g "daemon off;"