Merge branch 'refact/bash-dockerfile' of ssh://github.com/pratikbin/docker-registry-proxy into HEAD

pull/121/head
git 2022-03-16 01:17:15 +00:00
commit 2a3e6cab5c
4 changed files with 286 additions and 309 deletions

View File

@ -1,16 +1,16 @@
name: test name: test
on: on:
workflow_dispatch: # workflow_dispatch:
pull_request: # pull_request:
branches: # branches:
- master # - master
push:
jobs: jobs:
sanity-check: sanity-check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Get machine's first IPv4 address for eth0 - name: Get machine's first IPv4 address for eth0
id: ip id: ip
run: | 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 # 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 - name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1 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 - name: Build amd64 release image locally to Docker
uses: docker/build-push-action@v2 uses: docker/build-push-action@v2
with: with:
build-args: | build-args: |
DEBUG_BUILD=0 DEBUG_BUILD=0
BASE_IMAGE_SUFFIX= BASE_IMAGE_SUFFIX=
builder: ${{ steps.buildx.outputs.name }}
context: . context: .
file: ./Dockerfile
platforms: linux/amd64 platforms: linux/amd64
tags: sanity-check/docker-registry-proxy:latest tags: sanity-check/docker-registry-proxy:latest
push: false
load: true load: true
cache-from: type=local,src=/tmp/.buildx-cache/release cache-from: type=gha
# this only reads from the cache cache-to: type=gha,mode=max # this only reads from the cache
- name: Start proxy instance in docker (ENABLE_MANIFEST_CACHE=false) - name: Start proxy instance in docker (ENABLE_MANIFEST_CACHE=false)
run: | 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 \ -p 0.0.0.0:3128:3128 -e ENABLE_MANIFEST_CACHE=false \
-v $(pwd)/docker_mirror_cache:/docker_mirror_cache \ -v $(pwd)/docker_mirror_cache:/docker_mirror_cache \
-v $(pwd)/docker_mirror_certs:/ca \ -v $(pwd)/docker_mirror_certs:/ca \
sanity-check/docker-registry-proxy:latest 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 - name: Wait for container to be up
timeout-minutes: 1 timeout-minutes: 2
run: | run: |
declare -i IS_UP=0 declare -i IS_UP=0
while [[ $IS_UP -lt 1 ]]; do while [[ $IS_UP -lt 1 ]]; do
@ -84,14 +59,18 @@ jobs:
done done
echo "Container is up..." 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 - name: Grab the CA cert from running container via curl
run: | run: |
curl http://${{ steps.ip.outputs.ETHER }}:3128/ca.crt | sudo tee /usr/share/ca-certificates/docker_registry_proxy.crt 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 - name: Stop proxy instance in docker
timeout-minutes: 1
run: | run: |
timeout 58 docker stop docker_registry_proxy docker rm -f docker_registry_proxy
- name: Refresh system-wide CA store - name: Refresh system-wide CA store
run: | run: |
@ -107,17 +86,14 @@ jobs:
Environment="HTTPS_PROXY=http://${{ steps.ip.outputs.ETHER }}:3128/" Environment="HTTPS_PROXY=http://${{ steps.ip.outputs.ETHER }}:3128/"
EOD EOD
- name: Reload systemd from disk - name: Reload systemd from disk & dockerd via systemd
run: | run: |
sudo systemctl daemon-reload sudo systemctl daemon-reload
- name: Restart dockerd via systemd
run: |
sudo systemctl restart docker.service sudo systemctl restart docker.service
- name: Start proxy instance in docker again (ENABLE_MANIFEST_CACHE=true) - name: Start proxy instance in docker again (ENABLE_MANIFEST_CACHE=true)
run: | 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 \ -p 0.0.0.0:3128:3128 -e ENABLE_MANIFEST_CACHE=true \
-v $(pwd)/docker_mirror_cache:/docker_mirror_cache \ -v $(pwd)/docker_mirror_cache:/docker_mirror_cache \
-v $(pwd)/docker_mirror_certs:/ca \ -v $(pwd)/docker_mirror_certs:/ca \
@ -135,49 +111,27 @@ jobs:
echo "Container is up again..." echo "Container is up again..."
# This can be quite slow, since Github Actions runner Docker comes preloaded with a lot of images. # 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) - name: Remove images
timeout-minutes: 2
run: | 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 - name: First round of pulls
timeout-minutes: 2
run: | run: |
docker pull alpine:3.6 docker pull alpine:3.6
docker pull k8s.gcr.io/pause:3.3 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: | run: |
docker logs docker_registry_proxy > cold_cache.txt docker rmi -f alpine:3.6
docker rmi -f k8s.gcr.io/pause:3.3
- 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
- name: sleep 2s to allow cache to stale a bit - name: sleep 2s to allow cache to stale a bit
run: | run: |
sleep 2 sleep 2
- name: Second round of pulls - name: Get the logs
timeout-minutes: 2 if: ${{ always() }}
run: | run: |
docker pull alpine:3.6 docker logs docker_registry_proxy || true
docker pull k8s.gcr.io/pause:3.3 docker ps -a
- 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

