mirror of
https://github.com/aarmea/fahrengit-451.git
synced 2026-03-22 08:58:15 +00:00
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.
123 lines
4.1 KiB
YAML
123 lines
4.1 KiB
YAML
services:
|
|
|
|
# ── Forgejo ────────────────────────────────────────────────────────────────
|
|
forgejo:
|
|
image: codeberg.org/forgejo/forgejo:9
|
|
container_name: forgejo
|
|
restart: unless-stopped
|
|
environment:
|
|
- USER_UID=1000
|
|
- USER_GID=1000
|
|
- FORGEJO__server__DOMAIN=${DOMAIN}
|
|
- FORGEJO__server__ROOT_URL=https://${DOMAIN}/
|
|
- FORGEJO__server__HTTP_PORT=3000
|
|
- FORGEJO__server__DISABLE_SSH=true
|
|
- FORGEJO__service__DISABLE_REGISTRATION=${DISABLE_REGISTRATION:-true}
|
|
- FORGEJO__database__DB_TYPE=sqlite3
|
|
- FORGEJO__database__PATH=/data/forgejo/forgejo.db
|
|
- FORGEJO__log__LEVEL=Info
|
|
volumes:
|
|
- forgejo_data:/data
|
|
- /etc/timezone:/etc/timezone:ro
|
|
- /etc/localtime:/etc/localtime:ro
|
|
networks:
|
|
- internal
|
|
healthcheck:
|
|
test: ["CMD", "wget", "-qO-", "http://localhost:3000/api/healthz"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
# ── nginx (reverse proxy + GeoIP blocking) ─────────────────────────────────
|
|
nginx:
|
|
build:
|
|
context: ./nginx
|
|
dockerfile: Dockerfile
|
|
container_name: nginx
|
|
restart: unless-stopped
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
volumes:
|
|
- ./nginx/conf.d:/etc/nginx/conf.d:ro # static config fragments
|
|
- ./nginx/geoblock:/etc/nginx/geoblock:ro # rendered map snippet (written by watcher)
|
|
- ./certs/live:/etc/letsencrypt/live:ro
|
|
- ./certs/archive:/etc/letsencrypt/archive:ro
|
|
- ./certs/options-ssl-nginx.conf:/etc/letsencrypt/options-ssl-nginx.conf:ro
|
|
- ./certs/ssl-dhparams.pem:/etc/letsencrypt/ssl-dhparams.pem:ro
|
|
- certbot_webroot:/var/www/certbot:ro
|
|
- geoip_db:/usr/share/GeoIP:ro
|
|
- nginx_logs:/var/log/nginx
|
|
networks:
|
|
- internal
|
|
depends_on:
|
|
- forgejo
|
|
environment:
|
|
- DOMAIN=${DOMAIN}
|
|
healthcheck:
|
|
test: ["CMD", "nginx", "-t"]
|
|
interval: 60s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
# ── MaxMind GeoIP database updater ────────────────────────────────────────
|
|
geoipupdate:
|
|
image: ghcr.io/maxmind/geoipupdate:v7
|
|
container_name: geoipupdate
|
|
restart: unless-stopped
|
|
environment:
|
|
- GEOIPUPDATE_ACCOUNT_ID=${MAXMIND_ACCOUNT_ID}
|
|
- GEOIPUPDATE_LICENSE_KEY=${MAXMIND_LICENSE_KEY}
|
|
- GEOIPUPDATE_EDITION_IDS=GeoLite2-City
|
|
- GEOIPUPDATE_FREQUENCY=72 # hours — MaxMind updates twice a week
|
|
- GEOIPUPDATE_DB_DIR=/usr/share/GeoIP
|
|
volumes:
|
|
- geoip_db:/usr/share/GeoIP
|
|
networks:
|
|
- internal
|
|
|
|
# ── Geo-block config watcher ───────────────────────────────────────────────
|
|
# Watches geo_rules.yml; re-renders the nginx map snippet and reloads nginx
|
|
# whenever rules change.
|
|
geoblock_watcher:
|
|
build:
|
|
context: ./geoblock_watcher
|
|
dockerfile: Dockerfile
|
|
container_name: geoblock_watcher
|
|
restart: unless-stopped
|
|
volumes:
|
|
- ./geo_rules.yml:/app/geo_rules.yml:ro
|
|
- ./nginx/geoblock:/app/geoblock # shared with nginx (rw here)
|
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
networks:
|
|
- internal
|
|
depends_on:
|
|
- nginx
|
|
|
|
# ── Certbot (Let's Encrypt) ────────────────────────────────────────────────
|
|
certbot:
|
|
image: certbot/certbot:latest
|
|
container_name: certbot
|
|
restart: unless-stopped
|
|
volumes:
|
|
- ./certs:/etc/letsencrypt
|
|
- certbot_webroot:/var/www/certbot
|
|
entrypoint: >
|
|
/bin/sh -c "
|
|
trap exit TERM;
|
|
while :; do
|
|
certbot renew --webroot -w /var/www/certbot --quiet;
|
|
sleep 12h &
|
|
wait $${!};
|
|
done
|
|
"
|
|
|
|
volumes:
|
|
forgejo_data:
|
|
geoip_db:
|
|
certbot_webroot:
|
|
nginx_logs:
|
|
|
|
networks:
|
|
internal:
|
|
driver: bridge
|