diff --git a/agent/xds/delta.go b/agent/xds/delta.go index 762ea31c48..b2564dacfc 100644 --- a/agent/xds/delta.go +++ b/agent/xds/delta.go @@ -138,7 +138,7 @@ func (s *Server) processDelta(stream ADSDeltaStream, reqCh <-chan *envoy_discove } checkStreamACLs := func(cfgSnap *proxycfg.ConfigSnapshot) error { - return s.checkStreamACLs(stream.Context(), cfgSnap) + return s.authorize(stream.Context(), cfgSnap) } for { diff --git a/agent/xds/server.go b/agent/xds/server.go index d2e2b23081..a9b4c220b3 100644 --- a/agent/xds/server.go +++ b/agent/xds/server.go @@ -327,7 +327,7 @@ func (s *Server) process(stream ADSStream, reqCh <-chan *envoy_discovery_v3.Disc } checkStreamACLs := func(cfgSnap *proxycfg.ConfigSnapshot) error { - return s.checkStreamACLs(stream.Context(), cfgSnap) + return s.authorize(stream.Context(), cfgSnap) } for { @@ -564,13 +564,22 @@ func NewGRPCServer(s *Server, tlsConfigurator *tlsutil.Configurator) *grpc.Serve return srv } -func (s *Server) checkStreamACLs(streamCtx context.Context, cfgSnap *proxycfg.ConfigSnapshot) error { +// authorize the xDS request using the token stored in ctx. This authorization is +// a bit different from most interfaces. Instead of explicitly authorizing or +// filtering each piece of data in the response, the request is authorized +// by checking the token has `service:write` for the service ID of the destination +// service (for kind=ConnectProxy), or the gateway service (for other kinds). +// This authorization strategy requires that agent/proxycfg only fetches data +// using a token with the same permissions, and that it stores the data by +// proxy ID. We assume that any data in the snapshot was already filtered, +// which allows this authorization to be a shallow authorization check +// for all the data in a ConfigSnapshot. +func (s *Server) authorize(ctx context.Context, cfgSnap *proxycfg.ConfigSnapshot) error { if cfgSnap == nil { return status.Errorf(codes.Unauthenticated, "unauthenticated: no config snapshot") } - authz, err := s.ResolveToken(tokenFromContext(streamCtx)) - + authz, err := s.ResolveToken(tokenFromContext(ctx)) if acl.IsErrNotFound(err) { return status.Errorf(codes.Unauthenticated, "unauthenticated: %v", err) } else if acl.IsErrPermissionDenied(err) { diff --git a/contributing/service-mesh/README.md b/contributing/service-mesh/README.md index 3c770a88cc..7bc16f7f77 100644 --- a/contributing/service-mesh/README.md +++ b/contributing/service-mesh/README.md @@ -1,12 +1,15 @@ # Service Mesh (Connect) - call out: envoy/proxy is the data plane, Consul is the control plane -- agent/xds - gRPC service that implements - [xDS](https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol) -- [agent/proxycfg](https://github.com/hashicorp/consul/blob/master/agent/proxycfg/proxycfg.go) +- [xDS Server] - a gRPC service that implements [xDS] and handles requests from an [envoy proxy]. +- [agent/proxycfg] - CA Manager - certificate authority - command/connect/envoy - bootstrapping and running envoy - command/connect/proxy - built-in proxy that is dev-only and not supported for production. - `connect/` - "Native" service mesh +[xDS Server]: ./xds.md +[xDS]: https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol +[envoy proxy]: https://www.consul.io/docs/connect/proxies/envoy +[agent/proxycfg]: https://github.com/hashicorp/consul/blob/main/agent/proxycfg diff --git a/contributing/service-mesh/xds.md b/contributing/service-mesh/xds.md new file mode 100644 index 0000000000..73aeefb52b --- /dev/null +++ b/contributing/service-mesh/xds.md @@ -0,0 +1,25 @@ +# xDS Server + +The xDS Server is a gRPC service that implements [xDS] and handles requests from +an [envoy proxy]. + +[xDS]: https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol +[envoy proxy]: https://www.consul.io/docs/connect/proxies/envoy + + +## Authorization + +Requests to the xDS server are authorized based on an assumption of how +`proxycfg.ConfigSnapshot` are constructed. Most interfaces (HTTP, DNS, RPC) +authorize requests by authorizing the data in the response, or by filtering +out data that the requester is not authorized to view. The xDS server authorizes +requests by looking at the proxy ID in the request and ensuring the ACL token has +`service:write` access to either the destination service (for kind=ConnectProxy), or +the gateway service (for other kinds). + +This authorization strategy requires that [agent/proxycfg] only fetches data using a +token with the same permissions, and that it only stores data by proxy ID. We assume +that any data in the snapshot was already filtered, which allows this authorization to +only perform a shallow check against the proxy ID. + +[agent/proxycfg]: https://github.com/hashicorp/consul/blob/main/agent/proxycfg