Add complete transit engine supporting symmetric encryption (AES-256-GCM, XChaCha20-Poly1305), asymmetric signing (Ed25519, ECDSA P-256/P-384), and HMAC (SHA-256/SHA-512) with versioned key rotation, min decryption version enforcement, key trimming, batch operations, and rewrap. Includes proto definitions, gRPC handlers, REST routes, and comprehensive tests covering all 18 operations, auth enforcement, and edge cases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
231 lines
9.2 KiB
Go
231 lines
9.2 KiB
Go
// Package grpcserver implements the gRPC server for Metacrypt.
|
|
package grpcserver
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"log/slog"
|
|
"net"
|
|
"sync"
|
|
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials"
|
|
|
|
pb "git.wntrmute.dev/kyle/metacrypt/gen/metacrypt/v2"
|
|
internacme "git.wntrmute.dev/kyle/metacrypt/internal/acme"
|
|
"git.wntrmute.dev/kyle/metacrypt/internal/auth"
|
|
"git.wntrmute.dev/kyle/metacrypt/internal/config"
|
|
"git.wntrmute.dev/kyle/metacrypt/internal/engine"
|
|
"git.wntrmute.dev/kyle/metacrypt/internal/policy"
|
|
"git.wntrmute.dev/kyle/metacrypt/internal/seal"
|
|
)
|
|
|
|
// GRPCServer wraps the gRPC server and all service implementations.
|
|
type GRPCServer struct {
|
|
cfg *config.Config
|
|
sealMgr *seal.Manager
|
|
auth *auth.Authenticator
|
|
policy *policy.Engine
|
|
engines *engine.Registry
|
|
logger *slog.Logger
|
|
srv *grpc.Server
|
|
acmeHandlers map[string]*internacme.Handler
|
|
mu sync.Mutex
|
|
}
|
|
|
|
// New creates a new GRPCServer.
|
|
func New(cfg *config.Config, sealMgr *seal.Manager, authenticator *auth.Authenticator,
|
|
policyEngine *policy.Engine, engineRegistry *engine.Registry, logger *slog.Logger) *GRPCServer {
|
|
return &GRPCServer{
|
|
cfg: cfg,
|
|
sealMgr: sealMgr,
|
|
auth: authenticator,
|
|
policy: policyEngine,
|
|
engines: engineRegistry,
|
|
logger: logger,
|
|
acmeHandlers: make(map[string]*internacme.Handler),
|
|
}
|
|
}
|
|
|
|
// Start starts the gRPC server on cfg.Server.GRPCAddr.
|
|
func (s *GRPCServer) Start() error {
|
|
if s.cfg.Server.GRPCAddr == "" {
|
|
s.logger.Info("grpc_addr not configured, gRPC server disabled")
|
|
return nil
|
|
}
|
|
|
|
tlsCert, err := tls.LoadX509KeyPair(s.cfg.Server.TLSCert, s.cfg.Server.TLSKey)
|
|
if err != nil {
|
|
return fmt.Errorf("grpc: load TLS cert: %w", err)
|
|
}
|
|
tlsCfg := &tls.Config{
|
|
Certificates: []tls.Certificate{tlsCert},
|
|
MinVersion: tls.VersionTLS12,
|
|
}
|
|
creds := credentials.NewTLS(tlsCfg)
|
|
|
|
interceptor := chainInterceptors(
|
|
sealInterceptor(s.sealMgr, s.logger, sealRequiredMethods()),
|
|
authInterceptor(s.auth, s.logger, authRequiredMethods()),
|
|
adminInterceptor(s.logger, adminRequiredMethods()),
|
|
)
|
|
|
|
s.srv = grpc.NewServer(
|
|
grpc.Creds(creds),
|
|
grpc.UnaryInterceptor(interceptor),
|
|
)
|
|
|
|
pb.RegisterSystemServiceServer(s.srv, &systemServer{s: s})
|
|
pb.RegisterAuthServiceServer(s.srv, &authServer{s: s})
|
|
pb.RegisterEngineServiceServer(s.srv, &engineServer{s: s})
|
|
pb.RegisterPKIServiceServer(s.srv, &pkiServer{s: s})
|
|
pb.RegisterCAServiceServer(s.srv, &caServer{s: s})
|
|
pb.RegisterPolicyServiceServer(s.srv, &policyServer{s: s})
|
|
pb.RegisterACMEServiceServer(s.srv, &acmeServer{s: s})
|
|
pb.RegisterTransitServiceServer(s.srv, &transitServer{s: s})
|
|
|
|
lis, err := net.Listen("tcp", s.cfg.Server.GRPCAddr)
|
|
if err != nil {
|
|
return fmt.Errorf("grpc: listen %s: %w", s.cfg.Server.GRPCAddr, err)
|
|
}
|
|
|
|
s.logger.Info("starting gRPC server", "addr", s.cfg.Server.GRPCAddr)
|
|
if err := s.srv.Serve(lis); err != nil {
|
|
return fmt.Errorf("grpc: serve: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Shutdown gracefully stops the gRPC server.
|
|
func (s *GRPCServer) Shutdown() {
|
|
if s.srv != nil {
|
|
s.srv.GracefulStop()
|
|
}
|
|
}
|
|
|
|
// sealRequiredMethods returns the set of RPC full names that require the vault
|
|
// to be unsealed.
|
|
func sealRequiredMethods() map[string]bool {
|
|
return map[string]bool{
|
|
"/metacrypt.v2.AuthService/Login": true,
|
|
"/metacrypt.v2.AuthService/Logout": true,
|
|
"/metacrypt.v2.AuthService/TokenInfo": true,
|
|
"/metacrypt.v2.EngineService/Mount": true,
|
|
"/metacrypt.v2.EngineService/Unmount": true,
|
|
"/metacrypt.v2.EngineService/ListMounts": true,
|
|
"/metacrypt.v2.PKIService/GetRootCert": true,
|
|
"/metacrypt.v2.PKIService/GetChain": true,
|
|
"/metacrypt.v2.PKIService/GetIssuerCert": true,
|
|
"/metacrypt.v2.CAService/ImportRoot": true,
|
|
"/metacrypt.v2.CAService/GetRoot": true,
|
|
"/metacrypt.v2.CAService/CreateIssuer": true,
|
|
"/metacrypt.v2.CAService/DeleteIssuer": true,
|
|
"/metacrypt.v2.CAService/ListIssuers": true,
|
|
"/metacrypt.v2.CAService/GetIssuer": true,
|
|
"/metacrypt.v2.CAService/GetChain": true,
|
|
"/metacrypt.v2.CAService/IssueCert": true,
|
|
"/metacrypt.v2.CAService/GetCert": true,
|
|
"/metacrypt.v2.CAService/ListCerts": true,
|
|
"/metacrypt.v2.CAService/RenewCert": true,
|
|
"/metacrypt.v2.CAService/SignCSR": true,
|
|
"/metacrypt.v2.CAService/RevokeCert": true,
|
|
"/metacrypt.v2.CAService/DeleteCert": true,
|
|
"/metacrypt.v2.PolicyService/CreatePolicy": true,
|
|
"/metacrypt.v2.PolicyService/ListPolicies": true,
|
|
"/metacrypt.v2.PolicyService/GetPolicy": true,
|
|
"/metacrypt.v2.PolicyService/DeletePolicy": true,
|
|
"/metacrypt.v2.ACMEService/CreateEAB": true,
|
|
"/metacrypt.v2.ACMEService/SetConfig": true,
|
|
"/metacrypt.v2.ACMEService/ListAccounts": true,
|
|
"/metacrypt.v2.ACMEService/ListOrders": true,
|
|
"/metacrypt.v2.TransitService/CreateKey": true,
|
|
"/metacrypt.v2.TransitService/DeleteKey": true,
|
|
"/metacrypt.v2.TransitService/GetKey": true,
|
|
"/metacrypt.v2.TransitService/ListKeys": true,
|
|
"/metacrypt.v2.TransitService/RotateKey": true,
|
|
"/metacrypt.v2.TransitService/UpdateKeyConfig": true,
|
|
"/metacrypt.v2.TransitService/TrimKey": true,
|
|
"/metacrypt.v2.TransitService/Encrypt": true,
|
|
"/metacrypt.v2.TransitService/Decrypt": true,
|
|
"/metacrypt.v2.TransitService/Rewrap": true,
|
|
"/metacrypt.v2.TransitService/BatchEncrypt": true,
|
|
"/metacrypt.v2.TransitService/BatchDecrypt": true,
|
|
"/metacrypt.v2.TransitService/BatchRewrap": true,
|
|
"/metacrypt.v2.TransitService/Sign": true,
|
|
"/metacrypt.v2.TransitService/Verify": true,
|
|
"/metacrypt.v2.TransitService/Hmac": true,
|
|
"/metacrypt.v2.TransitService/GetPublicKey": true,
|
|
}
|
|
}
|
|
|
|
// authRequiredMethods returns the set of RPC full names that require a valid token.
|
|
func authRequiredMethods() map[string]bool {
|
|
return map[string]bool{
|
|
"/metacrypt.v2.AuthService/Logout": true,
|
|
"/metacrypt.v2.AuthService/TokenInfo": true,
|
|
"/metacrypt.v2.EngineService/Mount": true,
|
|
"/metacrypt.v2.EngineService/Unmount": true,
|
|
"/metacrypt.v2.EngineService/ListMounts": true,
|
|
"/metacrypt.v2.CAService/ImportRoot": true,
|
|
"/metacrypt.v2.CAService/CreateIssuer": true,
|
|
"/metacrypt.v2.CAService/DeleteIssuer": true,
|
|
"/metacrypt.v2.CAService/ListIssuers": true,
|
|
"/metacrypt.v2.CAService/IssueCert": true,
|
|
"/metacrypt.v2.CAService/GetCert": true,
|
|
"/metacrypt.v2.CAService/ListCerts": true,
|
|
"/metacrypt.v2.CAService/RenewCert": true,
|
|
"/metacrypt.v2.CAService/SignCSR": true,
|
|
"/metacrypt.v2.CAService/RevokeCert": true,
|
|
"/metacrypt.v2.CAService/DeleteCert": true,
|
|
"/metacrypt.v2.PolicyService/CreatePolicy": true,
|
|
"/metacrypt.v2.PolicyService/ListPolicies": true,
|
|
"/metacrypt.v2.PolicyService/GetPolicy": true,
|
|
"/metacrypt.v2.PolicyService/DeletePolicy": true,
|
|
"/metacrypt.v2.ACMEService/CreateEAB": true,
|
|
"/metacrypt.v2.ACMEService/SetConfig": true,
|
|
"/metacrypt.v2.ACMEService/ListAccounts": true,
|
|
"/metacrypt.v2.ACMEService/ListOrders": true,
|
|
"/metacrypt.v2.TransitService/CreateKey": true,
|
|
"/metacrypt.v2.TransitService/DeleteKey": true,
|
|
"/metacrypt.v2.TransitService/GetKey": true,
|
|
"/metacrypt.v2.TransitService/ListKeys": true,
|
|
"/metacrypt.v2.TransitService/RotateKey": true,
|
|
"/metacrypt.v2.TransitService/UpdateKeyConfig": true,
|
|
"/metacrypt.v2.TransitService/TrimKey": true,
|
|
"/metacrypt.v2.TransitService/Encrypt": true,
|
|
"/metacrypt.v2.TransitService/Decrypt": true,
|
|
"/metacrypt.v2.TransitService/Rewrap": true,
|
|
"/metacrypt.v2.TransitService/BatchEncrypt": true,
|
|
"/metacrypt.v2.TransitService/BatchDecrypt": true,
|
|
"/metacrypt.v2.TransitService/BatchRewrap": true,
|
|
"/metacrypt.v2.TransitService/Sign": true,
|
|
"/metacrypt.v2.TransitService/Verify": true,
|
|
"/metacrypt.v2.TransitService/Hmac": true,
|
|
"/metacrypt.v2.TransitService/GetPublicKey": true,
|
|
}
|
|
}
|
|
|
|
// adminRequiredMethods returns the set of RPC full names that require admin.
|
|
func adminRequiredMethods() map[string]bool {
|
|
return map[string]bool{
|
|
"/metacrypt.v2.SystemService/Seal": true,
|
|
"/metacrypt.v2.EngineService/Mount": true,
|
|
"/metacrypt.v2.EngineService/Unmount": true,
|
|
"/metacrypt.v2.CAService/ImportRoot": true,
|
|
"/metacrypt.v2.CAService/CreateIssuer": true,
|
|
"/metacrypt.v2.CAService/DeleteIssuer": true,
|
|
"/metacrypt.v2.CAService/RevokeCert": true,
|
|
"/metacrypt.v2.CAService/DeleteCert": true,
|
|
"/metacrypt.v2.PolicyService/CreatePolicy": true,
|
|
"/metacrypt.v2.PolicyService/DeletePolicy": true,
|
|
"/metacrypt.v2.ACMEService/SetConfig": true,
|
|
"/metacrypt.v2.ACMEService/ListAccounts": true,
|
|
"/metacrypt.v2.ACMEService/ListOrders": true,
|
|
"/metacrypt.v2.TransitService/CreateKey": true,
|
|
"/metacrypt.v2.TransitService/DeleteKey": true,
|
|
"/metacrypt.v2.TransitService/RotateKey": true,
|
|
"/metacrypt.v2.TransitService/UpdateKeyConfig": true,
|
|
"/metacrypt.v2.TransitService/TrimKey": true,
|
|
}
|
|
}
|