once/docker-certbot.py
Rama main · réplica de lectura del repositorio original.
#!/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()