diff --git a/Dockerfile b/Dockerfile index 9b6b48b..f589700 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,8 @@ COPY index.html /usr/share/nginx/html/index.html COPY 404.html /usr/share/nginx/html/404.html COPY resources /usr/share/nginx/html/resources +ADD https://ssl-config.mozilla.org/ffdhe2048.txt /etc/nginx/dhparam.pem + # Copy third party javascript from npm modules ENV THIRDPARTY_JS_PATH /usr/share/nginx/html/resources/js/third_party/ COPY --from=0 /workspace/node_modules/pako/dist/pako.min.js $THIRDPARTY_JS_PATH @@ -26,6 +28,8 @@ COPY --from=0 /workspace/node_modules/bootstrap/dist/css/bootstrap.min.css /usr/ COPY --from=0 /workspace/node_modules/bootstrap/dist/css/bootstrap.min.css.map /usr/share/nginx/html/resources/css/third_party/ RUN rm /etc/nginx/conf.d/default.conf +RUN mkdir -p /etc/montagu/proxy +RUN mkdir -p /var/www WORKDIR /app COPY entrypoint.sh . diff --git a/README.md b/README.md index 32c87dd..6f5c45b 100644 --- a/README.md +++ b/README.md @@ -3,25 +3,26 @@ A reverse proxy for Montagu. This allows us to expose a single port (443) and map different paths to different apps (containers). ## SSL configuration files -`nginx.montagu.conf` contains references to an SSL certificate, an SSL private key, and a DHE parameter, which it -expects at `/etc/montagu/proxy/certificate.pem`, `/etc/montagu/proxy/ssl_key.pem` and -`/etc/montagu/proxy/dhparam.pem`, respectively. SSL public certificates are stored -in the [montagu repository](https://github.com/vimc/montagu/tree/master/certs), the SSL private key and DHE parameter -files are stored in the vault and all 3 are copied into the running proxy container during deployment. -Secrets are stored in the vault at: - -``` -vault list secret/vimc/ssl/v2/support/key -``` -and - -``` -vault list secret/vimc/ssl/v2/support/dhparam -``` - -### Generating new DHE parameters -The DHE parameter files in the vault were generated by running `openssl dhparam -out workspace/dhparam.pem 4096` +`nginx.montagu.conf` contains references to an X509 certificate and a private +key, which it expects at `/etc/montagu/proxy/certificate.pem` and +`/etc/montagu/proxy/ssl_key.pem`, respectively. The `/etc/montagu/proxy` +directory can be mounted from a volume providing these certificates, or they +can be injected into the container using `docker copy`. + +These certificates can be provisioned using Let's Encrypt (or another ACME +provider). The `/var/www/.well-known/acme-challenge` directory inside the +container will be exposed under the `/.well-known/acme-challenge` path. This +directory can be mounted from a volume that is shared with a certbot image. + +When the container starts, if no certificate is present, a self-signed +certificate will be generated. This helps avoid a chicken and egg problem where +nginx cannot start without a certificate, and a certificate cannot be obtained +without nginx running. + +When a new the certificate is obtained and written to `/etc/montagu/proxy`, +nginx needs to be reloaded by entering the container and running +`nginx -s reload`. ## Build and run locally Run `./scripts/dev.sh`. This runs up the proxy along with the apis and portals, in order to manually test links, logins etc. diff --git a/entrypoint.sh b/entrypoint.sh index eb57ce3..1dc0b5c 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -18,18 +18,14 @@ sed -e "s/_PORT_/$port/g" \ root="/etc/montagu/proxy" mkdir -p $root -a="$root/certificate.pem" -b="$root/ssl_key.pem" -c="$root/dhparam.pem" +if [[ ! -f $root/certificate.pem ]]; then + echo "Generating self-signed certificate for $host" -echo "Waiting for SSL certificate files at:" -echo "- $a" -echo "- $b" -echo "- $c" - -while [ ! -e $a ] || [ ! -e $b ] || [ ! -e $c ]; do - sleep 2 -done + openssl req -quiet -x509 -newkey rsa:2048 \ + -sha256 -days 365 -noenc \ + -subj "/C=GB/L=Location/O=Vaccine Impact Modelling Consortium/OU=Montagu/CN=$host" \ + -keyout "$root/ssl_key.pem" -out "$root/certificate.pem" +fi -echo "Certificate files detected. Running nginx" +echo "Starting nginx" exec nginx -g "daemon off;" diff --git a/nginx.montagu.conf b/nginx.montagu.conf index 5c52db7..a598ba1 100644 --- a/nginx.montagu.conf +++ b/nginx.montagu.conf @@ -35,7 +35,7 @@ server { ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; - ssl_dhparam /etc/montagu/proxy/dhparam.pem; + ssl_dhparam /etc/nginx/dhparam.pem; root /usr/share/nginx/html; @@ -147,6 +147,11 @@ server { deny all; } + location /.well-known/acme-challenge/ { + root /var/www; + autoindex off; + } + location / { return 301 https://_HOST_$request_uri; } diff --git a/scripts/dev.sh b/scripts/dev.sh index 05c49db..2c494b2 100755 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -20,13 +20,7 @@ cleanup # This traps errors and Ctrl+C trap cleanup EXIT -echo "Generating SSL keypair" -mkdir workspace mkdir montagu_emails -docker run --rm \ - -v $PWD/workspace:/workspace \ - $ORG/montagu-cert-tool:master \ - gen-self-signed /workspace > /dev/null 2> /dev/null $here/run-dependencies.sh "$@" @@ -46,15 +40,8 @@ docker run -d \ nginx/nginx-prometheus-exporter:0.2.0 \ -nginx.scrape-uri "http://reverse-proxy/basic_status" -# the real dhparam will be 4096 bits but that takes ages to generate -openssl dhparam -out workspace/dhparam.pem 2048 - -docker cp workspace/certificate.pem reverse-proxy:/etc/montagu/proxy/ -docker cp workspace/ssl_key.pem reverse-proxy:/etc/montagu/proxy/ -docker cp workspace/dhparam.pem reverse-proxy:/etc/montagu/proxy/ docker cp $here/../2020 reverse-proxy:/usr/share/nginx/html docker cp $here/../2021 reverse-proxy:/usr/share/nginx/html -rm -rf workspace sleep 2s docker logs reverse-proxy diff --git a/scripts/run-integration-tests.sh b/scripts/run-integration-tests.sh index b1a00a2..4fb7e6b 100755 --- a/scripts/run-integration-tests.sh +++ b/scripts/run-integration-tests.sh @@ -18,13 +18,6 @@ $HERE/run-dependencies.sh export ORG=vimc -echo "Generating SSL keypair" -mkdir workspace -docker run --rm \ - -v $PWD/workspace:/workspace \ - $ORG/montagu-cert-tool:master \ - gen-self-signed /workspace > /dev/null 2> /dev/null - docker run -d \ -p "443:443" -p "80:80" \ --name reverse-proxy \ @@ -32,22 +25,12 @@ docker run -d \ $SHA_TAG 443 localhost docker run -d \ - -p "9113:9113" \ - --network montagu_proxy \ - --name montagu-metrics \ - --restart always \ - nginx/nginx-prometheus-exporter:0.2.0 \ - -nginx.scrape-uri "http://reverse-proxy/basic_status" - -# the real dhparam will be 4096 bits but that takes ages to generate -openssl dhparam -out workspace/dhparam.pem 2048 - -docker cp workspace/certificate.pem reverse-proxy:/etc/montagu/proxy/ -docker cp workspace/ssl_key.pem reverse-proxy:/etc/montagu/proxy/ -docker cp workspace/dhparam.pem reverse-proxy:/etc/montagu/proxy/ -rm -rf workspace - -sleep 2s + -p "9113:9113" \ + --network montagu_proxy \ + --name montagu-metrics \ + --restart always \ + nginx/nginx-prometheus-exporter:0.2.0 \ + -nginx.scrape-uri "http://reverse-proxy/basic_status" docker run \ --rm \