From cd2b254c6fad955199bc02264cd4439939f6ef14 Mon Sep 17 00:00:00 2001 From: Jahziel Villasana-Espinoza Date: Tue, 28 May 2024 11:31:17 -0400 Subject: [PATCH] fix: move encryption to ds --- server/datastore/mysql/apple_mdm.go | 63 ++++++++++++++++++++++++++++- server/datastore/mysql/config.go | 2 + server/datastore/mysql/mysql.go | 5 +++ server/service/mdm.go | 60 +-------------------------- 4 files changed, 71 insertions(+), 59 deletions(-) diff --git a/server/datastore/mysql/apple_mdm.go b/server/datastore/mysql/apple_mdm.go index e430eeb0ab..6a869af086 100644 --- a/server/datastore/mysql/apple_mdm.go +++ b/server/datastore/mysql/apple_mdm.go @@ -3,10 +3,14 @@ package mysql import ( "bytes" "context" + "crypto/aes" + "crypto/cipher" + "crypto/rand" "database/sql" "encoding/json" "errors" "fmt" + "io" "strings" "time" @@ -4117,6 +4121,50 @@ VALUES return nil } +func encrypt(plainText []byte, privateKey string) ([]byte, error) { + block, err := aes.NewCipher([]byte(privateKey)) + if err != nil { + return nil, fmt.Errorf("create new cipher: %w", err) + } + + aesGCM, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("create new gcm: %w", err) + } + + nonce := make([]byte, aesGCM.NonceSize()) + if _, err = io.ReadFull(rand.Reader, nonce); err != nil { + return nil, fmt.Errorf("generate nonce: %w", err) + } + + return aesGCM.Seal(nonce, nonce, plainText, nil), nil +} + +func decrypt(encrypted []byte, privateKey string) ([]byte, error) { + block, err := aes.NewCipher([]byte(privateKey)) + if err != nil { + return nil, fmt.Errorf("create new cipher: %w", err) + } + + aesGCM, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("create new gcm: %w", err) + } + + // Get the nonce size + nonceSize := aesGCM.NonceSize() + + // Extract the nonce from the encrypted data + nonce, ciphertext := encrypted[:nonceSize], encrypted[nonceSize:] + + decrypted, err := aesGCM.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, fmt.Errorf("generate nonce: %w", err) + } + + return decrypted, nil +} + func (ds *Datastore) InsertMDMConfigAssets(ctx context.Context, assets []fleet.MDMConfigAsset) error { stmt := ` INSERT INTO @@ -4132,8 +4180,12 @@ VALUES var insertVals strings.Builder for _, a := range assets { + encryptedVal, err := encrypt(a.Value, ds.serverPrivateKey) + if err != nil { + return ctxerr.Wrap(ctx, err, fmt.Sprintf("encrypting mdm config asset %s", a.Name)) + } insertVals.WriteString(`(?, ?),`) - args = append(args, a.Name, a.Value) + args = append(args, a.Name, encryptedVal) } stmt = fmt.Sprintf(stmt, strings.TrimSuffix(insertVals.String(), ",")) @@ -4167,6 +4219,15 @@ WHERE return nil, ctxerr.Wrap(ctx, err, "get mdm config assets by name") } + for i, a := range res { + decryptedVal, err := decrypt(a.Value, ds.serverPrivateKey) + if err != nil { + return nil, ctxerr.Wrap(ctx, err, fmt.Sprintf("decrypting mdm config asset %s", a.Name)) + } + + res[i].Value = decryptedVal + } + return res, nil } diff --git a/server/datastore/mysql/config.go b/server/datastore/mysql/config.go index 7871ba2aa7..f198a57618 100644 --- a/server/datastore/mysql/config.go +++ b/server/datastore/mysql/config.go @@ -25,6 +25,7 @@ type dbOptions struct { tracingConfig *config.LoggingConfig minLastOpenedAtDiff time.Duration sqlMode string + privateKey string } // Logger adds a logger to the datastore. @@ -73,6 +74,7 @@ func TracingEnabled(lconfig *config.LoggingConfig) DBOption { func WithFleetConfig(conf *config.FleetConfig) DBOption { return func(o *dbOptions) error { o.minLastOpenedAtDiff = conf.Osquery.MinSoftwareLastOpenedAtDiff + o.privateKey = conf.Server.PrivateKey return nil } } diff --git a/server/datastore/mysql/mysql.go b/server/datastore/mysql/mysql.go index 8b0ca18ea0..179de00a80 100644 --- a/server/datastore/mysql/mysql.go +++ b/server/datastore/mysql/mysql.go @@ -104,6 +104,10 @@ type Datastore struct { // // e.g.: testBatchSetMDMWindowsProfilesErr = "insert:fail" testBatchSetMDMWindowsProfilesErr string + + // This key is used to encrypt sensitive data stored in the Fleet DB, for example MDM + // certificates and keys. + serverPrivateKey string } // reader returns the DB instance to use for read-only statements, which is the @@ -335,6 +339,7 @@ func New(config config.MysqlConfig, c clock.Clock, opts ...DBOption) (*Datastore writeCh: make(chan itemToWrite), stmtCache: make(map[string]*sqlx.Stmt), minLastOpenedAtDiff: options.minLastOpenedAtDiff, + serverPrivateKey: options.privateKey, } go ds.writeChanLoop() diff --git a/server/service/mdm.go b/server/service/mdm.go index 626811db68..64353149b6 100644 --- a/server/service/mdm.go +++ b/server/service/mdm.go @@ -3,9 +3,6 @@ package service import ( "bytes" "context" - "crypto/aes" - "crypto/cipher" - "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" @@ -2121,50 +2118,6 @@ func (svc *Service) ResendHostMDMProfile(ctx context.Context, hostID uint, profi // GET /mdm/apple/request_csr //////////////////////////////////////////////////////////////////////////////// -func Encrypt(plainText []byte, privateKey string) ([]byte, error) { - block, err := aes.NewCipher([]byte(privateKey)) - if err != nil { - return nil, fmt.Errorf("create new cipher: %w", err) - } - - aesGCM, err := cipher.NewGCM(block) - if err != nil { - return nil, fmt.Errorf("create new gcm: %w", err) - } - - nonce := make([]byte, aesGCM.NonceSize()) - if _, err = io.ReadFull(rand.Reader, nonce); err != nil { - return nil, fmt.Errorf("generate nonce: %w", err) - } - - return aesGCM.Seal(nonce, nonce, plainText, nil), nil -} - -func Decrypt(encrypted []byte, privateKey string) ([]byte, error) { - block, err := aes.NewCipher([]byte(privateKey)) - if err != nil { - return nil, fmt.Errorf("create new cipher: %w", err) - } - - aesGCM, err := cipher.NewGCM(block) - if err != nil { - return nil, fmt.Errorf("create new gcm: %w", err) - } - - // Get the nonce size - nonceSize := aesGCM.NonceSize() - - // Extract the nonce from the encrypted data - nonce, ciphertext := encrypted[:nonceSize], encrypted[nonceSize:] - - decrypted, err := aesGCM.Open(nil, nonce, ciphertext, nil) - if err != nil { - return nil, fmt.Errorf("generate nonce: %w", err) - } - - return decrypted, nil -} - type getMDMAppleCSRRequest struct{} type getMDMAppleCSRResponse struct { @@ -2224,13 +2177,9 @@ func (svc *Service) GetMDMAppleCSR(ctx context.Context) ([]byte, error) { fleet.MDMAssetCAKey: apple_mdm.EncodePrivateKeyPEM(scepKey), fleet.MDMAssetAPNSKey: apple_mdm.EncodePrivateKeyPEM(apnsKey), } { - encryptedVal, err := Encrypt(v, svc.config.Server.PrivateKey) - if err != nil { - return nil, ctxerr.Wrap(ctx, err, fmt.Sprintf("encrypting mdm config asset %s", k)) - } assets = append(assets, fleet.MDMConfigAsset{ Name: k, - Value: encryptedVal, + Value: v, }) } @@ -2240,12 +2189,7 @@ func (svc *Service) GetMDMAppleCSR(ctx context.Context) ([]byte, error) { } else { for _, a := range savedAssets { if a.Name == fleet.MDMAssetAPNSKey { - // decrypt value first - decryptedKey, err := Decrypt(a.Value, svc.config.Server.PrivateKey) - if err != nil { - return nil, ctxerr.Wrap(ctx, err, "decrypting apns key") - } - block, _ := pem.Decode(decryptedKey) + block, _ := pem.Decode(a.Value) if block == nil { return nil, ctxerr.Wrap(ctx, errors.New("decoding apns key")) }