View File

@ -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 our very hackish entrypoint and ca-building scripts, make them executable
ADD entrypoint.sh /entrypoint.sh ADD entrypoint.sh /entrypoint.sh
ADD create_ca_cert.sh /create_ca_cert.sh RUN chmod +x /entrypoint.sh
RUN chmod +x /create_ca_cert.sh /entrypoint.sh
# Clients should only use 3128, not anything else. # Clients should only use 3128, not anything else.
EXPOSE 3128 EXPOSE 3128

View File

@ -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

View File

@ -1,41 +1,188 @@
#! /bin/bash #!/bin/bash
echo "Entrypoint starting." echo "Entrypoint starting."
set -Eeuo pipefail set -Eeuo pipefail
trap "echo TRAPed signal" HUP INT QUIT TERM 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? # 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. # this leads to a world of problems. ipv6 format being different, etc.
# below is a collection of hacks contributed over the years. # below is a collection of hacks contributed over the years.
echo "-- resolv.conf:" logInfo "-- resolv.conf:"
cat /etc/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. # 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') RESOLVERS=$(sed -e 's/%3//g' /etc/resolv.conf | awk '$1 == "nameserver" {print ($2 ~ ":")? "["$2"]": $2}' ORS=' ' | sed 's/ *$//g')
if [ "x$RESOLVERS" = "x" ]; then if [[ -z "${RESOLVERS}" ]]; then
echo "Warning: unable to determine DNS resolvers for nginx" >&2 logErr "Unable to determine DNS resolvers for nginx"
exit 66 exit 66
fi fi
echo "DEBUG, determined RESOLVERS from /etc/resolv.conf: '$RESOLVERS'" logInfo "DEBUG, determined RESOLVERS from /etc/resolv.conf: '${RESOLVERS}'"
conf="" conf=""
for ONE_RESOLVER in ${RESOLVERS}; do for RESOLVER in ${RESOLVERS}; do
echo "Possible resolver: $ONE_RESOLVER" logInfo "Possible resolver: ${RESOLVER}"
conf="resolver $ONE_RESOLVER; " conf="resolver ${RESOLVER}; "
done done
echo "Final chosen resolver: $conf" logInfo "Final chosen resolver: ${conf}"
confpath=/etc/nginx/resolvers.conf confpath="/etc/nginx/resolvers.conf"
if [ ! -e $confpath ] if [[ ! -f ${confpath} ]]; then
then logInfo "Using auto-determined resolver '${conf}' via '${confpath}'"
echo "Using auto-determined resolver '$conf' via '$confpath'" echo "${conf}" >${confpath}
echo "$conf" > $confpath
else else
echo "Not using resolver config, keep existing '$confpath' -- mounted by user?" logInfo "Not using resolver config, keep existing '${confpath}' -- mounted by user?"
fi fi
# The list of SAN (Subject Alternative Names) for which we will create a TLS certificate. # 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. # 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. # 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 # 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 docker.caching.proxy.internal registry-1.docker.io auth.docker.io ${REGISTRIES}; do
ONEREGISTRY=$(echo ${ONEREGISTRYIN} | xargs) # Remove whitespace ONEREGISTRY=$(echo "${ONEREGISTRYIN}" | xargs) # Remove whitespace
echo "Adding certificate for registry: $ONEREGISTRY" logInfo "Adding certificate for registry: ${ONEREGISTRY}"
ALLDOMAINS="${ALLDOMAINS},DNS:${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 done
# Clean the list and generate certificates. # Clean the list and generate certificates.
export ALLDOMAINS=${ALLDOMAINS:1} # remove the first comma and export # export ALLDOMAINS=${ALLDOMAINS:1} # remove the first comma and export
/create_ca_cert.sh # This uses ALLDOMAINS to generate the certificates. creatCa "${ALLDOMAINS:1}" # This uses ALLDOMAINS to generate the certificates.
# Target host interception. Empty by default. Used to intercept outgoing requests # Target host interception. Empty by default. Used to intercept outgoing requests
# from the proxy to the registries. # from the proxy to the registries.
echo -n "" > /etc/nginx/docker.targetHost.map touch /etc/nginx/docker.targetHost.map
# Now handle the auth part. # 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 # 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 # Ref: https://stackoverflow.com/a/47633817/219530
AUTH_REGISTRIES_DELIMITER=${AUTH_REGISTRIES_DELIMITER:-" "} AUTH_REGISTRIES_DELIMITER=${AUTH_REGISTRIES_DELIMITER:-" "}
s=$AUTH_REGISTRIES$AUTH_REGISTRIES_DELIMITER s=${AUTH_REGISTRIES}${AUTH_REGISTRIES_DELIMITER}
auth_array=(); auth_array=()
while [[ $s ]]; do while [[ ${s} ]]; do
auth_array+=( "${s%%"$AUTH_REGISTRIES_DELIMITER"*}" ); auth_array+=("${s%%"$AUTH_REGISTRIES_DELIMITER"*}")
s=${s#*"$AUTH_REGISTRIES_DELIMITER"}; s=${s#*"$AUTH_REGISTRIES_DELIMITER"}
done done
AUTH_REGISTRY_DELIMITER=${AUTH_REGISTRY_DELIMITER:-":"} AUTH_REGISTRY_DELIMITER=${AUTH_REGISTRY_DELIMITER:-":"}
for ONEREGISTRY in "${auth_array[@]}"; do for ONEREGISTRY in "${auth_array[@]}"; do
s=$ONEREGISTRY$AUTH_REGISTRY_DELIMITER s=${ONEREGISTRY}${AUTH_REGISTRY_DELIMITER}
registry_array=();
while [[ $s ]]; do registry_array=()
registry_array+=( "${s%%"$AUTH_REGISTRY_DELIMITER"*}" ); while [[ ${s} ]]; do
s=${s#*"$AUTH_REGISTRY_DELIMITER"}; registry_array+=("${s%%"$AUTH_REGISTRY_DELIMITER"*}")
s=${s#*"$AUTH_REGISTRY_DELIMITER"}
done done
AUTH_HOST="${registry_array[0]}" AUTH_HOST="${registry_array[0]}"
AUTH_USER="${registry_array[1]}" AUTH_USER="${registry_array[1]}"
AUTH_PASS="${registry_array[2]}" AUTH_PASS="${registry_array[2]}"
AUTH_BASE64=$(echo -n ${AUTH_USER}:${AUTH_PASS} | base64 -w0 | xargs) AUTH_BASE64=$(echo -n "${AUTH_USER}:${AUTH_PASS}" | base64 -w0 | xargs)
echo "Adding Auth for registry '${AUTH_HOST}' with user '${AUTH_USER}'." logInfo "Adding Auth for registry '${AUTH_HOST}' with user '${AUTH_USER}'."
echo "\"${AUTH_HOST}\" \"${AUTH_BASE64}\";" >> /etc/nginx/docker.auth.map printf '"%s" "%s";' "${AUTH_HOST}" "${AUTH_BASE64}" >>/etc/nginx/docker.auth.map
done done
fi fi
# create default config for the caching layer to listen on 443. # create default config for the caching layer to listen on 443.
echo " listen 443 ssl default_server;" > /etc/nginx/caching.layer.listen 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 "error_log /var/log/nginx/error.log warn;" >/etc/nginx/error.log.debug.warn
# Set Docker Registry cache size, by default, 32 GB ('32g') # Set Docker Registry cache size, by default, 32 GB ('32g')
CACHE_MAX_SIZE=${CACHE_MAX_SIZE:-32g} CACHE_MAX_SIZE=${CACHE_MAX_SIZE:-32g}
# The cache directory. This can get huge. Better to use a Docker volume pointing here! # The cache directory. This can get huge. Better to use a Docker volume pointing here!
# Set to 32gb which should be enough # 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. # 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 <<EOD >>/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 <<EOD
# First tier caching of manifests; configure via MANIFEST_CACHE_PRIMARY_REGEX and MANIFEST_CACHE_PRIMARY_TIME # First tier caching of manifests; configure via MANIFEST_CACHE_PRIMARY_REGEX and MANIFEST_CACHE_PRIMARY_TIME
location ~ ^/v2/(.*)/manifests/${MANIFEST_CACHE_PRIMARY_REGEX} { location ~ ^/v2/(.*)/manifests/${MANIFEST_CACHE_PRIMARY_REGEX} {
set \$docker_proxy_request_type "manifest-primary"; set \$docker_proxy_request_type "manifest-primary";
@ -115,8 +265,9 @@ echo -n "" >/etc/nginx/nginx.manifest.caching.config.conf
include "/etc/nginx/nginx.manifest.stale.conf"; include "/etc/nginx/nginx.manifest.stale.conf";
} }
EOD EOD
fi
[[ "a${ENABLE_MANIFEST_CACHE}" == "atrue" ]] && [[ "a${MANIFEST_CACHE_SECONDARY_REGEX}" != "a" ]] && cat <<EOD >>/etc/nginx/nginx.manifest.caching.config.conf if [[ -n "${MANIFEST_CACHE_SECONDARY_REGEX}" ]]; then
cat >>/etc/nginx/nginx.manifest.caching.config.conf <<EOD
# Secondary tier caching of manifests; configure via MANIFEST_CACHE_SECONDARY_REGEX and MANIFEST_CACHE_SECONDARY_TIME # Secondary tier caching of manifests; configure via MANIFEST_CACHE_SECONDARY_REGEX and MANIFEST_CACHE_SECONDARY_TIME
location ~ ^/v2/(.*)/manifests/${MANIFEST_CACHE_SECONDARY_REGEX} { location ~ ^/v2/(.*)/manifests/${MANIFEST_CACHE_SECONDARY_REGEX} {
set \$docker_proxy_request_type "manifest-secondary"; set \$docker_proxy_request_type "manifest-secondary";
@ -124,8 +275,8 @@ EOD
include "/etc/nginx/nginx.manifest.stale.conf"; include "/etc/nginx/nginx.manifest.stale.conf";
} }
EOD EOD
fi
[[ "a${ENABLE_MANIFEST_CACHE}" == "atrue" ]] && cat <<EOD >>/etc/nginx/nginx.manifest.caching.config.conf cat >>/etc/nginx/nginx.manifest.caching.config.conf <<EOD
# Default tier caching for manifests. Caches for ${MANIFEST_CACHE_DEFAULT_TIME} (from MANIFEST_CACHE_DEFAULT_TIME) # Default tier caching for manifests. Caches for ${MANIFEST_CACHE_DEFAULT_TIME} (from MANIFEST_CACHE_DEFAULT_TIME)
location ~ ^/v2/(.*)/manifests/ { location ~ ^/v2/(.*)/manifests/ {
set \$docker_proxy_request_type "manifest-default"; set \$docker_proxy_request_type "manifest-default";
@ -133,8 +284,8 @@ EOD
include "/etc/nginx/nginx.manifest.stale.conf"; include "/etc/nginx/nginx.manifest.stale.conf";
} }
EOD EOD
else
[[ "a${ENABLE_MANIFEST_CACHE}" != "atrue" ]] && cat <<EOD >>/etc/nginx/nginx.manifest.caching.config.conf cat >>/etc/nginx/nginx.manifest.caching.config.conf <<EOD
# Manifest caching is disabled. Enable it with ENABLE_MANIFEST_CACHE=true # Manifest caching is disabled. Enable it with ENABLE_MANIFEST_CACHE=true
location ~ ^/v2/(.*)/manifests/ { location ~ ^/v2/(.*)/manifests/ {
set \$docker_proxy_request_type "manifest-default-disabled"; set \$docker_proxy_request_type "manifest-default-disabled";
@ -142,13 +293,14 @@ EOD
include "/etc/nginx/nginx.manifest.stale.conf"; include "/etc/nginx/nginx.manifest.stale.conf";
} }
EOD EOD
fi
echo -e "\nManifest caching config: ---\n" logInfo "Manifest caching config: ---"
cat /etc/nginx/nginx.manifest.caching.config.conf cat /etc/nginx/nginx.manifest.caching.config.conf
echo "---" logInfo "---"
if [[ "a${ALLOW_PUSH}" == "atrue" ]]; then if [[ "${ALLOW_PUSH}" == true ]]; then
cat <<EOF > /etc/nginx/conf.d/allowed.methods.conf cat <<EOF >/etc/nginx/conf.d/allowed.methods.conf
# allow to upload big layers # allow to upload big layers
client_max_body_size 0; client_max_body_size 0;
@ -156,7 +308,7 @@ if [[ "a${ALLOW_PUSH}" == "atrue" ]]; then
proxy_cache_methods GET; proxy_cache_methods GET;
EOF EOF
else 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. # Block POST/PUT/DELETE. Don't use this proxy for pushing.
if ($request_method = POST) { if ($request_method = POST) {
return 405 "POST method is not allowed"; return 405 "POST method is not allowed";
@ -173,63 +325,59 @@ fi
# normally use non-debug version of nginx # normally use non-debug version of nginx
NGINX_BIN="/usr/sbin/nginx" NGINX_BIN="/usr/sbin/nginx"
if [[ "a${DEBUG}" == "atrue" ]]; then if [[ "${DEBUG}" == true ]]; then
if [[ ! -f /usr/bin/mitmweb ]]; then if [[ ! -f /usr/bin/mitmweb ]]; then
echo "To debug, you need the -debug version of this image, eg: :latest-debug" logErr "To debug, you need the -debug version of this image, eg: :latest-debug"
exit 3 exit 3
fi fi
# in debug mode, change caching layer to listen on 444, so that mitmproxy can sit in the middle. # 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 " listen 444 ssl default_server;" >/etc/nginx/caching.layer.listen
echo "Starting in DEBUG MODE (mitmproxy)." >&2 logErr "Starting in DEBUG MODE (mitmproxy)."
echo "Run mitmproxy with reverse pointing to the same certs..." 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 \ 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 termlog_verbosity=error --set stream_large_bodies=128k --web-port 8081 \
--set keep_host_header=true --set ssl_insecure=true \ --set keep_host_header=true --set ssl_insecure=true \
--mode reverse:https://127.0.0.1:444 --listen-host 0.0.0.0 \ --mode reverse:https://127.0.0.1:444 --listen-host 0.0.0.0 \
--listen-port 443 --certs /certs/fullchain_with_key.pem & --listen-port 443 --certs /certs/fullchain_with_key.pem &
echo "Access mitmweb via http://127.0.0.1:8081/ " logInfo "Access mitmweb via http://127.0.0.1:8081/ "
fi fi
if [[ "a${DEBUG_HUB}" == "atrue" ]]; then if [[ "${DEBUG_HUB}" == true ]]; then
if [[ ! -f /usr/bin/mitmweb ]]; then if [[ ! -f /usr/bin/mitmweb ]]; then
echo "To debug, you need the -debug version of this image, eg: :latest-debug" logErr "To debug, you need the -debug version of this image, eg: :latest-debug"
exit 3 exit 3
fi fi
# in debug hub mode, we remap targetHost to point to mitmproxy below # 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 '"registry-1.docker.io" "127.0.0.1:445";' >/etc/nginx/docker.targetHost.map
echo "Debugging outgoing DockerHub connections via mitmproxy on 8082." >&2 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 # 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 \ 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 termlog_verbosity=error --set stream_large_bodies=128k --web-port 8082 \
--set keep_host_header=false --set ssl_insecure=true \ --set keep_host_header=false --set ssl_insecure=true \
--mode reverse:https://registry-1.docker.io --listen-host 0.0.0.0 \ --mode reverse:https://registry-1.docker.io --listen-host 0.0.0.0 \
--listen-port 445 --certs /certs/fullchain_with_key.pem & --listen-port 445 --certs /certs/fullchain_with_key.pem &
echo "Warning, DockerHub outgoing debugging disables upstream SSL verification for all upstreams." >&2 logErr "Warning, DockerHub outgoing debugging disables upstream SSL verification for all upstreams."
VERIFY_SSL=false 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 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 # Timeout configurations
echo "" > /etc/nginx/nginx.timeouts.config.conf touch /etc/nginx/nginx.timeouts.config.conf
cat <<EOD >>/etc/nginx/nginx.timeouts.config.conf cat <<EOD >>/etc/nginx/nginx.timeouts.config.conf
# Timeouts # Timeouts
@ -250,9 +398,9 @@ cat <<EOD >>/etc/nginx/nginx.timeouts.config.conf
proxy_connect_send_timeout ${PROXY_CONNECT_SEND_TIMEOUT}; proxy_connect_send_timeout ${PROXY_CONNECT_SEND_TIMEOUT};
EOD EOD
echo -e "\nTimeout configs: ---" logInfo "Timeout configs: ---"
cat /etc/nginx/nginx.timeouts.config.conf cat /etc/nginx/nginx.timeouts.config.conf
echo -e "---\n" logInfo "---"
# Request buffering # Request buffering
echo "" > /etc/nginx/proxy.request.buffering.conf echo "" > /etc/nginx/proxy.request.buffering.conf
@ -269,9 +417,9 @@ cat /etc/nginx/proxy.request.buffering.conf
echo -e "---\n" echo -e "---\n"
# Upstream SSL verification. # Upstream SSL verification.
echo "" > /etc/nginx/docker.verify.ssl.conf touch /etc/nginx/docker.verify.ssl.conf
if [[ "a${VERIFY_SSL}" == "atrue" ]]; then if [[ "${VERIFY_SSL}" == true ]]; then
cat << EOD > /etc/nginx/docker.verify.ssl.conf cat <<EOD >/etc/nginx/docker.verify.ssl.conf
# We actually wanna be secure and avoid mitm attacks. # We actually wanna be secure and avoid mitm attacks.
# Fitting, since this whole thing is a mitm... # 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) # 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_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
proxy_ssl_verify_depth 2; proxy_ssl_verify_depth 2;
EOD EOD
echo "Upstream SSL certificate verification enabled." logInfo "Upstream SSL certificate verification enabled."
else else
echo "Upstream SSL certificate verification is DISABLED." logInfo "Upstream SSL certificate verification is DISABLED."
fi fi
echo "Testing nginx config..." logInfo "Testing nginx config..."
${NGINX_BIN} -t ${NGINX_BIN} -t
echo "Starting nginx! Have a nice day." logInfo "Starting nginx! Have a nice day."
${NGINX_BIN} -g "daemon off;" ${NGINX_BIN} -g "daemon off;"