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

Add letsencrypt support. #87

merged 3 commits into from
Dec 9, 2024

Conversation

plietar
Copy link
Contributor

@plietar plietar commented Nov 29, 2024

The actual certificate request happens outside of the proxy, but in order to prove our ownership of the domain we need to be able to host ACME challenges at port 80. We expose the /var/www/.well-known/acme-challenge directory, which can be mounted from a volume.

There is a chicken-and-egg problem when trying to start the container for the first time: nginx won't start without a certificate, and we can't get a certificate without nginx running. We break that cycle by generating a self-signed certificate if no other one is found. As soon as the real certificate is obtained, it can be swapped in by writing it and its key to /etc/montagu/proxy/ and running docker exec proxy nginx reload -s (or equivalent).

Instead of getting the Diffie-Hellman parameters alongside our certificates, we now download a set of parameters. DH parameters do not need to be secret nor unique. They only need to have been generated "correctly". The parameters we use come from RFC7919, following the latest guidance from Mozilla. The directive only applies to TLS1.2. Starting with TLS1.3, the standard requires the parameters from RFC7919 to be used, and presumably nginx/openssl hardcodes these.

@plietar plietar force-pushed the VIMC-7464-letsencrypt branch from 4756552 to cf98ed8 Compare December 2, 2024 10:53
@plietar plietar changed the base branch from VIMC-7463-multi-stage to VIMC-7152-plietar December 2, 2024 10:53
@plietar plietar force-pushed the VIMC-7464-letsencrypt branch 3 times, most recently from ea48931 to 004f49b Compare December 2, 2024 14:10
plietar added a commit to vimc/montagu-deploy that referenced this pull request Dec 2, 2024
This adds a new configuration option named `acme` and a new sub-command
`montagu renew-certificate`. Using these will fetch a certificate from
Let's Encrypt (or another ACME provider), inject it into the proxy
container and finally reload nginx. The host machine needs to be
configured to run the `renew-certificate` on a regular basis, using, for
example, crontab or systemd timers.

The ACME protocol is handled by running certbot as its own short-lived
container. Alternatives could have been a long-running container running
a cron-like daemon, installing certbot inside the nginx container and
using `docker exec` to run it, installing and running certbot directly
on the host, using a Python implementation of the ACME protocol directly
inside the montagu tool process.

The certbot container shares a volume with nginx to host the
acme-challenge files on port 80. To allow nginx to start and serve these
files before we have any certificate at all, the proxy generates a
self-signed certificate on startup if none exist yet. This certificate
would be replaced on the first renewal.

See vimc/montagu-proxy#87 for the proxy side of this, mapping the
`/.well-known/acme-challenge` path to a directory on the file system.

While we support alternative ACME servers, in production we will
probably stick to the default which uses Let's Encrypt. The
configuration option makes it easier to trial the process using servers
that aren't exposed publically, using a miniature ACME server such as
[pebble][pebble]

In the future the ACME configuration block may be extended to support
DNS challenges instead, allowing us to use this even on internal-only
services. If and when this happens, we won't even need nginx to host
acme-challenges and may be able to remove the need for the transient
self-signed certificates.

Once we get more experience with this, we may also want to move some of
this functionality into constellation, allowing it to be re-used in
other projects.

[pebble]: https://github.com/letsencrypt/pebble
Base automatically changed from VIMC-7152-plietar to master December 3, 2024 15:05
The actual certificate request happens outside of the proxy, but in
order to prove our ownership of the domain we need to be able to host
ACME challenges at port 80. We expose the
`/var/www/.well-known/acme-challenge` directory, which can be mounted
from a volume.

There is a chicken-and-egg problem when trying to start the container
for the first time: nginx won't start without a certificate, and we
can't get a certificate without nginx running. We break that cycle by
generating a self-signed certificate if no other one is found. As soon
as the real certificate is obtained, it can be swapped in by writing it
and its key to `/etc/montagu/proxy/` and running
`docker exec proxy nginx reload -s` (or equivalent).

Instead of getting the Diffie-Hellman parameters alongside our
certificates, we now download a set of parameters. DH parameters do not
need to be secret nor unique. They only need to have been generated
"correctly". The parameters we use come from [RFC7919][rfc7919],
following the latest guidance from Mozilla. The directive only applies
to TLS1.2. Starting with TLS1.3, [the standard][rfc8446] requires the
parameters from RFC7919 to be used, and presumably nginx/openssl
hardcodes these.

