From fd933d98f55f8ebb7f0857c98bbd5b816550ac2b Mon Sep 17 00:00:00 2001 From: Gil Desmarais Date: Fri, 12 Jun 2026 12:23:37 +0200 Subject: [PATCH 1/3] feat: integrate Caddy as default SSL terminator and reverse proxy - Add caddy:2-alpine service to docker-compose.quickstart.yml listening on https://localhost:4000 using Caddy's local CA - Add caddy:2-alpine service to docker-compose.yml listening on ports 80/443 with automatic HTTPS via Let's Encrypt for production - Remove direct host port mappings from html2rss-web in both compose files - Sanitize X-Forwarded-For and X-Real-IP headers using {client_ip} placeholder to prevent rate-limit bypass via IP spoofing - Disable Caddy admin API by default (admin off) to reduce attack surface - Support CADDY_GLOBAL_OPTIONS env var for CDN/load-balancer trusted_proxies config - Update bin/quickstart-verify to assert port mapping on caddy service Closes #1012 --- bin/quickstart-verify | 3 ++- docker-compose.quickstart.yml | 34 ++++++++++++++++++++++++++++++++-- docker-compose.yml | 35 +++++++++++++++++++++++++++++++++-- 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/bin/quickstart-verify b/bin/quickstart-verify index f18c1113..4ecc4358 100755 --- a/bin/quickstart-verify +++ b/bin/quickstart-verify @@ -52,6 +52,7 @@ end config = compose_config services = config.fetch('services') { abort('quickstart verify failed: missing top-level services map') } web = fetch_service!(services, 'html2rss-web') +caddy = fetch_service!(services, 'caddy') fetch_service!(services, 'botasaurus') assert_equal!(web['image'], EXPECTED_WEB_IMAGE, 'html2rss-web image') @@ -59,6 +60,6 @@ environment = web.fetch('environment') { abort('quickstart verify failed: html2r assert_equal!(environment['RACK_ENV'], EXPECTED_RACK_ENV, 'html2rss-web RACK_ENV') assert_equal!(environment['HTML2RSS_ACCESS_TOKEN'], EXPECTED_ACCESS_TOKEN, 'html2rss-web HTML2RSS_ACCESS_TOKEN') assert_equal!(environment['BOTASAURUS_SCRAPER_URL'], EXPECTED_SCRAPER_URL, 'html2rss-web BOTASAURUS_SCRAPER_URL') -assert_port_mapping!(web['ports']) +assert_port_mapping!(caddy['ports']) puts 'quickstart verify passed' diff --git a/docker-compose.quickstart.yml b/docker-compose.quickstart.yml index b21c9cf9..f0bcc09b 100644 --- a/docker-compose.quickstart.yml +++ b/docker-compose.quickstart.yml @@ -1,8 +1,34 @@ services: - html2rss-web: - image: ${HTML2RSS_WEB_IMAGE:-html2rss/web:1} + caddy: + image: caddy:2-alpine + restart: unless-stopped ports: - "127.0.0.1:4000:4000" + volumes: + - caddy_data:/data + - caddy_config:/config + depends_on: + - html2rss-web + command: + - sh + - -c + - | + caddy run --config - --adapter caddyfile < Date: Fri, 12 Jun 2026 17:13:57 +0200 Subject: [PATCH 2/3] chore: remove watchtower from compose file --- docker-compose.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 6c014fdc..6ae1ea18 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -58,14 +58,6 @@ services: # target: /app/config/feeds.yml # read_only: true - watchtower: - image: containrrr/watchtower - restart: unless-stopped - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - "${HOME}/.docker/config.json:/config.json" - command: --cleanup --interval 7200 - browserless: image: "ghcr.io/browserless/chromium" restart: unless-stopped From df5e99269e5f748ff31600758b3a35cb82aae583 Mon Sep 17 00:00:00 2001 From: Gil Desmarais Date: Fri, 12 Jun 2026 17:31:39 +0200 Subject: [PATCH 3/3] chore: add AUTO_SOURCE_ENABLED and HTML2RSS_ACCESS_TOKEN to compose environment --- docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 6ae1ea18..2e00663d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -42,6 +42,8 @@ services: PORT: 4000 BUILD_TAG: ${BUILD_TAG:-local} GIT_SHA: ${GIT_SHA:-local} + AUTO_SOURCE_ENABLED: ${AUTO_SOURCE_ENABLED:-false} + HTML2RSS_ACCESS_TOKEN: ${HTML2RSS_ACCESS_TOKEN:-} HTML2RSS_SECRET_KEY: ${HTML2RSS_SECRET_KEY:?set HTML2RSS_SECRET_KEY} HEALTH_CHECK_TOKEN: ${HEALTH_CHECK_TOKEN:?set HEALTH_CHECK_TOKEN} SENTRY_DSN: ${SENTRY_DSN:-}