Last active
January 6, 2026 02:09
-
-
Save aont/a973f0606e58a387d8d4e31b11851c47 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [Unit] | |
| Description=cert & deploy | |
| After=multi-user.target | |
| [Service] | |
| Type=simple | |
| ExecStart=/usr/libexec/acmebot-certbot/cert-deploy.sh | |
| EnvironmentFile=/etc/acmebot-certbot/env | |
| PrivateTmp=true | |
| [Install] | |
| WantedBy=multi-user.target |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Must run as root | |
| if (( EUID != 0 )); then | |
| echo "This script must be run as root. Exiting." | |
| exit 1 | |
| fi | |
| set -xe | |
| set -o pipefail | |
| FLAGFILE="/run/acmebot-certbot/deploy-flag.$(date "+%Y%m%d%H%M%S%N")" | |
| CERTPATH="$CONFIG_PATH/live/$CERT_NAME" | |
| local_ip="" | |
| cleanup() { | |
| # Always close port even on failure | |
| echo "[info] upnp port close" 1>&2 | |
| sudo -u"$EXECUTE_USER" -H upnpc -d 80 tcp || true | |
| } | |
| trap cleanup EXIT | |
| echo "[info] getting local address (ip -j route get + jq)" 1>&2 | |
| # ip -j route get 1.1.1.1 returns a JSON array; take the first element's "prefsrc" | |
| local_ip="$(ip -j route get 1.1.1.1 | jq -r '.[0].prefsrc // empty')" | |
| if [ -z "$local_ip" ] || [ "$local_ip" = "null" ]; then | |
| echo "[error] failed to detect local_ip via ip -j route get" 1>&2 | |
| exit 1 | |
| fi | |
| echo "[info] local_ip=${local_ip}" 1>&2 | |
| # NOTE: This opens WAN:80 -> LAN:80. If you intend WAN:80 -> LAN:8080, change the first "80" to "8080". | |
| echo "[info] upnp port open (WAN 80 -> LAN 80)" 1>&2 | |
| sudo -u"$EXECUTE_USER" -H upnpc -a "${local_ip}" 80 80 tcp 600 | |
| echo "[info] certbot (webroot)" 1>&2 | |
| # Run certbot and capture exit code without aborting the script immediately | |
| set +e | |
| sudo -u "$EXECUTE_USER" -H \ | |
| "${CERTBOT_VENV}/bin/certbot" \ | |
| certonly -q --webroot -w "$WEB_ROOT" \ | |
| --cert-name "${CERT_NAME}" \ | |
| --key-type rsa --rsa-key-size 4096 \ | |
| -d "$DOMAINLIST" \ | |
| --agree-tos --no-eff-email --email "$EMAIL" \ | |
| --deploy-hook "touch '$FLAGFILE'" \ | |
| --config-dir "$CONFIG_PATH" \ | |
| --work-dir "$WORK_DIR" \ | |
| --logs-dir "$LOGS_DIR" | |
| CERTBOT_STATUS=$? | |
| set -e | |
| if [ $CERTBOT_STATUS -ne 0 ]; then | |
| echo "[warn] Certbot exited with code $CERTBOT_STATUS" 1>&2 | |
| exit $CERTBOT_STATUS | |
| fi | |
| if [ ! -e "$FLAGFILE" ]; then | |
| echo "[info] cert not renewed. skip deploy/restart." 1>&2 | |
| exit 0 | |
| fi | |
| echo "[info] cert renewed. proceed deploy/restart." 1>&2 | |
| rm -f "$FLAGFILE" | |
| # Ensure cert files exist and are non-empty | |
| for f in fullchain.pem privkey.pem chain.pem; do | |
| if [ ! -s "${CERTPATH}/${f}" ]; then | |
| echo "[error] missing or empty ${CERTPATH}/${f}" 1>&2 | |
| exit 2 | |
| fi | |
| done | |
| echo "[info] put certs" 1>&2 | |
| # nginx | |
| cp -t "${NGINX_CERTS}" "${CERTPATH}/fullchain.pem" "${CERTPATH}/privkey.pem" | |
| # strongswan | |
| cp "${CERTPATH}/privkey.pem" "${IPSEC_ETC}"/private/privkey.pem | |
| cp "${CERTPATH}/chain.pem" "${IPSEC_ETC}"/cacerts/chain.pem | |
| cp "${CERTPATH}/fullchain.pem" "${IPSEC_ETC}"/certs/fullchain.pem | |
| echo "[info] nginx config test" 1>&2 | |
| nginx -t | |
| echo "[info] restart nginx and strongswan" 1>&2 | |
| systemctl restart nginx | |
| systemctl restart strongswan-starter |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [Unit] | |
| Description=Run certbot_update_cert periodically | |
| [Timer] | |
| OnCalendar=*-*-* 00:00:00 | |
| OnCalendar=*-*-* 12:00:00 | |
| RandomizedDelaySec=1h | |
| Persistent=true | |
| [Install] | |
| WantedBy=timers.target |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| EXECUTE_USER=acmebot | |
| ETC_DIR=/etc/acmebot-certbot | |
| LIBEXEC_DIR=/usr/libexec/acmebot-certbot | |
| SYSTEMD_DIR=/etc/systemd/system | |
| RUN_DIR=/run/acmebot-certbot | |
| CERT_NAME=hoge.ddns.net | |
| DOMAINLIST=hoge.ddns.net | |
| EMAIL=hoge@mail.com | |
| CERTBOT_VENV=/usr/libexec/acmebot-certbot/venv | |
| CONFIG_PATH=/var/lib/acmebot-certbot/config | |
| WORK_DIR=/run/acmebot-certbot/work | |
| LOGS_DIR=/var/log/acmebot-certbot | |
| WEB_ROOT=/var/lib/acmebot-certbot/webroot | |
| NGINX_CERTS=/etc/nginx/pki/certs | |
| IPSEC_ETC=/etc/ipsec.d |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Must run as root | |
| if (( EUID != 0 )); then | |
| echo "[error] This script must be run as root. Exiting." | |
| exit 1 | |
| fi | |
| set -xe | |
| missing=() | |
| # Check if `ip -j address` runs successfully | |
| if ! ip -j address >/dev/null 2>&1; then | |
| missing+=("ip -j address (iproute2)") | |
| fi | |
| # Check if upnpc exists | |
| if ! command -v upnpc >/dev/null 2>&1; then | |
| missing+=("upnpc") | |
| fi | |
| # Check if jq exists | |
| if ! command -v jq >/dev/null 2>&1; then | |
| missing+=("jq") | |
| fi | |
| # Check if python3 exists | |
| if ! command -v python3 >/dev/null 2>&1; then | |
| missing+=("python3") | |
| fi | |
| # Final check | |
| if [ "${#missing[@]}" -ne 0 ]; then | |
| echo "The following requirements are missing or not working:" | |
| for item in "${missing[@]}"; do | |
| echo " - $item" | |
| done | |
| exit 1 | |
| fi | |
| . ./env | |
| mkdir -p "$ETC_DIR" | |
| cp -t "$ETC_DIR" ./env | |
| mkdir -p "${RUN_DIR}" | |
| chown "${EXECUTE_USER}" "${RUN_DIR}" | |
| mkdir -p "${LIBEXEC_DIR}" | |
| install --mode=0755 cert-deploy.sh "${LIBEXEC_DIR}" | |
| if [[ ! -x "${CERTBOT_VENV}/bin/certbot" ]]; then | |
| if [[ ! -x "${CERTBOT_VENV}/bin/python" ]]; then | |
| if [[ ! -d "${CERTBOT_VENV}" ]]; then | |
| mkdir -p "${CERTBOT_VENV}" | |
| fi | |
| python3 -m venv "${CERTBOT_VENV}" | |
| fi | |
| "${CERTBOT_VENV}/bin/pip" install cffi==1.17.1 certbot | |
| fi | |
| mkdir -p "${CONFIG_PATH}" | |
| chown "${EXECUTE_USER}" "${CONFIG_PATH}" | |
| mkdir -p "${WORK_DIR}" | |
| chown "${EXECUTE_USER}" "${WORK_DIR}" | |
| mkdir -p "${LOGS_DIR}" | |
| chown "${EXECUTE_USER}" "${LOGS_DIR}" | |
| mkdir -p --mode=0755 "${WEB_ROOT}" | |
| chown "${EXECUTE_USER}" "${WEB_ROOT}" | |
| install --mode=0644 cert-deploy.service "${SYSTEMD_DIR}" | |
| install --mode=0644 cert-deploy.timer "${SYSTEMD_DIR}" | |
| systemctl daemon-reload | |
| systemctl enable cert-deploy.timer | |
| systemctl start cert-deploy.timer |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment