feat(hrafn): register the browser-fetch service and tighten its env perms
nevermore-claude handed hrafn to infra-ops for uptime ownership. Intake: verified health independently, brought it into the inventory, and fixed one security defect found during the check. - canonicalize stacks/hrafn/ (compose.yaml + .env.example + README) - list hrafn in the ana-docker running-stacks table - README records the load-bearing bits: shm_size 1gb is required for Chromium, playwright and the base image are version-coupled, SSRF denies private targets by default, and restart:unless-stopped does NOT act on healthcheck failure Security fix applied on the host, not in this tree: /opt/docker/compose/ hrafn/.env was 0644 with a live 57-char bearer token in it, readable by every local account (verified by reading it as `nobody`). Tightened to 0600; owner unchanged so the deploy path still works, confirmed via `docker compose config` as lkraven. Container untouched and still healthy. Known gaps recorded rather than silently accepted: no CI deploy, and the image builds from an rsync'd working tree living in the compose directory, so the running image has no commit provenance.
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
# hrafn — fleet browser-fetch service. Copy to `.env` and set HRAFN_TOKEN.
|
||||
# This file is the canonical config surface (contract § Configuration).
|
||||
|
||||
# ── auth (required) ──────────────────────────────────────────────────
|
||||
# Shared bearer token required on POST /v1/fetch. Provision via the
|
||||
# vault (secrets-broker); rotation = rotate the vault entry + recycle.
|
||||
HRAFN_TOKEN=CHANGE_ME
|
||||
|
||||
# ── network ──────────────────────────────────────────────────────────
|
||||
HRAFN_BIND=0.0.0.0
|
||||
HRAFN_PORT=8080
|
||||
|
||||
# ── fetch limits ─────────────────────────────────────────────────────
|
||||
# Max post-content-decoding body bytes of the main navigation response;
|
||||
# oversize is rejected (413), never truncated. Both raw and dom modes.
|
||||
HRAFN_MAX_BODY_BYTES=10485760
|
||||
# Whole-request budget default + hard cap (seconds). timeout_s INCLUDES
|
||||
# politeness-queue wait. A request may not exceed the cap.
|
||||
HRAFN_DEFAULT_TIMEOUT_S=30
|
||||
HRAFN_MAX_TIMEOUT_S=120
|
||||
# Default dom-mode wait state: load | domcontentloaded | networkidle.
|
||||
HRAFN_DEFAULT_WAIT_UNTIL=domcontentloaded
|
||||
|
||||
# ── concurrency + browser lifecycle ──────────────────────────────────
|
||||
# Global in-flight cap (acquired on leaving the politeness queue).
|
||||
HRAFN_CONCURRENCY=4
|
||||
# Recycle the browser after N requests or M seconds of age.
|
||||
HRAFN_BROWSER_MAX_REQUESTS=200
|
||||
HRAFN_BROWSER_MAX_AGE_S=1800
|
||||
|
||||
# ── politeness gate (per registrable domain) ─────────────────────────
|
||||
# Minimum seconds between fetch STARTS per domain (0 = no throttle).
|
||||
HRAFN_DOMAIN_MIN_INTERVAL_S=5
|
||||
# Per-domain interval overrides: comma-separated domain=seconds pairs.
|
||||
# HRAFN_DOMAIN_OVERRIDES=reddit.com=10, example.com=2
|
||||
HRAFN_DOMAIN_OVERRIDES=
|
||||
# Waiting-request queue depth per domain (excludes the in-flight fetch;
|
||||
# 0 = no queue). Overflow → 429 queue_full.
|
||||
HRAFN_QUEUE_DEPTH=4
|
||||
# Upstream Retry-After (on 429/503) embargoes the domain; clamped to
|
||||
# this many seconds so a hostile Retry-After can't lock a domain for a day.
|
||||
HRAFN_MAX_EMBARGO_S=300
|
||||
|
||||
# ── SSRF ─────────────────────────────────────────────────────────────
|
||||
# Private/reserved targets are DENIED by default. To permit specific
|
||||
# internal targets, list IPs/CIDRs (comma-separated). Empty = deny all
|
||||
# private ranges (the safe default). Write entries in IPv4 form for IPv4
|
||||
# targets: resolved IPv4-mapped IPv6 addresses are canonicalised to IPv4
|
||||
# before matching, so an IPv6-mapped CIDR (::ffff:10.0.0.0/120) will not
|
||||
# match them.
|
||||
# HRAFN_PRIVATE_ALLOWLIST=10.0.0.0/8, 192.168.1.5
|
||||
HRAFN_PRIVATE_ALLOWLIST=
|
||||
|
||||
# ── live smoke (dev only) ────────────────────────────────────────────
|
||||
# Set to 1 to enable the opt-in Reddit RSS smoke test. Off in CI.
|
||||
# HRAFN_LIVE_SMOKE=1
|
||||
Reference in New Issue
Block a user