Repository navigation
Description
Streaming RPCs skip authorization; only login is checked.
The stream interceptor chain in main.go has apiauthn and preview but no authz interceptor. Because reflection.Register(mux) is enabled, any principal that can log in (with the Google provider, any Google account) can list the API. Worse, the documented promise "an RPC with no rule is denied" doesn't hold for streams, so any streaming RPC added later would skip authorization silently.
Reproduced on main (ffed855) on GKE. dave is a ServiceAccount with no access policy bindings.
grpcurl -insecure localhost:18443 list
# Unauthenticated: missing bearer token
grpcurl -insecure -H "authorization: Bearer $(cat dave.token)" localhost:18443 list
# grpc.reflection.v1.ServerReflection
# grpc.reflection.v1alpha.ServerReflection
# substrate.ateapi.v1.Control
# substrate.ateapi.v1.WorkerService
grpcurl -insecure -H "authorization: Bearer $(cat dave.token)" localhost:18443 describe substrate.ateapi.v1.Control.MintActorJwt
# rpc MintActorJwt ( .substrate.ateapi.v1.MintActorJwtRequest ) returns ( .substrate.ateapi.v1.MintActorJwtResponse ) ...Every unary call dave makes is denied, so the gap is limited to streams.
grpc.ChainStreamInterceptor in cmd/ateapi/main.go has only apiauthn.StreamServerInterceptor and preview.StreamServerInterceptor. authz.UnaryServerInterceptor has no streaming counterpart.
Correction to the last sentence above: TestDefaultRPCPermissions_CoversEveryServedRPC in cmd/ateapi/internal/authz/registry_test.go fails if ateapi.proto gains a streaming RPC, so a new streaming RPC on Control or WorkerService would fail the build. The test only looks at ateapi.proto, though. Reflection, and any other service registered on the same server, skips authorization. docs/authorization.md ("Every RPC") says "An RPC with no rule is denied", which isn't true for these.
Impact today is low, since the API schema is public. The concern is that the "deny RPCs with no rule" guarantee doesn't hold for streams. With the Google provider configured as docs/authentication.md shows, any Google account can authenticate and reach these.
Possible fixes: add an authz stream interceptor that denies any method without a rule (and give reflection an explicit rule, or allow it only for a role), or stop registering reflection by default.
Found during the authorization bug bash, with help from an AI agent.
Activity
ateapi: deny streaming RPCs that have no authorization rule (agent-su…