Checkpoint: auth, engine, seal, server, grpc updates

Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
2026-03-15 09:54:04 -07:00
parent 33beb33a13
commit 44e5e6e174
21 changed files with 185 additions and 31 deletions

View File

@@ -2,6 +2,7 @@ package grpcserver
import (
"context"
"log/slog"
"strings"
"google.golang.org/grpc"
@@ -25,7 +26,7 @@ func tokenInfoFromContext(ctx context.Context) *auth.TokenInfo {
// authInterceptor validates the Bearer token from gRPC metadata and injects
// *auth.TokenInfo into the context. The set of method full names that require
// auth is passed in; all others pass through without validation.
func authInterceptor(authenticator *auth.Authenticator, methods map[string]bool) grpc.UnaryServerInterceptor {
func authInterceptor(authenticator *auth.Authenticator, logger *slog.Logger, methods map[string]bool) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
if !methods[info.FullMethod] {
return handler(ctx, req)
@@ -33,14 +34,17 @@ func authInterceptor(authenticator *auth.Authenticator, methods map[string]bool)
token := extractToken(ctx)
if token == "" {
logger.Debug("grpc request rejected: missing token", "method", info.FullMethod)
return nil, status.Error(codes.Unauthenticated, "missing authorization token")
}
tokenInfo, err := authenticator.ValidateToken(token)
if err != nil {
logger.Debug("grpc request rejected: invalid token", "method", info.FullMethod, "error", err)
return nil, status.Error(codes.Unauthenticated, "invalid token")
}
logger.Debug("grpc request authenticated", "method", info.FullMethod, "username", tokenInfo.Username)
ctx = context.WithValue(ctx, tokenInfoKey, tokenInfo)
return handler(ctx, req)
}
@@ -48,27 +52,30 @@ func authInterceptor(authenticator *auth.Authenticator, methods map[string]bool)
// adminInterceptor requires IsAdmin on the token info for the listed methods.
// Must run after authInterceptor.
func adminInterceptor(methods map[string]bool) grpc.UnaryServerInterceptor {
func adminInterceptor(logger *slog.Logger, methods map[string]bool) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
if !methods[info.FullMethod] {
return handler(ctx, req)
}
ti := tokenInfoFromContext(ctx)
if ti == nil || !ti.IsAdmin {
logger.Debug("grpc request rejected: admin required", "method", info.FullMethod)
return nil, status.Error(codes.PermissionDenied, "admin required")
}
logger.Debug("grpc admin request authorized", "method", info.FullMethod, "username", ti.Username)
return handler(ctx, req)
}
}
// sealInterceptor rejects calls with FailedPrecondition when the vault is
// sealed, for the listed methods.
func sealInterceptor(sealMgr *seal.Manager, methods map[string]bool) grpc.UnaryServerInterceptor {
func sealInterceptor(sealMgr *seal.Manager, logger *slog.Logger, methods map[string]bool) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
if !methods[info.FullMethod] {
return handler(ctx, req)
}
if sealMgr.State() != seal.StateUnsealed {
logger.Debug("grpc request rejected: vault sealed", "method", info.FullMethod)
return nil, status.Error(codes.FailedPrecondition, "vault is sealed")
}
return handler(ctx, req)

View File

@@ -66,9 +66,9 @@ func (s *GRPCServer) Start() error {
creds := credentials.NewTLS(tlsCfg)
interceptor := chainInterceptors(
sealInterceptor(s.sealMgr, sealRequiredMethods()),
authInterceptor(s.auth, authRequiredMethods()),
adminInterceptor(adminRequiredMethods()),
sealInterceptor(s.sealMgr, s.logger, sealRequiredMethods()),
authInterceptor(s.auth, s.logger, authRequiredMethods()),
adminInterceptor(s.logger, adminRequiredMethods()),
)
s.srv = grpc.NewServer(