Matt Keeler 326c0ecfbe
In-Memory gRPC (#19942)
* Implement In-Process gRPC for use by controller caching/indexing

This replaces the pipe base listener implementation we were previously using. The new style CAN avoid cloning resources which our controller caching/indexing is taking advantage of to not duplicate resource objects in memory.

To maintain safety for controllers and for them to be able to modify data they get back from the cache and the resource service, the client they are presented in their runtime will be wrapped with an autogenerated client which clones request and response messages as they pass through the client.

Another sizable change in this PR is to consolidate how server specific gRPC services get registered and managed. Before this was in a bunch of different methods and it was difficult to track down how gRPC services were registered. Now its all in one place.

* Fix race in tests

* Ensure the resource service is registered to the multiplexed handler for forwarding from client agents

* Expose peer streaming on the internal handler
2024-01-12 11:54:07 -05:00

92 lines
3.0 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package acl
import (
"context"
"github.com/hashicorp/go-hclog"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/consul/authmethod"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/proto-public/pbacl"
)
type Config struct {
ACLsEnabled bool
Logger hclog.Logger
LoadAuthMethod func(authMethod string, entMeta *acl.EnterpriseMeta) (*structs.ACLAuthMethod, Validator, error)
NewLogin func() Login
ForwardRPC func(structs.RPCInfo, func(*grpc.ClientConn) error) (bool, error)
ValidateEnterpriseRequest func(*acl.EnterpriseMeta, bool) error
LocalTokensEnabled func() bool
InPrimaryDatacenter bool
PrimaryDatacenter string
NewTokenWriter func() TokenWriter
}
//go:generate mockery --name Login --inpackage
type Login interface {
TokenForVerifiedIdentity(identity *authmethod.Identity, authMethod *structs.ACLAuthMethod, description string) (*structs.ACLToken, error)
}
//go:generate mockery --name Validator --inpackage
type Validator interface {
ValidateLogin(ctx context.Context, loginToken string) (*authmethod.Identity, error)
}
//go:generate mockery --name TokenWriter --inpackage
type TokenWriter interface {
Delete(secretID string, fromLogout bool) error
}
type Server struct {
Config
}
func NewServer(cfg Config) *Server {
return &Server{cfg}
}
func (s *Server) Register(registrar grpc.ServiceRegistrar) {
pbacl.RegisterACLServiceServer(registrar, s)
}
func (s *Server) requireACLsEnabled(logger hclog.Logger) error {
if s.ACLsEnabled {
return nil
}
logger.Warn("request blocked ACLs are disabled")
return status.Error(codes.FailedPrecondition, acl.ErrDisabled.Error())
}
func (s *Server) requireLocalTokens(logger hclog.Logger) error {
if s.LocalTokensEnabled() {
return nil
}
logger.Warn("request blocked because we're in a non-primary datacenter and token replication is disabled")
return status.Error(codes.FailedPrecondition, "token replication is required for auth methods to function")
}
func (s *Server) forwardWriteDC(dc string, fn func(*grpc.ClientConn) error, logger hclog.Logger) (bool, error) {
// For private/internal gRPC handlers, protoc-gen-rpc-glue generates the
// requisite methods to satisfy the structs.RPCInfo interface using fields
// from the pbcommon package. This service is public, so we can't use those
// fields in our proto definition. Instead, we construct our RPCInfo manually.
var rpcInfo struct {
structs.WriteRequest // Ensure RPCs are forwarded to the leader.
structs.DCSpecificRequest // Ensure RPCs are forwarded to the correct datacenter.
}
rpcInfo.Datacenter = dc
return s.ForwardRPC(&rpcInfo, func(conn *grpc.ClientConn) error {
logger.Trace("forwarding RPC", "datacenter", dc)
return fn(conn)
})
}