61 Commits

Author SHA1 Message Date
111657411e Add renewal notifications (#20)
Some checks failed
Build Image / Validate Image (push) Failing after 2m2s
Build Image / Publish Image (push) Has been skipped
Build Image / Notify (push) Has been skipped
Reviewed-on: #20
2024-05-30 19:44:10 +00:00
2b961950f8 Move renewal function to its own file 2024-05-30 19:44:10 +00:00
da87dcc8e3 Add new env vars to readme 2024-05-30 19:44:10 +00:00
f9d99b7c7f Add support for alerting on success or failure of renewal
Installs apprise and allows config of alerting for successful or failed renewals. Currently only supports renewals, not inital issuing. Not necessarily a problem as I'd hope you'd be watching the logs on first run.
I don't love how I create /config/.renew-list.sh. I should readdress this somehow
2024-05-30 19:44:10 +00:00
c590df5a10 Add setuptools for certbot dependencies 2024-05-30 19:44:10 +00:00
864a59f76a Update alpine Docker tag to v3.20.0 2024-05-30 19:44:10 +00:00
9513767892 revert c12eb0b381
revert Update alpine Docker tag to v3.20.0
2024-05-30 19:44:10 +00:00
8a1d95c4f5 Update certbot to v2.10.0 2024-05-30 19:44:10 +00:00
ab42a76755 Update alpine Docker tag to v3.20.0 2024-05-30 19:44:10 +00:00
1d5a66a385 Fix incorrect reference to default branch
Some checks failed
Test Pull Request / Build Image (pull_request) Successful in 4m1s
Test Pull Request / Notify (pull_request) Successful in 3s
Build Image / Publish Image (push) Has been cancelled
Build Image / Notify (push) Has been cancelled
Build Image / Validate Image (push) Has been cancelled
2024-05-27 20:53:20 +00:00
df3fc81a9d Update base renovate branch 2024-05-27 15:57:57 +00:00
2a41f240a3 Merge pull request 'Add automations and bump base versions' (#11) from develop into master
Some checks failed
Build Image / Publish Image (push) Has been cancelled
Build Image / Notify (push) Has been cancelled
Build Image / Validate Image (push) Has been cancelled
Reviewed-on: #11
2024-05-12 11:40:44 +00:00
1b8831b888 Correct build-main workflow
All checks were successful
Build Image / Validate Image (push) Successful in 1m40s
Build Image / Publish Image (push) Successful in 4m17s
Build Image / Notify (push) Successful in 3s
2024-05-12 11:33:34 +00:00
295c554933 Merge branch 'automation' into develop
Some checks failed
Build Image / Validate Image (push) Failing after 1m57s
Build Image / Publish Image (push) Has been skipped
Build Image / Notify (push) Has been skipped
2024-05-12 11:30:13 +00:00
d02cf72bbc Correct workflow dependency graph 2024-05-12 11:22:16 +00:00
5d3eed683d Add gitea actions
All checks were successful
Build Image / Validate Image (push) Successful in 2m19s
Build Image / Notify (push) Successful in 3s
Build Image / Publish Image (push) Successful in 4m13s
2024-05-12 11:00:57 +00:00
0157a462ed Remove Drone build badges 2024-05-12 10:58:19 +00:00
63f586768f Add gitea actions 2024-05-12 10:57:43 +00:00
5863d5b808 Change release formatting 2024-05-12 09:25:01 +00:00
c42462b4e1 Update goreleaser syntax 2024-05-12 09:24:53 +00:00
3db15537e8 Rebase to Alpine 3.19.1 2024-05-12 09:22:25 +00:00
9465157295 Update S6 to 3.1.6.2 2024-05-12 09:22:08 +00:00
8d36f0c117 Version control for Certbot packages 2024-05-12 09:21:44 +00:00
b574ae146e Enable Renovate 2024-05-12 09:21:23 +00:00
0eeb08f4d8 Update README.md [CI SKIP] 2024-01-13 17:30:08 +00:00
639120e46d Update README.md [CI SKIP] 2024-01-13 17:28:19 +00:00
75488a4009 Merge pull request '1.1.2' (#7) from develop into master
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #7
2023-08-02 11:54:14 +00:00
b962b65145 add ONE_SHOT so container exits after run
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-17 17:49:02 +00:00
0614aa0565 fix build issues due to cython 3.0.0 release 2023-07-17 17:19:30 +00:00
83f8c39e48 Merge pull request '1.1.1' (#6) from develop into master
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #6
2023-07-16 18:07:08 +00:00
961f218621 further small tweak to log formatting
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-16 17:39:48 +00:00
6134b05328 Merge pull request '1.1.0' (#5) from develop into master
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #5
2023-07-16 17:08:41 +00:00
ada326f2d8 tweak log formatting
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-16 16:45:36 +00:00
5d14d166eb add multi-certificate support
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-16 15:37:12 +00:00
3a6466612b enable tracking of env vars between runs
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-15 16:04:45 +00:00
36b71a0ebb add standalone and webroot methods
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-09 21:53:06 +00:00
e0da513893 Merge pull request '1.0.0' (#4) from develop into master
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #4
2023-06-21 18:20:15 +00:00
df56fcf997 make CLOUDFLARE_TOKEN override instead of append
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-21 17:48:29 +00:00
3c3cc47d32 add CLOUDFLARE_TOKEN to env vars
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-21 17:32:17 +00:00
053cef4c31 add build dependencies for arm
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-20 22:14:44 +00:00
b6d93c1164 update readme
Some checks failed
continuous-integration/drone/push Build is failing
2023-06-20 21:27:42 +00:00
a41c409f9d update build logic 2023-06-20 21:27:36 +00:00
e859aa7218 add goreleaser 2023-06-20 21:27:27 +00:00
a4b59c360e change to s6 2023-06-20 21:27:01 +00:00
e7bd87d5bd change to alpine 2023-06-20 21:26:11 +00:00
6ef0abfd6b Update 'README.md'
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-02 16:40:31 +00:00
868eb4eb59 Update '.drone.yml'
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-25 20:38:20 +00:00
578284bb10 Update '.drone.yml'
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-25 19:18:06 +00:00
e10bdd741d Update 'README.md' 2022-12-25 19:09:13 +00:00
0aa35c3ef6 Update 'README.md' 2022-12-25 19:07:54 +00:00
90f5095eef Add '.drone.yml' 2022-12-25 18:50:54 +00:00
3daee369ca Update 'README.md' 2022-12-25 18:48:33 +00:00
6a27c9232c Update 'README.md' 2022-06-21 16:56:35 +00:00
e130fc041e Prevented dhparams regenerating if they already exist 2022-06-21 11:58:46 +00:00
591e35c91a Formatting 2022-06-20 22:42:29 +00:00
375cf5da74 Add GENERATE_DHPARAM 2022-06-20 22:41:27 +00:00
09eb18adda Added PROPOGATION_TIME variable 2022-06-20 22:08:30 +00:00
d09988c241 Update 'README.md' 2022-06-19 17:11:20 +00:00
6d696dd4b2 Fix typo 2022-06-18 23:12:11 +00:00
c23657ce01 Formatting fix 2022-06-18 23:10:07 +00:00
634f0cac4a first commit 2022-06-18 23:08:39 +00:00
38 changed files with 1582 additions and 2 deletions

View File

@ -0,0 +1,88 @@
name: Build Image
on:
push:
branches:
- 'develop'
env:
TEST_TAG: mrmeeb/certbot-cron:test
FULL_TAG: git.mrmeeb.stream/mrmeeb/certbot-cron:develop
jobs:
"Validate Image":
runs-on: [ubuntu-docker-latest, linux/amd64]
steps:
- name: Build locally
uses: docker/build-push-action@v5
with:
load: true
tags: ${{ env.TEST_TAG }}
provenance: false
- name: Test certificate issuing
id: test
run: |
# First create a volume
docker volume create ${{ gitea.sha }} && \
# Then issue a certificate
docker run --rm -v ${{ gitea.sha }}:/config -e STAGING=true -e EMAIL=${{ secrets.EMAIL }} -e DOMAINS=${{ gitea.sha }}.mrmeeb.stream -e PLUGIN=cloudflare -e CLOUDFLARE_TOKEN=${{ secrets.CLOUDFLARE_TOKEN }} -e ONE_SHOT=true -e GENERATE_DHPARAM=false ${{ env.TEST_TAG }} && \
# Then revoke it again
docker run --rm --entrypoint "/usr/bin/certbot" -v ${{ gitea.sha }}:/config ${{ env.TEST_TAG }} revoke --non-interactive --agree-tos --email ${{ secrets.EMAIL }} --staging --config-dir /config/letsencrypt --work-dir /config/.tmp --logs-dir /config/logs --cert-path /config/letsencrypt/live/${{ gitea.sha }}.mrmeeb.stream/fullchain.pem
- name: Tidy up
if: always()
run: |
echo "Removing docker volume ${{ gitea.sha }}" && \
docker volume rm ${{ gitea.sha }}
- name: Test Failure
uses: rjstone/discord-webhook-notify@v1
if: failure()
with:
severity: error
details: Test Failed!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
username: Gitea
avatarUrl: ${{ vars.RUNNER_ICON_URL }}
"Publish Image":
runs-on: [ubuntu-docker-latest, linux/amd64]
needs: ["Validate Image"]
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Gitea Container Registry
uses: docker/login-action@v3
with:
registry: git.mrmeeb.stream
username: ${{ env.GITHUB_ACTOR }}
password: ${{ secrets.GTCR_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ env.FULL_TAG }}
provenance: false
- name: Build Failure
uses: rjstone/discord-webhook-notify@v1
if: failure()
with:
severity: error
details: Build Failed!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
username: Gitea
avatarUrl: ${{ vars.RUNNER_ICON_URL }}
"Notify":
runs-on: [ubuntu-docker-latest, linux/amd64]
needs: ["Validate Image", "Publish Image"]
steps:
- name: Notify of success
uses: rjstone/discord-webhook-notify@v1
if: success()
with:
severity: info
details: Build succeeded!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
username: Gitea
avatarUrl: ${{ vars.RUNNER_ICON_URL }}

View File

@ -0,0 +1,88 @@
name: Build Image
on:
push:
branches:
- 'master'
env:
TEST_TAG: mrmeeb/certbot-cron:test
FULL_TAG: git.mrmeeb.stream/mrmeeb/certbot-cron:latest
jobs:
"Validate Image":
runs-on: [ubuntu-docker-latest, linux/amd64]
steps:
- name: Build locally
uses: docker/build-push-action@v5
with:
load: true
tags: ${{ env.TEST_TAG }}
provenance: false
- name: Test certificate issuing
id: test
run: |
# First create a volume
docker volume create ${{ gitea.sha }} && \
# Then issue a certificate
docker run --rm -v ${{ gitea.sha }}:/config -e STAGING=true -e EMAIL=${{ secrets.EMAIL }} -e DOMAINS=${{ gitea.sha }}.mrmeeb.stream -e PLUGIN=cloudflare -e CLOUDFLARE_TOKEN=${{ secrets.CLOUDFLARE_TOKEN }} -e ONE_SHOT=true -e GENERATE_DHPARAM=false ${{ env.TEST_TAG }} && \
# Then revoke it again
docker run --rm --entrypoint "/usr/bin/certbot" -v ${{ gitea.sha }}:/config ${{ env.TEST_TAG }} revoke --non-interactive --agree-tos --email ${{ secrets.EMAIL }} --staging --config-dir /config/letsencrypt --work-dir /config/.tmp --logs-dir /config/logs --cert-path /config/letsencrypt/live/${{ gitea.sha }}.mrmeeb.stream/fullchain.pem
- name: Tidy up
if: always()
run: |
echo "Removing docker volume ${{ gitea.sha }}" && \
docker volume rm ${{ gitea.sha }}
- name: Test Failure
uses: rjstone/discord-webhook-notify@v1
if: failure()
with:
severity: error
details: Test Failed!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
username: Gitea
avatarUrl: ${{ vars.RUNNER_ICON_URL }}
"Publish Image":
runs-on: [ubuntu-docker-latest, linux/amd64]
needs: ["Validate Image"]
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Gitea Container Registry
uses: docker/login-action@v3
with:
registry: git.mrmeeb.stream
username: ${{ env.GITHUB_ACTOR }}
password: ${{ secrets.GTCR_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ env.FULL_TAG }}
provenance: false
- name: Build Failure
uses: rjstone/discord-webhook-notify@v1
if: failure()
with:
severity: error
details: Build Failed!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
username: Gitea
avatarUrl: ${{ vars.RUNNER_ICON_URL }}
"Notify":
runs-on: [ubuntu-docker-latest, linux/amd64]
needs: ["Validate Image", "Publish Image"]
steps:
- name: Notify of success
uses: rjstone/discord-webhook-notify@v1
if: success()
with:
severity: info
details: Build succeeded!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
username: Gitea
avatarUrl: ${{ vars.RUNNER_ICON_URL }}

View File

@ -0,0 +1,80 @@
name: Build Tagged Release Image
on:
push:
tags:
- '*'
env:
FULL_TAG: git.mrmeeb.stream/mrmeeb/certbot-cron
jobs:
"Build Image":
runs-on: [ubuntu-docker-latest, linux/amd64]
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Gitea Container Registry
uses: docker/login-action@v3
with:
registry: git.mrmeeb.stream
username: ${{ env.GITHUB_ACTOR }}
password: ${{ secrets.GTCR_TOKEN }}
- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.FULL_TAG }}
tags: |
type=pep440,pattern={{version}}
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
provenance: false
- name: Notify on failure
uses: rjstone/discord-webhook-notify@v1
if: failure()
with:
severity: error
details: Build failed!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
username: Gitea
avatarUrl: ${{ vars.RUNNER_ICON_URL }}
"Create Release":
runs-on: ubuntu-latest
needs: ["Build Image"]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: 1.22.2
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
with:
distribution: goreleaser
version: latest
args: release --clean -f .goreleaser-gitea.yaml
env:
GITEA_TOKEN: ${{ secrets.GORELEASER_TOKEN }}
"Notify":
runs-on: [ubuntu-docker-latest, linux/amd64]
needs: ["Build Image", "Create Release"]
steps:
- name: Notify of success
uses: rjstone/discord-webhook-notify@v1
if: success()
with:
severity: info
details: Build succeeded!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
username: Gitea
avatarUrl: ${{ vars.RUNNER_ICON_URL }}

View File

@ -0,0 +1,47 @@
name: Test Pull Request
on:
pull_request:
branches:
- 'master'
- 'develop'
env:
FULL_TAG: git.mrmeeb.stream/mrmeeb/certbot-cron:develop
jobs:
"Build Image":
runs-on: [ubuntu-docker-latest, linux/amd64]
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build
uses: docker/build-push-action@v5
with:
push: false
platforms: linux/amd64,linux/arm64
tags: ${{ env.FULL_TAG }}
provenance: false
- name: Notify on failure
uses: rjstone/discord-webhook-notify@v1
if: failure()
with:
severity: error
details: Build failed!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
username: Gitea
avatarUrl: ${{ vars.RUNNER_ICON_URL }}
"Notify":
runs-on: [ubuntu-docker-latest, linux/amd64]
needs: ["Build Image"]
steps:
- name: Notify of success
uses: rjstone/discord-webhook-notify@v1
if: success()
with:
severity: info
details: Build succeeded!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
username: Gitea
avatarUrl: ${{ vars.RUNNER_ICON_URL }}

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
dist-gitea/
.tags

35
.goreleaser-gitea.yaml Normal file
View File

@ -0,0 +1,35 @@
dist: dist-gitea
builds:
- skip: true
archives:
- format: binary
release:
draft: true
header: |
# Certbot Cron v{{ .Tag }}
## Docker Image:
`git.mrmeeb.stream/mrmeeb/certbot-cron:{{ .Tag }}`
gitea:
owner: MrMeeb
name: certbot-cron-docker
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
# .goreleaser.yaml
gitea_urls:
api: https://git.mrmeeb.stream/api/v1
download: https://git.mrmeeb.stream
# set to true if you use a self-signed certificate
skip_tls_verify: false

78
Dockerfile Normal file
View File

@ -0,0 +1,78 @@
FROM alpine:3.20.0 as base
ARG TARGETARCH
FROM base AS base-amd64
ENV S6_OVERLAY_ARCH=x86_64
FROM base AS base-arm64
ENV S6_OVERLAY_ARCH=aarch64
FROM base-${TARGETARCH}${TARGETVARIANT}
ARG S6_OVERLAY_VERSION="3.1.6.2"
# Core variables
ENV PUID=1000
ENV PGID=1000
ENV TZ=UTC
ENV GENERATE_DHPARAM=true
ENV INTERVAL="0 */6 * * *"
ENV ONE_SHOT=false
ENV APPRISE_URL=
ENV NOTIFY_ON_FAILURE=false
ENV NOTIFY_ON_SUCCESS=false
# Single domain
ENV DOMAINS=
ENV EMAIL=
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 CLOUDFLARE_TOKEN=
## Multi-cert support
ENV CERT_COUNT=1
#Get required packages
RUN apk update && apk add curl bash python3 py3-virtualenv procps tzdata nano shadow xz busybox-suid openssl
#Make folders
RUN mkdir /config && \
mkdir /app && \
#Create default user
useradd -u 1000 -U -d /config -s /bin/false mrmeeb && \
usermod -G users mrmeeb
#Install s6-overlay
RUN curl -fsSL "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz" | tar Jpxf - -C / && \
curl -fsSL "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-${S6_OVERLAY_ARCH}.tar.xz" | tar Jpxf - -C / && \
curl -fsSL "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-symlinks-noarch.tar.xz" | tar Jpxf - -C / && \
curl -fsSL "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-symlinks-arch.tar.xz" | tar Jpxf - -C /
ENV S6_BEHAVIOUR_IF_STAGE2_FAILS=2 S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 S6_VERBOSITY=1
RUN python3 -m venv /app/certbot/ && /app/certbot/bin/pip install --upgrade pip
#Get required packages for building, build, then cleanup
#Added additional pip steps to fix cython 3.0.0 issue - https://github.com/yaml/pyyaml/issues/601
COPY requirements.txt /app/certbot/requirements.txt
RUN apk add --no-cache --virtual .deps gcc python3-dev libc-dev libffi-dev && \
/app/certbot/bin/pip install wheel setuptools && \
/app/certbot/bin/pip install "Cython<3.0" pyyaml --no-build-isolation && \
/app/certbot/bin/pip install -r /app/certbot/requirements.txt && \
ln -s /app/certbot/bin/certbot /usr/bin/certbot && \
ln -s /app/certbot/bin/apprise /usr/bin/apprise && \
apk del .deps
COPY root /
RUN chmod +x /container-init.sh /certbot-prepare.sh /check-one-shot.sh /renew-function.sh && \
chown -R ${PUID}:${PGID} /app /config
ENTRYPOINT [ "/init" ]

192
README.md
View File

@ -1,3 +1,191 @@
# certbot-cron-docker
# Certbot Cron Docker
Docker container that runs certbot on a schedule to create and renew SSL certificates
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.
## Tags
|Tag |Description|
|-------|-----------|
|latest |Latest image built from the main branch. Usually coincides with a tagged release.|
|develop|Latest image built from the develop branch. Commits are made to the develop branch before being merged to main. Old versions of `develop` are removed after 14 days.|
## Running
### Docker CLI
```bash
docker run -d --name certbot \
-e EMAIL=admin@domain.com \
-e DOMAINS=domain.com \
-e PLUGIN=cloudflare \
-e CLOUDFLARE_TOKEN=123abc
-v ./certbot-cron:/config \
git.mrmeeb.stream/mrmeeb/certbot-cron:latest
```
### Docker Compose
```yaml
version: "3"
services:
certbot:
image: git.mrmeeb.stream/mrmeeb/certbot-cron:latest
container_name: certbot
restart: unless-stopped
volumes:
- ./certbot:/config
environment:
- EMAIL=admin@domain.com
- DOMAINS=domain.com,*.domain.com
- PLUGIN=cloudflare
- CLOUDFLARE_TOKEN=123abc
```
## Environment Variables:
### Core Options:
Core options to the container
| Variable | Default | Description |
| --- | --- | --- |
|PUID |int |1000 |Sets the UID 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 |
| ONE_SHOT | false | Whether container exits after first run of certbot, or starts cron-based auto-renewal |
| 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. [Details here](https://git.mrmeeb.stream/MrMeeb/certbot-cron-docker#multiple-certificates) |
| APPRISE_URL | None | URL for Apprise notifications. [Syntax](https://github.com/caronc/apprise?tab=readme-ov-file#supported-notifications)
| NOTIFY_ON_SUCCESS | false | Notify on a successful renewal attempt. Note that this isn't just when the cert is renewed, but on every renewal attempt. |
| NOTIFY_ON_FAILURE | false | Notify on a failed renewal attempt.
### Certificate Options
These options apply when `CERT_COUNT` is `1`
| Variable | Default | Description |
| --- | --- | --- |
| 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 |
| 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.** |
### 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 |
| CLOUDFLARE_TOKEN | null | 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` |
### Multiple Certificates
This container can issue multiple certificates each containing different domains. This could be used to issue a certificate for a public domain on Cloudflare, but then also for a local certificate from an internal Certificate Authority, for example. Another example would be you have a web-server hosting two separate websites and you want them to have dedicated SSL certificates instead of sharing one.
When issuing multiple certificates, first `CERT_COUNT` must be set to a value greater than 1.
#### Global Environment Variables
Some environment variables can be set globally, where they apply to all certificates (unless otherwise specifically specified). The following can be used globally:
| Variable | DESCRIPTION |
| --- | --- |
|EMAIL| Email address for renewal information & other communications |
|STAGING| Uses the LetsEncrypt staging endpoint for testing - avoids the aggressive rate-limiting of the production endpoint. **Not supported when using a custom Certificate Authority.** |
|CUSTOM_CA| 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| Custom server URL used by Certbot/ACME when requesting a certificate, e.g `https://ca.internal/acme/acme/directory` |
|PLUGIN| Options are `webroot`, `standalone`, or `cloudflare` |
|PROPOGATION_TIME| **(Applies to Cloudflare plugin)** 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 |
More detail on these environment variables may be found further up.
#### Certificate-specific Environment Variables
Any variable other than those described as **Core Options** can be set per-certificate in a multi-certificate environment. The syntax is `${VARIABLE_NAME}_${CERT_NUMBER}`. The only certificate-specific option that **must** be set is the `DOMAINS` option.
##### Multi-certificate container using global variables:
```yaml
certbot:
container_name: certbot
image: git.mrmeeb.stream/mrmeeb/certbot-cron
volumes:
- /docker/certbot-cron:/config
- /docker/nginx/www:/config/webroot
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/London
- GENERATE_DHPARAM=false
- CERT_COUNT=2
- EMAIL=admin@domain.com
- CUSTOM_CA=root.pem
- CUSTOM_CA_SERVER=https://ca.internal/acme/acme/directory
- PLUGIN=webroot
- STAGING=false
- DOMAINS_1=website1.com
- DOMAINS_2=website2.com
```
##### Multi-certificate container using different options for each certificate:
```yaml
certbot:
container_name: certbot
image: git.mrmeeb.stream/mrmeeb/certbot-cron
volumes:
- /docker/certbot-cron:/config
- /docker/nginx/www:/config/webroot
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/London
- GENERATE_DHPARAM=false
- CERT_COUNT=2
- EMAIL=admin@domain.com
- DOMAINS_1=website1.com
- CUSTOM_CA_1=root.pem
- CUSTOM_CA_SERVER_1=https://ca.internal/acme/acme/directory
- PLUGIN_1=webroot
- STAGING_1=false
- DOMAINS_2=website2.com
- PLUGIN_2=cloudflare
- CLOUDFLARE_TOKEN_2=abc123
- PROPOGATION_TIME_2=30
- STAGING_2=true
```
## Volumes
| Docker path | Purpose |
| --- | --- |
| /config | Stores configs and LetsEncrypt output for mounting in other containers
| /config/webroot | Mountpoint for the webroot of a separate webserver. **Required if `PLUGIN=webroot` is set**
## Ports
| Port | Purpose |
| --- | --- |
| 80 | Used by ACME to verify domain ownership. **Required if `PLUGIN=standalone` is set**

28
renovate.json Normal file
View File

@ -0,0 +1,28 @@
{
"extends": [":automergeMinor", ":automergePr", ":automergeRequireAllStatusChecks", ":dependencyDashboard", ":disableRateLimiting", ":rebaseStalePrs"],
"baseBranches": ["master"],
"major": {
"dependencyDashboardApproval": true
},
"customManagers": [
{
"customType": "regex",
"fileMatch": ["Dockerfile"],
"matchStrings": ["ARG S6_OVERLAY_VERSION=[\"](?<currentValue>.*)[\"]"],
"datasourceTemplate": "github-releases",
"depNameTemplate": "just-containers/s6-overlay"
}
],
"packageRules": [
{
"matchDatasources": ["github-releases"],
"matchDepNames": ["just-containers/s6-overlay"],
"extractVersion": "^v(?<version>.*)$",
"versioning": "loose"
},
{
"matchPackagePatterns": ["certbot"],
"groupName": "certbot"
}
]
}

5
requirements.txt Normal file
View File

@ -0,0 +1,5 @@
# For pinning Python packages to then be parsed by Renovate
certbot ==2.10.0
certbot-dns-cloudflare ==2.10.0
apprise ==1.8.0

796
root/certbot-prepare.sh Normal file
View File

@ -0,0 +1,796 @@
#!/command/with-contenv bash
# shellcheck shell=bash
# Halt container if anything returns a non-zero exit code
set -e
# Creating needed folders and files if they don't already exist
if [ ! -d /config/.secrets ]
then
mkdir /config/.secrets
fi
if [ ! -d /config/letsencrypt ]
then
mkdir /config/letsencrypt
fi
if [ ! -d /config/letsencrypt/keys ]
then
mkdir /config/letsencrypt/keys
fi
if [ ! -d /config/logs ]
then
mkdir /config/logs
fi
if [ ! -f /config/logs/renew.log ]
then
touch /config/logs/renew.log
fi
if [ ! -f /config/.crontab.txt ]
then
touch /config/.crontab.txt
fi
function better_exit {
echo ""
echo ""
echo ""
echo "You can ignore the below error messages - they happened because the container exited with a non-0 exit code due misconfiguration"
echo "=========================================================="
exit 1
}
# Check APPRISE_URL is set if either NOTIFY_ON_SUCCESS or NOTIFY_ON_FAILURE are set
if [ ! -z "${NOTIFY_ON_SUCCESS}" ] || [ ! -z "${NOTIFY_ON_FAILURE}" ] && [ -z "${APPRISE_URL}" ]; then
echo "You have notifications enabled but have not set APPRISE_URL. Please set APPRISE_URL and restart the container."
better_exit
fi
# Cleanup renew list and create it fresh, ready for commands to be run and added
echo "#!/command/with-contenv bash
date
echo \"Attempting to renew certificates\"
source /renew-function.sh
" > /config/.renew-list.sh
chmod +x /config/.renew-list.sh
# Create original config file to track changes to environmental variables
if [ ! -f /config/.donoteditthisfile ]
then
echo -e "ORIGDOMAINS=\"${DOMAINS}\" ORIGEMAIL=\"${EMAIL}\" ORIGSTAGING=\"${STAGING}\" ORIGCUSTOM_CA=\"${CUSTOM_CA}\" ORIGCUSTOM_CA_SERVER=\"${CUSTOM_CA_SERVER}\" ORIGPLUGIN=\"${PLUGIN}\" ORIGPROPOGATION_TIME=\"${PROPOGATION_TIME}\" ORIGCERT_COUNT=${CERT_COUNT}" > /config/.donoteditthisfile
fi
# Load original config file
. /config/.donoteditthisfile
# Revoke all certs if CERT_COUNT has decreased, starting fresh
if [ "${CERT_COUNT}" -lt "${ORIGCERT_COUNT}" ]; then
echo ""
echo "CERT_COUNT has decreased - revoking all certificates then reissuing to cleanup any lingerers."
# Use .donoteditthisfile_cert_* to get details of each issued certificate to revoke with correct parameters
x=1
while [ $x -le ${ORIGCERT_COUNT} ]; do
# Load config of particular cert
. /config/.donoteditthisfile_cert_${x}
# Setting up variables (requires two passes to clean away requirement for indirect variables)
## Pass 1
DOMAINS_P1=ORIGDOMAINS_${x}
EMAIL_P1=ORIGEMAIL_${x}
STAGING_P1=ORIGSTAGING_${x}
CUSTOM_CA_P1=ORIGCUSTOM_CA_${x}
CUSTOM_CA_SERVER_P1=ORIGCUSTOM_CA_SERVER_${x}
PLUGIN_P1=ORIGPLUGIN_${x}
PROPOGATION_TIME_P1=ORIGPROPOGATION_TIME_${x}
CLOUDFLARE_TOKEN_P1=ORIGCLOUDFLARE_TOKEN_${x}
## Pass 2
DOMAINS_MULTI=${!DOMAINS_P1}
EMAIL_MULTI=${!EMAIL_P1}
STAGING_MULTI=${!STAGING_P1}
CUSTOM_CA_MULTI=${!CUSTOM_CA_P1}
CUSTOM_CA_SERVER_MULTI=${!CUSTOM_CA_SERVER_P1}
PLUGIN_MULTI=${!PLUGIN_P1}
PROPOGATION_TIME_MULTI=${!PROPOGATION_TIME_P1}
CLOUDFLARE_TOKEN_MULTI=${!CLOUDFLARE_TOKEN_P1}
FIRST_DOMAIN_MULTI=$(echo ${DOMAINS_MULTI} | cut -d \, -f1)
echo ${FIRST_DOMAIN_MULTI}
if [ ! -z ${CUSTOM_CA_MULTI} ]
then
echo "A custom CA was used for issuing certificate ${x}. Using it to revoke as well."
if [ ! -d /config/custom_ca ]
then
mkdir /config/custom_ca
echo "Please place the custom CA root file used to generate the current certificate ${x} into /config/custom_ca and restart the container"
better_exit
fi
if [ -z "$(ls -A /config/custom_ca)" ]
then
echo "A root certificate called ${CUSTOM_CA_MULTI} was used to generate a certificate, but the /config/custom_ca dir is now empty. Please place this root certificate back this directory and restart the container so it can be safely revoked"
better_exit
fi
CUSTOM_CA_PATH_MULTI=/config/custom_ca/${CUSTOM_CA_MULTI}
CUSTOM_CA_SERVER_OPT_MULTI="--server ${CUSTOM_CA_SERVER_MULTI}"
fi
if [ $STAGING_MULTI = "true" ]
then
# Reusing the CUSTOM_CA_SERVER_OPT variable to add staging option if that was selected
CUSTOM_CA_SERVER_OPT_MULTI="--server https://acme-staging-v02.api.letsencrypt.org/directory"
fi
if [ -f /config/letsencrypt/live/"${FIRST_DOMAIN_MULTI}"/fullchain.pem ]
then
REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH_MULTI certbot revoke --non-interactive --agree-tos --email $EMAIL_MULTI --config-dir /config/letsencrypt --work-dir /config/.tmp --logs-dir /config/logs --cert-path /config/letsencrypt/live/"${FIRST_DOMAIN_MULTI}"/fullchain.pem ${CUSTOM_CA_SERVER_OPT_MULTI} || true
rm -rf /config/letsencrypt/archive/"${FIRST_DOMAIN_MULTI}"
rm -rf /config/letsencrypt/live/"${FIRST_DOMAIN_MULTI}"
rm -rf /config/letsencrypt/renewal/"${FIRST_DOMAIN_MULTI}".conf
fi
# Delete .donoteditthisfile_cert_${x}
rm -rf /config/.donoteditthisfile_cert_${x}
# Scrubbing variables before running next cert revoke to prevent overlap of values
DOMAINS_MULTI=
EMAIL_MULTI=
STAGING_MULTI=
CUSTOM_CA_MULTI=
CUSTOM_CA_SERVER_MULTI=
PLUGIN_MULTI=
PROPOGATION_TIME_MULTI=
CLOUDFLARE_TOKEN_MULTI=
CUSTOM_CA_PATH_MULTI=
CUSTOM_CA_SERVER_OPT_MULTI=
x=$(( $x + 1 ))
done
echo "Tidying up any potential lingering ACME challenges in /config/webroot from failed webroot activations"
rm -rf /config/webroot/.well-known/acme-challenge
fi
function single_domain {
# Checking for changes to config file, revoke certs if necessary
if [ ! "${DOMAINS}" = "${ORIGDOMAINS}" ] ||
[ ! "${EMAIL}" = "${ORIGEMAIL}" ] ||
[ ! "${STAGING}" = "${ORIGSTAGING}" ] ||
[ ! "${CUSTOM_CA}" = "${ORIGCUSTOM_CA}" ] ||
[ ! "${CUSTOM_CA_SERVER}" = "${ORIGCUSTOM_CA_SERVER}" ] ||
[ ! "${PLUGIN}" = "${ORIGPLUGIN}" ] ||
[ ! "${PROPOGATION_TIME}" = "${ORIGPROPOGATION_TIME}" ]
then
echo ""
echo "Configuration has changed since the last certificate was issued. Revoking and regenerating certs"
FIRST_DOMAIN=$(echo $ORIGDOMAINS | cut -d \, -f1)
if [ ! -z $ORIGCUSTOM_CA ]
then
echo "A custom CA was used for issuing. Using it to revoke as well."
if [ ! -d /config/custom_ca ]
then
mkdir /config/custom_ca
echo "Please place the custom CA root file used to generate the current certificate into /config/custom_ca and restart the container"
better_exit
fi
if [ -z "$(ls -A /config/custom_ca)" ]
then
echo "A root certificate called ${ORIGCUSTOM_CA} was used to generate a certificate, but the /config/custom_ca dir is now empty. Please place this root certificate back this directory and restart the container so it can be safely revoked"
better_exit
fi
ORIGCUSTOM_CA_PATH=/config/custom_ca/$ORIGCUSTOM_CA
ORIGCUSTOM_CA_SERVER_OPT="--server $ORIGCUSTOM_CA_SERVER"
fi
if [ $ORIGSTAGING = "true" ]
then
# Reusing the CUSTOM_CA_SERVER_OPT variable to add staging option if that was selected
ORIGCUSTOM_CA_SERVER_OPT="--server https://acme-staging-v02.api.letsencrypt.org/directory"
fi
if [ -f /config/letsencrypt/live/"${FIRST_DOMAIN}"/fullchain.pem ]
then
REQUESTS_CA_BUNDLE=$ORIGCUSTOM_CA_PATH certbot revoke --non-interactive --agree-tos --email $ORIGEMAIL --config-dir /config/letsencrypt --work-dir /config/.tmp --logs-dir /config/logs --cert-path /config/letsencrypt/live/"${FIRST_DOMAIN}"/fullchain.pem $ORIGCUSTOM_CA_SERVER_OPT || true
rm -rf /config/letsencrypt/archive/"${FIRST_DOMAIN}"
rm -rf /config/letsencrypt/live/"${FIRST_DOMAIN}"
rm -rf /config/letsencrypt/renewal/"${FIRST_DOMAIN}".conf
fi
fi
# Update config file with new env vars
echo -e "ORIGDOMAINS=\"${DOMAINS}\" ORIGEMAIL=\"${EMAIL}\" ORIGSTAGING=\"${STAGING}\" ORIGCUSTOM_CA=\"${CUSTOM_CA}\" ORIGCUSTOM_CA_SERVER=\"${CUSTOM_CA_SERVER}\" ORIGPLUGIN=\"${PLUGIN}\" ORIGPROPOGATION_TIME=\"${PROPOGATION_TIME}\" ORIGCERT_COUNT=${CERT_COUNT}" > /config/.donoteditthisfile
echo ""
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"
better_exit
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"
better_exit
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"
better_exit
fi
CUSTOM_CA_PATH=/config/custom_ca/$CUSTOM_CA
CUSTOM_CA_SERVER_OPT="--server $CUSTOM_CA_SERVER"
if [ $STAGING = "true" ]
then
echo "Staging option is not supported when using a custom CA. To remove this alert, set staging to false. If your CA has a standing endpoint, use the CUSTOM_CA_SERVER option to point to it instead"
better_exit
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
fi
if [ -n "$CLOUDFLARE_TOKEN" ]
then
echo "Cloudflare token is present"
echo "dns_cloudflare_api_token = $CLOUDFLARE_TOKEN" > /config/.secrets/cloudflare.ini
fi
if [ ! -s /config/.secrets/cloudflare.ini ]
then
echo "cloudflare.ini is empty - please add your Cloudflare credentials or API key before continuing. This can be done by setting CLOUDFLARE_TOKEN, or by editing /config/.secrets/cloudflare.ini directly"
better_exit
fi
#Securing cloudflare.ini to supress warnings
chmod 600 /config/.secrets/cloudflare.ini
echo "Creating certificates, or attempting to renew if they already exist"
if [ $STAGING = true ]
then
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
# Add to renewal list
echo "renew \"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"
elif [ $STAGING = false ]
then
echo "Using production endpoint"
${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds $PROPOGATION_TIME --dns-cloudflare-credentials /config/.secrets/cloudflare.ini
# Add to renewal list
echo "renew \"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"
else
echo "Unrecognised option for STAGING variable - check your configuration"
better_exit
fi
## Run with Standalone plugin
elif [ $PLUGIN == "standalone" ]
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 "renew \"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 "renew \"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"
better_exit
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 "renew \"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 "renew \"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"
better_exit
fi
else
echo "Unrecognised option for PLUGIN variable - check your configuration"
fi
}
function multi_domain {
# Update config file with new env vars
echo -e "ORIGDOMAINS=\"${DOMAINS}\" ORIGEMAIL=\"${EMAIL}\" ORIGSTAGING=\"${STAGING}\" ORIGCUSTOM_CA=\"${CUSTOM_CA}\" ORIGCUSTOM_CA_SERVER=\"${CUSTOM_CA_SERVER}\" ORIGPLUGIN=\"${PLUGIN}\" ORIGPROPOGATION_TIME=\"${PROPOGATION_TIME}\" ORIGCERT_COUNT=${CERT_COUNT}" > /config/.donoteditthisfile
## Start multi-domain looper
x=1
while [ $x -le $CERT_COUNT ]
do
# Setting up variables (requires two passes to clean away requirement for indirect variable)
## Pass 1
DOMAINS_P1=DOMAINS_${x}
EMAIL_P1=EMAIL_${x}
STAGING_P1=STAGING_${x}
CUSTOM_CA_P1=CUSTOM_CA_${x}
CUSTOM_CA_SERVER_P1=CUSTOM_CA_SERVER_${x}
PLUGIN_P1=PLUGIN_${x}
PROPOGATION_TIME_P1=PROPOGATION_TIME_${x}
CLOUDFLARE_TOKEN_P1=CLOUDFLARE_TOKEN_${x}
## Pass 2
DOMAINS_MULTI=${!DOMAINS_P1}
EMAIL_MULTI=${!EMAIL_P1}
STAGING_MULTI=${!STAGING_P1}
CUSTOM_CA_MULTI=${!CUSTOM_CA_P1}
CUSTOM_CA_SERVER_MULTI=${!CUSTOM_CA_SERVER_P1}
PLUGIN_MULTI=${!PLUGIN_P1}
PROPOGATION_TIME_MULTI=${!PROPOGATION_TIME_P1}
CLOUDFLARE_TOKEN_MULTI=${!CLOUDFLARE_TOKEN_P1}
# Inheriting global default if undefined for certain variables
if [ -z ${EMAIL_MULTI} ]; then
EMAIL_MULTI=${EMAIL}
fi
if [ -z ${STAGING_MULTI} ]; then
STAGING_MULTI=${STAGING}
fi
if [ -z ${CUSTOM_CA_MULTI} ]; then
CUSTOM_CA_MULTI=${CUSTOM_CA}
fi
if [ -z ${CUSTOM_CA_SERVER_MULTI} ]; then
CUSTOM_CA_SERVER_MULTI=${CUSTOM_CA_SERVER}
fi
if [ -z ${PLUGIN_MULTI} ]; then
PLUGIN_MULTI=${PLUGIN}
fi
if [ -z ${PROPOGATION_TIME_MULTI} ]; then
PROPOGATION_TIME_MULTI=${PROPOGATION_TIME}
fi
# Create original config file to track changes to environmental variables
if [ ! -f /config/.donoteditthisfile_cert_${x} ]
then
echo -e "ORIGDOMAINS_${x}=\"${DOMAINS_MULTI}\" ORIGEMAIL_${x}=\"${EMAIL_MULTI}\" ORIGSTAGING_${x}=\"${STAGING_MULTI}\" ORIGCUSTOM_CA_${x}=\"${CUSTOM_CA_MULTI}\" ORIGCUSTOM_CA_SERVER_${x}=\"${CUSTOM_CA_SERVER_MULTI}\" ORIGPLUGIN_${x}=\"${PLUGIN_MULTI}\" ORIGPROPOGATION_TIME_${x}=\"${PROPOGATION_TIME_MULTI}\"" > /config/.donoteditthisfile_cert_${x}
fi
# Load original config file
. /config/.donoteditthisfile_cert_${x}
ORIGDOMAINS_MULTI=ORIGDOMAINS_${x}
ORIGEMAIL_MULTI=ORIGEMAIL_${x}
ORIGSTAGING_MULTI=ORIGSTAGING_${x}
ORIGCUSTOM_CA_MULTI=ORIGCUSTOM_CA_${x}
ORIGCUSTOM_CA_SERVER_MULTI=ORIGCUSTOM_CA_SERVER_${x}
ORIGPLUGIN_MULTI=ORIGPLUGIN_${x}
ORIGPROPOGATION_TIME_MULTI=ORIGPROPOGATION_TIME_${x}
ORIGCLOUDFLARE_TOKEN_MULTI=ORIGCLOUDFLARE_TOKEN_${x}
# Log variables to console (have to remove indent because bash dumb)
echo "
----------------------------------------------------------------------
CERTIFICATE ${x} ENVIRONMENT
----------------------------------------------------------------------"
echo \
"DOMAINS_${x}=${DOMAINS_MULTI}
EMAIL_${x}=${EMAIL_MULTI}
STAGING_${x}=${STAGING_MULTI}
CUSTOM_CA_${x}=${CUSTOM_CA_MULTI}
CUSTOM_CA_SERVER_${x}=${CUSTOM_CA_SERVER_MULTI}
PLUGIN_${x}=${PLUGIN_MULTI}"
## Get plugin-specific data if single certificate config
if [ ${PLUGIN_MULTI} == 'cloudflare' ]; then
echo \
"PROPOGATION_TIME_${x}=${PROPOGATION_TIME_MULTI}"
fi
if [ ${PLUGIN_MULTI} == 'cloudflare' ] && [ ! -z ${CLOUDFLARE_TOKEN_MULTI} ]; then
echo \
"CLOUDFLARE_TOKEN_${x}=[hidden]"
elif [ ${PLUGIN_MULTI} == 'cloudflare' ] && [ -z ${CLOUDFLARE_TOKEN_MULTI} ]; then
echo \
"CLOUDFLARE_TOKEN_${x}="
fi
echo \
"----------------------------------------------------------------------
"
# Begin actually requesting the certificate
echo "Requesting certificate $x"
# Checking for changes to config file, revoke certs if necessary
if [ ! "${DOMAINS_MULTI}" = "${!ORIGDOMAINS_MULTI}" ] ||
[ ! "${EMAIL_MULTI}" = "${!ORIGEMAIL_MULTI}" ] ||
[ ! "${STAGING_MULTI}" = "${!ORIGSTAGING_MULTI}" ] ||
[ ! "${CUSTOM_CA_MULTI}" = "${!ORIGCUSTOM_CA_MULTI}" ] ||
[ ! "${CUSTOM_CA_SERVER_MULTI}" = "${!ORIGCUSTOM_CA_SERVER_MULTI}" ] ||
[ ! "${PLUGIN_MULTI}" = "${!ORIGPLUGIN_MULTI}" ] ||
[ ! "${PROPOGATION_TIME_MULTI}" = "${!ORIGPROPOGATION_TIME_MULTI}" ]
then
echo ""
echo "Configuration has changed since certificate ${x} was last issued. Revoking and regenerating cert ${x}"
FIRST_DOMAIN_MULTI=$(echo ${!ORIGDOMAINS_MULTI} | cut -d \, -f1)
if [ ! -z ${!ORIGCUSTOM_CA_MULTI} ]
then
echo "A custom CA was used for issuing certificate ${x}. Using it to revoke as well."
if [ ! -d /config/custom_ca ]
then
mkdir /config/custom_ca
echo "Please place the custom CA root file used to generate the current certificate ${x} into /config/custom_ca and restart the container"
better_exit
fi
if [ -z "$(ls -A /config/custom_ca)" ]
then
echo "A root certificate called ${!ORIGCUSTOM_CA_MULTI} was used to generate a certificate, but the /config/custom_ca dir is now empty. Please place this root certificate back this directory and restart the container so it can be safely revoked"
better_exit
fi
ORIGCUSTOM_CA_PATH_MULTI=/config/custom_ca/${!ORIGCUSTOM_CA_MULTI}
ORIGCUSTOM_CA_SERVER_OPT_MULTI="--server ${!ORIGCUSTOM_CA_SERVER_MULTI}"
fi
if [ $ORIGSTAGING_MULTI = "true" ]
then
# Reusing the CUSTOM_CA_SERVER_OPT variable to add staging option if that was selected
ORIGCUSTOM_CA_SERVER_OPT_MULTI="--server https://acme-staging-v02.api.letsencrypt.org/directory"
fi
if [ -f /config/letsencrypt/live/"${FIRST_DOMAIN_MULTI}"/fullchain.pem ]
then
REQUESTS_CA_BUNDLE=$ORIGCUSTOM_CA_PATH_MULTI certbot revoke --non-interactive --agree-tos --email $ORIGEMAIL_MULTI --config-dir /config/letsencrypt --work-dir /config/.tmp --logs-dir /config/logs --cert-path /config/letsencrypt/live/"${FIRST_DOMAIN_MULTI}"/fullchain.pem ${ORIGCUSTOM_CA_SERVER_OPT_MULTI} || true
rm -rf /config/letsencrypt/archive/"${FIRST_DOMAIN_MULTI}"
rm -rf /config/letsencrypt/live/"${FIRST_DOMAIN_MULTI}"
rm -rf /config/letsencrypt/renewal/"${FIRST_DOMAIN_MULTI}".conf
fi
echo "Tidying up any potential lingering ACME challenges in /config/webroot from failed webroot activations"
rm -rf /config/webroot/.well-known/acme-challenge
fi
# Update config file with new cert-specific env vars
echo -e "ORIGDOMAINS_${x}=\"${DOMAINS_MULTI}\" ORIGEMAIL_${x}=\"${EMAIL_MULTI}\" ORIGSTAGING_${x}=\"${STAGING_MULTI}\" ORIGCUSTOM_CA_${x}=\"${CUSTOM_CA_MULTI}\" ORIGCUSTOM_CA_SERVER_${x}=\"${CUSTOM_CA_SERVER_MULTI}\" ORIGPLUGIN_${x}=\"${PLUGIN_MULTI}\" ORIGPROPOGATION_TIME_${x}=\"${PROPOGATION_TIME_MULTI}\"" > /config/.donoteditthisfile_cert_${x}
echo ""
if [ ! -z ${CUSTOM_CA_MULTI} ]
then
echo "Using a custom CA for issuing certificate ${x}"
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"
better_exit
fi
if [ -z "$(ls -A /config/custom_ca)" ]
then
echo "The CUSTOM_CA_${x} option is populated, but the /config/custom_ca dir is empty. Please place your root certificate for certificate ${x} in this directory and restart the container"
better_exit
fi
if [ -z ${CUSTOM_CA_SERVER_MULTI} ]
then
echo "CUSTOM_CA_SERVER_${x} has not been defined. It is required when using a custom CA to issue certificate ${x}"
better_exit
fi
CUSTOM_CA_PATH_MULTI=/config/custom_ca/${CUSTOM_CA_MULTI}
CUSTOM_CA_SERVER_OPT_MULTI="--server ${CUSTOM_CA_SERVER_MULTI}"
if [ ${STAGING_MULTI} = "true" ]
then
echo "Staging option is not supported when using a custom CA. To remove this alert, set staging to false. If your CA has a standing endpoint, use the CUSTOM_CA_SERVER_${x} option to point to it instead"
better_exit
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_MULTI}" --email "${EMAIL_MULTI}" -d "${DOMAINS_MULTI}")
## Run with Cloudflare plugin
if [ ${PLUGIN_MULTI} == "cloudflare" ]
then
echo "Using Cloudflare plugin"
if [ ! -f /config/.secrets/cloudflare.ini ]
then
touch /config/.secrets/cloudflare.ini
fi
if [ -n "${CLOUDFLARE_TOKEN_MULTI}" ]
then
echo "Cloudflare token is present"
echo "dns_cloudflare_api_token = ${CLOUDFLARE_TOKEN_MULTI}" > /config/.secrets/cloudflare.ini
fi
if [ ! -s /config/.secrets/cloudflare.ini ]
then
echo "cloudflare.ini is empty - please add your Cloudflare credentials or API key before continuing. This can be done by setting CLOUDFLARE_TOKEN_${x}"
better_exit
fi
#Securing cloudflare.ini to supress warnings
chmod 600 /config/.secrets/cloudflare.ini
echo "Creating certificates, or attempting to renew if they already exist"
if [ ${STAGING_MULTI} = true ]
then
echo "Using staging endpoint - THIS SHOULD BE USED FOR TESTING ONLY"
${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds ${PROPOGATION_TIME_MULTI} --dns-cloudflare-credentials /config/.secrets/cloudflare.ini --staging
# Add to renewal list
echo "## Certificate ${x}" >> /config/.renew-list.sh
echo "renew \"${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds ${PROPOGATION_TIME_MULTI} --dns-cloudflare-credentials /config/.secrets/cloudflare.ini --staging\"" >> /config/.renew-list.sh
echo "" >> /config/.renew-list.sh
echo "Creation/renewal attempt complete"
elif [ ${STAGING_MULTI} = false ]
then
echo "Using production endpoint"
${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds ${PROPOGATION_TIME_MULTI} --dns-cloudflare-credentials /config/.secrets/cloudflare.ini
# Add to renewal list
echo "## Certificate ${x}" >> /config/.renew-list.sh
echo "renew \"REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds ${PROPOGATION_TIME_MULTI} --dns-cloudflare-credentials /config/.secrets/cloudflare.ini\"" >> /config/.renew-list.sh
echo "" >> /config/.renew-list.sh
echo "Creation/renewal attempt complete"
else
echo "Unrecognised option for STAGING variable - check your configuration"
better_exit
fi
## Run with Standalone plugin
elif [ ${PLUGIN_MULTI} == "standalone" ]
then
echo "Using HTTP verification via built-in web-server - please ensure port 80 is exposed."
if [ ${STAGING_MULTI} = true ]
then
echo "Using staging endpoint - THIS SHOULD BE USED FOR TESTING ONLY"
REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --standalone --staging
# Add to renewal list
echo "## Certificate ${x}" >> /config/.renew-list.sh
echo "renew \"REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --standalone --staging\"" >> /config/.renew-list.sh
echo "" >> /config/.renew-list.sh
echo "Creation/renewal attempt complete"
elif [ ${STAGING_MULTI} = false ]
then
echo "Using production endpoint"
REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --standalone
# Add to renewal list
echo "## Certificate ${x}" >> /config/.renew-list.sh
echo "renew \"REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --standalone\"" >> /config/.renew-list.sh
echo "" >> /config/.renew-list.sh
echo "Creation/renewal attempt complete"
else
echo "Unrecognised option for STAGING variable - check your configuration"
better_exit
fi
## Run with webroot plugin
elif [ ${PLUGIN_MULTI} == "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_MULTI} = true ]
then
echo "Using staging endpoint - THIS SHOULD BE USED FOR TESTING ONLY"
REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot --staging
# Add to renewal list
echo "## Certificate ${x}" >> /config/.renew-list.sh
echo "renew \"REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot --staging\"" >> /config/.renew-list.sh
echo "" >> /config/.renew-list.sh
echo "Creation/renewal attempt complete"
elif [ ${STAGING_MULTI} = false ]
then
echo "Using production endpoint"
REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot
# Add to renewal list
echo "## Certificate ${x}" >> /config/.renew-list.sh
echo "renew \"REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot\"" >> /config/.renew-list.sh
echo "" >> /config/.renew-list.sh
echo "Creation/renewal attempt complete"
else
echo "Unrecognised option for STAGING variable - check your configuration"
better_exit
fi
else
echo "Unrecognised option for PLUGIN variable - check your configuration"
fi
# Scrubbing variables before running next cert to prevent overlap of values
DOMAINS_MULTI=
EMAIL_MULTI=
STAGING_MULTI=
CUSTOM_CA_MULTI=
CUSTOM_CA_SERVER_MULTI=
PLUGIN_MULTI=
PROPOGATION_TIME_MULTI=
CLOUDFLARE_TOKEN_MULTI=
CUSTOM_CA_PATH_MULTI=
CUSTOM_CA_SERVER_OPT_MULTI=
ORIGDOMAINS_MULTI=
ORIGEMAIL_MULTI=
ORIGSTAGING_MULTI=
ORIGCUSTOM_CA_MULTI=
ORIGCUSTOM_CA_SERVER_MULTI=
ORIGPLUGIN_MULTI=
ORIGPROPOGATION_TIME_MULTI=
ORIGCLOUDFLARE_TOKEN_MULTI=
FIRST_DOMAIN_MULTI=
ORIGCUSTOM_CA_PATH_MULTI=
ORIGCUSTOM_CA_SERVER_OPT_MULTI=
x=$(( $x + 1 ))
done
}
if [ $CERT_COUNT == 1 ]
then
single_domain
elif [ $CERT_COUNT -gt 1 ]
then
multi_domain
else
echo "CERT_COUNT varaible not recognised. It needs to be a value of 1 or greater."
fi
# Finish /config/.renew-list.sh now all certs have been added
echo "
echo \"Renewal attempts complete\"" >> /config/.renew-list.sh
if [ $GENERATE_DHPARAM = true ] && [ ! -s /config/letsencrypt/keys/ssl-dhparams.pem ]
then
echo ""
echo "Generating Diffie-Hellman keys, saved to /config/letsencrypt/keys. This can take a long time!"
openssl dhparam -out /config/letsencrypt/keys/ssl-dhparams.pem 4096
fi
if [ $ONE_SHOT == "true" ]; then
echo ""
echo "ONE_SHOT is true - exiting container"
elif [ $ONE_SHOT == "false" ]; then
echo "$INTERVAL /config/.renew-list.sh >> /config/logs/renew.log" > /config/.crontab.txt
echo ""
echo "Starting automatic renewal job. Schedule is $INTERVAL"
crontab /config/.crontab.txt
fi

9
root/check-one-shot.sh Normal file
View File

@ -0,0 +1,9 @@
#!/command/with-contenv bash
# shellcheck shell=bash
if [ $ONE_SHOT == "true" ]; then
# Cleanly kill container by sending kill signal to supervisor process
kill 1
fi

93
root/container-init.sh Normal file
View File

@ -0,0 +1,93 @@
#!/command/with-contenv bash
# shellcheck shell=bash
echo ""
echo ""
echo "================================================"
echo "| __ _______ __ ___________________ |"
echo "| / |/ / __ \/ |/ / ____/ ____/ __ ) |"
echo "| / /|_/ / /_/ / /|_/ / __/ / __/ / __ | |"
echo "| / / / / _, _/ / / / /___/ /___/ /_/ / |"
echo "| /_/ /_/_/ |_/_/ /_/_____/_____/_____/ |"
echo "| |"
echo "================================================"
echo ""
echo "Initialising container"
if [ ${CERT_COUNT} == 1 ]; then
echo \
"----------------------------------------------------------------------
ENVIRONMENT
----------------------------------------------------------------------"
else
echo \
"----------------------------------------------------------------------
ENVIRONMENT (Certificate options logged later)
----------------------------------------------------------------------"
fi
echo \
"PUID=${PUID}
PGID=${PGID}
TZ=${TZ}
ONE_SHOT=${ONE_SHOT}
INTERVAL=${INTERVAL}
GENERATE_DHPARAM=${GENERATE_DHPARAM}
CERT_COUNT=${CERT_COUNT}
NOTIFY_ON_SUCCESS=${NOTIFY_ON_SUCCESS}
NOTIFY_ON_FAILURE=${NOTIFY_ON_FAILURE}"
if [ ! -z ${APPRISE_URL} ]; then
echo \
"APPRISE_URL=[hidden]"
fi
## Send extra detail to logs if single certificate config
if [ ${CERT_COUNT} == 1 ]; then
echo \
"DOMAINS=${DOMAINS}
EMAIL=${EMAIL}
STAGING=${STAGING}
CUSTOM_CA=${CUSTOM_CA}
CUSTOM_CA_SERVER=${CUSTOM_CA_SERVER}
PLUGIN=${PLUGIN}"
fi
## Get plugin-specific data if single certificate config
if [ ${CERT_COUNT} == 1 ] && [ ${PLUGIN} == 'cloudflare' ]; then
echo \
"PROPOGATION_TIME=${PROPOGATION_TIME}"
fi
if [ ${CERT_COUNT} == 1 ] && [ ${PLUGIN} == 'cloudflare' ] && [ ! -z ${CLOUDFLARE_TOKEN} ]; then
echo \
"CLOUDFLARE_TOKEN=[hidden]"
elif [ ${CERT_COUNT} == 1 ] && [ ${PLUGIN} == 'cloudflare' ] && [ -z ${CLOUDFLARE_TOKEN} ]; then
echo \
"CLOUDFLARE_TOKEN="
fi
echo \
"----------------------------------------------------------------------
"
#Setting UID and GID as configured
if [[ ! "${PUID}" -eq 0 ]] && [[ ! "${PGID}" -eq 0 ]]; then
echo "Executing usermod..."
mkdir "/tmp/temphome"
usermod -d "/tmp/temphome" mrmeeb
usermod -o -u "${PUID}" mrmeeb
usermod -d /config mrmeeb
rm -rf "/tmp/temphome"
groupmod -o -g "${PGID}" mrmeeb
else
echo "Running as root is not supported, please fix your PUID and PGID!"
sleep infinity
fi
echo "Checking permissions in /config and /app."
if [ ! "$(stat -c %u /app)" -eq "${PUID}" ] || [ ! "$(stat -c %g /app)" -eq "${PGID}" ]
then
echo "Fixing permissions for /app (this can take some time)."
chown -R mrmeeb:mrmeeb /app
fi
if [ ! "$(stat -c %u /config)" -eq "${PUID}" ] || [ ! "$(stat -c %g /config)" -eq "${PGID}" ]
then
echo "Fixing permissions for /config (this can take some time)."
chown -R mrmeeb:mrmeeb /config
fi

View File

@ -0,0 +1 @@
oneshot

View File

@ -0,0 +1 @@
exec s6-setuidgid mrmeeb /certbot-prepare.sh

View File

@ -0,0 +1 @@
oneshot

View File

@ -0,0 +1 @@
exec /check-one-shot.sh

View File

@ -0,0 +1 @@
oneshot

View File

@ -0,0 +1 @@
/container-init.sh

View File

@ -0,0 +1 @@
echo "$e" > /run/s6-linux-init-container-results/exitcode

View File

@ -0,0 +1,3 @@
#!/command/with-contenv bash
# shellcheck shell=bash
exec crond -f

View File

@ -0,0 +1 @@
longrun

View File

@ -0,0 +1 @@
echo "$e" > /run/s6-linux-init-container-results/exitcode

View File

@ -0,0 +1,3 @@
#!/command/with-contenv bash
# shellcheck shell=bash
tail -fn 0 /config/logs/renew.log

View File

@ -0,0 +1 @@
longrun

27
root/renew-function.sh Normal file
View File

@ -0,0 +1,27 @@
function renew() {
#Variables:
#$1 = Certbot command
RENEWAL_DOMAINS=$(echo $1 | sed -r 's/.*\s-d\s(\S*).*/\1/')
CUSTOM_CA_PATH=$(echo $1 | sed -r 's/REQUESTS_CA_BUNDLE=(\S*)\s(.*)/\1/')
CERTBOT_COMMAND=$(echo $1 | sed -r 's/REQUESTS_CA_BUNDLE=(\S*)\s(.*)/\2/')
echo "Renewing certificate for ${RENEWAL_DOMAINS}"
echo "REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH} ${CERTBOT_COMMAND}" | bash
if [ $? = 0 ]; then
echo "Renewal attempt of certificate for ${RENEWAL_DOMAINS} succeeded"
if [ "${NOTIFY_ON_SUCCESS}" = "true" ]; then
apprise -b "Renewal of certificate for ${RENEWAL_DOMAINS} succeeded" ${APPRISE_URL}
fi
else
echo "Renewal attempt of certificate for ${RENEWAL_DOMAINS} failed"
if [ "${NOTIFY_ON_FAILURE}" = "true" ]; then
apprise -b "Renewal of certificate for ${RENEWAL_DOMAINS} failed" ${APPRISE_URL}
fi
fi
}