fix(cuprate): stop publishing the unauthenticated unrestricted RPC

The manifest bound cuprated's unrestricted RPC (full node control) to
0.0.0.0 inside the container with
i_know_what_im_doing_allow_public_unrestricted_rpc = true, relying on
ports[].bind: 127.0.0.1 to keep it private. That only restricts the HOST
side. Verified live on archi-dev-box 2026-08-22: a peer container got a
valid unauthenticated get_info off container port 18081 — and still did
after cuprate was moved to its own network, because podman bridges route
to each other unless created with --opt isolate=true, which the
orchestrator's auto-create does not pass. Every app on the node could
therefore drive full node control with no credential.

The PR justified this as the pattern bitcoin-knots already uses, but
knots writes rpcuser/rpcpassword from generated secrets, so a 0.0.0.0
bind there still is not control without credentials. cuprated has no RPC
authentication at all, so the two are not equivalent.

Unrestricted RPC is now left at cuprated's own default — container
loopback only, published nowhere, reachable by nothing — which is what
upstream intends by refusing a non-local bind without an explicit
override. Restricted RPC (the safe-for-public subset wallets use) and p2p
are unchanged, and health_check moves to 18089 since 18184 is gone.

Re-verified after the change: peer container gets connection refused on
18081 (exit 7), restricted RPC and the health endpoint still answer, the
node still syncs, validator APPROVED, 76/76 container tests pass
including the unauthenticated-port canary (still 28 — an auth: local
port was removed, not an auth: none one).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-22 03:10:53 -04:00
co-authored by Claude Opus 5
parent d6b48ce095
commit f1b5d2d267
+14 -16
View File
@@ -67,17 +67,18 @@ app:
auth: none auth: none
auth_rationale: >- auth_rationale: >-
Monero p2p gossip. Peers are anonymous by design and speak the Monero wire protocol, not HTTP. Monero p2p gossip. Peers are anonymous by design and speak the Monero wire protocol, not HTTP.
# Unrestricted RPC: full node control (mining, wallet-adjacent calls). # Unrestricted RPC (full node control) is deliberately NOT published.
# cuprated enables this by default bound to 127.0.0.1 and refuses to # cuprated has no RPC authentication, and for a published port to reach
# start if pointed at a non-local address without an explicit opt-in # it the service would have to bind 0.0.0.0 inside the container — at
# flag — this mirrors that intent at the gate too. Host port differs # which point every other app can reach it directly on 18081, since
# from Monero's canonical 18081 for the same reason as P2P above # ports[].bind only restricts the HOST side and podman bridges route to
# (nostr-rs-relay already holds 18081 on this fleet). # each other (verified live 2026-08-22: a peer container on archy-net
- host: 18184 # got an unauthenticated get_info, from a *different* network). That is
container: 18081 # unlike bitcoin-knots, whose 0.0.0.0 RPC still demands the rpcuser /
protocol: tcp # rpcpassword it writes from generated secrets. So unrestricted RPC is
bind: 127.0.0.1 # left at cuprated's own default — container loopback only, reachable by
auth: local # nothing — which is also what upstream intends by refusing a non-local
# bind without an explicit i_know_what_im_doing override.
# Restricted RPC: Monero's own purpose-built safe-for-public subset — # Restricted RPC: Monero's own purpose-built safe-for-public subset —
# what wallets use when connecting to a "remote node". Disabled by # what wallets use when connecting to a "remote node". Disabled by
# cuprated's own default; enabled via files[] below. A dashboard login # cuprated's own default; enabled via files[] below. A dashboard login
@@ -128,17 +129,14 @@ app:
network = "Mainnet" network = "Mainnet"
target_max_memory = 3000000000 target_max_memory = 3000000000
[rpc.unrestricted]
address = "0.0.0.0"
i_know_what_im_doing_allow_public_unrestricted_rpc = true
[rpc.restricted] [rpc.restricted]
enable = true enable = true
overwrite: false overwrite: false
health_check: health_check:
type: tcp type: tcp
endpoint: localhost:18184 # Restricted RPC — the only RPC surface published now.
endpoint: localhost:18089
interval: 30s interval: 30s
timeout: 5s timeout: 5s
retries: 3 retries: 3