Compare commits

..

No commits in common. "36b71a0ebbcf3eeb0a0e1c8e4eb4a0bbfc338885" and "df56fcf9979bd0250938f5cbd772df2409a50d3f" have entirely different histories.

5 changed files with 86 additions and 253 deletions

View File

@ -11,30 +11,17 @@ FROM base-${TARGETARCH}${TARGETVARIANT}
ARG S6_OVERLAY_VERSION=3.1.5.0 ARG S6_OVERLAY_VERSION=3.1.5.0
# Core variables
ENV PUID=1000
ENV PGID=1000
ENV TZ=UTC
ENV GENERATE_DHPARAM=true
ENV INTERVAL="0 */6 * * *"
# Single domain
ENV DOMAINS= ENV DOMAINS=
ENV EMAIL= ENV EMAIL=
ENV INTERVAL="0 */6 * * *"
ENV STAGING=false ENV STAGING=false
# Custom CA support
ENV CUSTOM_CA=
ENV CUSTOM_CA_SERVER=
# Different plugin support (to support Cloudflare but also normal mode)
ENV PLUGIN=standalone
ENV PROPOGATION_TIME=10 ENV PROPOGATION_TIME=10
ENV GENERATE_DHPARAM=true
ENV TZ=UTC
ENV PUID=1000
ENV PGID=1000
ENV CLOUDFLARE_TOKEN= ENV CLOUDFLARE_TOKEN=
## Multi-cert support
ENV CERT_COUNT=1
#Get required packages #Get required packages
RUN apk update && apk add curl bash python3 py3-virtualenv procps tzdata nano shadow xz busybox-suid openssl RUN apk update && apk add curl bash python3 py3-virtualenv procps tzdata nano shadow xz busybox-suid openssl

View File

@ -2,7 +2,7 @@
![Drone (self-hosted) with branch](https://img.shields.io/drone/build/MrMeeb/certbot-cron-docker/master?label=latest&server=https%3A%2F%2Fdrone.mrmeeb.stream&style=for-the-badge) ![Drone (self-hosted) with branch](https://img.shields.io/drone/build/MrMeeb/certbot-cron-docker/develop?label=develop&server=https%3A%2F%2Fdrone.mrmeeb.stream&style=for-the-badge) ![Drone (self-hosted) with branch](https://img.shields.io/drone/build/MrMeeb/certbot-cron-docker/master?label=latest&server=https%3A%2F%2Fdrone.mrmeeb.stream&style=for-the-badge) ![Drone (self-hosted) with branch](https://img.shields.io/drone/build/MrMeeb/certbot-cron-docker/develop?label=develop&server=https%3A%2F%2Fdrone.mrmeeb.stream&style=for-the-badge)
Dockerised Certbot that utilises cron to schedule creating and renewing SSL certificates. Supports standalone, webroot or Cloudflare methods. Automatic renewal attempt happens every 6 hours by default. Dockerised Certbot that utilises cron to schedule creating and renewing SSL certificates. Uses Cloudflare for DNS-01 verification. Automatic renewal attempt happens every 6 hours by default.
## Tags ## Tags
@ -18,9 +18,8 @@ Dockerised Certbot that utilises cron to schedule creating and renewing SSL cert
docker run -d --name certbot \ docker run -d --name certbot \
-e EMAIL=admin@domain.com \ -e EMAIL=admin@domain.com \
-e DOMAINS=domain.com \ -e DOMAINS=domain.com \
-e PLUGIN=cloudflare \
-e CLOUDFLARE_TOKEN=123abc -e CLOUDFLARE_TOKEN=123abc
-v ./certbot-cron:/config \ -v /docker/certbot-cron:/config \
git.mrmeeb.stream/mrmeeb/certbot-cron:latest git.mrmeeb.stream/mrmeeb/certbot-cron:latest
``` ```
@ -37,78 +36,30 @@ services:
environment: environment:
- EMAIL=admin@domain.com - EMAIL=admin@domain.com
- DOMAINS=domain.com,*.domain.com - DOMAINS=domain.com,*.domain.com
- PLUGIN=cloudflare
- CLOUDFLARE_TOKEN=123abc - CLOUDFLARE_TOKEN=123abc
``` ```
## Environment Variables: ## Environment Variables:
### Core Options: | Variable | Default Value | Description |
Core options to the container
| Variable | Default | Description |
| --- | --- | --- | | --- | --- | --- |
|PUID |int |1000 |Sets the UID of the user certbot runs under | |PUID |int |1000 |Sets the UID of the user certbot runs under |
|PGID |int |1000 |Sets the GID of the user certbot runs under | |PGID |int |1000 |Sets the GID of the user certbot runs under |
|TZ |[List of valid TZs](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) |UTC |Sets the timezone of the container | |TZ |[List of valid TZs](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) |UTC |Sets the timezone of the container |
| GENERATE_DHPARAM | true (case-sensitive) | Generate Diffie-Hellman keys in /config/letsencrypt/keys |
| INTERVAL | 0 */6 * * * | How often certbot attempts to renew the certificate. Cron syntax |
| CERT_COUNT | 1 | How many certificates certbot will try to issue (more than 1 not yet implemented) |
### Certificate Options
These options apply when `CERT_COUNT` is `1`
| Variable | Default | Description |
| --- | --- | --- |
| EMAIL | None | Email address for renewal information & other communications | | EMAIL | None | Email address for renewal information & other communications |
| DOMAINS | None | Domains to be included in the certificate. Comma separated list, no spaces. Wildcards supported | | DOMAINS | None | Domains to be included in the certificate. Comma separated list, no spaces. Wildcards supported |
| STAGING | false (case-sensitive) | Uses the LetsEncrypt staging endpoint for testing - avoids the aggressive rate-limiting of the production endpoint. **Not supported when using a custom Certificate Authority.** | | INTERVAL | 0 */6 * * * | How often certbot attempts to renew the certificate. Cron syntax |
| STAGING | false (case-sensitive) | Uses the LetsEncrypt staging endpoint for testing - avoids the aggressive rate-limiting of the production endpoint |
### Plugins
Plugins that can used for issuing a certificate
| Variable | Default | Description |
| --- | --- | --- |
| PLUGIN | standalone | Options are `webroot`, `standalone`, or `cloudflare` |
- `webroot` - relies on a webserver running on the FQDN for which you're trying to issue a certificate to serve validation files
- Requires the webserver's root directory to be mounted to the container as `/config/webroot`
- `standalone` - certbot spawns a webserver on port 80 for validation
- Requires this container to be bound to port 80 on the host
- `cloudflare` - Creates a TXT record with Cloudflare pointing to the domain you're requesting a certificate for
- Requires the domain you're requesting a certificate for to be entered in Cloudflare
#### Cloudflare Plugin
Options that affect the behaviour of certbot running with the Cloudflare plugin
| Variable | Default | Description |
| --- | --- | --- |
| PROPOGATION_TIME | 10 | The amount of time (seconds) that certbot waits for the TXT records to propogate to Cloudflare before verifying - the more domains in the certificate, the longer you might need | | PROPOGATION_TIME | 10 | The amount of time (seconds) that certbot waits for the TXT records to propogate to Cloudflare before verifying - the more domains in the certificate, the longer you might need |
| CLOUDFLARE_TOKEN | null | Cloudflare token for verification | | GENERATE_DHPARAM | true (case-sensitive) | Generate Diffie-Hellman keys in /config/letsencrypt/keys |
| CLOUDFLARE_TOKEN | N/A | Cloudflare token for verification |
### Custom Certificate Authority
Options to use a custom Certificate Authority, for example when issuing internal certificates
| Variable | Default | Description |
| --- | --- | --- |
| CUSTOM_CA | null | Name of the root certificate Certbot/ACME will trust requesting the certificate, e.g `root.pem`. **Must be placed in `/config/custom_ca`** |
| CUSTOM_CA_SERVER | null | Custom server URL used by Certbot/ACME when requesting a certificate, e.g `https://ca.internal/acme/acme/directory` |
## Volumes ## Volumes
| Docker path | Purpose | | Docker path | Purpose |
| --- | --- | | --- | --- |
| /config | Stores configs and LetsEncrypt output for mounting in other containers | /config | Stores configs and LetsEncrypt output for mounting in other containers
| /config/custom_ca | Mountpoint for a custom Certificate Authority root certificate. **Required if `CUSTOM_CA` is set**
| /config/webroot | Mountpoint for the webroot of a separate webserver. **Required if `PLUGIN=webroot` is set**
## Ports ## Other
| Port | Purpose | Thanks to [this guy](https://stackoverflow.com/questions/63447441/docker-stop-for-crond-times-out) for explaining how to make cron actually shutdown when stopping the container.
| --- | --- |
| 80 | Used by ACME to verify domain ownership. **Required if `PLUGIN=standalone` is set**

View File

@ -32,177 +32,54 @@ then
touch /config/.crontab.txt touch /config/.crontab.txt
fi fi
#Cleanup renew list and create it fresh, ready for commands to be run and added if [ ! -f /config/.secrets/cloudflare.ini ]
echo "#!/command/with-contenv bash" > /config/renew-list.sh then
echo "" >> /config/.renew-list.sh
function single_domain {
if [ ! -z $CUSTOM_CA ]
then
echo "Using a custom CA for issuing certificates"
if [ ! -d /config/custom_ca ]
then
mkdir /config/custom_ca
echo "Please place your custom CA file into /config/custom_ca and restart the container"
exit 1
fi
if [ -z "$(ls -A /config/custom_ca)" ]
then
echo "The CUSTOM_CA option is populated, but the /config/custom_ca dir is empty. Please place your root certificate in this directory and restart the container"
exit 1
fi
if [ -z $CUSTOM_CA_SERVER ]
then
echo "CUSTOM_CA_SERVER has not been defined. It is required for using a custom CA to issue a certificate"
exit 1
fi
#REQUESTS_CA_BUNDLE=/config/custom_ca/$CUSTOM_CA
CUSTOM_CA_PATH=/config/custom_ca/$CUSTOM_CA
CUSTOM_CA_SERVER_OPT="--server $CUSTOM_CA_SERVER"
if [ $STAGING ]
then
echo "Staging is not supported when using a custom CA, so overriding. To remove this alert, set staging to false."
STAGING=false
fi
fi
BASE_COMMAND=(certbot certonly --non-interactive --config-dir /config/letsencrypt --work-dir /config/.tmp --logs-dir /config/logs --key-path /config/letsencrypt/keys --expand --agree-tos $CUSTOM_CA_SERVER_OPT --email $EMAIL -d $DOMAINS)
## Run with Cloudflare plugin
if [ $PLUGIN == "cloudflare" ]
then
echo "Using Cloudflare plugin"
if [ ! -f /config/.secrets/cloudflare.ini ]
then
touch /config/.secrets/cloudflare.ini touch /config/.secrets/cloudflare.ini
fi fi
if [ -n "$CLOUDFLARE_TOKEN" ] if [ -n "$CLOUDFLARE_TOKEN" ]
then then
echo "Cloudflare token is present" echo "Cloudflare token is present"
echo "dns_cloudflare_api_token = $CLOUDFLARE_TOKEN" > /config/.secrets/cloudflare.ini echo "dns_cloudflare_api_token = $CLOUDFLARE_TOKEN" > /config/.secrets/cloudflare.ini
fi fi
if [ ! -s /config/.secrets/cloudflare.ini ] if [ ! -s /config/.secrets/cloudflare.ini ]
then then
echo "cloudflare.ini is empty - please add your Cloudflare credentials or API key before continuing. This can be done as an ENV var, or by editing the file directly" echo "cloudflare.ini is empty - please add your Cloudflare credentials or API key before continuing. This can be done as an ENV var, or by editing the file directly"
exit 1 exit 22
fi fi
#Securing cloudflare.ini to supress warnings #Securing cloudflare.ini to supress warnings
chmod 600 /config/.secrets/cloudflare.ini chmod 600 /config/.secrets/cloudflare.ini
echo "Creating certificates, or attempting to renew if they already exist" echo "Creating certificates, or attempting to renew if they already exist"
if [ $STAGING = true ] if [[ $STAGING = true ]]
then then
echo "Using staging endpoint - THIS SHOULD BE USED FOR TESTING ONLY" echo "Using staging endpoint - THIS SHOULD BE USED FOR TESTING ONLY"
${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds $PROPOGATION_TIME --dns-cloudflare-credentials /config/.secrets/cloudflare.ini --staging certbot certonly --staging --non-interactive --config-dir /config/letsencrypt --work-dir /config/.tmp --logs-dir /config/logs --key-path /config/letsencrypt/keys --expand --agree-tos --dns-cloudflare --dns-cloudflare-propagation-seconds $PROPOGATION_TIME --dns-cloudflare-credentials /config/.secrets/cloudflare.ini --email $EMAIL -d $DOMAINS
# Add to renewal list
echo "REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds $PROPOGATION_TIME --dns-cloudflare-credentials /config/.secrets/cloudflare.ini --staging" >> /config/renew-list.sh
echo "Creation/renewal attempt complete" echo "Creation/renewal attempt complete"
elif [ $STAGING = false ] elif [[ $STAGING = false ]]
then then
echo "Using production endpoint" echo "Using production endpoint"
${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds $PROPOGATION_TIME --dns-cloudflare-credentials /config/.secrets/cloudflare.ini certbot certonly --non-interactive --config-dir /config/letsencrypt --work-dir /config/.tmp --logs-dir /config/logs --key-path /config/letsencrypt/keys --expand --agree-tos --dns-cloudflare --dns-cloudflare-propagation-seconds $PROPOGATION_TIME --dns-cloudflare-credentials /config/.secrets/cloudflare.ini --email $EMAIL -d $DOMAINS
# Add to renewal list
echo "REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds $PROPOGATION_TIME --dns-cloudflare-credentials /config/.secrets/cloudflare.ini" >> /config/renew-list.sh
echo "Creation/renewal attempt complete" echo "Creation/renewal attempt complete"
else else
echo "Unrecognised option for STAGING variable - check your configuration" echo "Unrecognised option for STAGING variable - check your configuration"
exit 1 exit 22
fi fi
## Run with Standalone plugin if [[ $GENERATE_DHPARAM = true ]] && [[ ! -s /config/letsencrypt/keys/ssl-dhparams.pem ]]
elif [ $PLUGIN == "standalone" ] then
then
echo "Using HTTP verification via built-in web-server - please ensure port 80 is exposed."
if [ $STAGING = true ]
then
echo "Using staging endpoint - THIS SHOULD BE USED FOR TESTING ONLY"
REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --standalone --staging
# Add to renewal list
echo "REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --standalone --staging" >> /config/renew-list.sh
echo "Creation/renewal attempt complete"
elif [ $STAGING = false ]
then
echo "Using production endpoint"
REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --standalone
# Add to renewal list
echo "REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --standalone" >> /config/renew-list.sh
echo "Creation/renewal attempt complete"
else
echo "Unrecognised option for STAGING variable - check your configuration"
exit 1
fi
## Run with webroot plugin
elif [ $PLUGIN == "webroot" ]
then
echo "Using HTTP verification via webroot - please ensure you have mounted a webroot at /config/webroot from a web-server reachable via the domain you are issuing a certificate for."
if [ $STAGING = true ]
then
echo "Using staging endpoint - THIS SHOULD BE USED FOR TESTING ONLY"
REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot --staging
# Add to renewal list
echo "REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot --staging" >> /config/renew-list.sh
echo "Creation/renewal attempt complete"
elif [ $STAGING = false ]
then
echo "Using production endpoint"
REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot
# Add to renewal list
echo "REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot" >> /config/renew-list.sh
echo "Creation/renewal attempt complete"
else
echo "Unrecognised option for STAGING variable - check your configuration"
exit 1
fi
else
echo "Unrecognised option for PLUGIN variable - check your configuration"
fi
if [ $GENERATE_DHPARAM = true ] && [ ! -s /config/letsencrypt/keys/ssl-dhparams.pem ]
then
echo "Generating Diffie-Hellman keys, saved to /config/letsencrypt/keys" echo "Generating Diffie-Hellman keys, saved to /config/letsencrypt/keys"
openssl dhparam -out /config/letsencrypt/keys/ssl-dhparams.pem 4096 openssl dhparam -out /config/letsencrypt/keys/ssl-dhparams.pem 4096
fi
echo "$INTERVAL /certbot-renew.sh >> /config/logs/renew.log" > /config/.crontab.txt
echo "Starting automatic renewal job. Schedule is $INTERVAL"
crontab /config/.crontab.txt
}
if [ $CERT_COUNT == 1 ]
then
single_domain
fi fi
echo "$INTERVAL /certbot-renew.sh >> /config/logs/renew.log" > /config/.crontab.txt
echo "Starting automatic renewal job. Schedule is $INTERVAL"
crontab /config/.crontab.txt

View File

@ -3,4 +3,18 @@
echo '' echo ''
date date
echo "Attempting to renew certificates" echo "Attempting to renew certificates"
bash /config/.renew-list.sh if [[ $STAGING = true ]]
then
echo "Using staging endpoint - THIS SHOULD BE USED FOR TESTING ONLY"
certbot certonly --staging --non-interactive --config-dir /config/letsencrypt --work-dir /config/.tmp --logs-dir /config/logs --key-path /config/letsencrypt/keys --expand --agree-tos --dns-cloudflare --dns-cloudflare-propagation-seconds $PROPOGATION_TIME --dns-cloudflare-credentials /config/.secrets/cloudflare.ini --email $EMAIL -d $DOMAINS
echo "Renewal attempt complete"
elif [[ $STAGING = false ]]
then
echo "Using production endpoint"
certbot certonly --non-interactive --config-dir /config/letsencrypt --work-dir /config/.tmp --logs-dir /config/logs --key-path /config/letsencrypt/keys --expand --agree-tos --dns-cloudflare --dns-cloudflare-propagation-seconds $PROPOGATION_TIME --dns-cloudflare-credentials /config/.secrets/cloudflare.ini --email $EMAIL -d $DOMAINS
echo "Renewal attempt complete"
else
echo "Unrecognised option for STAGING variable - check your configuration"
exit 8
fi

View File

@ -15,14 +15,18 @@ echo ""
echo "Initialising container" echo "Initialising container"
echo " echo "
---------------------------------------------------------------------- ----------------------------------------------------------------------
ENVIRONMENT (only core variables shown) ENVIRONMENT
---------------------------------------------------------------------- ----------------------------------------------------------------------
PUID=${PUID} PUID=${PUID}
PGID=${PGID} PGID=${PGID}
TZ=${TZ} TZ=${TZ}
DOMAINS=${DOMAINS}
EMAIL=${EMAIL}
INTERVAL=${INTERVAL} INTERVAL=${INTERVAL}
STAGING=${STAGING}
PROPOGATION_TIME=${PROPOGATION_TIME}
GENERATE_DHPARAM=${GENERATE_DHPARAM} GENERATE_DHPARAM=${GENERATE_DHPARAM}
CERT_COUNT=${CERT_COUNT} CLOUDFLARE_TOKEN=${CLOUDFLARE_TOKEN}
---------------------------------------------------------------------- ----------------------------------------------------------------------
" "
@ -37,7 +41,7 @@ if [[ ! "${PUID}" -eq 0 ]] && [[ ! "${PGID}" -eq 0 ]]; then
groupmod -o -g "${PGID}" mrmeeb groupmod -o -g "${PGID}" mrmeeb
else else
echo "Running as root is not supported, please fix your PUID and PGID!" echo "Running as root is not supported, please fix your PUID and PGID!"
sleep infinity exit 1
fi fi
echo "Checking permissions in /config and /app." echo "Checking permissions in /config and /app."