Skip to content

fix(caddy): block the unauthenticated beacon validator namespace from public (VANA-BC-001) - #27

Merged
inf-1oop merged 1 commit into
mainfrom
fix/caddy-block-validator-namespace
Sep 4, 2026
Merged

fix(caddy): block the unauthenticated beacon validator namespace from public (VANA-BC-001)#27
inf-1oop merged 1 commit into
mainfrom
fix/caddy-block-validator-namespace

Conversation

@volod-vana

Copy link
Copy Markdown
Member

What

The public Caddy vhost forwards all of /eth/* to the beacon REST gateway. That prefix includes Prysm's validator namespace, which is unauthenticated by design (it is meant to be reached only by the validator client on a trusted network). As shipped it is reachable by anyone on the internet.

An unauthenticated remote POST /eth/v1/validator/prepare_beacon_proposer writes into Prysm's TrackedValidatorsCache, which is the source of SuggestedFeeRecipient in the execution payload. The validator client signs the produced block without comparing the payload's fee recipient against its configured one. So on a node that is both publicly exposed and actively proposing, an attacker can set the fee recipient for a validator index and redirect that block's execution-layer priority fees to an address they control. The proposer schedule needed for timing is published by the same namespace (/eth/v1/validator/duties/proposer/{epoch}), and block production (/eth/v{2,3}/validator/blocks/{slot}) is exposed too.

Reference: VANA-BC-001. Verified live against rpc.vana.org (the write returns 200 and reaches the real Prysm handler; a dummy recipient was used, nothing moved) and reproduced locally against this exact config.

Fix

  • Deny /eth/v1|v2|v3/validator and everything under it on the public path with a 403, ahead of the public consensus handler.
  • Move the trusted-IP handler ahead of the public handlers. It was dead code before, because @public_el / @public_cl matched first, which also meant RPC_TRUSTED_IP_RANGES had no effect. Trusted ranges now keep full access to the execution and consensus layers, including the validator namespace the validator client needs.
  • Public reads are unchanged.

Testing

Reproduced the deployment locally (this Caddyfile in front of a stub beacon) and confirmed:

  • public client: every /eth/v{1,2,3}/validator/* path, including prepare_beacon_proposer, returns 403
  • public client: /eth/v1/node/syncing, /eth/v1/beacon/genesis and other reads still reach the beacon (200)
  • public client: EL JSON-RPC on / still routes to geth
  • trusted-IP client: full access preserved, including the validator namespace

caddy validate passes.

Follow-ups (not in this PR)

  • Narrow the public consensus handler to an explicit read-only allowlist (the paths are sketched in comments) instead of forwarding the whole read namespace. Kept broad here so the security fix does not risk breaking existing public reads.
  • Set CORS_ALLOWED_ORIGINS explicitly and pass it to the caddy service in docker-compose.yml instead of defaulting to *.
  • archive.vana.org:3500 reportedly exposes the REST API directly on a public port; apply the same policy there.

… public

The public Caddy vhost forwarded all of /eth/* to the beacon REST gateway,
which includes Prysm's unauthenticated validator namespace. An unauthenticated
remote POST to /eth/v1/validator/prepare_beacon_proposer overwrites a
proposer's execution-layer fee recipient, and on a node that is both publicly
exposed and actively proposing this redirects the block's priority fees to an
attacker address. /eth/v1/validator/duties/proposer and the
/eth/v{2,3}/validator/blocks endpoints widen the surface. Ref VANA-BC-001.

Deny /eth/v1|v2|v3/validator (and everything under it) on the public path with
a 403, ahead of the public consensus handler. Move the trusted-IP handler ahead
of the public handlers so RPC_TRUSTED_IP_RANGES actually takes effect (it was
dead code, matched after the public wildcards) and the validator client keeps
full access over trusted ranges. Public reads are unchanged.

Left as documented follow-ups: narrow the public consensus handler to an
explicit read-only allowlist, and set CORS_ALLOWED_ORIGINS explicitly instead
of defaulting to *.

Claude-Session: https://claude.ai/code/session_01W58RjBw7v1TvptXHQoDjsA
@inf-1oop
inf-1oop merged commit a4cb188 into main Sep 4, 2026
3 checks passed
@inf-1oop
inf-1oop deleted the fix/caddy-block-validator-namespace branch September 4, 2026 20:08
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.

2 participants