Implement a two-level key hierarchy: the MEK now wraps per-engine DEKs stored in a new barrier_keys table, rather than encrypting all barrier entries directly. A v2 ciphertext format (0x02) embeds the key ID so the barrier can resolve which DEK to use on decryption. v1 ciphertext remains supported for backward compatibility. Key changes: - crypto: EncryptV2/DecryptV2/ExtractKeyID for v2 ciphertext with key IDs - barrier: key registry (CreateKey, RotateKey, ListKeys, MigrateToV2, ReWrapKeys) - seal: RotateMEK re-wraps DEKs without re-encrypting data - engine: Mount auto-creates per-engine DEK - REST + gRPC: barrier/keys, barrier/rotate-mek, barrier/rotate-key, barrier/migrate - proto: BarrierService (v1 + v2) with ListKeys, RotateMEK, RotateKey, Migrate - db: migration v2 adds barrier_keys table Also includes: security audit report, CSRF protection, engine design specs (sshca, transit, user), path-bound AAD migration tool, policy engine enhancements, and ARCHITECTURE.md updates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
206 lines
7.7 KiB
Go
206 lines
7.7 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.VersionTLS13,
|
|
}
|
|
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.RegisterBarrierServiceServer(s.srv, &barrierServer{s: s})
|
|
pb.RegisterACMEServiceServer(s.srv, &acmeServer{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.BarrierService/ListKeys": true,
|
|
"/metacrypt.v2.BarrierService/RotateMEK": true,
|
|
"/metacrypt.v2.BarrierService/RotateKey": true,
|
|
"/metacrypt.v2.BarrierService/Migrate": 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.BarrierService/ListKeys": true,
|
|
"/metacrypt.v2.BarrierService/RotateMEK": true,
|
|
"/metacrypt.v2.BarrierService/RotateKey": true,
|
|
"/metacrypt.v2.BarrierService/Migrate": 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/ListPolicies": true,
|
|
"/metacrypt.v2.PolicyService/GetPolicy": true,
|
|
"/metacrypt.v2.PolicyService/DeletePolicy": true,
|
|
"/metacrypt.v2.ACMEService/SetConfig": true,
|
|
"/metacrypt.v2.ACMEService/ListAccounts": true,
|
|
"/metacrypt.v2.ACMEService/ListOrders": true,
|
|
"/metacrypt.v2.BarrierService/ListKeys": true,
|
|
"/metacrypt.v2.BarrierService/RotateMEK": true,
|
|
"/metacrypt.v2.BarrierService/RotateKey": true,
|
|
"/metacrypt.v2.BarrierService/Migrate": true,
|
|
}
|
|
}
|