Ctrl K
ComunidadGuías…

once/docker-certbot.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env python3

import os
import signal
import sys
import time
from urllib.parse import urlsplit
import subprocess


# This is written in Python instead of Bash because I encountered a weird problem I have no idea to fix.
#
# In docker-wrapper.sh, we run each daemon in background with `&`, and
# use `wait` to wait them to exit gracefully.
# This works fine if the daemon is NOT a shell script.
# If the daemon is a shell script, the signal IS NOT delivered to the shell script.
# The process running docker-wrapper.sh will die,
# leaving the shell script running with parent pid becoming pid=1.
#
# It is observed that if the daemon is written in a different runtime, such as Go or Python,
# this problem does not recur.
# Thus this is written in Python.


def handle_signal(signum, _frame):
    print(f"certbot: got signal {signum}")
    sys.exit(0)


def parse_domain(val):
    url = urlsplit(val)
    hostname = url.hostname
    if hostname is None:
        raise ValueError(val)
    return hostname


def get_run_internal_from_env():
    run_internal = 86400

    run_internal_env_name = "AUTHGEAR_CERTBOT_RUN_INTERVAL"
    run_internal_env_val = os.getenv(run_internal_env_name)
    if run_internal_env_val is not None and run_internal_env_val != "":
        try:
            run_internal = int(run_internal_env_val)
            if run_internal < 0:
                raise ValueError()
        except ValueError:
            print(f"certbot: {run_internal_env_name} is invalid: {run_internal_env_val}", file=sys.stderr)
            sys.exit(1)

    return run_internal


def get_certbot_use_staging_from_env():
    certbot_use_staging = True
    if os.getenv("AUTHGEAR_CERTBOT_ENVIRONMENT") == "production":
        certbot_use_staging = False
    return certbot_use_staging


def get_email_from_env():
    email_env_name = "AUTHGEAR_ONCE_ADMIN_USER_EMAIL"
    email_env_val = os.getenv(email_env_name)
    if email_env_val is None:
        print(f"certbot: {email_env_name} is undefined", file=sys.stderr)
        sys.exit(1)
    return email_env_val


def get_domains_from_env():
    domains = []
    env_vars = [
        "AUTHGEAR_HTTP_ORIGIN_PORTAL",
        "AUTHGEAR_HTTP_ORIGIN_ACCOUNTS",
        "AUTHGEAR_HTTP_ORIGIN_PROJECT",
    ]
    for env_var in env_vars:
        val = os.getenv(env_var)
        if val is None:
            print(f"certbot: {env_var} is undefined", file=sys.stderr)
            sys.exit(1)
        try:
            domain = parse_domain(val)
            domains.append(domain)
        except ValueError:
            print(f"certbot: {env_var} is invalid: {val}")
            sys.exit(1)
    return domains


def get_certbot_enabled_from_env():
    enabled = os.getenv("AUTHGEAR_CERTBOT_ENABLED") == "true"
    return enabled


def run_once():
    enabled = get_certbot_enabled_from_env()
    if not enabled:
        print(f"certbot: disabled by env")
        return None

    domains = get_domains_from_env()
    certbot_use_staging = get_certbot_use_staging_from_env()
    email_env_val = get_email_from_env()

    cmd = []

    cmd.append("certbot")
    cmd.append("--config-dir")
    cmd.append(os.environ["AUTHGEARONCE_CERTBOT_DATA_DIRECTORY"])

    cmd.append("run")

    if certbot_use_staging:
        cmd.append("--staging")

    cmd.append("--non-interactive")
    cmd.append("--nginx")
    cmd.append("-m")
    cmd.append(email_env_val)
    cmd.append("--agree-tos")
    cmd.append("--no-eff-email")
    cmd.append("--expand")
    cmd.append("--keep-until-expiring")
    cmd.append("--renew-with-new-domains")
    for domain in domains:
        cmd.append("--domain")
        cmd.append(domain)

    completed_process = subprocess.run(cmd, stdin=None, stdout=None, stderr=None, shell=False, check=False)
    print(f"certbot: exited with {completed_process.returncode}")
    return completed_process


def main():
    # Run once and exit.
    if len(sys.argv) >= 2 and sys.argv[1] == "--block-on-getting-tls-certificates":
        completed_process = run_once()
        if completed_process is not None:
            if completed_process.returncode != 0:
                # NOTE(once): exiting with 10 is a public API between the once image and the once command.
                sys.exit(10)
            else:
                sys.exit(0)
        else:
            sys.exit(0)
    else:
        # SIGINT is usually generated by CTRL-C
        signal.signal(signal.SIGINT, handle_signal)
        # SIGTERM is generated by docker container stop.
        signal.signal(signal.SIGTERM, handle_signal)

        run_internal = get_run_internal_from_env()

        print(f"certbot: run_internal={run_internal}")
        while True:
            run_once()
            time.sleep(run_internal)


if __name__ == "__main__":
    main()