Linux onboarding

Install the BamGuard Certbot Hook

Download the latest release, install the manual DNS authentication hook, and register your first Monitoring Root.

Before you start

The hook is officially tested on Linux. Other Unix systems are not currently claimed as supported. It requires Certbot, Python 3 with the requests package, Bash, curl, tar, and gzip.

Use the official Certbot installation instructions for your Linux distribution and web server.
Review Let's Encrypt's DNS-01 documentation for challenge and CNAME delegation details.

Release available

Version v0.0.2 is published for this environment.

Download and install

The normal installation uses the versioned HTTPS archive. It does not require GnuPG, key import, checksum comparison, or signature verification. Run the commands as one Bash block so download or extraction failure stops before installation.

export BAMGUARD_CERTBOT_HOOK_VERSION='v0.0.2'

set -euo pipefail
umask 077
VERSION="${BAMGUARD_CERTBOT_HOOK_VERSION:?Set the published release version}"
[[ "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z]+)*$ ]] || exit 1
PACKAGE="bamguard-certbot-hook-${VERSION}"
ARCHIVE="${PACKAGE}.tar.gz"
BASE_URL="https://www.bamguard.com/downloads/bamguard-certbot-hook/${VERSION}"
WORK_DIRECTORY="$(mktemp -d)"
trap 'rm -rf -- "$WORK_DIRECTORY"' EXIT
cd "$WORK_DIRECTORY"

curl --fail --show-error --location --output "$ARCHIVE" "${BASE_URL}/${ARCHIVE}"
tar -xzf "$ARCHIVE"
sudo install -d -o root -g root -m 0755 /etc/letsencrypt
sudo install -o root -g root -m 0700 \
  "${PACKAGE}/src/bam-acme-auth.py" /etc/letsencrypt/bam-acme-auth.py
if ! sudo test -e /etc/letsencrypt/bamguard.ini; then
  sudo install -o root -g root -m 0600 \
    "${PACKAGE}/src/bamguard.ini" /etc/letsencrypt/bamguard.ini
fi

Optional package verification

Full release-key fingerprintB3E78CE35CD2F79F77D72903C258AFFA517A0A6B

If you want to verify the package, install GnuPG and GNU sha256sum and use this block instead of the normal installation block. It verifies the linked public key, detached signature, and checksum before extracting or installing the archive. The expected fingerprint must come from this page, not the artifact directory.

export BAMGUARD_CERTBOT_HOOK_VERSION='v0.0.2'
export BAMGUARD_CERTBOT_HOOK_SIGNING_FINGERPRINT='B3E78CE35CD2F79F77D72903C258AFFA517A0A6B'

set -euo pipefail
umask 077
VERSION="${BAMGUARD_CERTBOT_HOOK_VERSION:?Set the published release version}"
[[ "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z]+)*$ ]] || exit 1
EXPECTED_SIGNING_FINGERPRINT="$(
  printf '%s' "${BAMGUARD_CERTBOT_HOOK_SIGNING_FINGERPRINT:?Set the fingerprint shown on this page}" |
    tr -d '[:space:]' | tr '[:lower:]' '[:upper:]'
)"
[[ "$EXPECTED_SIGNING_FINGERPRINT" =~ ^([0-9A-F]{40}|[0-9A-F]{64})$ ]] || exit 1

PACKAGE="bamguard-certbot-hook-${VERSION}"
ARCHIVE="${PACKAGE}.tar.gz"
BASE_URL="https://www.bamguard.com/downloads/bamguard-certbot-hook/${VERSION}"
WORK_DIRECTORY="$(mktemp -d)"
VERIFY_HOME="$(mktemp -d)"
trap 'rm -rf -- "$WORK_DIRECTORY" "$VERIFY_HOME"' EXIT
chmod 0700 "$VERIFY_HOME"
cd "$WORK_DIRECTORY"

for FILE in \
  "$ARCHIVE" \
  "${ARCHIVE}.asc" \
  "${ARCHIVE}.sha256" \
  "${PACKAGE}.release-key.asc" \
  "${PACKAGE}.release-key-fingerprint.txt"
do
  curl --fail --show-error --location --output "$FILE" "${BASE_URL}/${FILE}"
done

ARTIFACT_FINGERPRINT="$(tr -d '[:space:]' < "${PACKAGE}.release-key-fingerprint.txt" | tr '[:lower:]' '[:upper:]')"
[[ "$ARTIFACT_FINGERPRINT" == "$EXPECTED_SIGNING_FINGERPRINT" ]]
KEY_FINGERPRINT="$(gpg --batch --show-keys --with-colons "${PACKAGE}.release-key.asc" | awk -F: '$1 == "fpr" { print toupper($10); exit }')"
[[ "$KEY_FINGERPRINT" == "$EXPECTED_SIGNING_FINGERPRINT" ]]
sha256sum --check --strict "${ARCHIVE}.sha256"
gpg --homedir "$VERIFY_HOME" --batch --import "${PACKAGE}.release-key.asc"
SIGNATURE_STATUS="$(gpg --homedir "$VERIFY_HOME" --batch --status-fd 1 --verify "${ARCHIVE}.asc" "$ARCHIVE")"
printf '%s\n' "$SIGNATURE_STATUS" | awk -v expected="$EXPECTED_SIGNING_FINGERPRINT" '
  $1 == "[GNUPG:]" && $2 == "VALIDSIG" && ($3 == expected || $NF == expected) { valid = 1 }
  END { exit(valid ? 0 : 1) }
'

tar -xzf "$ARCHIVE"
sudo install -d -o root -g root -m 0755 /etc/letsencrypt
sudo install -o root -g root -m 0700 \
  "${PACKAGE}/src/bam-acme-auth.py" /etc/letsencrypt/bam-acme-auth.py
if ! sudo test -e /etc/letsencrypt/bamguard.ini; then
  sudo install -o root -g root -m 0600 \
    "${PACKAGE}/src/bamguard.ini" /etc/letsencrypt/bamguard.ini
fi

Configure credentials safely

  1. Open Certbot Hook → API Keys.
  2. Create a credential group and one Hook API key.
  3. Save the complete one-time bamguard.ini configuration.

Paste the saved configuration into the protected file. The generated API URL and key belong together; do not retain the release template values or place the secret in command-line arguments, source control, images, or logs.

sudoedit /etc/letsencrypt/bamguard.ini
sudo chown root:root /etc/letsencrypt/bamguard.ini
sudo chmod 0600 /etc/letsencrypt/bamguard.ini

Keep this file and /etc/letsencrypt/bam-acmedns.json private and include both in secure backups.

Run the first DNS-01 challenge

This is a Certbot --manual-auth-hook, not a --deploy-hook. It does not install or reload the issued certificate; add the installer or deploy hook required by your server according to Certbot's documentation.

sudo certbot certonly \
  --manual \
  --manual-auth-hook /etc/letsencrypt/bam-acme-auth.py \
  --preferred-challenges dns \
  --debug-challenges \
  -d example.org \
  -d '*.example.org'

The hook prints the exact _acme-challenge CNAME on first use. Add it to the authoritative DNS zone while --debug-challenges keeps Certbot paused. Continue only after the CNAME and delegated TXT record resolve.

dig +short CNAME _acme-challenge.example.org
dig +short TXT <acme-dns-fulldomain>

Confirm the Monitoring Root

After Certbot succeeds, open Certificate Monitoring → Monitoring Roots. The requested endpoint must appear in Endpoints with a Last Hook activity value. Monitoring setup pending only means the separate CT mapping is still being prepared.

Later renewals reuse the stored domain registration. Keep both local credential files available to the user that runs unattended Certbot renewal.