Skip to content

[codex] Add Caddy agent ingress allowlist#34

Draft
wingsrabbit wants to merge 2 commits into
mainfrom
codex/v1-12-agent-allowlist
Draft

[codex] Add Caddy agent ingress allowlist#34
wingsrabbit wants to merge 2 commits into
mainfrom
codex/v1-12-agent-allowlist

Conversation

@wingsrabbit

@wingsrabbit wingsrabbit commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • Bump ONC to v1.12.
  • Add nodes.agent_allowed_cidrs as the configured agent ingress allowlist, with IPv4/IPv6 IP/CIDR validation and normalization.
  • Move gunicorn to 127.0.0.1:18080; Caddy now owns public 80/443/8080 and protects 8080 with a remote_ip allowlist.
  • Regenerate and hot-reload Caddy when nodes are added, edited, deleted, disabled/enabled, or their allowed source CIDRs change.
  • Update the admin Nodes UI to require/display allowed sources and warn when the observed public_ip does not match.

Security Semantics

  • Caddy provides a coarse 8080 IP gate using the enabled nodes' allowlist union.
  • Flask still binds each agent token to that node's own agent_allowed_cidrs; a request from node A's IP using node B's token is rejected with 403.
  • Disabled nodes are excluded from the Caddy allowlist and their tokens are rejected by Flask with 403.
  • Invalid agent tokens still return 401 when a request reaches Flask.

Migration

  • Existing databases get a new nodes.agent_allowed_cidrs column.
  • During db.init_db(), nodes with an empty allowlist inherit their existing public_ip as /32 or /128 when valid.
  • Nodes without a usable public_ip remain blank and must be filled by an admin before they can pass the 8080 allowlist.
  • public_ip remains an observation from the latest report; it is no longer used as ingress configuration.

Compatibility

  • Requests to 8080 from non-allowlisted IPs are rejected at Caddy with 403 and do not reach gunicorn.
  • 80/443 web/admin/public access is not IP-restricted by the agent allowlist.
  • deploy/update.sh now also copies supervisord.conf and entrypoint.sh so existing containers switch to the internal gunicorn + Caddy 8080 model during standard update.
  • The standard update health check now runs inside nc-center with docker exec because 18080 is container-internal.
  • manage.py create-node now hot-reloads Caddy with the candidate allowlist before writing the node.

Validation

  • python3 -m unittest discover -s backend -p 'test_*.py' passed: 10 tests.\n- python3 -m compileall backend agent passed.\n- npm run build in frontend/ passed; Vite reported the existing large chunk warning.\n- Generated Caddyfile sample includes :8080, remote_ip, guarded reverse_proxy 127.0.0.1:18080, and fallback respond 403.\n- Local machine does not have caddy or Docker installed, so real caddy validate was not run locally.\n\n## Rollback\n- Revert this PR and deploy the previous release.\n- If already deployed, run the standard updater after revert, or rerun install-center.sh from the previous release to restore the old runtime wiring.\n- Database rollback is not required for app startup: the added column is additive. If a strict schema rollback is desired, back up nc.sqlite first and drop nodes.agent_allowed_cidrs manually only after reverting code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant