From bcb2712a5078bc0eba1457cb4ed6324080a5918e Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Fri, 12 Aug 2022 01:11:21 +0200 Subject: [PATCH 1/6] Allow changing dehydrated BASEDIR from env variable This is to allow it to be placed in a volume that the user mounts into the container, usually in /certs. Introduce a new env variable DEHYDRATED_BASEDIR. If this is unset, the behavior will stay the same as before: `BASEDIR=/dehydrated`. This is for backwards compatibility. `DEHYDRATED_BASEDIR` and `CERT_DIR` interact: * default (<=0.4.0) if `DEHYDRATED_BASEDIR` is not set: * `DEHYDRATED_BASEDIR="/dehydrated" CERT_DIR="/certs"` * `/dehydrated` is not stored in persistent storage and will be deleted with the container (including the letsencrypt account) * recommended (>0.4.0): * set `DEHYDRATED_BASEDIR="/certs/dehydrated"`. This will set `CERT_DIR="$DEHYDRATED_BASEDIR/certs"`. This allows all the certificates and dehydrated state (most importantly, accounts) to be stored in one directory, usually a volume mounted at `/certs`. Introduce a new variable, DEHYDRATED_WELLKNOWN, to set the directory where dehydrated will place .well-known. This allows the user to point it to the webserver document root and not have to create any symlinks. --- etc/dehydrated/config | 5 +++-- meta-entrypoint.sh | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/etc/dehydrated/config b/etc/dehydrated/config index 8a2121c..84cc44c 100644 --- a/etc/dehydrated/config +++ b/etc/dehydrated/config @@ -1,4 +1,5 @@ # See https://github.com/dehydrated-io/dehydrated/blob/master/docs/examples/config -WELLKNOWN="/static/.well-known/acme-challenge" -BASEDIR="/dehydrated" +WELLKNOWN="$DEHYDRATED_WELLKNOWN" +BASEDIR="$DEHYDRATED_BASEDIR" +CERTDIR="$CERT_DIR" diff --git a/meta-entrypoint.sh b/meta-entrypoint.sh index bea0df5..95cab3a 100644 --- a/meta-entrypoint.sh +++ b/meta-entrypoint.sh @@ -8,8 +8,10 @@ CREATE_SELFSIGNED=${CREATE_SELFSIGNED:-'true'} STAGING=${STAGING:-'false'} NO_COLOR=${NO_COLOR:-''} SELF_SIGNED_CERT_VALIDITY_DAYS=${SELF_SIGNED_CERT_VALIDITY_DAYS:-30} -CERT_DIR=${CERT_DIR:-"/certs/"} -export JAVA_OPTS="-Djava.awt.headless=true -XX:+UseG1GC -Dfile.encoding=UTF-8 -Ddomain=${DOMAIN}" +: "${CERT_DIR:="${DEHYDRATED_BASEDIR}/certs"}" +: "${DEHYDRATED_BASEDIR:="/dehydrated"}" +: "${DEHYDRATED_WELLKNOWN:="/static"}" +export JAVA_OPTS="-Djava.awt.headless=true -XX:+UseG1GC -Dfile.encoding=UTF-8 -Dcertdir=${CERT_DIR} -Ddomain=${DOMAIN}" function main() { From 0b68e6f4332756cb8e53bfe85198f73c21d93581 Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Fri, 12 Aug 2022 01:17:02 +0200 Subject: [PATCH 2/6] meta-entrypoint.sh: create BASEDIR and WELLKNOWN if nonexistent Sometimes on first start they don't exist yet. --- meta-entrypoint.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/meta-entrypoint.sh b/meta-entrypoint.sh index 95cab3a..c674ec9 100644 --- a/meta-entrypoint.sh +++ b/meta-entrypoint.sh @@ -70,6 +70,8 @@ createSelfSignedCert() { } function fetchCerts() { + mkdir -vp "$DEHYDRATED_BASEDIR" + mkdir -vp "$DEHYDRATED_WELLKNOWN" if [[ "${STAGING}" == "true" ]]; then echo 'CA="https://acme-staging-v02.api.letsencrypt.org/directory"' >> /etc/dehydrated/config From c1aa5ca245f931ecc02fa1e21ad057db08384d9a Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Fri, 12 Aug 2022 01:18:12 +0200 Subject: [PATCH 3/6] meta-entrypoint.sh: remove --out from dehydrated, not needed any more The output directory for certificates is set via CERTDIR in /etc/dehydrated/config. --- meta-entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meta-entrypoint.sh b/meta-entrypoint.sh index c674ec9..67bc6ea 100644 --- a/meta-entrypoint.sh +++ b/meta-entrypoint.sh @@ -87,7 +87,7 @@ function fetchCerts() { while [[ "${SIG_INT_RECEIVED}" == 'false' ]]; do green "Fetching certificates" - dehydrated --domain ${DOMAIN} --cron --accept-terms --out ${CERT_DIR} && exitCode=$? || exitCode=$? + dehydrated --domain ${DOMAIN} --cron --accept-terms && exitCode=$? || exitCode=$? if [[ "${exitCode}" > 0 ]]; then red "Fetching certificates failed" fi From 695a93cae494200aa4eb4f207d7b714925621599 Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Fri, 12 Aug 2022 01:19:41 +0200 Subject: [PATCH 4/6] README.md: document new DEHYDRATED_BASEDIR env variable --- README.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8e7bfce..59e6906 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,9 @@ Your tomcat server must be configured to * respond with HTTP return code less than 400, on `http://localhost:${LOCAL_HTTP_PORT}/` (default port 8080). If successful, the certificate files will be stored here: - * Certificate file: `/certs/${DOMAIN}/cert.pem` - * Certificate private key file: `/certs/${DOMAIN}/privkey.pem` - * Certificate chain file: `/certs/${DOMAIN}/fullchain.pem` + * Certificate file: `${CERT_DIR}/${DOMAIN}/cert.pem` + * Certificate private key file: `${CERT_DIR}/${DOMAIN}/privkey.pem` + * Certificate chain file: `${CERT_DIR}/${DOMAIN}/fullchain.pem` # Configuration at runtime @@ -63,8 +63,20 @@ If successful, the certificate files will be stored here: * `ENABLE_LETSENCRYPT` - if set to `false` the letsencrypt process is not started * `CREATE_SELFSIGNED` - if set to `false` no selfsigned certifcate is generated at start up. Depending on your setup this might result in failing startup of the tomcat connectors -* Persistence: Your certs are stored inside your container at `CERT_DIR` (default: `/certs/`), so you might want to - persist this folder. + * `DEHYDRATED_BASEDIR` - defaults to `/dehydrated` for compatibility with letsencrypt-tomcat <= 0.40. + * `CERT_DIR` - if `DEHYDRATED_BASEDIR` is set, defaults to `$DEHYDRATED_BASEDIR/certs`, which is the default of dehydrated. If `DEHYDRATED_BASEDIR` is not set, defaults to `/certs` to be compatible with older letsencrypt-tomcat releases <=0.4.0. + * `DEHYDRATED_WELLKNOWN` - path where dehydrated will place .well-known/acme-challenge. Should usually be the document root of a webserver. + +Note: if you want to save certificates and accounts in persistent storage it is highly recommended to set `DEHYDRATED_BASEDIR` env variable as described below. + +`DEHYDRATED_BASEDIR` and `CERT_DIR` interact: + * default (<=0.4.0) if `DEHYDRATED_BASEDIR` is not set: + * `DEHYDRATED_BASEDIR="/dehydrated" CERT_DIR="/certs"` + * `/dehydrated` is not stored in persistent storage and will be deleted with the container (including the letsencrypt account) + * recommended (>0.4.0): + * set `DEHYDRATED_BASEDIR="/certs/dehydrated"`. This will set `CERT_DIR="$DEHYDRATED_BASEDIR/certs"`. This allows all the certificates and dehydrated state (most importantly, accounts) to be stored in one directory, usually a volume mounted at `/certs`. + +It is possible to set environment variables in any place Docker supports, and will make them set in the container. This is: in a Dockerfile, as run parameters, as docker-compose.yml configuration, or in an .env file. # Run Examples From b34fb20d07eb47f7b0707c411af13e6ea77dfb21 Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Fri, 12 Aug 2022 01:32:31 +0200 Subject: [PATCH 5/6] Update examples - use new DEHYDRATED_BASEDIR env variable - point DEHYDRATED_WELLKNOWN to document root of webserver instead of symlinking /static to it --- examples/embedded-tomcat/Dockerfile | 1 + .../src/main/java/info/schnatterer/tomcat/Main.java | 2 +- examples/spring-boot/Dockerfile | 1 + .../src/main/resources/application.properties | 6 +++--- examples/standalone/Dockerfile | 9 +++------ examples/standalone/tomcat/conf/server.xml | 6 +++--- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/examples/embedded-tomcat/Dockerfile b/examples/embedded-tomcat/Dockerfile index 5184532..2af80ed 100644 --- a/examples/embedded-tomcat/Dockerfile +++ b/examples/embedded-tomcat/Dockerfile @@ -21,6 +21,7 @@ RUN mvn package FROM letsencrypt-tomcat as aggregator # Copy letsencrypt-related stuff RUN mv /letsencrypt /dist +ENV DEHYDRATED_BASEDIR="/certs/dehydrated" # Copy Libraries: Apache Portable Runtime (APR) and JNI wrappers for APR used by Tomcat (libtcnative) RUN cp -r /lib/* /dist/ diff --git a/examples/embedded-tomcat/src/main/java/info/schnatterer/tomcat/Main.java b/examples/embedded-tomcat/src/main/java/info/schnatterer/tomcat/Main.java index 7148497..0affa61 100644 --- a/examples/embedded-tomcat/src/main/java/info/schnatterer/tomcat/Main.java +++ b/examples/embedded-tomcat/src/main/java/info/schnatterer/tomcat/Main.java @@ -15,7 +15,7 @@ public class Main { private static final int HTTPS_PORT = 8443; public static final String DOMAIN = System.getenv("DOMAIN"); - public static final String CERT_FOLDER = "/certs/"; + public static final String CERT_FOLDER = System.getenv("CERT_DIR"); public static final String PK = CERT_FOLDER + DOMAIN + "/privkey.pem"; public static final String CRT = CERT_FOLDER + DOMAIN + "/cert.pem"; public static final String CA = CERT_FOLDER + DOMAIN + "/fullchain.pem"; diff --git a/examples/spring-boot/Dockerfile b/examples/spring-boot/Dockerfile index 51a6298..64ef303 100644 --- a/examples/spring-boot/Dockerfile +++ b/examples/spring-boot/Dockerfile @@ -21,6 +21,7 @@ RUN mvn package FROM letsencrypt-tomcat as aggregator # Copy letsencrypt-related stuff RUN mv /letsencrypt /dist +ENV DEHYDRATED_BASEDIR="/certs/dehydrated" # Copy Libraries: Apache Portable Runtime (APR) and JNI wrappers for APR used by Tomcat (libtcnative) RUN cp -r /lib/* /dist/ diff --git a/examples/spring-boot/src/main/resources/application.properties b/examples/spring-boot/src/main/resources/application.properties index e14803b..4cfb082 100644 --- a/examples/spring-boot/src/main/resources/application.properties +++ b/examples/spring-boot/src/main/resources/application.properties @@ -4,6 +4,6 @@ server.http.port=8080 # Needed so the letsencrypt challenge is served by tomcat spring.resources.staticLocations=file:/static -server.ssl.certificateKeyFile=/certs/${DOMAIN}/privkey.pem -server.ssl.certificateFile=/certs/${DOMAIN}/cert.pem -server.ssl.certificateChainFile=/certs/${DOMAIN}/fullchain.pem \ No newline at end of file +server.ssl.certificateKeyFile=${certdir}/${DOMAIN}/privkey.pem +server.ssl.certificateFile=${certdir}/${DOMAIN}/cert.pem +server.ssl.certificateChainFile=${certdir}/${DOMAIN}/fullchain.pem diff --git a/examples/standalone/Dockerfile b/examples/standalone/Dockerfile index 4621005..77eaae5 100644 --- a/examples/standalone/Dockerfile +++ b/examples/standalone/Dockerfile @@ -7,17 +7,14 @@ FROM schnatterer/letsencrypt-tomcat:${LETSENCRYPT_TOMCAT_VERSION} as letsencrypt FROM letsencrypt-tomcat as aggregator # Copy letsencrypt-related stuff RUN mv /letsencrypt /dist +ENV DEHYDRATED_BASEDIR="/certs/dehydrated" # Copy reloding connector, so tomcat can automatically reload certificates at runtime RUN mkdir -p /dist/opt/bitnami/tomcat/lib/ RUN cp -r /tomcat-reloading-connector/* /dist/opt/bitnami/tomcat/lib/ -# Make standalone tomcat serve static files in directories used for letsencrypt challenges -RUN mkdir -p /dist/opt/bitnami/tomcat/webapps/ROOT/.well-known/acme-challenge -# It would be simpler to link ROOT -> static but it seems that tomcat does not follow symlinks when serving static content -# So just do it the other way round -RUN rm -rf /dist/static/.well-known/acme-challenge -RUN ln -s /opt/bitnami/tomcat/webapps/ROOT/.well-known/acme-challenge /dist/static/.well-known/acme-challenge +# set directory where dehydrated will create .well-known/acme-challenge +ENV DEHYDRATED_WELLKNOWN="/opt/bitnami/tomcat/webapps/ROOT" # Copy examples/standalone tomcat config COPY examples/standalone/tomcat /dist/opt/bitnami/tomcat/ diff --git a/examples/standalone/tomcat/conf/server.xml b/examples/standalone/tomcat/conf/server.xml index e47a6d6..d9fa724 100644 --- a/examples/standalone/tomcat/conf/server.xml +++ b/examples/standalone/tomcat/conf/server.xml @@ -103,9 +103,9 @@ maxThreads="150" SSLEnabled="true" > - From c5e67806fbce15069a56b83843b2dae64c98d29f Mon Sep 17 00:00:00 2001 From: Jernej Jakob Date: Sat, 25 Mar 2023 15:24:25 +0100 Subject: [PATCH 6/6] Fix dehydrated ACME challenge failing WELLKNOWN path was missing '.well-known/acme-challenge' which was causing the ACME challenge to fail. Env variables used in dehydrated config must be exported. --- README.md | 4 ++-- examples/standalone/Dockerfile | 6 +++--- meta-entrypoint.sh | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 59e6906..46a8786 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ If successful, the certificate files will be stored here: Depending on your setup this might result in failing startup of the tomcat connectors * `DEHYDRATED_BASEDIR` - defaults to `/dehydrated` for compatibility with letsencrypt-tomcat <= 0.40. * `CERT_DIR` - if `DEHYDRATED_BASEDIR` is set, defaults to `$DEHYDRATED_BASEDIR/certs`, which is the default of dehydrated. If `DEHYDRATED_BASEDIR` is not set, defaults to `/certs` to be compatible with older letsencrypt-tomcat releases <=0.4.0. - * `DEHYDRATED_WELLKNOWN` - path where dehydrated will place .well-known/acme-challenge. Should usually be the document root of a webserver. + * `DEHYDRATED_WELLKNOWN` - path where dehydrated will place ACME challenge files. Should usually be the document root of a webserver with `/.well-known/acme-challenge` appended. Note: if you want to save certificates and accounts in persistent storage it is highly recommended to set `DEHYDRATED_BASEDIR` env variable as described below. @@ -106,4 +106,4 @@ docker build -t schnatterer/letsencrypt-tomcat . docker build -t schnatterer/letsencrypt-tomcat:standalone --file=examples/standalone/Dockerfile . docker build -t schnatterer/letsencrypt-tomcat:spring-boot --file=examples/spring-boot/Dockerfile . docker build -t schnatterer/letsencrypt-tomcat:embedded-tomcat --file=examples/embedded-tomcat/Dockerfile . -``` \ No newline at end of file +``` diff --git a/examples/standalone/Dockerfile b/examples/standalone/Dockerfile index 77eaae5..076a664 100644 --- a/examples/standalone/Dockerfile +++ b/examples/standalone/Dockerfile @@ -13,8 +13,8 @@ ENV DEHYDRATED_BASEDIR="/certs/dehydrated" RUN mkdir -p /dist/opt/bitnami/tomcat/lib/ RUN cp -r /tomcat-reloading-connector/* /dist/opt/bitnami/tomcat/lib/ -# set directory where dehydrated will create .well-known/acme-challenge -ENV DEHYDRATED_WELLKNOWN="/opt/bitnami/tomcat/webapps/ROOT" +# set directory where dehydrated will create ACME challenge files +ENV DEHYDRATED_WELLKNOWN="/opt/bitnami/tomcat/webapps/ROOT/.well-known/acme-challenge" # Copy examples/standalone tomcat config COPY examples/standalone/tomcat /dist/opt/bitnami/tomcat/ @@ -28,4 +28,4 @@ FROM tomcat VOLUME /certs/ COPY --from=aggregator --chown=1001:0 /dist / ENTRYPOINT [ "/meta-entrypoint.sh", "/opt/bitnami/scripts/tomcat/entrypoint.sh" ] -CMD [ "/opt/bitnami/scripts/tomcat/run.sh" ] \ No newline at end of file +CMD [ "/opt/bitnami/scripts/tomcat/run.sh" ] diff --git a/meta-entrypoint.sh b/meta-entrypoint.sh index 67bc6ea..afa4ebf 100644 --- a/meta-entrypoint.sh +++ b/meta-entrypoint.sh @@ -11,6 +11,7 @@ SELF_SIGNED_CERT_VALIDITY_DAYS=${SELF_SIGNED_CERT_VALIDITY_DAYS:-30} : "${CERT_DIR:="${DEHYDRATED_BASEDIR}/certs"}" : "${DEHYDRATED_BASEDIR:="/dehydrated"}" : "${DEHYDRATED_WELLKNOWN:="/static"}" +export CERT_DIR DEHYDRATED_BASEDIR DEHYDRATED_WELLKNOWN export JAVA_OPTS="-Djava.awt.headless=true -XX:+UseG1GC -Dfile.encoding=UTF-8 -Dcertdir=${CERT_DIR} -Ddomain=${DOMAIN}" @@ -117,4 +118,4 @@ RED='\033[0;31m' DEFAULT_COLOR='\033[0m' -main "$@" \ No newline at end of file +main "$@"