From 54e3872442b94b3ad2bf4ecfacca759c61920c8d Mon Sep 17 00:00:00 2001 From: Rui Lopes Date: Sat, 28 Mar 2020 16:23:00 +0000 Subject: [PATCH] provision and use a docker registry repository --- Vagrantfile | 2 + provision/provision-base.sh | 90 ++++++++++++++++ provision/provision-docker.sh | 59 ++++++++++ .../src/main/groovy/provision.groovy | 28 ++++- provision/use-docker-repository.sh | 102 ++++++++++++++++++ 5 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 provision/provision-docker.sh create mode 100644 provision/use-docker-repository.sh diff --git a/Vagrantfile b/Vagrantfile index 518566c..2c976df 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -27,7 +27,9 @@ Vagrant.configure(2) do |config| config.vm.synced_folder '.', '/vagrant', type: 'nfs' end config.vm.provision :shell, path: 'provision/provision-base.sh' + config.vm.provision :shell, path: 'provision/provision-docker.sh' config.vm.provision :shell, path: 'provision/provision-nexus.sh' + config.vm.provision :shell, path: 'provision/use-docker-repository.sh' config.vm.provision :shell, path: 'provision/use-raw-repository.sh' config.vm.provision :shell, path: 'provision/use-maven-repository-from-mvn.sh' config.vm.provision :shell, path: 'provision/use-maven-repository-from-gradle.sh' diff --git a/provision/provision-base.sh b/provision/provision-base.sh index ba2a908..0ced53a 100644 --- a/provision/provision-base.sh +++ b/provision/provision-base.sh @@ -129,6 +129,96 @@ server { proxy_pass http://127.0.0.1:8081; } } + +# docker-group repository. +server { + listen 5001 ssl http2; + server_name $config_fqdn; + access_log /var/log/nginx/$config_fqdn-docker-group.access.log; + + ssl_certificate /etc/ssl/private/$config_fqdn-crt.pem; + ssl_certificate_key /etc/ssl/private/$config_fqdn-keypair.pem; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + # see https://github.com/cloudflare/sslconfig/blob/master/conf + # see https://blog.cloudflare.com/it-takes-two-to-chacha-poly/ + # see https://blog.cloudflare.com/do-the-chacha-better-mobile-performance-with-cryptography/ + # NB even though we have CHACHA20 here, the OpenSSL library that ships with Ubuntu 16.04 does not have it. so this is a nop. no problema. + ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!aNULL:!MD5; + + tcp_nodelay on; + client_max_body_size 10G; + proxy_send_timeout 120; + proxy_read_timeout 300; + proxy_buffering off; + proxy_set_header X-Forwarded-Proto \$scheme; + proxy_set_header Host \$host; + proxy_set_header X-Forwarded-Host \$host; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + + location / { + proxy_pass http://127.0.0.1:6001; + } +} + +# docker-hub-proxy repository. +server { + listen 5002 ssl http2; + server_name $config_fqdn; + access_log /var/log/nginx/$config_fqdn-docker-hub-proxy.access.log; + + ssl_certificate /etc/ssl/private/$config_fqdn-crt.pem; + ssl_certificate_key /etc/ssl/private/$config_fqdn-keypair.pem; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + # see https://github.com/cloudflare/sslconfig/blob/master/conf + # see https://blog.cloudflare.com/it-takes-two-to-chacha-poly/ + # see https://blog.cloudflare.com/do-the-chacha-better-mobile-performance-with-cryptography/ + # NB even though we have CHACHA20 here, the OpenSSL library that ships with Ubuntu 16.04 does not have it. so this is a nop. no problema. + ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!aNULL:!MD5; + + tcp_nodelay on; + client_max_body_size 10G; + proxy_send_timeout 120; + proxy_read_timeout 300; + proxy_buffering off; + proxy_set_header X-Forwarded-Proto \$scheme; + proxy_set_header Host \$host; + proxy_set_header X-Forwarded-Host \$host; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + + location / { + proxy_pass http://127.0.0.1:6002; + } +} + +# docker-hosted repository. +server { + listen 5003 ssl http2; + server_name $config_fqdn; + access_log /var/log/nginx/$config_fqdn-docker-hosted.access.log; + + ssl_certificate /etc/ssl/private/$config_fqdn-crt.pem; + ssl_certificate_key /etc/ssl/private/$config_fqdn-keypair.pem; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + # see https://github.com/cloudflare/sslconfig/blob/master/conf + # see https://blog.cloudflare.com/it-takes-two-to-chacha-poly/ + # see https://blog.cloudflare.com/do-the-chacha-better-mobile-performance-with-cryptography/ + # NB even though we have CHACHA20 here, the OpenSSL library that ships with Ubuntu 16.04 does not have it. so this is a nop. no problema. + ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!aNULL:!MD5; + + tcp_nodelay on; + client_max_body_size 10G; + proxy_send_timeout 120; + proxy_read_timeout 300; + proxy_buffering off; + proxy_set_header X-Forwarded-Proto \$scheme; + proxy_set_header Host \$host; + proxy_set_header X-Forwarded-Host \$host; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + + location / { + proxy_pass http://127.0.0.1:6003; + } +} EOF ln -s ../sites-available/$config_fqdn.conf /etc/nginx/sites-enabled/ systemctl restart nginx diff --git a/provision/provision-docker.sh b/provision/provision-docker.sh new file mode 100644 index 0000000..89a51c5 --- /dev/null +++ b/provision/provision-docker.sh @@ -0,0 +1,59 @@ +#!/bin/bash +set -eux + +# NB execute apt-cache madison docker-ce to known the available versions. +docker_version="${1:-5:19.03.8~3-0~ubuntu-bionic}"; shift || true +registry_proxy_domain="${1:-$(hostname --fqdn)}"; shift || true +# NB as-of docker 19.03.8, there is still no way to specify a registry mirror credentials, +# as such, we cannot use our docker-group registry, instead we must use the docker-proxy +# registry and allow anonymous access to it. +# see https://github.com/moby/moby/issues/30880 +registry_proxy_host="$registry_proxy_domain:5002" +registry_proxy_url="https://$registry_proxy_host" + +# prevent apt-get et al from asking questions. +# NB even with this, you'll still get some warnings that you can ignore: +# dpkg-preconfigure: unable to re-open stdin: No such file or directory +export DEBIAN_FRONTEND=noninteractive + +# install docker. +# see https://docs.docker.com/install/linux/docker-ce/ubuntu/ +apt-get install -y apt-transport-https software-properties-common gnupg2 +wget -qO- https://download.docker.com/linux/ubuntu/gpg | apt-key add - +add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" +apt-get update +apt-get install -y "docker-ce=$docker_version" "docker-ce-cli=$docker_version" containerd.io + +# configure it. +cat >/etc/docker/daemon.json </etc/systemd/system/docker.service.d/override.conf <<'EOF' +[Service] +ExecStart= +ExecStart=/usr/bin/dockerd +EOF +systemctl daemon-reload +systemctl restart docker + +# let the vagrant user manage docker. +usermod -aG docker vagrant + +# kick the tires. +ctr version +docker version +docker info diff --git a/provision/provision-nexus/src/main/groovy/provision.groovy b/provision/provision-nexus/src/main/groovy/provision.groovy index c87bf37..028af79 100644 --- a/provision/provision-nexus/src/main/groovy/provision.groovy +++ b/provision/provision-nexus/src/main/groovy/provision.groovy @@ -66,6 +66,23 @@ repository.createNugetProxy("chocolatey.org-proxy", "https://chocolatey.org/api/ repository.createNugetGroup("chocolatey-group", ["chocolatey-hosted", "chocolatey.org-proxy"], "default") +// create a docker registry repository backed by the default blob store. +repository.createDockerHosted("docker-hosted", 6003, null, "default", true, true, WritePolicy.ALLOW, true) +// create a docker proxy repository backed by the default blob store. +// see https://help.sonatype.com/repomanager3/formats/docker-registry +// TODO set Allow Nexus Repository Manager to download and cache foreign layers. +// NB as-of docker 19.03.5, there is still no way to specify a registry mirror credentials... +// as such, we cannot use our docker-group registry, instead we must use the docker-proxy +// registry, enable the Docker Bearer Token Realm and allow anonymous access to it. +// see https://github.com/moby/moby/issues/30880 +// NB this will make https://nexus.example.com:5002/v2/library/debian/manifests/buster-slim proxy +// to https://registry-1.docker.io/v2/library/debian/manifests/buster-slim +// https://registry-1.docker.io/v2/library/golang/tags/list +repository.createDockerProxy("docker-hub-proxy", "https://registry-1.docker.io", "HUB", null, 6002, null, "default", true, true, false) +// create a docker group repository that merges the docker-hosted and docker-hub-proxy together. +repository.createDockerGroup("docker-group", 6001, null, ["docker-hosted", "docker-hub-proxy"], true, "default", true) + + // see http://stackoverflow.com/questions/8138164/groovy-generate-random-string-from-given-character-set def random(String alphabet, int n) { new Random().with { @@ -94,12 +111,19 @@ taskConfiguration.setString("snapshotRetentionDays", "30") taskScheduler.scheduleTask(taskConfiguration, new Daily(new Date().clearTime().next())) -// enable the NuGet API-Key Realm. +// NB you can list the available realms with realmManager.availableRealms. realmManager = container.lookup(RealmManager.class.name) +// enable the NuGet API-Key Realm. realmManager.enableRealm("NuGetApiKey") - // enable the npm Bearer Token Realm. realmManager.enableRealm("NpmToken") +// enable the Docker Bearer Token Realm. +realmManager.enableRealm("DockerToken") +// allow Anonymous access to nexus to be able to use the docker-hub-proxy repository. +// NB this might be worked around by creating an docker-anonymous user and force its +// credentials in the nginx reverse-proxy configuration. +// see https://github.com/moby/moby/issues/30880#issuecomment-601513505 +security.anonymousAccess = true // set the admin password. // NB we set it to something different than the default (admin123) to get diff --git a/provision/use-docker-repository.sh b/provision/use-docker-repository.sh new file mode 100644 index 0000000..75108c9 --- /dev/null +++ b/provision/use-docker-repository.sh @@ -0,0 +1,102 @@ +#!/bin/bash +set -eux + +nexus_domain=$(hostname --fqdn) +docker_group_registry_host="$nexus_domain:5001" +docker_hosted_registry_host="$nexus_domain:5003" +registry_username='alice.doe' +registry_password='password' + +# login into the registry. +echo "logging in the registry $docker_group_registry_host..." +docker login $docker_group_registry_host --username "$registry_username" --password-stdin <main.go <<'EOF' +package main + +import ( + "fmt" + "flag" + "log" + "net/http" +) + +func main() { + log.SetFlags(0) + + var listenAddress = flag.String("listen", ":8000", "Listen address.") + + flag.Parse() + + if flag.NArg() != 0 { + flag.Usage() + log.Fatalf("\nERROR You MUST NOT pass any positional arguments") + } + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + fmt.Printf("%s %s%s\n", r.Method, r.Host, r.URL) + fmt.Fprintf(w, "%s %s%s\n", r.Method, r.Host, r.URL) + }) + + fmt.Printf("Listening at http://%s\n", *listenAddress) + + err := http.ListenAndServe(*listenAddress, nil) + if err != nil { + log.Fatalf("Failed to ListenAndServe: %v", err) + } +} +EOF +cat >Dockerfile <<'EOF' +FROM golang:1.14.1-buster as builder +WORKDIR /app +COPY main.go . +RUN CGO_ENABLED=0 go build -ldflags="-s" -o=go-hello + +# NB we use the buster-slim (instead of scratch) image so we +# can enter the container to execute bash etc. +FROM debian:buster-slim +COPY --from=builder /app/go-hello . +WORKDIR / +EXPOSE 8000 +ENTRYPOINT ["/go-hello"] +EOF + +# build the image. +docker build -t go-hello:1.0.0 . +docker image ls go-hello:1.0.0 + +# push the image to the docker-hosted registry. +docker tag go-hello:1.0.0 $docker_hosted_registry_host/go-hello:1.0.0 +docker push $docker_hosted_registry_host/go-hello:1.0.0 + +# show the repository (image) details directly from the docker-hosted registry. +# see https://docs.docker.com/registry/spec/api/ +# see https://docs.docker.com/registry/spec/manifest-v2-2/ +wget -qO- --user "$registry_username" --password "$registry_password" \ + "https://$docker_hosted_registry_host/v2/go-hello/tags/list" | jq . +manifest=$(wget -qO- --user "$registry_username" --password "$registry_password" \ + '--header=Accept: application/vnd.docker.distribution.manifest.v2+json' \ + "https://$docker_hosted_registry_host/v2/go-hello/manifests/1.0.0") +config_digest=$(echo "$manifest" | jq -r .config.digest) +echo "$manifest" | jq . +wget -qO- --user "$registry_username" --password "$registry_password" \ + "https://$docker_hosted_registry_host/v2/go-hello/blobs/$config_digest" | jq . + +# remove it from local cache. +docker image remove go-hello:1.0.0 +docker image remove $docker_hosted_registry_host/go-hello:1.0.0 + +# pull it from the docker-group registry. +docker pull $docker_group_registry_host/go-hello:1.0.0