Skip to content

Commit 61396ba

Browse files
committed
Fixes
1 parent 7c9f538 commit 61396ba

File tree

6 files changed

+61
-23
lines changed

6 files changed

+61
-23
lines changed

cmd/manager.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,11 @@ func startManagerGRPCServer(opts ManagerServerOpts, ms *manager.ManagerServer) *
525525
if opts.AdminToken != "" {
526526
interceptor := manager.NewAdminAuthInterceptor(opts.AdminToken, manager.AdminProtectedMethods())
527527
grpcOpts = append(grpcOpts, grpc.ChainUnaryInterceptor(interceptor))
528-
logger.Info().Msg("Admin gRPC auth enabled")
528+
if opts.CertFile == "" || opts.KeyFile == "" {
529+
logger.Warn().Msg("Admin gRPC auth enabled WITHOUT TLS — admin token will be sent in cleartext. Use --cert_file and --key_file in production.")
530+
} else {
531+
logger.Info().Msg("Admin gRPC auth enabled with TLS")
532+
}
529533
} else {
530534
logger.Warn().Msg("Admin gRPC auth disabled (set --admin_token for production)")
531535
}

pkg/iam/crypto.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ type MasterKey struct {
2424
key []byte
2525
}
2626

27-
// Key returns the raw key bytes for use with external encryption routines.
27+
// Key returns a copy of the raw key bytes for use with external encryption routines.
2828
func (m *MasterKey) Key() []byte {
2929
if m == nil {
3030
return nil
3131
}
32-
return m.key
32+
return append([]byte{}, m.key...)
3333
}
3434

