-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Add support for extracting certs from Caddy v2 data #3485
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -478,94 +478,45 @@ DSM-generated letsencrypt certificates get auto-renewed every three months. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Caddy | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| For Caddy v2 you can specify the `key_type` in your server's global settings, which would end up looking something like this if you're using a `Caddyfile`: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```caddyfile | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| debug | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| admin localhost:2019 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| http_port 80 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| https_port 443 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default_sni example.com | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| key_type rsa4096 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| If you are instead using a json config for Caddy v2, you can set it in your site's TLS automation policies: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ??? example "Caddy v2 JSON example snippet" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```json | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "apps": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "http": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "servers": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "srv0": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "listen": [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ":443" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "routes": [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "match": [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "host": [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "mail.example.com", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "handle": [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "handler": "subroute", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "routes": [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "handle": [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "body": "", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "handler": "static_response" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "terminal": true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "tls": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "automation": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "policies": [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "subjects": [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "mail.example.com", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "key_type": "rsa2048", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "issuer": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "email": "[email protected]", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "module": "acme" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "issuer": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "email": "[email protected]", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "module": "acme" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Caddy](https://caddyserver.com/) is an open-source web server with built-in SSL certificate generation. You can use the [official Docker image](https://hub.docker.com/_/caddy) and write your own Caddyfile, or use [lucaslorentz/caddy-docker-proxy](https://github.com/lucaslorentz/caddy-docker-proxy), which lets you add labels to your services in order to generate a Caddyfile. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The generated certificates can then be mounted: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DMS supports retrieving certificates from Caddy data when the local file system storage is used (the default). You just need to mount your Caddy data and set `SSL_TYPE: letsencrypt` in the DMS container. Here is a minimal example with `lucaslorentz/caddy-docker-proxy`: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You removed the admonition (example type, the purple collapsible section in our docs): Adding that back requires 4 space indentation of the codeblock that follows, like the JSON snippet prior was. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ```yaml | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| version: "3.7" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+486
to
+487
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Compose V2 schema does not use Drop
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| volumes: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.crt:/etc/letsencrypt/live/mail.example.com/fullchain.pem | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - ${CADDY_DATA_DIR}/certificates/acme-v02.api.letsencrypt.org-directory/mail.example.com/mail.example.com.key:/etc/letsencrypt/live/mail.example.com/privkey.pem | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
-567
to
-568
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And here in the old docs we have an example filepath that your PR presumably operates on and is still roughly the same? Like with certbot section, it advises mounting the relevant cert folder to docker-mailserver/target/scripts/helpers/ssl.sh Lines 394 to 412 in 8f97171
Traefik has special support because of the Our docs do cover docker-mailserver/target/scripts/helpers/change-detection.sh Lines 46 to 63 in 8f97171
However in your current PR state, your approach for Caddy would miss out on this and would not benefit. The Traefik The difference with normal
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The reason for implementing Caddy support is that we can't know in advance the path where Caddy will store the certificates. Caddy uses Lets Encrypt or ZeroSSL by default, and may be configured to issue local certs, or use another CA. The paths depends on the issuer, and may look like the paths in the old docs, but they may also have Th paths could also change over time, for example if a certificate is initially issued with Lets Encrypt, but for some reason the renewal fails and Caddy falls back to ZeroSSL. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| caddy_data: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| services: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| caddy: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consistency with nginx + traefik example configs:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| image: lucaslorentz/caddy-docker-proxy:2.8.5 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ports: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - 80:80 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - 443:443 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| volumes: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - /var/run/docker.sock:/var/run/docker.sock | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - caddy_data:/data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| labels: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| caddy.local_certs: # Remove this label when going to production | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dms: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consistency with nginx + traefik example configs:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| image: ghcr.io/docker-mailserver/docker-mailserver:latest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hostname: mail.example.com | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ports: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - "25:25" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+506
to
+507
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not relevant config to the example, drop it:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| volumes: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - caddy_data:/caddy_data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| environment: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SSL_TYPE: letsencrypt | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SSL_DOMAIN: mail.example.com # Ensure this is exactly the same as the domain you pass to Caddy | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't be necessary once feedback is applied.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LOG_LEVEL: trace | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| labels: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| caddy_0: mail.example.com | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| caddy_0.respond: "Hello DMS" # Add a dummy directive so that Caddyfile gets generated correctly | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Uncomment to make a proxy for Rspamd | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # caddy_1: rspamd.example.com | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # caddy_1.reverse_proxy: "{{upstreams 11334}}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Traefik v2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -113,6 +113,8 @@ function _setup_ssl() { | |||||||||||||||||||||||||||||||||||||||||||||||||
| # Variable only intended for troubleshooting via debug output | ||||||||||||||||||||||||||||||||||||||||||||||||||
| local EXTRACTED_DOMAIN | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| _log 'debug' "Detected traefik certificates" | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| # Conditional handling depends on the success of `_extract_certs_from_acme`, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # Failure tries the next fallback FQDN to try extract a certificate from. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # Subshell not used in conditional to ensure extraction log output is still captured | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -124,12 +126,38 @@ function _setup_ssl() { | |||||||||||||||||||||||||||||||||||||||||||||||||
| EXTRACTED_DOMAIN=('DOMAINNAME' "${DOMAINNAME}") | ||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||
| _log 'warn' "letsencrypt (acme.json) failed to identify a certificate to extract" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we like to use The other supported
docker-mailserver/target/scripts/helpers/error.sh Lines 41 to 42 in 8f97171
Suggested change
I'm not sure if that's the right choice, it's technically a misconfiguration related to it though. The logs would provide context of the warning prior to it and the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using the panic above would trigger DMS shutdown fully, even from the I think we may not have panicked here as the You don't need to apply the suggestion, we could just revert the In the extraction method call, you'll see some other failures are logged with
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| _log 'trace' "letsencrypt (acme.json) extracted certificate using ${EXTRACTED_DOMAIN[0]}: '${EXTRACTED_DOMAIN[1]}'" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| function _caddy_support() { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ -d /caddy_data/caddy/certificates ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # Variable only intended for troubleshooting via debug output | ||||||||||||||||||||||||||||||||||||||||||||||||||
| local EXTRACTED_DOMAIN | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| _log 'debug' "Detected caddy certificates" | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| # Conditional handling depends on the success of `_extract_certs_from_caddy_data`, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # Failure tries the next fallback FQDN to try extract a certificate from. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # Subshell not used in conditional to ensure extraction log output is still captured | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ -n ${SSL_DOMAIN} ]] && _extract_certs_from_caddy_data "${SSL_DOMAIN}"; then | ||||||||||||||||||||||||||||||||||||||||||||||||||
| EXTRACTED_DOMAIN=('SSL_DOMAIN' "${SSL_DOMAIN}") | ||||||||||||||||||||||||||||||||||||||||||||||||||
| elif _extract_certs_from_caddy_data "${HOSTNAME}"; then | ||||||||||||||||||||||||||||||||||||||||||||||||||
| EXTRACTED_DOMAIN=('HOSTNAME' "${HOSTNAME}") | ||||||||||||||||||||||||||||||||||||||||||||||||||
| elif _extract_certs_from_caddy_data "${DOMAINNAME}"; then | ||||||||||||||||||||||||||||||||||||||||||||||||||
| EXTRACTED_DOMAIN=('DOMAINNAME' "${DOMAINNAME}") | ||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||
| _log 'warn' "letsencrypt (caddy data) failed to identify a certificate to extract" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| _log 'trace' "letsencrypt (caddy data) extracted certificate using ${EXTRACTED_DOMAIN[0]}: '${EXTRACTED_DOMAIN[1]}'" | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+138
to
+157
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is effectively copy/paste of the traefik method with the important distinction being calls to As you're introducing a variant here, rather than duplicating this logic, you could instead detect caddy vs traefik handling into a common extract method. |
||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| # TLS strength/level configuration | ||||||||||||||||||||||||||||||||||||||||||||||||||
| case "${TLS_LEVEL}" in | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ( "modern" ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -179,6 +207,7 @@ function _setup_ssl() { | |||||||||||||||||||||||||||||||||||||||||||||||||
| # TODO: SSL_DOMAIN is Traefik specific, it no longer seems relevant and should be considered for removal. | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| _traefik_support | ||||||||||||||||||||||||||||||||||||||||||||||||||
| _caddy_support | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| # checks folders in /etc/letsencrypt/live to identify which one to implicitly use: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| local LETSENCRYPT_DOMAIN LETSENCRYPT_KEY | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -440,6 +469,54 @@ function _extract_certs_from_acme() { | |||||||||||||||||||||||||||||||||||||||||||||||||
| _log 'trace' "_extract_certs_from_acme | Certificate successfully extracted for '${CERT_DOMAIN}'" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| function _extract_certs_from_caddy_data() { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| local CERT_DOMAIN=${1} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| local SANITIZED_CERT_DOMAIN | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ -z ${CERT_DOMAIN} ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||
| _log 'warn' "_extract_certs_from_caddy_data | CERT_DOMAIN is empty" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| SANITIZED_CERT_DOMAIN=$(_caddy_sanitize_domain "${CERT_DOMAIN}") | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| # Currently we advise SSL_DOMAIN for wildcard support using a `*.example.com` value, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # The filepath however should be `example.com`, avoiding the wildcard part: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ ${SSL_DOMAIN} == "${CERT_DOMAIN}" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||
| CERT_DOMAIN=$(_strip_wildcard_prefix "${SSL_DOMAIN}") | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| mkdir -p "/etc/letsencrypt/live/${CERT_DOMAIN}/" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # Caddy can use different CA to obtain a certificate (Lets Encrypt, ZeroSSL or | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # local). We don't know which one was used, so we use a '*' to match both. In | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # case both exist (improbable), just the first one will be copied by GNU cp | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cp -v /caddy_data/caddy/certificates/*/"${SANITIZED_CERT_DOMAIN}/${SANITIZED_CERT_DOMAIN}.key" "/etc/letsencrypt/live/${CERT_DOMAIN}/key.pem" || exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cp -v /caddy_data/caddy/certificates/*/"${SANITIZED_CERT_DOMAIN}/${SANITIZED_CERT_DOMAIN}.crt" "/etc/letsencrypt/live/${CERT_DOMAIN}/fullchain.pem" || exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| _log 'trace' "_extract_certs_from_caddy_data | Certificate successfully extracted for '${CERT_DOMAIN}'" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+472
to
+496
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Regarding earlier review feedback on a common extraction method, we can observe that there is common logic that can be shared, only a conditional is necessary for when traefik vs caddy differ. A slight refactor would make that a single conditional block? Common check: docker-mailserver/target/scripts/helpers/ssl.sh Lines 415 to 419 in 8f97171
Attempt to retrieve / detect the certificate files (traefik example): docker-mailserver/target/scripts/helpers/ssl.sh Lines 421 to 428 in 8f97171
Common step (now that cert content is found, ensure directory for docker-mailserver/target/scripts/helpers/ssl.sh Lines 430 to 436 in 8f97171
NOTE: The above Final step, populate a copy of the cert content to internal files DMS is configured to use (traefik example): docker-mailserver/target/scripts/helpers/ssl.sh Lines 437 to 440 in 8f97171
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| # Make the same substitutions that Caddy does on domains | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # This is a translation to sh of the Safe function in | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # https://github.com/caddyserver/certmagic/blob/v0.19.1/storage.go#L242 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| function _caddy_sanitize_domain() { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| local CERT_DOMAIN=${1,,} # lowercase | ||||||||||||||||||||||||||||||||||||||||||||||||||
| CERT_DOMAIN=${CERT_DOMAIN##+([[:space:]])} # trim leading spaces | ||||||||||||||||||||||||||||||||||||||||||||||||||
| CERT_DOMAIN=${CERT_DOMAIN%%+([[:space:]])} # trim trailing spaces | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| # replace a few specific characters | ||||||||||||||||||||||||||||||||||||||||||||||||||
| CERT_DOMAIN=$(sed ' | ||||||||||||||||||||||||||||||||||||||||||||||||||
| s/ /_/g; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| s/+/_plus_/g; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| s/*/wildcard_/g; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| s/:/-/g; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| s/\.\.//g; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ' <<< "${CERT_DOMAIN}") | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| # Remove all non-word characters and print result to stdout | ||||||||||||||||||||||||||||||||||||||||||||||||||
| # Note: \w in the original code is equivalent to [:alnum:]_ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "${CERT_DOMAIN//[^[:alnum:][email protected]]/}" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+498
to
+518
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The CertMagic referenced code is used for plenty of methods to sanitize a filepath. Most of these should not be relevant for DMS support as I've not looked at the filenames Caddy is generating, I assume This method could instead just check for existence of the file like we do in a later step: docker-mailserver/target/scripts/helpers/ssl.sh Lines 375 to 391 in 8f97171
@georglauterbach or @casperklein might have a better suggestion to scan your child dirs of
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, just use |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| # Remove the `*.` prefix if it exists, else returns the input value | ||||||||||||||||||||||||||||||||||||||||||||||||||
| function _strip_wildcard_prefix { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| [[ ${1} == "*."* ]] && echo "${1:2}" || echo "${1}" | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While the Caddy section was outdated a fair bit, you have dropped information about configuring the key type (Caddyfile and JSON examples). The self-generated and nginx-proxy sections both touch on this too, but it's most likely not too relevant / specific to DMS, so dropping these old examples is acceptable.