-
Notifications
You must be signed in to change notification settings - Fork 0
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
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4756552
to
cf98ed8
Compare
fc0e523
to
f6a1ac2
Compare
ea48931
to
004f49b
Compare
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
f6a1ac2
to
5e42c07
Compare
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
004f49b
to
491d478
Compare
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
richfitz
approved these changes
Dec 9, 2024
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 runningdocker 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.