3535
var (

pkg/manager/admin_auth.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package manager
55

66
import (
77
"context"
8+
"crypto/subtle"
89
"strings"
910

1011
"github.com/LeeDigitalWorks/zapfs/pkg/logger"
@@ -64,7 +65,7 @@ func NewAdminAuthInterceptor(token string, protectedMethods []string) grpc.Unary
6465
}
6566

6667
provided := strings.TrimPrefix(authHeader, "Bearer ")
67-
if provided != token {
68+
if subtle.ConstantTimeCompare([]byte(provided), []byte(token)) != 1 {
6869
logger.Warn().Str("method", info.FullMethod).Msg("Admin RPC rejected: invalid token")
6970
return nil, status.Error(codes.Unauthenticated, "invalid admin token")
7071
}

pkg/manager/raft_fsm.go

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -727,16 +727,17 @@ func (ms *ManagerServer) applyRegisterFederation(data json.RawMessage) interface
727727

728728
// Encrypt federation credentials before storing in Raft state
729729
if req.External != nil && len(ms.masterKey) > 0 {
730-
if encrypted, err := encryptSecret(ms.masterKey, req.External.AccessKeyId); err == nil {
731-
req.External.AccessKeyId = encrypted
732-
} else {
733-
logger.Error().Err(err).Str("bucket", req.LocalBucket).Msg("Failed to encrypt federation access key")
730+
encrypted, err := encryptSecret(ms.masterKey, req.External.AccessKeyId)
731+
if err != nil {
732+
return fmt.Errorf("encrypt federation access key for %s: %w", req.LocalBucket, err)
734733
}
735-
if encrypted, err := encryptSecret(ms.masterKey, req.External.SecretAccessKey); err == nil {
736-
req.External.SecretAccessKey = encrypted
737-
} else {
738-
logger.Error().Err(err).Str("bucket", req.LocalBucket).Msg("Failed to encrypt federation secret key")
734+
req.External.AccessKeyId = encrypted
735+
736+
encrypted, err = encryptSecret(ms.masterKey, req.External.SecretAccessKey)
737+
if err != nil {
738+
return fmt.Errorf("encrypt federation secret key for %s: %w", req.LocalBucket, err)
739739
}
740+
req.External.SecretAccessKey = encrypted
740741
}
741742

742743
// Store federation config
@@ -923,16 +924,17 @@ func (ms *ManagerServer) applyUpdateFederationCredentials(data json.RawMessage)
923924
accessKeyID := req.AccessKeyID
924925
secretAccessKey := req.SecretAccessKey
925926
if len(ms.masterKey) > 0 {
926-
if encrypted, err := encryptSecret(ms.masterKey, accessKeyID); err == nil {
927-
accessKeyID = encrypted
928-
} else {
929-
logger.Error().Err(err).Str("bucket", req.Bucket).Msg("Failed to encrypt federation access key")
927+
encrypted, err := encryptSecret(ms.masterKey, accessKeyID)
928+
if err != nil {
929+
return fmt.Errorf("encrypt federation access key for %s: %w", req.Bucket, err)
930930
}
931-
if encrypted, err := encryptSecret(ms.masterKey, secretAccessKey); err == nil {
932-
secretAccessKey = encrypted
933-
} else {
934-
logger.Error().Err(err).Str("bucket", req.Bucket).Msg("Failed to encrypt federation secret key")
931+
accessKeyID = encrypted
932+
933+
encrypted, err = encryptSecret(ms.masterKey, secretAccessKey)
934+
if err != nil {
935+
return fmt.Errorf("encrypt federation secret key for %s: %w", req.Bucket, err)
935936
}
937+
secretAccessKey = encrypted
936938
}
937939

938940
fedInfo.External.AccessKeyId = accessKeyID

pkg/manager/raft_server.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,22 @@ func (t *tlsStreamLayer) Dial(address raft.ServerAddress, timeout time.Duration)
7474
if err != nil {
7575
return nil, err
7676
}
77+
// Set deadline for the TLS handshake to prevent a slow/malicious
78+
// peer from blocking the Raft transport goroutine indefinitely.
79+
if err := conn.SetDeadline(time.Now().Add(timeout)); err != nil {
80+
conn.Close()
81+
return nil, err
82+
}
7783
tlsConn := tls.Client(conn, t.tlsConfig)
7884
if err := tlsConn.Handshake(); err != nil {
7985
conn.Close()
8086
return nil, err
8187
}
88+
// Clear the deadline so the connection can be used normally.
89+
if err := conn.SetDeadline(time.Time{}); err != nil {
90+
conn.Close()
91+
return nil, err
92+
}
8293
return tlsConn, nil
8394
}
8495

pkg/manager/server.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ type ClusterCapacity struct {
4545
// ManagerServer is the main manager server that coordinates the ZapFS cluster.
4646
// It implements both ManagerServiceServer (cluster management) and IAMServiceServer
4747
// (credential sync to metadata services).
48+
//
49+
// Lock ordering (to prevent deadlocks):
50+
//
51+
// state.Lock() > collectionsRecoveryRequiredMu > clusterCapacityMu
52+
//
53+
// The state lock must always be acquired first. The secondary mutexes
54+
// (collectionsRecoveryRequiredMu, clusterCapacityMu) protect independent
55+
// caches and may be acquired while state is held, but never the reverse.
4856
type ManagerServer struct {
4957
manager_pb.UnimplementedManagerServiceServer
5058
iam_pb.UnimplementedIAMServiceServer
@@ -98,8 +106,11 @@ type ManagerServer struct {
98106
// Backup scheduler (enterprise)
99107
backupScheduler *BackupScheduler
100108

101-
// Encryption key for federation secrets in Raft state (nil = no encryption)
102-
masterKey []byte
109+
// Encryption key for federation secrets in Raft state (nil = no encryption).
110+
// Must be set via SetMasterKey before the Raft node processes any federation commands.
111+
// Protected by masterKeyOnce to ensure safe publication to the Raft goroutine.
112+
masterKey []byte
113+
masterKeyOnce sync.Once
103114

104115
// Shutdown
105116
shutdownCh chan struct{}
@@ -440,15 +451,24 @@ func (ms *ManagerServer) Shutdown() {
440451
// SetMasterKey sets the encryption key for federation secrets in Raft state.
441452
// When set, federation credentials (AccessKeyId, SecretAccessKey) are encrypted
442453
// before being stored in the FSM state and decrypted when read.
454+
// Must be called before the Raft node processes any federation commands.
455+
// Can only be called once; subsequent calls are no-ops.
443456
// Pass nil to disable encryption (credentials stored in plaintext).
444457
func (ms *ManagerServer) SetMasterKey(key []byte) {
445-
ms.masterKey = key
458+
ms.masterKeyOnce.Do(func() {
459+
ms.masterKey = key
460+
})
446461
}
447462

448463
// DecryptFederationCredentials decrypts the AccessKeyId and SecretAccessKey
449464
// from a FederationInfo that was stored in encrypted form in Raft state.
450465
// Returns the decrypted accessKeyID and secretAccessKey.
451466
// If no masterKey is set or the values are not encrypted, returns them as-is.
467+
//
468+
// Note: The metadata service's DB layer (vitess/postgres) has its own independent
469+
// encryption via iam.EncryptCredentialSecret/DecryptCredentialSecret. This method
470+
// is for code that reads directly from the Raft FSM state (e.g., manager-side
471+
// federation operations, diagnostics, or snapshot inspection).
452472
func (ms *ManagerServer) DecryptFederationCredentials(fedInfo *FederationInfo) (accessKeyID, secretAccessKey string, err error) {
453473
if fedInfo == nil || fedInfo.External == nil {
454474
return "", "", nil

0 commit comments

Comments
 (0)