fahrengit-451/bootstrap_certs.sh
Albert Armea cf99cd50f2 Initial commit
Change written by Claude Sonnet 4.6:

I need to set up a Git hosting service for personal projects where certain repositories need to be blocked using the locale corresponding to the user’s IP address.

Here are my constraints:
*   The entire system will be running on a single VPS instance. I should be able to `docker compose up -d` the whole thing.
*   For each repository that this feature is enabled, I need to be able to set the blocked locales down to the state level, along with a custom HTTP status code and response body.
*   You may assume that the IP address of the request is where it actually came from — for this exercise, if the user uses a VPN to bypass the restriction, that is on them.
*   To simplify a reverse proxy setup, you may assume that all Git operations will happen over HTTPS. I will firewall off SSH access.
*    I will be using Let's Encrypt for HTTPS.

Some suggestions from prior research:
*   nginx seems like a reasonable reverse proxy that supports all of the requirements, but you may use a different one if it is simpler to implement or maintain.
*   I can obtain a MaxMind API key to get a geo-IP lookup table. If you use this, you will need to add a service that automatically retrieves the table at a reasonable frequency.
*   Forgejo seems like a reasonable, lightweight Git service, but you may use a different one if you’re aware of one that actually supports these requirements out of the box.

Write me a production-ready `docker-compose.yml` and supporting scripts or configuration scaffolding for me to implement this.
2026-03-21 18:34:50 +00:00

91 lines
4.2 KiB
Bash

#!/usr/bin/env bash
# bootstrap_certs.sh
# ─────────────────────────────────────────────────────────────────────────────
# Run this ONCE before `docker compose up -d` to obtain the initial Let's
# Encrypt certificate. nginx must be able to serve the ACME challenge, so we
# bring up only the services needed for that, run certbot, then download the
# Certbot recommended TLS options, and finally start everything.
#
# Prerequisites:
# • docker compose v2 installed
# • DNS for $DOMAIN already pointing to this server's IP
# • Ports 80 and 443 open in your firewall
# • .env file present (copy from .env.example and fill in)
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail
if [[ ! -f .env ]]; then
echo "ERROR: .env file not found. Copy .env.example → .env and fill in your values."
exit 1
fi
# shellcheck disable=SC1091
source .env
DOMAIN="${DOMAIN:?DOMAIN must be set in .env}"
EMAIL="${LETSENCRYPT_EMAIL:?LETSENCRYPT_EMAIL must be set in .env}"
CERTS_DIR="./certs"
echo "==> Creating certificate directory structure..."
mkdir -p "${CERTS_DIR}/live/${DOMAIN}"
mkdir -p "${CERTS_DIR}/archive"
# ── Download Certbot recommended TLS options ──────────────────────────────────
if [[ ! -f "${CERTS_DIR}/options-ssl-nginx.conf" ]]; then
echo "==> Downloading recommended TLS options..."
curl -sSL \
"https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf" \
-o "${CERTS_DIR}/options-ssl-nginx.conf"
fi
if [[ ! -f "${CERTS_DIR}/ssl-dhparams.pem" ]]; then
echo "==> Downloading DH parameters..."
curl -sSL \
"https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem" \
-o "${CERTS_DIR}/ssl-dhparams.pem"
fi
# ── Create a dummy certificate so nginx can start (needed for ACME challenge) ─
DUMMY_LIVE="${CERTS_DIR}/live/${DOMAIN}"
if [[ ! -f "${DUMMY_LIVE}/fullchain.pem" ]]; then
echo "==> Generating temporary self-signed certificate..."
openssl req -x509 -nodes -newkey rsa:4096 -days 1 \
-keyout "${DUMMY_LIVE}/privkey.pem" \
-out "${DUMMY_LIVE}/fullchain.pem" \
-subj "/CN=${DOMAIN}"
fi
# ── Start nginx (and dependencies) ───────────────────────────────────────────
echo "==> Starting nginx with temporary certificate..."
docker compose up -d nginx forgejo geoipupdate geoblock_watcher
echo "==> Waiting for nginx to be ready..."
sleep 5
# ── Obtain the real certificate via webroot challenge ────────────────────────
echo "==> Requesting Let's Encrypt certificate for ${DOMAIN}..."
docker compose run --rm certbot certonly \
--webroot \
--webroot-path /var/www/certbot \
--email "${EMAIL}" \
--agree-tos \
--no-eff-email \
-d "${DOMAIN}"
# ── Reload nginx with the real certificate ────────────────────────────────────
echo "==> Reloading nginx with the real certificate..."
docker compose exec nginx nginx -s reload
# ── Start remaining services ──────────────────────────────────────────────────
echo "==> Starting all services..."
docker compose up -d
echo ""
echo "✓ Bootstrap complete. Your Git service should be live at https://${DOMAIN}/"
echo ""
echo "Next steps:"
echo " 1. Visit https://${DOMAIN}/ and complete the Forgejo setup wizard."
echo " 2. Create your admin account."
echo " 3. Set DISABLE_REGISTRATION=true in .env, then: docker compose up -d forgejo"
echo " 4. Edit geo_rules.yml to configure per-repo geo-blocking."