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

Add letsencrypt support. #87

Merged
merged 3 commits into from
Dec 9, 2024
Merged
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
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 .
Expand Down
37 changes: 19 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
20 changes: 8 additions & 12 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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;"
7 changes: 6 additions & 1 deletion nginx.montagu.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -147,6 +147,11 @@ server {
deny all;
}

location /.well-known/acme-challenge/ {
root /var/www;
autoindex off;
}

location / {
return 301 https://_HOST_$request_uri;
}
Expand Down
13 changes: 0 additions & 13 deletions scripts/dev.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 "$@"

Expand All @@ -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
Expand Down
29 changes: 6 additions & 23 deletions scripts/run-integration-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,19 @@ $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 \
--network montagu_proxy\
$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 \
Expand Down