From db25b030ee163aae986f2778964126b78187d525 Mon Sep 17 00:00:00 2001 From: Cian Hughes Date: Tue, 11 Aug 2026 21:29:09 +0100 Subject: [PATCH] Added certbot to stack --- cli/deploy.py | 32 +++++++++++++++++++ i-form-data-repository/docker-services.yml | 13 ++++++++ .../docker/nginx/Dockerfile | 5 ++- .../docker/nginx/conf.d/default.conf | 13 ++++++-- .../docker/nginx/entrypoint.sh | 18 +++++++++++ 5 files changed, 77 insertions(+), 4 deletions(-) create mode 100755 i-form-data-repository/docker/nginx/entrypoint.sh diff --git a/cli/deploy.py b/cli/deploy.py index 7bd6c12..61eb327 100644 --- a/cli/deploy.py +++ b/cli/deploy.py @@ -262,3 +262,35 @@ def prod_clean(): rm("-rf", "static", "node_modules") logger.success("Clean complete. Re-create environment before next deploy.") + + +@app.command() +def init_ssl( + email: str = typer.Argument( + ..., help="Email address for Let's Encrypt registration" + ), + domain: str = typer.Argument(..., help="Domain name to fetch certificate for"), +): + """Initialize Let's Encrypt SSL certificates for the first time.""" + logger.info(f"Initializing Let's Encrypt SSL for {domain}...") + + with local.cwd(REPO_DIR): + logger.info("Requesting certificate via certbot webroot...") + cmd = f"certbot certonly --webroot -w /var/www/certbot -d {domain} --email {email} --agree-tos --no-eff-email --force-renewal" + docker_compose("exec", "certbot", "sh", "-c", cmd) + + logger.info("Restarting frontend to apply new certificates...") + docker_compose("restart", "frontend") + + logger.success("SSL initialization complete!") + + +@app.command() +def renew_ssl(): + """Manually force a renewal check for Let's Encrypt and reload Nginx.""" + logger.info("Triggering manual SSL renewal check...") + with local.cwd(REPO_DIR): + docker_compose("exec", "certbot", "certbot", "renew") + logger.info("Reloading Nginx...") + docker_compose("exec", "frontend", "nginx", "-s", "reload") + logger.success("SSL renewal check complete!") diff --git a/i-form-data-repository/docker-services.yml b/i-form-data-repository/docker-services.yml index 4bdd2dc..cede24e 100644 --- a/i-form-data-repository/docker-services.yml +++ b/i-form-data-repository/docker-services.yml @@ -43,6 +43,16 @@ services: options: max-size: "50m" max-file: "3" + volumes: + - certbot_conf:/etc/letsencrypt + - certbot_www:/var/www/certbot + certbot: + image: docker.io/certbot/certbot + restart: "unless-stopped" + volumes: + - certbot_conf:/etc/letsencrypt + - certbot_www:/var/www/certbot + entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" cache: image: docker.io/library/redis:7 restart: "unless-stopped" @@ -141,3 +151,6 @@ services: interval: 30s timeout: 20s retries: 3 +volumes: + certbot_conf: + certbot_www: diff --git a/i-form-data-repository/docker/nginx/Dockerfile b/i-form-data-repository/docker/nginx/Dockerfile index 98d9f65..4c4acb0 100644 --- a/i-form-data-repository/docker/nginx/Dockerfile +++ b/i-form-data-repository/docker/nginx/Dockerfile @@ -2,4 +2,7 @@ FROM docker.io/library/nginx COPY nginx.conf /etc/nginx/nginx.conf COPY conf.d/* /etc/nginx/conf.d/ COPY test.key /etc/ssl/private/test.key -COPY test.crt /etc/ssl/certs/test.crt \ No newline at end of file +COPY test.crt /etc/ssl/certs/test.crt +COPY entrypoint.sh /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/i-form-data-repository/docker/nginx/conf.d/default.conf b/i-form-data-repository/docker/nginx/conf.d/default.conf index 17825d8..12b88b0 100644 --- a/i-form-data-repository/docker/nginx/conf.d/default.conf +++ b/i-form-data-repository/docker/nginx/conf.d/default.conf @@ -12,7 +12,14 @@ server { listen 80 default_server; # IPv4 listen [::]:80 default_server; # IPv6 server_name _; - return 301 https://$host$request_uri; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://$host$request_uri; + } } # Upstream servers @@ -35,8 +42,8 @@ server { # https://mozilla.github.io/server-side-tls/ssl-config-generator/ # The provided certificate (test.crt) and private key (test.key) is only for # testing and must never be used in production environment. - ssl_certificate /etc/ssl/certs/test.crt; - ssl_certificate_key /etc/ssl/private/test.key; + ssl_certificate /etc/ssl/certs/invenio.crt; + ssl_certificate_key /etc/ssl/private/invenio.key; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; diff --git a/i-form-data-repository/docker/nginx/entrypoint.sh b/i-form-data-repository/docker/nginx/entrypoint.sh new file mode 100755 index 0000000..c6fa84c --- /dev/null +++ b/i-form-data-repository/docker/nginx/entrypoint.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +# Check if Let's Encrypt live certificates exist +if [ -d "/etc/letsencrypt/live" ] && [ "$(ls -A /etc/letsencrypt/live)" ]; then + # Certs exist, find the first domain folder (ignoring README) + DOMAIN=$(find /etc/letsencrypt/live -mindepth 1 -maxdepth 1 -type d | head -n 1 | xargs basename) + echo "Using Let's Encrypt certificates for $DOMAIN" + ln -sf /etc/letsencrypt/live/$DOMAIN/fullchain.pem /etc/ssl/certs/invenio.crt + ln -sf /etc/letsencrypt/live/$DOMAIN/privkey.pem /etc/ssl/private/invenio.key +else + # Fallback to test certs + echo "Using fallback test certificates" + cp /etc/ssl/certs/test.crt /etc/ssl/certs/invenio.crt + cp /etc/ssl/private/test.key /etc/ssl/private/invenio.key +fi + +# Execute the default CMD (nginx) +exec "$@"