[rfc7919]: https://www.rfc-editor.org/rfc/rfc7919
[rfc8446]: https://www.rfc-editor.org/rfc/rfc8446#section-4.2.8.1
@plietar plietar marked this pull request as ready for review December 6, 2024 13:46
@plietar plietar force-pushed the VIMC-7464-letsencrypt branch from 004f49b to 491d478 Compare December 6, 2024 16:53
plietar added a commit to vimc/montagu-deploy that referenced this pull request Dec 6, 2024
This adds a new configuration option named `acme` and a new sub-command
`montagu renew-certificate`. Using these will fetch a certificate from
Let's Encrypt (or another ACME provider), inject it into the proxy
container and finally reload nginx. The host machine needs to be
configured to run the `renew-certificate` on a regular basis, using, for
example, crontab or systemd timers.

The ACME protocol is handled by running certbot as its own short-lived
container. Alternatives could have been a long-running container running
a cron-like daemon, installing certbot inside the nginx container and
using `docker exec` to run it, installing and running certbot directly
on the host, using a Python implementation of the ACME protocol directly
inside the montagu tool process.

The certbot container shares a volume with nginx to host the
acme-challenge files on port 80. To allow nginx to start and serve these
files before we have any certificate at all, the proxy generates a
self-signed certificate on startup if none exist yet. This certificate
would be replaced on the first renewal.

See vimc/montagu-proxy#87 for the proxy side of this, mapping the
`/.well-known/acme-challenge` path to a directory on the file system.

While we support alternative ACME servers, in production we will
probably stick to the default which uses Let's Encrypt. The
configuration option makes it easier to trial the process using servers
that aren't exposed publically, using a miniature ACME server such as
[pebble][pebble]

In the future the ACME configuration block may be extended to support
DNS challenges instead, allowing us to use this even on internal-only
services. If and when this happens, we won't even need nginx to host
acme-challenges and may be able to remove the need for the transient
self-signed certificates.

Once we get more experience with this, we may also want to move some of
this functionality into constellation, allowing it to be re-used in
other projects.

[pebble]: https://github.com/letsencrypt/pebble
plietar added a commit to vimc/montagu-deploy that referenced this pull request Dec 6, 2024
This adds a new configuration option named `acme` and a new sub-command
`montagu renew-certificate`. Using these will fetch a certificate from
Let's Encrypt (or another ACME provider), inject it into the proxy
container and finally reload nginx. The host machine needs to be
configured to run the `renew-certificate` on a regular basis, using, for
example, crontab or systemd timers.

The ACME protocol is handled by running certbot as its own short-lived
container. Alternatives could have been a long-running container running
a cron-like daemon, installing certbot inside the nginx container and
using `docker exec` to run it, installing and running certbot directly
on the host, using a Python implementation of the ACME protocol directly
inside the montagu tool process.

The certbot container shares a volume with nginx to host the
acme-challenge files on port 80. To allow nginx to start and serve these
files before we have any certificate at all, the proxy generates a
self-signed certificate on startup if none exist yet. This certificate
would be replaced on the first renewal.

See vimc/montagu-proxy#87 for the proxy side of this, mapping the
`/.well-known/acme-challenge` path to a directory on the file system.

While we support alternative ACME servers, in production we will
probably stick to the default which uses Let's Encrypt. The
configuration option makes it easier to trial the process using servers
that aren't exposed publically, using a miniature ACME server such as
[pebble][pebble]

In the future the ACME configuration block may be extended to support
DNS challenges instead, allowing us to use this even on internal-only
services. If and when this happens, we won't even need nginx to host
acme-challenges and may be able to remove the need for the transient
self-signed certificates.

Once we get more experience with this, we may also want to move some of
this functionality into constellation, allowing it to be re-used in
other projects.

[pebble]: https://github.com/letsencrypt/pebble
@plietar plietar requested a review from richfitz December 6, 2024 18:45
@plietar plietar merged commit d8d8eda into master Dec 9, 2024
1 check passed
@plietar plietar deleted the VIMC-7464-letsencrypt branch December 9, 2024 09:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants