From 3d47d3eea97dc05895743442bb407adfa691f7c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 23 May 2024 15:28:11 +0200 Subject: [PATCH 001/215] PMM-13129 Encrypt/decrypt basics. --- encryption/encryption.go | 100 ++++++++++++++++++++++++++++++++++ encryption/encryption_test.go | 23 ++++++++ go.mod | 2 + go.sum | 5 ++ 4 files changed, 130 insertions(+) create mode 100644 encryption/encryption.go create mode 100644 encryption/encryption_test.go diff --git a/encryption/encryption.go b/encryption/encryption.go new file mode 100644 index 0000000000..eb15a05720 --- /dev/null +++ b/encryption/encryption.go @@ -0,0 +1,100 @@ +package encryption + +import ( + "bytes" + "encoding/base64" + "fmt" + "os" + + "github.com/google/tink/go/aead" + "github.com/google/tink/go/insecurecleartextkeyset" + "github.com/google/tink/go/keyset" + "github.com/google/tink/go/tink" +) + +type encryption struct { + path string + key string +} + +func New(keyPath string) (*encryption, error) { + e := new(encryption) + e.path = keyPath + + bytes, err := os.ReadFile(e.path) + switch { + case os.IsNotExist(err): + fmt.Println("not exists") + err = e.generateKey() + if err != nil { + return nil, err + } + case err != nil: + return nil, err + default: + e.key = string(bytes) + } + + return e, nil +} + +func (e encryption) encrypt(secret string) (string, error) { + primitive, err := e.getPrimitive() + if err != nil { + return "", err + } + cipherText, err := primitive.Encrypt([]byte(secret), []byte("")) + if err != nil { + return "", err + } + + return string(cipherText), nil +} + +func (e encryption) decrypt(cipherText string) (string, error) { + primitive, err := e.getPrimitive() + if err != nil { + return "", err + } + secret, err := primitive.Decrypt([]byte(cipherText), []byte("")) + if err != nil { + return "", err + } + + return string(secret), nil +} + +func (e encryption) getPrimitive() (tink.AEAD, error) { + serializedKeyset, err := base64.StdEncoding.DecodeString(e.key) + if err != nil { + return nil, err + } + + binaryReader := keyset.NewBinaryReader(bytes.NewBuffer(serializedKeyset)) + parsedHandle, err := insecurecleartextkeyset.Read(binaryReader) + if err != nil { + return nil, err + } + + return aead.New(parsedHandle) +} + +func (e encryption) generateKey() error { + handle, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) + if err != nil { + return err + } + + buff := &bytes.Buffer{} + err = insecurecleartextkeyset.Write(handle, keyset.NewBinaryWriter(buff)) + if err != nil { + return err + } + + e.key = base64.StdEncoding.EncodeToString(buff.Bytes()) + return e.saveKeyToFile() +} + +func (e encryption) saveKeyToFile() error { + return os.WriteFile(e.path, []byte(e.key), 0644) +} diff --git a/encryption/encryption_test.go b/encryption/encryption_test.go new file mode 100644 index 0000000000..57bfd2199d --- /dev/null +++ b/encryption/encryption_test.go @@ -0,0 +1,23 @@ +package encryption + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNew(t *testing.T) { + testPath := "/Users/jiri.ctvrtka/test.key" + secret := "secret" + e, err := New(testPath) + require.NoError(t, err) + assert.Equal(t, testPath, e.path) + assert.NotEmpty(t, e.key) + cipherText, err := e.encrypt(secret) + require.NoError(t, err) + assert.NotEmpty(t, cipherText) + decryptedSecret, err := e.decrypt(cipherText) + require.NoError(t, err) + assert.Equal(t, secret, decryptedSecret) +} diff --git a/go.mod b/go.mod index 633fe44020..476a84314e 100644 --- a/go.mod +++ b/go.mod @@ -99,6 +99,8 @@ require ( github.com/golang/mock v1.4.4 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.0.0 // indirect + github.com/google/tink v1.7.0 // indirect + github.com/google/tink/go v1.7.0 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-msgpack/v2 v2.1.1 // indirect github.com/hashicorp/go-uuid v1.0.2 // indirect diff --git a/go.sum b/go.sum index 00755a9541..c51697a92a 100644 --- a/go.sum +++ b/go.sum @@ -238,6 +238,10 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/tink v1.7.0 h1:CG2tuj0GYZCHBQN51ATxok8L3qGiCHP2N5nbDZihjxg= +github.com/google/tink v1.7.0/go.mod h1:eu7D8x3z2rMO7fyvHVhMx8yoFH+vH8EZR1uO3hjEIhQ= +github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w= +github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -430,6 +434,7 @@ github.com/pganalyze/pg_query_go/v2 v2.2.0 h1:OW+reH+ZY7jdEuPyuLGlf1m7dLbE+fDudK github.com/pganalyze/pg_query_go/v2 v2.2.0/go.mod h1:XAxmVqz1tEGqizcQ3YSdN90vCOHBWjJi8URL1er5+cA= github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= From 8bc2399edbebf74a2e214e77ac8f07e5e29e39f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 23 May 2024 17:21:14 +0200 Subject: [PATCH 002/215] PMM-13129 DB connection, part of migration. --- encryption/database.go | 35 +++++++++++++++++ encryption/database_test.go | 21 ++++++++++ encryption/encryption.go | 72 ++++++++++++++++++++++++++--------- encryption/encryption_test.go | 15 +++++++- 4 files changed, 124 insertions(+), 19 deletions(-) create mode 100644 encryption/database.go create mode 100644 encryption/database_test.go diff --git a/encryption/database.go b/encryption/database.go new file mode 100644 index 0000000000..0637ef3a35 --- /dev/null +++ b/encryption/database.go @@ -0,0 +1,35 @@ +package encryption + +import ( + "database/sql" + "fmt" + + _ "github.com/lib/pq" +) + +type DatabaseConnection struct { + Host, User, Password string + Port int16 + EncryptedItems []EncryptedItem +} + +type EncryptedItem struct { + Database, Table string + Columns []string +} + +func (c DatabaseConnection) Connect() error { + psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s sslmode=disable", c.Host, c.Port, c.User, c.Password) + db, err := sql.Open("postgres", psqlconn) + if err != nil { + return err + } + defer db.Close() + + err = db.Ping() + if err != nil { + return err + } + + return nil +} diff --git a/encryption/database_test.go b/encryption/database_test.go new file mode 100644 index 0000000000..91e9838ae2 --- /dev/null +++ b/encryption/database_test.go @@ -0,0 +1,21 @@ +package encryption + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDatabaseConnection_Connect(t *testing.T) { + dbConnection := DatabaseConnection{ + Host: "127.0.0.1", + Port: 5432, + User: "pmm-agent", + Password: "pmm-agent-password", + EncryptedItems: []EncryptedItem{ + {Database: "pmm-agent", Table: "accounts", Columns: []string{"username", "password"}}, + }, + } + + assert.NoError(t, dbConnection.Connect()) +} diff --git a/encryption/encryption.go b/encryption/encryption.go index eb15a05720..664913aa69 100644 --- a/encryption/encryption.go +++ b/encryption/encryption.go @@ -2,6 +2,8 @@ package encryption import ( "bytes" + "context" + "database/sql" "encoding/base64" "fmt" "os" @@ -12,19 +14,18 @@ import ( "github.com/google/tink/go/tink" ) -type encryption struct { - path string - key string +type Encryption struct { + Path string + Key string } -func New(keyPath string) (*encryption, error) { - e := new(encryption) - e.path = keyPath +func New(keyPath string) (*Encryption, error) { + e := new(Encryption) + e.Path = keyPath - bytes, err := os.ReadFile(e.path) + bytes, err := os.ReadFile(e.Path) switch { case os.IsNotExist(err): - fmt.Println("not exists") err = e.generateKey() if err != nil { return nil, err @@ -32,13 +33,13 @@ func New(keyPath string) (*encryption, error) { case err != nil: return nil, err default: - e.key = string(bytes) + e.Key = string(bytes) } return e, nil } -func (e encryption) encrypt(secret string) (string, error) { +func (e Encryption) encrypt(secret string) (string, error) { primitive, err := e.getPrimitive() if err != nil { return "", err @@ -51,7 +52,7 @@ func (e encryption) encrypt(secret string) (string, error) { return string(cipherText), nil } -func (e encryption) decrypt(cipherText string) (string, error) { +func (e Encryption) decrypt(cipherText string) (string, error) { primitive, err := e.getPrimitive() if err != nil { return "", err @@ -64,8 +65,8 @@ func (e encryption) decrypt(cipherText string) (string, error) { return string(secret), nil } -func (e encryption) getPrimitive() (tink.AEAD, error) { - serializedKeyset, err := base64.StdEncoding.DecodeString(e.key) +func (e Encryption) getPrimitive() (tink.AEAD, error) { + serializedKeyset, err := base64.StdEncoding.DecodeString(e.Key) if err != nil { return nil, err } @@ -79,7 +80,7 @@ func (e encryption) getPrimitive() (tink.AEAD, error) { return aead.New(parsedHandle) } -func (e encryption) generateKey() error { +func (e Encryption) generateKey() error { handle, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) if err != nil { return err @@ -91,10 +92,47 @@ func (e encryption) generateKey() error { return err } - e.key = base64.StdEncoding.EncodeToString(buff.Bytes()) + e.Key = base64.StdEncoding.EncodeToString(buff.Bytes()) return e.saveKeyToFile() } -func (e encryption) saveKeyToFile() error { - return os.WriteFile(e.path, []byte(e.key), 0644) +func (e Encryption) saveKeyToFile() error { + return os.WriteFile(e.Path, []byte(e.Key), 0644) +} + +func (e Encryption) Migrate(c *DatabaseConnection) error { + connection := fmt.Sprintf("host=%s port=%d user=%s password=%s sslmode=disable", c.Host, c.Port, c.User, c.Password) + db, err := sql.Open("postgres", connection) + if err != nil { + return err + } + defer db.Close() + + err = db.Ping() + if err != nil { + return err + } + + // TODO read and update for all rows in scope of 1 transcation + tx, err := db.BeginTx(context.TODO(), nil) + if err != nil { + return err + } + defer tx.Rollback() + + rows, err := tx.Query("") + if err != nil { + return err + } + defer rows.Close() //nolint:errcheck + + for rows.Next() { + // TODO How to identify row + } + err = rows.Err() + if err != nil { + return err + } + + return nil } diff --git a/encryption/encryption_test.go b/encryption/encryption_test.go index 57bfd2199d..d2a5fe5265 100644 --- a/encryption/encryption_test.go +++ b/encryption/encryption_test.go @@ -10,14 +10,25 @@ import ( func TestNew(t *testing.T) { testPath := "/Users/jiri.ctvrtka/test.key" secret := "secret" + e, err := New(testPath) require.NoError(t, err) - assert.Equal(t, testPath, e.path) - assert.NotEmpty(t, e.key) + assert.Equal(t, testPath, e.Path) + assert.NotEmpty(t, e.Key) cipherText, err := e.encrypt(secret) require.NoError(t, err) assert.NotEmpty(t, cipherText) decryptedSecret, err := e.decrypt(cipherText) require.NoError(t, err) assert.Equal(t, secret, decryptedSecret) + + // db := &DatabaseConnection{ + // Host: "127.0.0.1", + // Port: 5432, + // User: "pmm-agent", + // Password: "pmm-agent-password", + // EncryptedItems: []EncryptedItem{ + // {Database: "pmm-agent", Table: "accounts", Columns: []string{"username", "password"}}, + // }, + // } } From 1872323a00643cd874ea8e6d69a8fbd1e2781d0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 23 May 2024 17:41:06 +0200 Subject: [PATCH 003/215] PMM-13129 Tidy. --- go.mod | 4 +--- go.sum | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 476a84314e..9ec1176343 100644 --- a/go.mod +++ b/go.mod @@ -43,6 +43,7 @@ require ( github.com/go-sql-driver/mysql v1.7.1 github.com/gogo/status v1.1.1 github.com/golang-migrate/migrate/v4 v4.17.0 + github.com/google/tink/go v1.7.0 github.com/google/uuid v1.6.0 github.com/grafana/grafana-api-golang-client v0.27.0 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 @@ -99,11 +100,8 @@ require ( github.com/golang/mock v1.4.4 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.0.0 // indirect - github.com/google/tink v1.7.0 // indirect - github.com/google/tink/go v1.7.0 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-msgpack/v2 v2.1.1 // indirect - github.com/hashicorp/go-uuid v1.0.2 // indirect github.com/kr/fs v0.1.0 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/miekg/dns v1.1.41 // indirect diff --git a/go.sum b/go.sum index c51697a92a..9a6e145d8f 100644 --- a/go.sum +++ b/go.sum @@ -238,8 +238,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/tink v1.7.0 h1:CG2tuj0GYZCHBQN51ATxok8L3qGiCHP2N5nbDZihjxg= -github.com/google/tink v1.7.0/go.mod h1:eu7D8x3z2rMO7fyvHVhMx8yoFH+vH8EZR1uO3hjEIhQ= github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w= github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -432,9 +430,9 @@ github.com/percona/promconfig v0.2.5 h1:f/HN/CbECQs7d9RIB6MKVkuXstsrsqEDxRvf6yig github.com/percona/promconfig v0.2.5/go.mod h1:Y2uXi5QNk71+ceJHuI9poank+0S1kjxd3K105fXKVkg= github.com/pganalyze/pg_query_go/v2 v2.2.0 h1:OW+reH+ZY7jdEuPyuLGlf1m7dLbE+fDudKXhLs0Ttpk= github.com/pganalyze/pg_query_go/v2 v2.2.0/go.mod h1:XAxmVqz1tEGqizcQ3YSdN90vCOHBWjJi8URL1er5+cA= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= +github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= From 60966fd0f859f108dba50176ddf93acbe0e5ccd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 27 May 2024 12:03:24 +0200 Subject: [PATCH 004/215] PMM-13129 Migration basics. --- encryption/database.go | 1 + encryption/encryption.go | 103 ++++++++++++++++++++++++++++------ encryption/encryption_test.go | 23 +++++--- 3 files changed, 102 insertions(+), 25 deletions(-) diff --git a/encryption/database.go b/encryption/database.go index 0637ef3a35..c4caec571c 100644 --- a/encryption/database.go +++ b/encryption/database.go @@ -15,6 +15,7 @@ type DatabaseConnection struct { type EncryptedItem struct { Database, Table string + Identificators []string // TODO way to identify row to update once read (SQL part?) Columns []string } diff --git a/encryption/encryption.go b/encryption/encryption.go index 664913aa69..1b68951ac6 100644 --- a/encryption/encryption.go +++ b/encryption/encryption.go @@ -5,8 +5,10 @@ import ( "context" "database/sql" "encoding/base64" + "errors" "fmt" "os" + "strings" "github.com/google/tink/go/aead" "github.com/google/tink/go/insecurecleartextkeyset" @@ -113,25 +115,94 @@ func (e Encryption) Migrate(c *DatabaseConnection) error { return err } - // TODO read and update for all rows in scope of 1 transcation - tx, err := db.BeginTx(context.TODO(), nil) - if err != nil { - return err + if len(c.EncryptedItems) == 0 { + return errors.New("Migration: Database with target tables/columns not defined") } - defer tx.Rollback() - rows, err := tx.Query("") - if err != nil { - return err - } - defer rows.Close() //nolint:errcheck + for _, item := range c.EncryptedItems { + // TODO read and update for all rows in scope of 1 transcation + tx, err := db.BeginTx(context.TODO(), nil) + if err != nil { + return err + } + defer tx.Rollback() - for rows.Next() { - // TODO How to identify row - } - err = rows.Err() - if err != nil { - return err + // TODO injection? + what := append(item.Identificators, item.Columns...) + query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ","), item.Table) + rows, err := tx.Query(query) + if err != nil { + return err + } + + columnTypes, err := rows.ColumnTypes() + if err != nil { + return err + } + columns := make(map[string]string) + for _, columnType := range columnTypes { + columns[columnType.Name()] = columnType.DatabaseTypeName() + } + + encryptedRows := []string{} + for rows.Next() { + row := make([]any, len(what)) + i := 0 + for _, t := range columns { + switch t { + case "VARCHAR": + row[i] = new(string) + default: + return fmt.Errorf("unsupported identificator type %s", t) + } + + i++ + } + + err = rows.Scan( + row..., + ) + if err != nil { + return err + } + + where := []string{} + for k, id := range item.Identificators { + where = append(where, fmt.Sprintf("%s = '%s'", id, *row[k].(*string))) + } + whereSQL := fmt.Sprintf("WHERE %s", strings.Join(where, " AND ")) + + encryptedValues := []string{} + i = 0 + for _, v := range row[len(item.Identificators):] { + s, err := e.encrypt(*v.(*string)) + if err != nil { + return err + } + encryptedValues = append(encryptedValues, fmt.Sprintf("%s = '%s'", item.Columns[i], base64.StdEncoding.EncodeToString([]byte(s)))) + i++ + } + setSQL := fmt.Sprintf("SET %s", strings.Join(encryptedValues, ", ")) + + sql := fmt.Sprintf("UPDATE %s %s %s", item.Table, setSQL, whereSQL) + encryptedRows = append(encryptedRows, sql) + } + err = rows.Close() + if err != nil { + return err + } + + for _, r := range encryptedRows { + _, err := tx.Exec(r) + if err != nil { + return err + } + } + + err = tx.Commit() + if err != nil { + return err + } } return nil diff --git a/encryption/encryption_test.go b/encryption/encryption_test.go index d2a5fe5265..2654112add 100644 --- a/encryption/encryption_test.go +++ b/encryption/encryption_test.go @@ -22,13 +22,18 @@ func TestNew(t *testing.T) { require.NoError(t, err) assert.Equal(t, secret, decryptedSecret) - // db := &DatabaseConnection{ - // Host: "127.0.0.1", - // Port: 5432, - // User: "pmm-agent", - // Password: "pmm-agent-password", - // EncryptedItems: []EncryptedItem{ - // {Database: "pmm-agent", Table: "accounts", Columns: []string{"username", "password"}}, - // }, - // } + c := &DatabaseConnection{ + Host: "127.0.0.1", + Port: 5432, + User: "pmm-agent", + Password: "pmm-agent-password", + EncryptedItems: []EncryptedItem{{ + Database: "pmm-agent", + Table: "acc", + Identificators: []string{"ID"}, + Columns: []string{"username", "password"}}, + }, + } + + assert.NoError(t, e.Migrate(c)) } From 46b24db3814d38ee552300a841e41258c766035a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 27 May 2024 12:11:22 +0200 Subject: [PATCH 005/215] PMM-13129 Format. --- encryption/encryption.go | 2 +- encryption/encryption_test.go | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/encryption/encryption.go b/encryption/encryption.go index 1b68951ac6..5d5c1f62c0 100644 --- a/encryption/encryption.go +++ b/encryption/encryption.go @@ -99,7 +99,7 @@ func (e Encryption) generateKey() error { } func (e Encryption) saveKeyToFile() error { - return os.WriteFile(e.Path, []byte(e.Key), 0644) + return os.WriteFile(e.Path, []byte(e.Key), 0o644) } func (e Encryption) Migrate(c *DatabaseConnection) error { diff --git a/encryption/encryption_test.go b/encryption/encryption_test.go index 2654112add..295b36325f 100644 --- a/encryption/encryption_test.go +++ b/encryption/encryption_test.go @@ -27,11 +27,13 @@ func TestNew(t *testing.T) { Port: 5432, User: "pmm-agent", Password: "pmm-agent-password", - EncryptedItems: []EncryptedItem{{ - Database: "pmm-agent", - Table: "acc", - Identificators: []string{"ID"}, - Columns: []string{"username", "password"}}, + EncryptedItems: []EncryptedItem{ + { + Database: "pmm-agent", + Table: "acc", + Identificators: []string{"ID"}, + Columns: []string{"username", "password"}, + }, }, } From f6dcd3543161a1b736a02af2ae094cfbacf606ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 28 May 2024 14:15:23 +0200 Subject: [PATCH 006/215] PMM-13129 Encrypt, EncryptDB, Decrypt, DecryptDB, refactor. --- encryption/database.go | 77 ++++++++++--- encryption/database_test.go | 7 +- encryption/encryption.go | 207 ++++++++++++++++------------------ encryption/encryption_test.go | 15 ++- encryption/helpers.go | 30 +++++ encryption/models.go | 25 ++++ 6 files changed, 226 insertions(+), 135 deletions(-) create mode 100644 encryption/helpers.go create mode 100644 encryption/models.go diff --git a/encryption/database.go b/encryption/database.go index c4caec571c..3a341a2b57 100644 --- a/encryption/database.go +++ b/encryption/database.go @@ -3,34 +3,79 @@ package encryption import ( "database/sql" "fmt" + "strings" _ "github.com/lib/pq" ) -type DatabaseConnection struct { - Host, User, Password string - Port int16 - EncryptedItems []EncryptedItem +func (c DatabaseConnection) Connect() (*sql.DB, error) { + db, err := sql.Open("postgres", c.DSN()) + if err != nil { + return nil, err + } + + err = db.Ping() + if err != nil { + return nil, err + } + + return db, nil } -type EncryptedItem struct { - Database, Table string - Identificators []string // TODO way to identify row to update once read (SQL part?) - Columns []string +func (c DatabaseConnection) DSN() string { + if c.SSLMode == "" { + c.SSLMode = "disable" + } + + return fmt.Sprintf("host=%s port=%d user=%s password=%s sslmode=%s", c.Host, c.Port, c.User, c.Password, c.SSLMode) } -func (c DatabaseConnection) Connect() error { - psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s sslmode=disable", c.Host, c.Port, c.User, c.Password) - db, err := sql.Open("postgres", psqlconn) +func (item EncryptedItem) Read(tx *sql.Tx) (*QueryValues, error) { + what := append(item.Identificators, item.Columns...) + query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ","), item.Table) + rows, err := tx.Query(query) if err != nil { - return err + return nil, err } - defer db.Close() - err = db.Ping() + q := new(QueryValues) + for rows.Next() { + row, err := prepareRowPointers(rows) + if err != nil { + return nil, err + } + err = rows.Scan(row...) + if err != nil { + return nil, err + } + + i := 1 + set := []string{} + setValues := []any{} + for k, v := range row[len(item.Identificators):] { + set = append(set, fmt.Sprintf("%s = $%d", item.Columns[k], i)) + setValues = append(setValues, v) + i++ + } + setSQL := fmt.Sprintf("SET %s", strings.Join(set, ", ")) + q.SetValues = append(q.SetValues, setValues) + + where := []string{} + whereValues := []any{} + for k, id := range item.Identificators { + where = append(where, fmt.Sprintf("%s = $%d", id, i)) + whereValues = append(whereValues, row[k]) + i++ + } + whereSQL := fmt.Sprintf("WHERE %s", strings.Join(where, " AND ")) + q.WhereValues = append(q.WhereValues, whereValues) + + q.Query = fmt.Sprintf("UPDATE %s %s %s", item.Table, setSQL, whereSQL) + } + err = rows.Close() if err != nil { - return err + return nil, err } - return nil + return q, nil } diff --git a/encryption/database_test.go b/encryption/database_test.go index 91e9838ae2..c42eb9d4a4 100644 --- a/encryption/database_test.go +++ b/encryption/database_test.go @@ -12,10 +12,7 @@ func TestDatabaseConnection_Connect(t *testing.T) { Port: 5432, User: "pmm-agent", Password: "pmm-agent-password", - EncryptedItems: []EncryptedItem{ - {Database: "pmm-agent", Table: "accounts", Columns: []string{"username", "password"}}, - }, } - - assert.NoError(t, dbConnection.Connect()) + _, err := dbConnection.Connect() + assert.NoError(t, err) } diff --git a/encryption/encryption.go b/encryption/encryption.go index 5d5c1f62c0..87e3b94037 100644 --- a/encryption/encryption.go +++ b/encryption/encryption.go @@ -3,12 +3,9 @@ package encryption import ( "bytes" "context" - "database/sql" "encoding/base64" "errors" - "fmt" "os" - "strings" "github.com/google/tink/go/aead" "github.com/google/tink/go/insecurecleartextkeyset" @@ -16,11 +13,6 @@ import ( "github.com/google/tink/go/tink" ) -type Encryption struct { - Path string - Key string -} - func New(keyPath string) (*Encryption, error) { e := new(Encryption) e.Path = keyPath @@ -41,7 +33,7 @@ func New(keyPath string) (*Encryption, error) { return e, nil } -func (e Encryption) encrypt(secret string) (string, error) { +func (e Encryption) Encrypt(secret string) (string, error) { primitive, err := e.getPrimitive() if err != nil { return "", err @@ -51,149 +43,113 @@ func (e Encryption) encrypt(secret string) (string, error) { return "", err } - return string(cipherText), nil + return base64.StdEncoding.EncodeToString(cipherText), nil } -func (e Encryption) decrypt(cipherText string) (string, error) { - primitive, err := e.getPrimitive() +func (e Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection) error { + db, err := c.Connect() if err != nil { - return "", err + return err } - secret, err := primitive.Decrypt([]byte(cipherText), []byte("")) - if err != nil { - return "", err + defer db.Close() + + if len(c.EncryptedItems) == 0 { + return errors.New("DB Connection: Database target tables/columns not defined") } + for _, item := range c.EncryptedItems { + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer tx.Rollback() - return string(secret), nil -} + res, err := item.Read(tx) + if err != nil { + return err + } -func (e Encryption) getPrimitive() (tink.AEAD, error) { - serializedKeyset, err := base64.StdEncoding.DecodeString(e.Key) - if err != nil { - return nil, err - } + for k, v := range res.SetValues { + for i, val := range v { + encrypted, err := e.Encrypt(*val.(*string)) + if err != nil { + return err + } + res.SetValues[k][i] = base64.StdEncoding.EncodeToString([]byte(encrypted)) + } + data := append([]any{}, v...) + data = append(data, res.WhereValues[k]...) + _, err := tx.Exec(res.Query, data...) + if err != nil { + return err + } + } - binaryReader := keyset.NewBinaryReader(bytes.NewBuffer(serializedKeyset)) - parsedHandle, err := insecurecleartextkeyset.Read(binaryReader) - if err != nil { - return nil, err + err = tx.Commit() + if err != nil { + return err + } } - return aead.New(parsedHandle) + return nil } -func (e Encryption) generateKey() error { - handle, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) +func (e Encryption) Decrypt(cipherText string) (string, error) { + primitive, err := e.getPrimitive() if err != nil { - return err + return "", err } - buff := &bytes.Buffer{} - err = insecurecleartextkeyset.Write(handle, keyset.NewBinaryWriter(buff)) + decoded, err := base64.StdEncoding.DecodeString(cipherText) if err != nil { - return err + return "", err } - e.Key = base64.StdEncoding.EncodeToString(buff.Bytes()) - return e.saveKeyToFile() -} + secret, err := primitive.Decrypt([]byte(decoded), []byte("")) + if err != nil { + return "", err + } -func (e Encryption) saveKeyToFile() error { - return os.WriteFile(e.Path, []byte(e.Key), 0o644) + return string(secret), nil } -func (e Encryption) Migrate(c *DatabaseConnection) error { - connection := fmt.Sprintf("host=%s port=%d user=%s password=%s sslmode=disable", c.Host, c.Port, c.User, c.Password) - db, err := sql.Open("postgres", connection) +func (e Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection) error { + db, err := c.Connect() if err != nil { return err } defer db.Close() - err = db.Ping() - if err != nil { - return err - } - if len(c.EncryptedItems) == 0 { - return errors.New("Migration: Database with target tables/columns not defined") + return errors.New("DB Connection: Database target tables/columns not defined") } for _, item := range c.EncryptedItems { - // TODO read and update for all rows in scope of 1 transcation - tx, err := db.BeginTx(context.TODO(), nil) + tx, err := db.BeginTx(ctx, nil) if err != nil { return err } defer tx.Rollback() - // TODO injection? - what := append(item.Identificators, item.Columns...) - query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ","), item.Table) - rows, err := tx.Query(query) - if err != nil { - return err - } - - columnTypes, err := rows.ColumnTypes() + res, err := item.Read(tx) if err != nil { return err } - columns := make(map[string]string) - for _, columnType := range columnTypes { - columns[columnType.Name()] = columnType.DatabaseTypeName() - } - encryptedRows := []string{} - for rows.Next() { - row := make([]any, len(what)) - i := 0 - for _, t := range columns { - switch t { - case "VARCHAR": - row[i] = new(string) - default: - return fmt.Errorf("unsupported identificator type %s", t) + for k, v := range res.SetValues { + for i, val := range v { + decoded, err := base64.StdEncoding.DecodeString(*val.(*string)) + if err != nil { + return err } - - i++ - } - - err = rows.Scan( - row..., - ) - if err != nil { - return err - } - - where := []string{} - for k, id := range item.Identificators { - where = append(where, fmt.Sprintf("%s = '%s'", id, *row[k].(*string))) - } - whereSQL := fmt.Sprintf("WHERE %s", strings.Join(where, " AND ")) - - encryptedValues := []string{} - i = 0 - for _, v := range row[len(item.Identificators):] { - s, err := e.encrypt(*v.(*string)) + decrypted, err := e.Decrypt(string(decoded)) if err != nil { return err } - encryptedValues = append(encryptedValues, fmt.Sprintf("%s = '%s'", item.Columns[i], base64.StdEncoding.EncodeToString([]byte(s)))) - i++ + res.SetValues[k][i] = decrypted } - setSQL := fmt.Sprintf("SET %s", strings.Join(encryptedValues, ", ")) - - sql := fmt.Sprintf("UPDATE %s %s %s", item.Table, setSQL, whereSQL) - encryptedRows = append(encryptedRows, sql) - } - err = rows.Close() - if err != nil { - return err - } - - for _, r := range encryptedRows { - _, err := tx.Exec(r) + data := append([]any{}, v...) + data = append(data, res.WhereValues[k]...) + _, err := tx.Exec(res.Query, data...) if err != nil { return err } @@ -207,3 +163,38 @@ func (e Encryption) Migrate(c *DatabaseConnection) error { return nil } + +func (e Encryption) getPrimitive() (tink.AEAD, error) { + serializedKeyset, err := base64.StdEncoding.DecodeString(e.Key) + if err != nil { + return nil, err + } + + binaryReader := keyset.NewBinaryReader(bytes.NewBuffer(serializedKeyset)) + parsedHandle, err := insecurecleartextkeyset.Read(binaryReader) + if err != nil { + return nil, err + } + + return aead.New(parsedHandle) +} + +func (e Encryption) generateKey() error { + handle, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) + if err != nil { + return err + } + + buff := &bytes.Buffer{} + err = insecurecleartextkeyset.Write(handle, keyset.NewBinaryWriter(buff)) + if err != nil { + return err + } + + e.Key = base64.StdEncoding.EncodeToString(buff.Bytes()) + return e.saveKeyToFile() +} + +func (e Encryption) saveKeyToFile() error { + return os.WriteFile(e.Path, []byte(e.Key), 0o644) +} diff --git a/encryption/encryption_test.go b/encryption/encryption_test.go index 295b36325f..0897809949 100644 --- a/encryption/encryption_test.go +++ b/encryption/encryption_test.go @@ -1,24 +1,25 @@ package encryption import ( + "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestNew(t *testing.T) { - testPath := "/Users/jiri.ctvrtka/test.key" - secret := "secret" +func TestEncryption(t *testing.T) { + testPath := "/srv/pmm-encryption.key" + secret := "password1" e, err := New(testPath) require.NoError(t, err) assert.Equal(t, testPath, e.Path) assert.NotEmpty(t, e.Key) - cipherText, err := e.encrypt(secret) + cipherText, err := e.Encrypt(secret) require.NoError(t, err) assert.NotEmpty(t, cipherText) - decryptedSecret, err := e.decrypt(cipherText) + decryptedSecret, err := e.Decrypt(cipherText) require.NoError(t, err) assert.Equal(t, secret, decryptedSecret) @@ -37,5 +38,7 @@ func TestNew(t *testing.T) { }, } - assert.NoError(t, e.Migrate(c)) + ctx := context.Background() + assert.NoError(t, e.EncryptDB(ctx, c)) + assert.NoError(t, e.DecryptDB(ctx, c)) } diff --git a/encryption/helpers.go b/encryption/helpers.go new file mode 100644 index 0000000000..1ede556e93 --- /dev/null +++ b/encryption/helpers.go @@ -0,0 +1,30 @@ +package encryption + +import ( + "database/sql" + "fmt" +) + +func prepareRowPointers(rows *sql.Rows) ([]any, error) { + columnTypes, err := rows.ColumnTypes() + if err != nil { + return nil, err + } + columns := make(map[string]string) + for _, columnType := range columnTypes { + columns[columnType.Name()] = columnType.DatabaseTypeName() + } + + row := []any{} + for _, t := range columns { + switch t { + case "VARCHAR": + row = append(row, new(string)) + default: + // TODO support more identificators types + return nil, fmt.Errorf("unsupported identificator type %s", t) + } + } + + return row, nil +} diff --git a/encryption/models.go b/encryption/models.go new file mode 100644 index 0000000000..bf6f46e07b --- /dev/null +++ b/encryption/models.go @@ -0,0 +1,25 @@ +package encryption + +type Encryption struct { + Path string + Key string +} + +type DatabaseConnection struct { + Host, User, Password string + Port int16 + SSLMode string + EncryptedItems []EncryptedItem +} + +type EncryptedItem struct { + Database, Table string + Identificators []string + Columns []string +} + +type QueryValues struct { + Query string + SetValues [][]any + WhereValues [][]any +} From 01a10042e26c4114f81572aa0479b204426510cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 28 May 2024 14:28:56 +0200 Subject: [PATCH 007/215] PMM-13129 Encryption test workflow. --- .github/workflows/encryption.yml | 108 +++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 .github/workflows/encryption.yml diff --git a/.github/workflows/encryption.yml b/.github/workflows/encryption.yml new file mode 100644 index 0000000000..dd454523a8 --- /dev/null +++ b/.github/workflows/encryption.yml @@ -0,0 +1,108 @@ +name: 'PMM Encryption' + +on: + push: + branches: + - v3 + - pmm-* + tags: + - v[0-9]+.[0-9]+.[0-9]+* + + pull_request: + paths-ignore: + - "admin/**" + - "agent/**" + - "api-tests/**" + - "cli-tests/**" + - "docs/**" + - "managed/**" + - "qan-api2/**" + - "update/**" + - "vmproxy/**" + +jobs: + test: + name: Tests + runs-on: ubuntu-22.04 + strategy: + matrix: + images: + - { postgres: 'postgres:10', pmm_server: 'perconalab/pmm-server:3-dev-latest' } + - { postgres: 'postgres:11', pmm_server: 'perconalab/pmm-server:3-dev-latest' } + - { postgres: 'postgres:12', pmm_server: 'perconalab/pmm-server:3-dev-latest' } + - { postgres: 'postgres:13', pmm_server: 'perconalab/pmm-server:3-dev-latest' } + + # Percona + latest PMM Server release + - { postgres: 'perconalab/percona-distribution-postgresql:11', pg_libs: 'pg_stat_statements,pg_stat_monitor', pmm_server: 'perconalab/pmm-server:3-dev-latest' } + - { postgres: 'perconalab/percona-distribution-postgresql:12.8-pg_stat', pg_libs: 'pg_stat_statements,pg_stat_monitor', pmm_server: 'perconalab/pmm-server:3-dev-latest' } + - { postgres: 'perconalab/percona-distribution-postgresql:13.5-pg_stat', pg_libs: 'pg_stat_statements,pg_stat_monitor', pmm_server: 'perconalab/pmm-server:3-dev-latest' } + + - { postgres: 'postgres:9.4', pmm_server: 'perconalab/pmm-server:3-dev-latest' } + - { postgres: 'postgres:9.5', pmm_server: 'perconalab/pmm-server:3-dev-latest' } + - { postgres: 'postgres:9.6', pmm_server: 'perconalab/pmm-server:3-dev-latest' } + + continue-on-error: true + + env: + POSTGRES_IMAGE: ${{ matrix.images.postgres }} + PMM_SERVER_IMAGE: ${{ matrix.images.pmm_server }} + PG_PRELOADED_LIBS: ${{ matrix.images.pg_libs }} + + defaults: + run: + working-directory: ${{ github.workspace }}/encryption + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go release + uses: actions/setup-go@v5 + with: + go-version-file: ${{ github.workspace }}/go.mod + cache: false + + - name: Enable Go build cache + uses: actions/cache@v4 + with: + path: ~/.cache/go-build + key: ${{ runner.os }}-go-build-${{ github.ref }}-${{ hashFiles('**') }} + restore-keys: | + ${{ runner.os }}-go-build-${{ github.ref }}- + ${{ runner.os }}-go-build- + + - name: Enable Go modules cache + uses: actions/cache@v4 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-modules-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-go-modules- + + - name: Download Go modules + run: go mod download -x + + - name: Build and install + run: make install + + - name: Launch containers + env: + ENV_UP_FLAGS: "--detach" + run: go test ./... + + - name: Upload coverage results + uses: codecov/codecov-action@v4 + with: + file: cover.out + flags: encryption + env_vars: POSTGRES_IMAGE,PMM_SERVER_IMAGE + fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Run debug commands on failure + if: ${{ failure() }} + run: | + env + go version + go env + pwd + git status From ed5fdbbbd0efb397b0e167e07850047b933c6f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 28 May 2024 14:30:36 +0200 Subject: [PATCH 008/215] PMM-13129 Remove install. --- .github/workflows/encryption.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/encryption.yml b/.github/workflows/encryption.yml index dd454523a8..ef47d299c3 100644 --- a/.github/workflows/encryption.yml +++ b/.github/workflows/encryption.yml @@ -81,9 +81,6 @@ jobs: - name: Download Go modules run: go mod download -x - - name: Build and install - run: make install - - name: Launch containers env: ENV_UP_FLAGS: "--detach" From 767b555c747e45ac9aee05c9e8b0ba5b16aa864a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 5 Jun 2024 15:01:25 +0200 Subject: [PATCH 009/215] PMM-13129 Encrypt/Decrypt agents. --- managed/models/agent_helpers.go | 12 ++++++++++- managed/models/agent_model.go | 37 +++++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index d509bdf435..1bd99dab79 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -28,6 +28,7 @@ import ( "google.golang.org/grpc/status" "gopkg.in/reform.v1" + "github.com/percona/pmm/encryption" "github.com/percona/pmm/version" ) @@ -865,6 +866,15 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara } } + enc, err := encryption.New("/srv/pmm-encrytion.key") + if err != nil { + return nil, err + } + encryptedPassword, err := enc.Encrypt(params.Password) + if err != nil { + fmt.Println(err) + } + row := &Agent{ AgentID: id, AgentType: agentType, @@ -872,7 +882,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara ServiceID: pointer.ToStringOrNil(params.ServiceID), NodeID: pointer.ToStringOrNil(params.NodeID), Username: pointer.ToStringOrNil(params.Username), - Password: pointer.ToStringOrNil(params.Password), + Password: pointer.ToStringOrNil(encryptedPassword), AgentPassword: pointer.ToStringOrNil(params.AgentPassword), TLS: params.TLS, TLSSkipVerify: params.TLSSkipVerify, diff --git a/managed/models/agent_model.go b/managed/models/agent_model.go index bd7014b303..954f415720 100644 --- a/managed/models/agent_model.go +++ b/managed/models/agent_model.go @@ -33,6 +33,7 @@ import ( "golang.org/x/crypto/bcrypt" "gopkg.in/reform.v1" + "github.com/percona/pmm/encryption" "github.com/percona/pmm/version" ) @@ -320,6 +321,16 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair) s username := pointer.GetString(s.Username) password := pointer.GetString(s.Password) + enc, err := encryption.New("/srv/pmm-encrytion.key") + if err != nil { + return "" + } + + decryptedPassword, err := enc.Decrypt(password) + if err != nil { + fmt.Println(err) + } + if tdp == nil { tdp = s.TemplateDelimiters(service) } @@ -328,7 +339,7 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair) s case MySQLdExporterType: cfg := mysql.NewConfig() cfg.User = username - cfg.Passwd = password + cfg.Passwd = decryptedPassword cfg.Net = unix cfg.Addr = socket if socket == "" { @@ -359,7 +370,7 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair) s case QANMySQLPerfSchemaAgentType, QANMySQLSlowlogAgentType: cfg := mysql.NewConfig() cfg.User = username - cfg.Passwd = password + cfg.Passwd = decryptedPassword cfg.Net = unix cfg.Addr = socket if socket == "" { @@ -394,7 +405,7 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair) s case ProxySQLExporterType: cfg := mysql.NewConfig() cfg.User = username - cfg.Passwd = password + cfg.Passwd = decryptedPassword cfg.Net = unix cfg.Addr = socket if socket == "" { @@ -475,7 +486,7 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair) s } switch { case password != "": - u.User = url.UserPassword(username, password) + u.User = url.UserPassword(username, decryptedPassword) case username != "": u.User = url.User(username) } @@ -536,7 +547,7 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair) s } switch { case password != "": - u.User = url.UserPassword(username, password) + u.User = url.UserPassword(username, decryptedPassword) case username != "": u.User = url.User(username) } @@ -665,10 +676,24 @@ func (s Agent) Files() map[string]string { // TemplateDelimiters returns a pair of safe template delimiters that are not present in agent parameters. func (s Agent) TemplateDelimiters(svc *Service) *DelimiterPair { + enc, err := encryption.New("/srv/pmm-encrytion.key") + if err != nil { + return nil + } + + enc, err = encryption.New("/srv/pmm-encrytion.key") + if err != nil { + return nil + } + decryptedPassword, err := enc.Decrypt(pointer.GetString(s.Password)) + if err != nil { + fmt.Println(err) + } + templateParams := []string{ pointer.GetString(svc.Address), pointer.GetString(s.Username), - pointer.GetString(s.Password), + decryptedPassword, pointer.GetString(s.MetricsPath), } From 81a074b13d18270c1f134b9eae134969bdcc81f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 10 Jun 2024 10:27:21 +0200 Subject: [PATCH 010/215] PMM-13129 Changes. --- encryption/encryption.go | 124 ++++++++++++++++---------------- encryption/encryption_test.go | 16 ++--- encryption/helpers.go | 43 +++++++++++ encryption/models.go | 7 +- managed/cmd/pmm-managed/main.go | 15 +++- managed/models/agent_helpers.go | 8 +-- managed/models/agent_model.go | 23 ++---- 7 files changed, 138 insertions(+), 98 deletions(-) diff --git a/encryption/encryption.go b/encryption/encryption.go index 87e3b94037..beea483ea3 100644 --- a/encryption/encryption.go +++ b/encryption/encryption.go @@ -1,19 +1,49 @@ package encryption import ( - "bytes" "context" "encoding/base64" "errors" "os" + "sync" +) + +const DefaultEncryptionKeyPath = "/srv/pmm-encryption.key" - "github.com/google/tink/go/aead" - "github.com/google/tink/go/insecurecleartextkeyset" - "github.com/google/tink/go/keyset" - "github.com/google/tink/go/tink" +var ( + config *Encryption + configMtx sync.RWMutex ) -func New(keyPath string) (*Encryption, error) { +func Init(keyPath string) error { + err := create(keyPath) + if err != nil { + return err + } + + return nil +} + +func InitFromEnv() error { + encryption := os.Getenv("PMM_ENCRYPTION") + if encryption == "0" { + return nil + } + + keyPath := os.Getenv("PMM_ENCRYPTION_KEY") + if keyPath == "" { + keyPath = DefaultEncryptionKeyPath + } + + err := create(keyPath) + if err != nil { + return err + } + + return nil +} + +func create(keyPath string) error { e := new(Encryption) e.Path = keyPath @@ -22,31 +52,40 @@ func New(keyPath string) (*Encryption, error) { case os.IsNotExist(err): err = e.generateKey() if err != nil { - return nil, err + return err } case err != nil: - return nil, err + return err default: e.Key = string(bytes) } - return e, nil -} - -func (e Encryption) Encrypt(secret string) (string, error) { primitive, err := e.getPrimitive() if err != nil { - return "", err + return err } + e.Primitive = primitive + + configMtx.Lock() + config = e + configMtx.Unlock() + + return nil +} + +func Encrypt(secret string) (string, error) { + configMtx.RLock() + primitive := config.Primitive + configMtx.RUnlock() cipherText, err := primitive.Encrypt([]byte(secret), []byte("")) if err != nil { - return "", err + return secret, err } return base64.StdEncoding.EncodeToString(cipherText), nil } -func (e Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection) error { +func EncryptDB(ctx context.Context, c *DatabaseConnection) error { db, err := c.Connect() if err != nil { return err @@ -70,7 +109,7 @@ func (e Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection) error for k, v := range res.SetValues { for i, val := range v { - encrypted, err := e.Encrypt(*val.(*string)) + encrypted, err := Encrypt(*val.(*string)) if err != nil { return err } @@ -93,26 +132,24 @@ func (e Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection) error return nil } -func (e Encryption) Decrypt(cipherText string) (string, error) { - primitive, err := e.getPrimitive() - if err != nil { - return "", err - } - +func Decrypt(cipherText string) (string, error) { decoded, err := base64.StdEncoding.DecodeString(cipherText) if err != nil { - return "", err + return cipherText, err } + configMtx.RLock() + primitive := config.Primitive + configMtx.RUnlock() secret, err := primitive.Decrypt([]byte(decoded), []byte("")) if err != nil { - return "", err + return cipherText, err } return string(secret), nil } -func (e Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection) error { +func DecryptDB(ctx context.Context, c *DatabaseConnection) error { db, err := c.Connect() if err != nil { return err @@ -141,7 +178,7 @@ func (e Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection) error if err != nil { return err } - decrypted, err := e.Decrypt(string(decoded)) + decrypted, err := Decrypt(string(decoded)) if err != nil { return err } @@ -163,38 +200,3 @@ func (e Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection) error return nil } - -func (e Encryption) getPrimitive() (tink.AEAD, error) { - serializedKeyset, err := base64.StdEncoding.DecodeString(e.Key) - if err != nil { - return nil, err - } - - binaryReader := keyset.NewBinaryReader(bytes.NewBuffer(serializedKeyset)) - parsedHandle, err := insecurecleartextkeyset.Read(binaryReader) - if err != nil { - return nil, err - } - - return aead.New(parsedHandle) -} - -func (e Encryption) generateKey() error { - handle, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) - if err != nil { - return err - } - - buff := &bytes.Buffer{} - err = insecurecleartextkeyset.Write(handle, keyset.NewBinaryWriter(buff)) - if err != nil { - return err - } - - e.Key = base64.StdEncoding.EncodeToString(buff.Bytes()) - return e.saveKeyToFile() -} - -func (e Encryption) saveKeyToFile() error { - return os.WriteFile(e.Path, []byte(e.Key), 0o644) -} diff --git a/encryption/encryption_test.go b/encryption/encryption_test.go index 0897809949..77d4dfb2f7 100644 --- a/encryption/encryption_test.go +++ b/encryption/encryption_test.go @@ -9,17 +9,15 @@ import ( ) func TestEncryption(t *testing.T) { - testPath := "/srv/pmm-encryption.key" + testPath := "/Users/jiri.ctvrtka/pmm-encryption.key" secret := "password1" - e, err := New(testPath) + err := create(testPath) require.NoError(t, err) - assert.Equal(t, testPath, e.Path) - assert.NotEmpty(t, e.Key) - cipherText, err := e.Encrypt(secret) + cipherText, err := Encrypt(secret) require.NoError(t, err) assert.NotEmpty(t, cipherText) - decryptedSecret, err := e.Decrypt(cipherText) + decryptedSecret, err := Decrypt(cipherText) require.NoError(t, err) assert.Equal(t, secret, decryptedSecret) @@ -32,13 +30,13 @@ func TestEncryption(t *testing.T) { { Database: "pmm-agent", Table: "acc", - Identificators: []string{"ID"}, + Identificators: []string{"id"}, Columns: []string{"username", "password"}, }, }, } ctx := context.Background() - assert.NoError(t, e.EncryptDB(ctx, c)) - assert.NoError(t, e.DecryptDB(ctx, c)) + assert.NoError(t, EncryptDB(ctx, c)) + assert.NoError(t, DecryptDB(ctx, c)) } diff --git a/encryption/helpers.go b/encryption/helpers.go index 1ede556e93..c181eed326 100644 --- a/encryption/helpers.go +++ b/encryption/helpers.go @@ -1,8 +1,16 @@ package encryption import ( + "bytes" "database/sql" + "encoding/base64" "fmt" + "os" + + "github.com/google/tink/go/aead" + "github.com/google/tink/go/insecurecleartextkeyset" + "github.com/google/tink/go/keyset" + "github.com/google/tink/go/tink" ) func prepareRowPointers(rows *sql.Rows) ([]any, error) { @@ -28,3 +36,38 @@ func prepareRowPointers(rows *sql.Rows) ([]any, error) { return row, nil } + +func (e *Encryption) getPrimitive() (tink.AEAD, error) { + serializedKeyset, err := base64.StdEncoding.DecodeString(e.Key) + if err != nil { + return nil, err + } + + binaryReader := keyset.NewBinaryReader(bytes.NewBuffer(serializedKeyset)) + parsedHandle, err := insecurecleartextkeyset.Read(binaryReader) + if err != nil { + return nil, err + } + + return aead.New(parsedHandle) +} + +func (e *Encryption) generateKey() error { + handle, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) + if err != nil { + return err + } + + buff := &bytes.Buffer{} + err = insecurecleartextkeyset.Write(handle, keyset.NewBinaryWriter(buff)) + if err != nil { + return err + } + e.Key = base64.StdEncoding.EncodeToString(buff.Bytes()) + + return e.saveKeyToFile() +} + +func (e *Encryption) saveKeyToFile() error { + return os.WriteFile(e.Path, []byte(e.Key), 0o644) +} diff --git a/encryption/models.go b/encryption/models.go index bf6f46e07b..eb043d57eb 100644 --- a/encryption/models.go +++ b/encryption/models.go @@ -1,8 +1,11 @@ package encryption +import "github.com/google/tink/go/tink" + type Encryption struct { - Path string - Key string + Path string + Key string + Primitive tink.AEAD } type DatabaseConnection struct { diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index c6b4a85b70..3c7248eba9 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -74,6 +74,7 @@ import ( "github.com/percona/pmm/api/serverpb" "github.com/percona/pmm/api/uieventspb" "github.com/percona/pmm/api/userpb" + "github.com/percona/pmm/encryption" "github.com/percona/pmm/managed/models" "github.com/percona/pmm/managed/services/agents" agentgrpc "github.com/percona/pmm/managed/services/agents/grpc" @@ -518,6 +519,11 @@ type setupDeps struct { // setup performs setup tasks that depend on database. func setup(ctx context.Context, deps *setupDeps) bool { l := reform.NewPrintfLogger(deps.l.Debugf) + err := encryption.Init(encryption.DefaultEncryptionKeyPath) + if err != nil { + deps.l.Warnf("Failed to setup encryption: %s.", err) + return false + } db := reform.NewDB(deps.sqlDB, postgresql.Dialect, l) // log and ignore validation errors; fail on other errors @@ -771,9 +777,14 @@ func main() { //nolint:cyclop,maintidx } ds := cfg.Config.Services.Telemetry.DataSources + encryptedPassword, err := encryption.Decrypt(*postgresDBPasswordF) + if err != nil { + logrus.Debugf("Encryption: %#v", err) + } + pmmdb := ds.PmmDBSelect pmmdb.Credentials.Username = *postgresDBUsernameF - pmmdb.Credentials.Password = *postgresDBPasswordF + pmmdb.Credentials.Password = encryptedPassword pmmdb.DSN.Scheme = "postgres" // TODO: should be configurable pmmdb.DSN.Host = *postgresAddrF pmmdb.DSN.DB = *postgresDBNameF @@ -888,7 +899,7 @@ func main() { //nolint:cyclop,maintidx Addr: *postgresAddrF, DBName: *postgresDBNameF, DBUsername: *postgresDBUsernameF, - DBPassword: *postgresDBPasswordF, + DBPassword: encryptedPassword, SSLMode: *postgresSSLModeF, SSLCAPath: *postgresSSLCAPathF, SSLKeyPath: *postgresSSLKeyPathF, diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 1bd99dab79..da7df1fad3 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -866,13 +866,9 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara } } - enc, err := encryption.New("/srv/pmm-encrytion.key") + encryptedPassword, err := encryption.Encrypt(params.Password) if err != nil { - return nil, err - } - encryptedPassword, err := enc.Encrypt(params.Password) - if err != nil { - fmt.Println(err) + logrus.Debugf("Encryption: %#v", err) } row := &Agent{ diff --git a/managed/models/agent_model.go b/managed/models/agent_model.go index 954f415720..5ad10fa47a 100644 --- a/managed/models/agent_model.go +++ b/managed/models/agent_model.go @@ -30,6 +30,7 @@ import ( "github.com/go-sql-driver/mysql" "github.com/lib/pq" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "golang.org/x/crypto/bcrypt" "gopkg.in/reform.v1" @@ -321,14 +322,9 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair) s username := pointer.GetString(s.Username) password := pointer.GetString(s.Password) - enc, err := encryption.New("/srv/pmm-encrytion.key") + decryptedPassword, err := encryption.Decrypt(password) if err != nil { - return "" - } - - decryptedPassword, err := enc.Decrypt(password) - if err != nil { - fmt.Println(err) + logrus.Debugf("Encryption: %#v", err) } if tdp == nil { @@ -676,18 +672,9 @@ func (s Agent) Files() map[string]string { // TemplateDelimiters returns a pair of safe template delimiters that are not present in agent parameters. func (s Agent) TemplateDelimiters(svc *Service) *DelimiterPair { - enc, err := encryption.New("/srv/pmm-encrytion.key") - if err != nil { - return nil - } - - enc, err = encryption.New("/srv/pmm-encrytion.key") - if err != nil { - return nil - } - decryptedPassword, err := enc.Decrypt(pointer.GetString(s.Password)) + decryptedPassword, err := encryption.Decrypt(pointer.GetString(s.Password)) if err != nil { - fmt.Println(err) + logrus.Debugf("Encryption: %#v", err) } templateParams := []string{ From 6de482d1542bda36c4b8f1c63ce32b95ab2b4309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 10 Jun 2024 10:59:55 +0200 Subject: [PATCH 011/215] PMM-13145 Fix for tests. --- managed/cmd/pmm-managed/main.go | 2 +- managed/utils/testdb/db.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index 3c7248eba9..a9e4cbb427 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -821,7 +821,7 @@ func main() { //nolint:cyclop,maintidx Address: *postgresAddrF, Name: *postgresDBNameF, Username: *postgresDBUsernameF, - Password: *postgresDBPasswordF, + Password: encryptedPassword, SSLMode: *postgresSSLModeF, SSLCAPath: *postgresSSLCAPathF, SSLKeyPath: *postgresSSLKeyPathF, diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index d5707e367a..e99599ff11 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/percona/pmm/encryption" "github.com/percona/pmm/managed/models" ) @@ -37,6 +38,9 @@ const ( func Open(tb testing.TB, setupFixtures models.SetupFixturesMode, migrationVersion *int) *sql.DB { tb.Helper() + err := encryption.Init(encryption.DefaultEncryptionKeyPath) + require.NoError(tb, err) + setupParams := models.SetupDBParams{ Address: "127.0.0.1:5432", Username: username, From 142c5e591f75a01cd8278a780f49142a38df975a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 10 Jun 2024 11:16:33 +0200 Subject: [PATCH 012/215] PMM-13129 Fix Mongo test. --- managed/services/agents/mongodb_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/managed/services/agents/mongodb_test.go b/managed/services/agents/mongodb_test.go index 8accc04e9e..bda7a910cc 100644 --- a/managed/services/agents/mongodb_test.go +++ b/managed/services/agents/mongodb_test.go @@ -24,6 +24,7 @@ import ( "github.com/percona/pmm/api/agentpb" "github.com/percona/pmm/api/inventorypb" + "github.com/percona/pmm/encryption" "github.com/percona/pmm/managed/models" "github.com/percona/pmm/version" ) @@ -387,6 +388,9 @@ func TestMongodbExporterConfig2411(t *testing.T) { } func TestMongodbExporterConfig(t *testing.T) { + err := encryption.Init(encryption.DefaultEncryptionKeyPath) + require.NoError(t, err) + pmmAgentVersion := version.MustParse("2.0.0") node := &models.Node{ Address: "1.2.3.4", @@ -508,6 +512,9 @@ func TestMongodbExporterConfig(t *testing.T) { } func TestNewMongodbExporterConfig(t *testing.T) { + err := encryption.Init(encryption.DefaultEncryptionKeyPath) + require.NoError(t, err) + pmmAgentVersion := version.MustParse("2.10.0") node := &models.Node{ Address: "1.2.3.4", From a1ba20e5d03e0ccc0141dce061639bd162eb06f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 10 Jun 2024 12:29:13 +0200 Subject: [PATCH 013/215] PMM-13129 Fix. --- managed/cmd/pmm-managed/main.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index a9e4cbb427..7e251e46a4 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -777,14 +777,9 @@ func main() { //nolint:cyclop,maintidx } ds := cfg.Config.Services.Telemetry.DataSources - encryptedPassword, err := encryption.Decrypt(*postgresDBPasswordF) - if err != nil { - logrus.Debugf("Encryption: %#v", err) - } - pmmdb := ds.PmmDBSelect pmmdb.Credentials.Username = *postgresDBUsernameF - pmmdb.Credentials.Password = encryptedPassword + pmmdb.Credentials.Password = *postgresDBPasswordF pmmdb.DSN.Scheme = "postgres" // TODO: should be configurable pmmdb.DSN.Host = *postgresAddrF pmmdb.DSN.DB = *postgresDBNameF @@ -821,7 +816,7 @@ func main() { //nolint:cyclop,maintidx Address: *postgresAddrF, Name: *postgresDBNameF, Username: *postgresDBUsernameF, - Password: encryptedPassword, + Password: *postgresDBPasswordF, SSLMode: *postgresSSLModeF, SSLCAPath: *postgresSSLCAPathF, SSLKeyPath: *postgresSSLKeyPathF, @@ -899,7 +894,7 @@ func main() { //nolint:cyclop,maintidx Addr: *postgresAddrF, DBName: *postgresDBNameF, DBUsername: *postgresDBUsernameF, - DBPassword: encryptedPassword, + DBPassword: *postgresDBPasswordF, SSLMode: *postgresSSLModeF, SSLCAPath: *postgresSSLCAPathF, SSLKeyPath: *postgresSSLKeyPathF, From b9f8cd855b996010b10a9e0e38114991c6239baf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 10 Jun 2024 12:33:35 +0200 Subject: [PATCH 014/215] PMM-13129 Encrypt fixture. --- managed/models/database.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/managed/models/database.go b/managed/models/database.go index 476e8db4c4..7760195c06 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -31,6 +31,7 @@ import ( "strings" "github.com/lib/pq" + "github.com/percona/pmm/encryption" "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -1229,6 +1230,8 @@ func setupFixture1(q *reform.Querier, params SetupDBParams) error { return err } + encryptedPassword, err := encryption.Encrypt(params.Username) + ap := &CreateAgentParams{ PMMAgentID: PMMServerAgentID, ServiceID: service.ServiceID, @@ -1236,7 +1239,7 @@ func setupFixture1(q *reform.Querier, params SetupDBParams) error { TLSSkipVerify: params.SSLMode == DisableSSLMode || params.SSLMode == VerifyCaSSLMode, CommentsParsingDisabled: true, Username: params.Username, - Password: params.Password, + Password: encryptedPassword, } if ap.TLS { ap.PostgreSQLOptions = &PostgreSQLOptions{} From ab2a6414a2c515b19a45284e258d9162f8e48384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 10 Jun 2024 13:02:16 +0200 Subject: [PATCH 015/215] PMM-13129 Encryption test. --- encryption/encryption_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/encryption/encryption_test.go b/encryption/encryption_test.go index 77d4dfb2f7..29dead5165 100644 --- a/encryption/encryption_test.go +++ b/encryption/encryption_test.go @@ -9,10 +9,9 @@ import ( ) func TestEncryption(t *testing.T) { - testPath := "/Users/jiri.ctvrtka/pmm-encryption.key" secret := "password1" - err := create(testPath) + err := create(DefaultEncryptionKeyPath) require.NoError(t, err) cipherText, err := Encrypt(secret) require.NoError(t, err) @@ -24,13 +23,14 @@ func TestEncryption(t *testing.T) { c := &DatabaseConnection{ Host: "127.0.0.1", Port: 5432, - User: "pmm-agent", - Password: "pmm-agent-password", + User: "postgres", + Password: "", + SSLMode: "disable", EncryptedItems: []EncryptedItem{ { Database: "pmm-agent", - Table: "acc", - Identificators: []string{"id"}, + Table: "agents", + Identificators: []string{"agent_id"}, Columns: []string{"username", "password"}, }, }, From c5189423b03ddb1cbef0db91ab12b33795ccd75c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 10 Jun 2024 13:09:34 +0200 Subject: [PATCH 016/215] PMM-13129 File mode test. --- encryption/helpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encryption/helpers.go b/encryption/helpers.go index c181eed326..318cacaed8 100644 --- a/encryption/helpers.go +++ b/encryption/helpers.go @@ -69,5 +69,5 @@ func (e *Encryption) generateKey() error { } func (e *Encryption) saveKeyToFile() error { - return os.WriteFile(e.Path, []byte(e.Key), 0o644) + return os.WriteFile(e.Path, []byte(e.Key), 0777) } From 66996a2d80e796b8103c7ac89007e591dfbf2f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 10 Jun 2024 13:12:38 +0200 Subject: [PATCH 017/215] PMM-13129 Fix credentials for test env. --- encryption/database_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/encryption/database_test.go b/encryption/database_test.go index c42eb9d4a4..95e29ed3f3 100644 --- a/encryption/database_test.go +++ b/encryption/database_test.go @@ -10,8 +10,8 @@ func TestDatabaseConnection_Connect(t *testing.T) { dbConnection := DatabaseConnection{ Host: "127.0.0.1", Port: 5432, - User: "pmm-agent", - Password: "pmm-agent-password", + User: "postgres", + Password: "", } _, err := dbConnection.Connect() assert.NoError(t, err) From 81e22b7bc635ed1d6889e2f912bcc8cc1fc1f1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 10 Jun 2024 15:03:14 +0200 Subject: [PATCH 018/215] PMM-13129 Clean. --- encryption/encryption_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/encryption/encryption_test.go b/encryption/encryption_test.go index 29dead5165..539611a4cd 100644 --- a/encryption/encryption_test.go +++ b/encryption/encryption_test.go @@ -25,7 +25,6 @@ func TestEncryption(t *testing.T) { Port: 5432, User: "postgres", Password: "", - SSLMode: "disable", EncryptedItems: []EncryptedItem{ { Database: "pmm-agent", From c386d1d6f096a52f3f8a96edbb2c6ca3fa8ad6d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 10 Jun 2024 15:08:02 +0200 Subject: [PATCH 019/215] PMM-13129 Correct DB for encryption test. --- encryption/encryption_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encryption/encryption_test.go b/encryption/encryption_test.go index 539611a4cd..84f1ea6925 100644 --- a/encryption/encryption_test.go +++ b/encryption/encryption_test.go @@ -27,7 +27,7 @@ func TestEncryption(t *testing.T) { Password: "", EncryptedItems: []EncryptedItem{ { - Database: "pmm-agent", + Database: "pmm-managed", Table: "agents", Identificators: []string{"agent_id"}, Columns: []string{"username", "password"}, From cb200b11b741da9fe196e8d0c94084c59f8d7649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 12 Jun 2024 09:13:54 +0200 Subject: [PATCH 020/215] PMM-13129 Moved to utils folder. --- .github/workflows/encryption.yml | 105 ------------------ managed/cmd/pmm-managed/main.go | 2 +- managed/models/agent_helpers.go | 2 +- managed/models/agent_model.go | 2 +- managed/models/database.go | 2 +- managed/services/agents/mongodb_test.go | 2 +- managed/utils/testdb/db.go | 2 +- {encryption => utils/encryption}/database.go | 0 .../encryption}/database_test.go | 0 .../encryption}/encryption.go | 0 .../encryption}/encryption_test.go | 0 {encryption => utils/encryption}/helpers.go | 0 {encryption => utils/encryption}/models.go | 0 13 files changed, 6 insertions(+), 111 deletions(-) delete mode 100644 .github/workflows/encryption.yml rename {encryption => utils/encryption}/database.go (100%) rename {encryption => utils/encryption}/database_test.go (100%) rename {encryption => utils/encryption}/encryption.go (100%) rename {encryption => utils/encryption}/encryption_test.go (100%) rename {encryption => utils/encryption}/helpers.go (100%) rename {encryption => utils/encryption}/models.go (100%) diff --git a/.github/workflows/encryption.yml b/.github/workflows/encryption.yml deleted file mode 100644 index ef47d299c3..0000000000 --- a/.github/workflows/encryption.yml +++ /dev/null @@ -1,105 +0,0 @@ -name: 'PMM Encryption' - -on: - push: - branches: - - v3 - - pmm-* - tags: - - v[0-9]+.[0-9]+.[0-9]+* - - pull_request: - paths-ignore: - - "admin/**" - - "agent/**" - - "api-tests/**" - - "cli-tests/**" - - "docs/**" - - "managed/**" - - "qan-api2/**" - - "update/**" - - "vmproxy/**" - -jobs: - test: - name: Tests - runs-on: ubuntu-22.04 - strategy: - matrix: - images: - - { postgres: 'postgres:10', pmm_server: 'perconalab/pmm-server:3-dev-latest' } - - { postgres: 'postgres:11', pmm_server: 'perconalab/pmm-server:3-dev-latest' } - - { postgres: 'postgres:12', pmm_server: 'perconalab/pmm-server:3-dev-latest' } - - { postgres: 'postgres:13', pmm_server: 'perconalab/pmm-server:3-dev-latest' } - - # Percona + latest PMM Server release - - { postgres: 'perconalab/percona-distribution-postgresql:11', pg_libs: 'pg_stat_statements,pg_stat_monitor', pmm_server: 'perconalab/pmm-server:3-dev-latest' } - - { postgres: 'perconalab/percona-distribution-postgresql:12.8-pg_stat', pg_libs: 'pg_stat_statements,pg_stat_monitor', pmm_server: 'perconalab/pmm-server:3-dev-latest' } - - { postgres: 'perconalab/percona-distribution-postgresql:13.5-pg_stat', pg_libs: 'pg_stat_statements,pg_stat_monitor', pmm_server: 'perconalab/pmm-server:3-dev-latest' } - - - { postgres: 'postgres:9.4', pmm_server: 'perconalab/pmm-server:3-dev-latest' } - - { postgres: 'postgres:9.5', pmm_server: 'perconalab/pmm-server:3-dev-latest' } - - { postgres: 'postgres:9.6', pmm_server: 'perconalab/pmm-server:3-dev-latest' } - - continue-on-error: true - - env: - POSTGRES_IMAGE: ${{ matrix.images.postgres }} - PMM_SERVER_IMAGE: ${{ matrix.images.pmm_server }} - PG_PRELOADED_LIBS: ${{ matrix.images.pg_libs }} - - defaults: - run: - working-directory: ${{ github.workspace }}/encryption - - steps: - - name: Check out code - uses: actions/checkout@v4 - - - name: Set up Go release - uses: actions/setup-go@v5 - with: - go-version-file: ${{ github.workspace }}/go.mod - cache: false - - - name: Enable Go build cache - uses: actions/cache@v4 - with: - path: ~/.cache/go-build - key: ${{ runner.os }}-go-build-${{ github.ref }}-${{ hashFiles('**') }} - restore-keys: | - ${{ runner.os }}-go-build-${{ github.ref }}- - ${{ runner.os }}-go-build- - - - name: Enable Go modules cache - uses: actions/cache@v4 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-modules-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-go-modules- - - - name: Download Go modules - run: go mod download -x - - - name: Launch containers - env: - ENV_UP_FLAGS: "--detach" - run: go test ./... - - - name: Upload coverage results - uses: codecov/codecov-action@v4 - with: - file: cover.out - flags: encryption - env_vars: POSTGRES_IMAGE,PMM_SERVER_IMAGE - fail_ci_if_error: false - token: ${{ secrets.CODECOV_TOKEN }} - - - name: Run debug commands on failure - if: ${{ failure() }} - run: | - env - go version - go env - pwd - git status diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index 7e251e46a4..e8dba5c34e 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -74,7 +74,6 @@ import ( "github.com/percona/pmm/api/serverpb" "github.com/percona/pmm/api/uieventspb" "github.com/percona/pmm/api/userpb" - "github.com/percona/pmm/encryption" "github.com/percona/pmm/managed/models" "github.com/percona/pmm/managed/services/agents" agentgrpc "github.com/percona/pmm/managed/services/agents/grpc" @@ -108,6 +107,7 @@ import ( "github.com/percona/pmm/managed/utils/envvars" "github.com/percona/pmm/managed/utils/interceptors" platformClient "github.com/percona/pmm/managed/utils/platform" + "github.com/percona/pmm/utils/encryption" pmmerrors "github.com/percona/pmm/utils/errors" "github.com/percona/pmm/utils/logger" "github.com/percona/pmm/utils/sqlmetrics" diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 0951ea4d86..2e88d19d52 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -28,7 +28,7 @@ import ( "google.golang.org/grpc/status" "gopkg.in/reform.v1" - "github.com/percona/pmm/encryption" + "github.com/percona/pmm/utils/encryption" "github.com/percona/pmm/version" ) diff --git a/managed/models/agent_model.go b/managed/models/agent_model.go index 13565fa54a..dd2fd67d05 100644 --- a/managed/models/agent_model.go +++ b/managed/models/agent_model.go @@ -34,7 +34,7 @@ import ( "golang.org/x/crypto/bcrypt" "gopkg.in/reform.v1" - "github.com/percona/pmm/encryption" + "github.com/percona/pmm/utils/encryption" "github.com/percona/pmm/version" ) diff --git a/managed/models/database.go b/managed/models/database.go index 7760195c06..bf0bcf9795 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -31,7 +31,7 @@ import ( "strings" "github.com/lib/pq" - "github.com/percona/pmm/encryption" + "github.com/percona/pmm/utils/encryption" "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" diff --git a/managed/services/agents/mongodb_test.go b/managed/services/agents/mongodb_test.go index bda7a910cc..6ee2f140cd 100644 --- a/managed/services/agents/mongodb_test.go +++ b/managed/services/agents/mongodb_test.go @@ -24,8 +24,8 @@ import ( "github.com/percona/pmm/api/agentpb" "github.com/percona/pmm/api/inventorypb" - "github.com/percona/pmm/encryption" "github.com/percona/pmm/managed/models" + "github.com/percona/pmm/utils/encryption" "github.com/percona/pmm/version" ) diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index e99599ff11..e5bda845de 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -25,8 +25,8 @@ import ( "github.com/stretchr/testify/require" - "github.com/percona/pmm/encryption" "github.com/percona/pmm/managed/models" + "github.com/percona/pmm/utils/encryption" ) const ( diff --git a/encryption/database.go b/utils/encryption/database.go similarity index 100% rename from encryption/database.go rename to utils/encryption/database.go diff --git a/encryption/database_test.go b/utils/encryption/database_test.go similarity index 100% rename from encryption/database_test.go rename to utils/encryption/database_test.go diff --git a/encryption/encryption.go b/utils/encryption/encryption.go similarity index 100% rename from encryption/encryption.go rename to utils/encryption/encryption.go diff --git a/encryption/encryption_test.go b/utils/encryption/encryption_test.go similarity index 100% rename from encryption/encryption_test.go rename to utils/encryption/encryption_test.go diff --git a/encryption/helpers.go b/utils/encryption/helpers.go similarity index 100% rename from encryption/helpers.go rename to utils/encryption/helpers.go diff --git a/encryption/models.go b/utils/encryption/models.go similarity index 100% rename from encryption/models.go rename to utils/encryption/models.go From f953b92bb7c1184f239159e4889db4014225cc63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 12 Jun 2024 09:27:00 +0200 Subject: [PATCH 021/215] PMM-13129 Empty password fix. --- utils/encryption/database.go | 6 +++++- utils/encryption/database_test.go | 7 ++++--- utils/encryption/encryption_test.go | 9 ++++----- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/utils/encryption/database.go b/utils/encryption/database.go index 3a341a2b57..9c1754417e 100644 --- a/utils/encryption/database.go +++ b/utils/encryption/database.go @@ -27,7 +27,11 @@ func (c DatabaseConnection) DSN() string { c.SSLMode = "disable" } - return fmt.Sprintf("host=%s port=%d user=%s password=%s sslmode=%s", c.Host, c.Port, c.User, c.Password, c.SSLMode) + if c.Password != "" { + c.Password = fmt.Sprintf("password=%s", c.Password) + } + + return fmt.Sprintf("host=%s port=%d user=%s %s sslmode=%s", c.Host, c.Port, c.User, c.Password, c.SSLMode) } func (item EncryptedItem) Read(tx *sql.Tx) (*QueryValues, error) { diff --git a/utils/encryption/database_test.go b/utils/encryption/database_test.go index 95e29ed3f3..1cc0689497 100644 --- a/utils/encryption/database_test.go +++ b/utils/encryption/database_test.go @@ -3,7 +3,7 @@ package encryption import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestDatabaseConnection_Connect(t *testing.T) { @@ -13,6 +13,7 @@ func TestDatabaseConnection_Connect(t *testing.T) { User: "postgres", Password: "", } - _, err := dbConnection.Connect() - assert.NoError(t, err) + c, err := dbConnection.Connect() + require.NoError(t, err) + require.NoError(t, c.Close()) } diff --git a/utils/encryption/encryption_test.go b/utils/encryption/encryption_test.go index 84f1ea6925..9b0cef4d17 100644 --- a/utils/encryption/encryption_test.go +++ b/utils/encryption/encryption_test.go @@ -4,7 +4,6 @@ import ( "context" "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -15,10 +14,10 @@ func TestEncryption(t *testing.T) { require.NoError(t, err) cipherText, err := Encrypt(secret) require.NoError(t, err) - assert.NotEmpty(t, cipherText) + require.NotEmpty(t, cipherText) decryptedSecret, err := Decrypt(cipherText) require.NoError(t, err) - assert.Equal(t, secret, decryptedSecret) + require.Equal(t, secret, decryptedSecret) c := &DatabaseConnection{ Host: "127.0.0.1", @@ -36,6 +35,6 @@ func TestEncryption(t *testing.T) { } ctx := context.Background() - assert.NoError(t, EncryptDB(ctx, c)) - assert.NoError(t, DecryptDB(ctx, c)) + require.NoError(t, EncryptDB(ctx, c)) + require.NoError(t, DecryptDB(ctx, c)) } From 70e9634f3aefff05ecf1509eb30435618a1f2017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 12 Jun 2024 09:46:28 +0200 Subject: [PATCH 022/215] PMM-13129 Debug logs to warning level. --- managed/models/agent_helpers.go | 2 +- managed/models/agent_model.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 2e88d19d52..ff85f1a65a 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -886,7 +886,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara encryptedPassword, err := encryption.Encrypt(params.Password) if err != nil { - logrus.Debugf("Encryption: %#v", err) + logrus.Warningf("Encryption: %#v", err) } row := &Agent{ diff --git a/managed/models/agent_model.go b/managed/models/agent_model.go index dd2fd67d05..88a0fcaef5 100644 --- a/managed/models/agent_model.go +++ b/managed/models/agent_model.go @@ -326,7 +326,7 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p decryptedPassword, err := encryption.Decrypt(password) if err != nil { - logrus.Debugf("Encryption: %#v", err) + logrus.Warningf("Encryption: %#v", err) } if tdp == nil { @@ -684,7 +684,7 @@ func (s Agent) Files() map[string]string { func (s Agent) TemplateDelimiters(svc *Service) *DelimiterPair { decryptedPassword, err := encryption.Decrypt(pointer.GetString(s.Password)) if err != nil { - logrus.Debugf("Encryption: %#v", err) + logrus.Warningf("Encryption: %#v", err) } templateParams := []string{ From bccf86e362cbcfe8df3f8e96998bf5631ebe7d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 12 Jun 2024 09:47:27 +0200 Subject: [PATCH 023/215] PMM-13129 Format. --- managed/models/database.go | 3 ++- utils/encryption/helpers.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index bf0bcf9795..f6833393d6 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -31,12 +31,13 @@ import ( "strings" "github.com/lib/pq" - "github.com/percona/pmm/utils/encryption" "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "gopkg.in/reform.v1" "gopkg.in/reform.v1/dialects/postgresql" + + "github.com/percona/pmm/utils/encryption" ) const ( diff --git a/utils/encryption/helpers.go b/utils/encryption/helpers.go index 318cacaed8..c0110c24a4 100644 --- a/utils/encryption/helpers.go +++ b/utils/encryption/helpers.go @@ -69,5 +69,5 @@ func (e *Encryption) generateKey() error { } func (e *Encryption) saveKeyToFile() error { - return os.WriteFile(e.Path, []byte(e.Key), 0777) + return os.WriteFile(e.Path, []byte(e.Key), 0o777) } From 3e64fbae72a2b7d17ad0eb01982a5c94d2eeac5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Fri, 14 Jun 2024 10:18:50 +0200 Subject: [PATCH 024/215] PMM-13129 Small change in generated query. --- utils/encryption/database.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/encryption/database.go b/utils/encryption/database.go index 9c1754417e..af9738df37 100644 --- a/utils/encryption/database.go +++ b/utils/encryption/database.go @@ -36,7 +36,7 @@ func (c DatabaseConnection) DSN() string { func (item EncryptedItem) Read(tx *sql.Tx) (*QueryValues, error) { what := append(item.Identificators, item.Columns...) - query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ","), item.Table) + query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ", "), item.Table) rows, err := tx.Query(query) if err != nil { return nil, err From 917de87bc25f1db2fb4ab8593253d48a3eb3466d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 17 Jun 2024 10:57:59 +0200 Subject: [PATCH 025/215] PMM-13129 Password set check. --- managed/models/database.go | 6 +----- managed/services/management/agent.go | 9 ++++++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index f6833393d6..476e8db4c4 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -36,8 +36,6 @@ import ( "google.golang.org/grpc/status" "gopkg.in/reform.v1" "gopkg.in/reform.v1/dialects/postgresql" - - "github.com/percona/pmm/utils/encryption" ) const ( @@ -1231,8 +1229,6 @@ func setupFixture1(q *reform.Querier, params SetupDBParams) error { return err } - encryptedPassword, err := encryption.Encrypt(params.Username) - ap := &CreateAgentParams{ PMMAgentID: PMMServerAgentID, ServiceID: service.ServiceID, @@ -1240,7 +1236,7 @@ func setupFixture1(q *reform.Querier, params SetupDBParams) error { TLSSkipVerify: params.SSLMode == DisableSSLMode || params.SSLMode == VerifyCaSSLMode, CommentsParsingDisabled: true, Username: params.Username, - Password: encryptedPassword, + Password: params.Password, } if ap.TLS { ap.PostgreSQLOptions = &PostgreSQLOptions{} diff --git a/managed/services/management/agent.go b/managed/services/management/agent.go index 6e55fecac7..73e10a1339 100644 --- a/managed/services/management/agent.go +++ b/managed/services/management/agent.go @@ -19,6 +19,7 @@ import ( "context" "github.com/AlekSi/pointer" + "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" @@ -26,6 +27,7 @@ import ( agentv1beta1 "github.com/percona/pmm/api/managementpb/agent" "github.com/percona/pmm/managed/models" + "github.com/percona/pmm/utils/encryption" ) // AgentService represents service for working with agents. @@ -134,6 +136,11 @@ func (s *AgentService) agentToAPI(agent *models.Agent) (*agentv1beta1.UniversalA return nil, err } + decryptedPassword, err := encryption.Decrypt(pointer.GetString(agent.Password)) + if err != nil { + logrus.Warningf("Encryption: %#v", err) + } + ua := &agentv1beta1.UniversalAgent{ AgentId: agent.AgentID, AgentType: string(agent.AgentType), @@ -143,7 +150,7 @@ func (s *AgentService) agentToAPI(agent *models.Agent) (*agentv1beta1.UniversalA Disabled: agent.Disabled, DisabledCollectors: agent.DisabledCollectors, IsConnected: s.r.IsConnected(agent.AgentID), - IsAgentPasswordSet: agent.AgentPassword != nil, + IsAgentPasswordSet: decryptedPassword != "", IsAwsSecretKeySet: agent.AWSSecretKey != nil, IsPasswordSet: agent.Password != nil, ListenPort: uint32(pointer.GetUint16(agent.ListenPort)), From 6146958bc8688e44b27f0abcb35ef7d7111515af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 17 Jun 2024 12:17:56 +0200 Subject: [PATCH 026/215] PMM-13129 Fix wrong field. --- managed/services/management/agent.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/managed/services/management/agent.go b/managed/services/management/agent.go index 73e10a1339..d8105ff1b5 100644 --- a/managed/services/management/agent.go +++ b/managed/services/management/agent.go @@ -150,9 +150,9 @@ func (s *AgentService) agentToAPI(agent *models.Agent) (*agentv1beta1.UniversalA Disabled: agent.Disabled, DisabledCollectors: agent.DisabledCollectors, IsConnected: s.r.IsConnected(agent.AgentID), - IsAgentPasswordSet: decryptedPassword != "", + IsAgentPasswordSet: agent.AgentPassword != nil, IsAwsSecretKeySet: agent.AWSSecretKey != nil, - IsPasswordSet: agent.Password != nil, + IsPasswordSet: decryptedPassword != "", ListenPort: uint32(pointer.GetUint16(agent.ListenPort)), LogLevel: pointer.GetString(agent.LogLevel), MaxQueryLength: agent.MaxQueryLength, From cf2c40f091a35735f117fda2fadfee22b24b3968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 19 Jun 2024 10:26:36 +0200 Subject: [PATCH 027/215] PMM-13129 Init in migration. --- managed/cmd/pmm-managed/main.go | 6 ------ managed/models/database.go | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index 397806b7e2..85b0c512f7 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -107,7 +107,6 @@ import ( "github.com/percona/pmm/managed/utils/envvars" "github.com/percona/pmm/managed/utils/interceptors" platformClient "github.com/percona/pmm/managed/utils/platform" - "github.com/percona/pmm/utils/encryption" pmmerrors "github.com/percona/pmm/utils/errors" "github.com/percona/pmm/utils/logger" "github.com/percona/pmm/utils/sqlmetrics" @@ -520,11 +519,6 @@ type setupDeps struct { // setup performs setup tasks that depend on database. func setup(ctx context.Context, deps *setupDeps) bool { l := reform.NewPrintfLogger(deps.l.Debugf) - err := encryption.Init(encryption.DefaultEncryptionKeyPath) - if err != nil { - deps.l.Warnf("Failed to setup encryption: %s.", err) - return false - } db := reform.NewDB(deps.sqlDB, postgresql.Dialect, l) // log and ignore validation errors; fail on other errors diff --git a/managed/models/database.go b/managed/models/database.go index 476e8db4c4..588f6ac29a 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -31,6 +31,7 @@ import ( "strings" "github.com/lib/pq" + "github.com/percona/pmm/utils/encryption" "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -1045,6 +1046,11 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. logger = reform.NewPrintfLogger(params.Logf) } + err := encryption.Init(encryption.DefaultEncryptionKeyPath) + if err != nil { + return nil, err + } + db := reform.NewDB(sqlDB, postgresql.Dialect, logger) errCV := checkVersion(ctx, db) if pErr, ok := errCV.(*pq.Error); ok && pErr.Code == "28000" { //nolint:errorlint From 38beab999bcd5f85357822b5228adc1d6548ee9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 20 Jun 2024 11:25:06 +0200 Subject: [PATCH 028/215] PMM-13129 Precheck if already encrypted, moved into managed utils. --- managed/models/agent_helpers.go | 2 +- managed/models/agent_model.go | 2 +- managed/services/agents/mongodb_test.go | 2 +- managed/services/management/agent.go | 2 +- {utils => managed/utils}/encryption/database.go | 5 ++++- {utils => managed/utils}/encryption/encryption.go | 8 +++++++- {utils => managed/utils}/encryption/helpers.go | 0 {utils => managed/utils}/encryption/models.go | 3 +++ managed/utils/testdb/db.go | 2 +- 9 files changed, 19 insertions(+), 7 deletions(-) rename {utils => managed/utils}/encryption/database.go (89%) rename {utils => managed/utils}/encryption/encryption.go (95%) rename {utils => managed/utils}/encryption/helpers.go (100%) rename {utils => managed/utils}/encryption/models.go (85%) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index ff85f1a65a..817929556a 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -28,7 +28,7 @@ import ( "google.golang.org/grpc/status" "gopkg.in/reform.v1" - "github.com/percona/pmm/utils/encryption" + "github.com/percona/pmm/managed/utils/encryption" "github.com/percona/pmm/version" ) diff --git a/managed/models/agent_model.go b/managed/models/agent_model.go index 88a0fcaef5..ab289e85b4 100644 --- a/managed/models/agent_model.go +++ b/managed/models/agent_model.go @@ -34,7 +34,7 @@ import ( "golang.org/x/crypto/bcrypt" "gopkg.in/reform.v1" - "github.com/percona/pmm/utils/encryption" + "github.com/percona/pmm/managed/utils/encryption" "github.com/percona/pmm/version" ) diff --git a/managed/services/agents/mongodb_test.go b/managed/services/agents/mongodb_test.go index 6ee2f140cd..cc723d483d 100644 --- a/managed/services/agents/mongodb_test.go +++ b/managed/services/agents/mongodb_test.go @@ -25,7 +25,7 @@ import ( "github.com/percona/pmm/api/agentpb" "github.com/percona/pmm/api/inventorypb" "github.com/percona/pmm/managed/models" - "github.com/percona/pmm/utils/encryption" + "github.com/percona/pmm/managed/utils/encryption" "github.com/percona/pmm/version" ) diff --git a/managed/services/management/agent.go b/managed/services/management/agent.go index d8105ff1b5..03e44db3cf 100644 --- a/managed/services/management/agent.go +++ b/managed/services/management/agent.go @@ -27,7 +27,7 @@ import ( agentv1beta1 "github.com/percona/pmm/api/managementpb/agent" "github.com/percona/pmm/managed/models" - "github.com/percona/pmm/utils/encryption" + "github.com/percona/pmm/managed/utils/encryption" ) // AgentService represents service for working with agents. diff --git a/utils/encryption/database.go b/managed/utils/encryption/database.go similarity index 89% rename from utils/encryption/database.go rename to managed/utils/encryption/database.go index af9738df37..355aeaee1f 100644 --- a/utils/encryption/database.go +++ b/managed/utils/encryption/database.go @@ -31,7 +31,10 @@ func (c DatabaseConnection) DSN() string { c.Password = fmt.Sprintf("password=%s", c.Password) } - return fmt.Sprintf("host=%s port=%d user=%s %s sslmode=%s", c.Host, c.Port, c.User, c.Password, c.SSLMode) + return fmt.Sprintf( + "host=%s port=%d user=%s %s sslmode=%s sslrootcert=%s sslkey=%s sslcert=%s", + c.Host, c.Port, c.User, c.Password, c.SSLMode, c.SSLCAPath, c.SSLKeyPath, c.SSLCertPath, + ) } func (item EncryptedItem) Read(tx *sql.Tx) (*QueryValues, error) { diff --git a/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go similarity index 95% rename from utils/encryption/encryption.go rename to managed/utils/encryption/encryption.go index beea483ea3..9109231acf 100644 --- a/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -109,7 +109,13 @@ func EncryptDB(ctx context.Context, c *DatabaseConnection) error { for k, v := range res.SetValues { for i, val := range v { - encrypted, err := Encrypt(*val.(*string)) + value := *val.(*string) + _, err := base64.StdEncoding.DecodeString(value) + if err == nil { + continue + } + + encrypted, err := Encrypt(value) if err != nil { return err } diff --git a/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go similarity index 100% rename from utils/encryption/helpers.go rename to managed/utils/encryption/helpers.go diff --git a/utils/encryption/models.go b/managed/utils/encryption/models.go similarity index 85% rename from utils/encryption/models.go rename to managed/utils/encryption/models.go index eb043d57eb..3d7b5c5c59 100644 --- a/utils/encryption/models.go +++ b/managed/utils/encryption/models.go @@ -12,6 +12,9 @@ type DatabaseConnection struct { Host, User, Password string Port int16 SSLMode string + SSLCAPath string + SSLKeyPath string + SSLCertPath string EncryptedItems []EncryptedItem } diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index e5bda845de..1591028e39 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -26,7 +26,7 @@ import ( "github.com/stretchr/testify/require" "github.com/percona/pmm/managed/models" - "github.com/percona/pmm/utils/encryption" + "github.com/percona/pmm/managed/utils/encryption" ) const ( From 2846918cbeb4e83cf162484be76219cb6a153721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 20 Jun 2024 13:06:03 +0200 Subject: [PATCH 029/215] PMM-13129 Migration. --- managed/models/database.go | 37 +++++++++++- managed/utils/encryption/database.go | 4 +- .../utils}/encryption/database_test.go | 2 +- managed/utils/encryption/encryption.go | 56 +++++++++++-------- .../utils}/encryption/encryption_test.go | 3 +- managed/utils/encryption/helpers.go | 2 +- managed/utils/encryption/models.go | 1 + 7 files changed, 75 insertions(+), 30 deletions(-) rename {utils => managed/utils}/encryption/database_test.go (85%) rename {utils => managed/utils}/encryption/encryption_test.go (92%) diff --git a/managed/models/database.go b/managed/models/database.go index 588f6ac29a..5013183b60 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -31,12 +31,13 @@ import ( "strings" "github.com/lib/pq" - "github.com/percona/pmm/utils/encryption" "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "gopkg.in/reform.v1" "gopkg.in/reform.v1/dialects/postgresql" + + "github.com/percona/pmm/managed/utils/encryption" ) const ( @@ -1068,6 +1069,40 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. if err := migrateDB(db, params); err != nil { return nil, err } + + host, p, err := net.SplitHostPort(params.Address) + if err != nil { + return nil, err + } + + port, err := strconv.ParseInt(p, 10, 16) + if err != nil { + return nil, err + } + + c := &encryption.DatabaseConnection{ + Host: host, + Port: int16(port), + User: params.Username, + Password: params.Password, + SSLMode: params.SSLMode, + SSLCAPath: params.SSLCAPath, + SSLKeyPath: params.SSLKeyPath, + SSLCertPath: params.SSLCertPath, + EncryptedItems: []encryption.EncryptedItem{ + { + Database: "pmm-managed", + Table: "agents", + Identificators: []string{"agent_id"}, + Columns: []string{"username", "password"}, + }, + }, + } + + if err := encryption.EncryptDB(ctx, c); err != nil { + return nil, err + } + return db, nil } diff --git a/managed/utils/encryption/database.go b/managed/utils/encryption/database.go index 355aeaee1f..67e7787d15 100644 --- a/managed/utils/encryption/database.go +++ b/managed/utils/encryption/database.go @@ -32,8 +32,8 @@ func (c DatabaseConnection) DSN() string { } return fmt.Sprintf( - "host=%s port=%d user=%s %s sslmode=%s sslrootcert=%s sslkey=%s sslcert=%s", - c.Host, c.Port, c.User, c.Password, c.SSLMode, c.SSLCAPath, c.SSLKeyPath, c.SSLCertPath, + "host=%s port=%d dbname=%s user=%s %s sslmode=%s sslrootcert=%s sslkey=%s sslcert=%s", + c.Host, c.Port, c.DBName, c.User, c.Password, c.SSLMode, c.SSLCAPath, c.SSLKeyPath, c.SSLCertPath, ) } diff --git a/utils/encryption/database_test.go b/managed/utils/encryption/database_test.go similarity index 85% rename from utils/encryption/database_test.go rename to managed/utils/encryption/database_test.go index 1cc0689497..9cf97b3b94 100644 --- a/utils/encryption/database_test.go +++ b/managed/utils/encryption/database_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestDatabaseConnection_Connect(t *testing.T) { +func TestDatabaseConnection(t *testing.T) { dbConnection := DatabaseConnection{ Host: "127.0.0.1", Port: 5432, diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 9109231acf..085d5ce3ce 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -2,6 +2,7 @@ package encryption import ( "context" + "database/sql" "encoding/base64" "errors" "os" @@ -86,16 +87,18 @@ func Encrypt(secret string) (string, error) { } func EncryptDB(ctx context.Context, c *DatabaseConnection) error { - db, err := c.Connect() - if err != nil { - return err - } - defer db.Close() - - if len(c.EncryptedItems) == 0 { - return errors.New("DB Connection: Database target tables/columns not defined") - } for _, item := range c.EncryptedItems { + c.DBName = item.Database + db, err := c.Connect() + if err != nil { + return err + } + defer db.Close() + + if len(c.EncryptedItems) == 0 { + return errors.New("DB Connection: Database target tables/columns not defined") + } + tx, err := db.BeginTx(ctx, nil) if err != nil { return err @@ -109,10 +112,16 @@ func EncryptDB(ctx context.Context, c *DatabaseConnection) error { for k, v := range res.SetValues { for i, val := range v { - value := *val.(*string) - _, err := base64.StdEncoding.DecodeString(value) - if err == nil { - continue + var value string + if v, ok := val.(*sql.NullString); ok { + value = v.String + } + + if value != "" { + _, err := base64.StdEncoding.DecodeString(value) + if err == nil { + continue + } } encrypted, err := Encrypt(value) @@ -156,17 +165,18 @@ func Decrypt(cipherText string) (string, error) { } func DecryptDB(ctx context.Context, c *DatabaseConnection) error { - db, err := c.Connect() - if err != nil { - return err - } - defer db.Close() + for _, item := range c.EncryptedItems { + c.DBName = item.Database + db, err := c.Connect() + if err != nil { + return err + } + defer db.Close() - if len(c.EncryptedItems) == 0 { - return errors.New("DB Connection: Database target tables/columns not defined") - } + if len(c.EncryptedItems) == 0 { + return errors.New("DB Connection: Database target tables/columns not defined") + } - for _, item := range c.EncryptedItems { tx, err := db.BeginTx(ctx, nil) if err != nil { return err @@ -180,7 +190,7 @@ func DecryptDB(ctx context.Context, c *DatabaseConnection) error { for k, v := range res.SetValues { for i, val := range v { - decoded, err := base64.StdEncoding.DecodeString(*val.(*string)) + decoded, err := base64.StdEncoding.DecodeString(val.(*sql.NullString).String) if err != nil { return err } diff --git a/utils/encryption/encryption_test.go b/managed/utils/encryption/encryption_test.go similarity index 92% rename from utils/encryption/encryption_test.go rename to managed/utils/encryption/encryption_test.go index 9b0cef4d17..bfca95ae46 100644 --- a/utils/encryption/encryption_test.go +++ b/managed/utils/encryption/encryption_test.go @@ -10,8 +10,7 @@ import ( func TestEncryption(t *testing.T) { secret := "password1" - err := create(DefaultEncryptionKeyPath) - require.NoError(t, err) + require.NoError(t, create(DefaultEncryptionKeyPath)) cipherText, err := Encrypt(secret) require.NoError(t, err) require.NotEmpty(t, cipherText) diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index c0110c24a4..1422ae6923 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -27,7 +27,7 @@ func prepareRowPointers(rows *sql.Rows) ([]any, error) { for _, t := range columns { switch t { case "VARCHAR": - row = append(row, new(string)) + row = append(row, new(sql.NullString)) default: // TODO support more identificators types return nil, fmt.Errorf("unsupported identificator type %s", t) diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go index 3d7b5c5c59..39a0825208 100644 --- a/managed/utils/encryption/models.go +++ b/managed/utils/encryption/models.go @@ -11,6 +11,7 @@ type Encryption struct { type DatabaseConnection struct { Host, User, Password string Port int16 + DBName string SSLMode string SSLCAPath string SSLKeyPath string From f7455812eb8ca8b8bb7173990562eeda095ac7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Sun, 23 Jun 2024 10:05:00 +0200 Subject: [PATCH 030/215] PMM-13129 Fix for EncryptDB. Encrypt/Decrypt username. --- managed/models/agent_model.go | 19 ++++++++++++------- managed/models/database.go | 10 +++++----- managed/utils/encryption/encryption.go | 2 +- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/managed/models/agent_model.go b/managed/models/agent_model.go index ab289e85b4..1250f15834 100644 --- a/managed/models/agent_model.go +++ b/managed/models/agent_model.go @@ -324,6 +324,10 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p username := pointer.GetString(s.Username) password := pointer.GetString(s.Password) + decryptedUsername, err := encryption.Decrypt(username) + if err != nil { + logrus.Warningf("Encryption: %#v", err) + } decryptedPassword, err := encryption.Decrypt(password) if err != nil { logrus.Warningf("Encryption: %#v", err) @@ -336,7 +340,7 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p switch s.AgentType { case MySQLdExporterType: cfg := mysql.NewConfig() - cfg.User = username + cfg.User = decryptedUsername cfg.Passwd = decryptedPassword cfg.Net = unix cfg.Addr = socket @@ -371,7 +375,7 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p case QANMySQLPerfSchemaAgentType, QANMySQLSlowlogAgentType: cfg := mysql.NewConfig() - cfg.User = username + cfg.User = decryptedUsername cfg.Passwd = decryptedPassword cfg.Net = unix cfg.Addr = socket @@ -410,7 +414,7 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p case ProxySQLExporterType: cfg := mysql.NewConfig() - cfg.User = username + cfg.User = decryptedUsername cfg.Passwd = decryptedPassword cfg.Net = unix cfg.Addr = socket @@ -492,13 +496,14 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p } switch { case password != "": - u.User = url.UserPassword(username, decryptedPassword) + u.User = url.UserPassword(decryptedUsername, decryptedPassword) case username != "": - u.User = url.User(username) + u.User = url.User(decryptedUsername) } dsn := u.String() dsn = strings.ReplaceAll(dsn, url.QueryEscape(tdp.Left), tdp.Left) dsn = strings.ReplaceAll(dsn, url.QueryEscape(tdp.Right), tdp.Right) + return dsn case PostgresExporterType, QANPostgreSQLPgStatementsAgentType, QANPostgreSQLPgStatMonitorAgentType: @@ -553,9 +558,9 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p } switch { case password != "": - u.User = url.UserPassword(username, decryptedPassword) + u.User = url.UserPassword(decryptedUsername, decryptedPassword) case username != "": - u.User = url.User(username) + u.User = url.User(decryptedUsername) } dsn := u.String() diff --git a/managed/models/database.go b/managed/models/database.go index 5013183b60..3aca17f110 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1047,11 +1047,6 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. logger = reform.NewPrintfLogger(params.Logf) } - err := encryption.Init(encryption.DefaultEncryptionKeyPath) - if err != nil { - return nil, err - } - db := reform.NewDB(sqlDB, postgresql.Dialect, logger) errCV := checkVersion(ctx, db) if pErr, ok := errCV.(*pq.Error); ok && pErr.Code == "28000" { //nolint:errorlint @@ -1099,6 +1094,11 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. }, } + err = encryption.Init(encryption.DefaultEncryptionKeyPath) + if err != nil { + return nil, err + } + if err := encryption.EncryptDB(ctx, c); err != nil { return nil, err } diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 085d5ce3ce..cf08db9f3a 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -128,7 +128,7 @@ func EncryptDB(ctx context.Context, c *DatabaseConnection) error { if err != nil { return err } - res.SetValues[k][i] = base64.StdEncoding.EncodeToString([]byte(encrypted)) + res.SetValues[k][i] = encrypted } data := append([]any{}, v...) data = append(data, res.WhereValues[k]...) From a4f92620e07b00ab3e7fbd1380588d96a877aff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 24 Jun 2024 10:30:09 +0200 Subject: [PATCH 031/215] PMM-13129 Formatting of encryption error, createAgent username fix. --- managed/models/agent_helpers.go | 8 ++++++-- managed/models/agent_model.go | 6 +++--- managed/services/management/agent.go | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 817929556a..c9fff897f5 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -884,9 +884,13 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara } } + encryptedUsername, err := encryption.Encrypt(params.Username) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } encryptedPassword, err := encryption.Encrypt(params.Password) if err != nil { - logrus.Warningf("Encryption: %#v", err) + logrus.Warningf("Encryption: %v", err) } row := &Agent{ @@ -895,7 +899,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara PMMAgentID: ¶ms.PMMAgentID, ServiceID: pointer.ToStringOrNil(params.ServiceID), NodeID: pointer.ToStringOrNil(params.NodeID), - Username: pointer.ToStringOrNil(params.Username), + Username: pointer.ToStringOrNil(encryptedUsername), Password: pointer.ToStringOrNil(encryptedPassword), AgentPassword: pointer.ToStringOrNil(params.AgentPassword), TLS: params.TLS, diff --git a/managed/models/agent_model.go b/managed/models/agent_model.go index 1250f15834..6b097dcfe2 100644 --- a/managed/models/agent_model.go +++ b/managed/models/agent_model.go @@ -326,11 +326,11 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p decryptedUsername, err := encryption.Decrypt(username) if err != nil { - logrus.Warningf("Encryption: %#v", err) + logrus.Warningf("Encryption: %v", err) } decryptedPassword, err := encryption.Decrypt(password) if err != nil { - logrus.Warningf("Encryption: %#v", err) + logrus.Warningf("Encryption: %v", err) } if tdp == nil { @@ -689,7 +689,7 @@ func (s Agent) Files() map[string]string { func (s Agent) TemplateDelimiters(svc *Service) *DelimiterPair { decryptedPassword, err := encryption.Decrypt(pointer.GetString(s.Password)) if err != nil { - logrus.Warningf("Encryption: %#v", err) + logrus.Warningf("Encryption: %v", err) } templateParams := []string{ diff --git a/managed/services/management/agent.go b/managed/services/management/agent.go index 03e44db3cf..4e6c14a244 100644 --- a/managed/services/management/agent.go +++ b/managed/services/management/agent.go @@ -138,7 +138,7 @@ func (s *AgentService) agentToAPI(agent *models.Agent) (*agentv1beta1.UniversalA decryptedPassword, err := encryption.Decrypt(pointer.GetString(agent.Password)) if err != nil { - logrus.Warningf("Encryption: %#v", err) + logrus.Warningf("Encryption: %v", err) } ua := &agentv1beta1.UniversalAgent{ From f9518d565b133579fa88b49b064cf314b7a4afb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 24 Jun 2024 11:01:02 +0200 Subject: [PATCH 032/215] PMM-13129 Remove unused method for now. --- managed/utils/encryption/encryption.go | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index cf08db9f3a..4835f3708b 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -25,25 +25,6 @@ func Init(keyPath string) error { return nil } -func InitFromEnv() error { - encryption := os.Getenv("PMM_ENCRYPTION") - if encryption == "0" { - return nil - } - - keyPath := os.Getenv("PMM_ENCRYPTION_KEY") - if keyPath == "" { - keyPath = DefaultEncryptionKeyPath - } - - err := create(keyPath) - if err != nil { - return err - } - - return nil -} - func create(keyPath string) error { e := new(Encryption) e.Path = keyPath From 00dae9c25a34960bd3f02ef9b61d63acf55c34ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 24 Jun 2024 11:05:48 +0200 Subject: [PATCH 033/215] PMM-13129 Correct mode for cert file. --- managed/utils/encryption/helpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index 1422ae6923..081d01f51a 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -69,5 +69,5 @@ func (e *Encryption) generateKey() error { } func (e *Encryption) saveKeyToFile() error { - return os.WriteFile(e.Path, []byte(e.Key), 0o777) + return os.WriteFile(e.Path, []byte(e.Key), 0o644) } From e8ee71e9c3a1f1100d1b9a3d332d434a1d09a10e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 24 Jun 2024 11:27:55 +0200 Subject: [PATCH 034/215] PMM-13129 Remove DB test, small refactor. --- managed/models/agent_helpers.go | 4 ++-- managed/utils/encryption/database_test.go | 19 ------------------- 2 files changed, 2 insertions(+), 21 deletions(-) delete mode 100644 managed/utils/encryption/database_test.go diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index c9fff897f5..4e089f8284 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -899,8 +899,8 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara PMMAgentID: ¶ms.PMMAgentID, ServiceID: pointer.ToStringOrNil(params.ServiceID), NodeID: pointer.ToStringOrNil(params.NodeID), - Username: pointer.ToStringOrNil(encryptedUsername), - Password: pointer.ToStringOrNil(encryptedPassword), + Username: &encryptedUsername, + Password: &encryptedPassword, AgentPassword: pointer.ToStringOrNil(params.AgentPassword), TLS: params.TLS, TLSSkipVerify: params.TLSSkipVerify, diff --git a/managed/utils/encryption/database_test.go b/managed/utils/encryption/database_test.go deleted file mode 100644 index 9cf97b3b94..0000000000 --- a/managed/utils/encryption/database_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package encryption - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestDatabaseConnection(t *testing.T) { - dbConnection := DatabaseConnection{ - Host: "127.0.0.1", - Port: 5432, - User: "postgres", - Password: "", - } - c, err := dbConnection.Connect() - require.NoError(t, err) - require.NoError(t, c.Close()) -} From 285275a5d9d4cf0504a891d8fa1a48a3167578ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 24 Jun 2024 11:49:12 +0200 Subject: [PATCH 035/215] PMM-13129 Encryption for external exporter. --- managed/models/agent_helpers.go | 14 ++++++++++++-- managed/models/agent_helpers_test.go | 6 ++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 4e089f8284..cf6eac03e1 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -710,14 +710,24 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar if metricsPath == "" { metricsPath = "/metrics" } + + encryptedUsername, err := encryption.Encrypt(params.Username) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + encryptedPassword, err := encryption.Encrypt(params.Password) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + row := &Agent{ PMMAgentID: pmmAgentID, AgentID: id, AgentType: ExternalExporterType, RunsOnNodeID: runsOnNodeID, ServiceID: pointer.ToStringOrNil(params.ServiceID), - Username: pointer.ToStringOrNil(params.Username), - Password: pointer.ToStringOrNil(params.Password), + Username: &encryptedUsername, + Password: &encryptedPassword, MetricsScheme: &scheme, MetricsPath: &metricsPath, ListenPort: pointer.ToUint16(uint16(params.ListenPort)), diff --git a/managed/models/agent_helpers_test.go b/managed/models/agent_helpers_test.go index 0285a76485..6fbe4491c5 100644 --- a/managed/models/agent_helpers_test.go +++ b/managed/models/agent_helpers_test.go @@ -467,6 +467,12 @@ func TestAgentHelpers(t *testing.T) { ListenPort: 9104, }) require.NoError(t, err) + + assert.NotEmpty(t, agent.Username) + assert.NotEmpty(t, agent.Password) + agent.Username = nil + agent.Password = nil + assert.Equal(t, &models.Agent{ AgentID: agent.AgentID, AgentType: models.ExternalExporterType, From 36274295814d698df2d369b04d9835aeb320a585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 24 Jun 2024 12:39:16 +0200 Subject: [PATCH 036/215] PMM-13129 Fix tests after external exporter encryption. --- managed/services/inventory/agents_test.go | 20 ++++++++++---------- managed/services/management/rds_test.go | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/managed/services/inventory/agents_test.go b/managed/services/inventory/agents_test.go index 24dd2e2bb3..fa064c608b 100644 --- a/managed/services/inventory/agents_test.go +++ b/managed/services/inventory/agents_test.go @@ -125,7 +125,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000008", PmmAgentId: "/agent_id/00000000-0000-4000-8000-000000000005", ServiceId: s.ServiceId, - Username: "username", + Username: actualAgent.(*inventorypb.MySQLdExporter).Username, Status: inventorypb.AgentStatus_UNKNOWN, } assert.Equal(t, expectedMySQLdExporter, actualAgent) @@ -154,7 +154,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-00000000000a", PmmAgentId: pmmAgent.AgentId, ServiceId: ms.ServiceId, - Username: "username", + Username: actualAgent.(*inventorypb.MongoDBExporter).Username, Status: inventorypb.AgentStatus_UNKNOWN, } assert.Equal(t, expectedMongoDBExporter, actualAgent) @@ -173,7 +173,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-00000000000b", PmmAgentId: pmmAgent.AgentId, ServiceId: s.ServiceId, - Username: "username", + Username: actualAgent.(*inventorypb.QANMySQLSlowlogAgent).Username, Status: inventorypb.AgentStatus_UNKNOWN, } assert.Equal(t, expectedQANMySQLSlowlogAgent, actualAgent) @@ -200,7 +200,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-00000000000d", PmmAgentId: pmmAgent.AgentId, ServiceId: ps.ServiceId, - Username: "username", + Username: actualAgent.(*inventorypb.PostgresExporter).Username, Status: inventorypb.AgentStatus_UNKNOWN, } assert.Equal(t, expectedPostgresExporter, actualAgent) @@ -220,7 +220,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-00000000000e", RunsOnNodeId: models.PMMServerNodeID, ServiceId: ps.ServiceId, - Username: "username", + Username: actualAgent.(*inventorypb.ExternalExporter).Username, Scheme: "http", MetricsPath: "/metrics", ListenPort: 9222, @@ -415,7 +415,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000006", RunsOnNodeId: models.PMMServerNodeID, ServiceId: service.ServiceId, - Username: "username", + Username: agent.Username, Scheme: "http", MetricsPath: "/metrics", ListenPort: 12345, @@ -499,7 +499,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000007", PmmAgentId: pmmAgent.AgentId, ServiceId: ms.ServiceId, - Username: "username", + Username: actualAgent.Username, PushMetricsEnabled: true, Status: inventorypb.AgentStatus_UNKNOWN, } @@ -590,7 +590,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000007", PmmAgentId: pmmAgent.AgentId, ServiceId: ps.ServiceId, - Username: "username", + Username: actualAgent.Username, PushMetricsEnabled: true, Status: inventorypb.AgentStatus_UNKNOWN, } @@ -647,7 +647,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000007", PmmAgentId: "/agent_id/00000000-0000-4000-8000-000000000005", ServiceId: s.ServiceId, - Username: "username", + Username: actualAgent.Username, PushMetricsEnabled: true, Status: inventorypb.AgentStatus_UNKNOWN, } @@ -724,7 +724,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000006", RunsOnNodeId: models.PMMServerNodeID, ServiceId: service.ServiceId, - Username: "username", + Username: agent.Username, Scheme: "http", MetricsPath: "/metrics", ListenPort: 12345, diff --git a/managed/services/management/rds_test.go b/managed/services/management/rds_test.go index c103825de1..b3f435ab20 100644 --- a/managed/services/management/rds_test.go +++ b/managed/services/management/rds_test.go @@ -305,7 +305,7 @@ func TestRDSService(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000008", PmmAgentId: "pmm-server", ServiceId: "/service_id/00000000-0000-4000-8000-000000000007", - Username: "username", + Username: resp.MysqldExporter.Username, TablestatsGroupTableLimit: 1000, Status: inventorypb.AgentStatus_UNKNOWN, }, @@ -313,7 +313,7 @@ func TestRDSService(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000009", PmmAgentId: "pmm-server", ServiceId: "/service_id/00000000-0000-4000-8000-000000000007", - Username: "username", + Username: resp.QanMysqlPerfschema.Username, QueryExamplesDisabled: true, Status: inventorypb.AgentStatus_UNKNOWN, }, @@ -395,7 +395,7 @@ func TestRDSService(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-00000000000d", PmmAgentId: "pmm-server", ServiceId: "/service_id/00000000-0000-4000-8000-00000000000c", - Username: "username", + Username: resp.PostgresqlExporter.Username, Status: inventorypb.AgentStatus_UNKNOWN, AutoDiscoveryLimit: 10, MaxExporterConnections: 15, @@ -404,7 +404,7 @@ func TestRDSService(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-00000000000e", PmmAgentId: "pmm-server", ServiceId: "/service_id/00000000-0000-4000-8000-00000000000c", - Username: "username", + Username: resp.QanPostgresqlPgstatements.Username, Status: inventorypb.AgentStatus_UNKNOWN, }, } From abebb36d7685920f44633b7f5158c99c724c522a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 24 Jun 2024 12:48:06 +0200 Subject: [PATCH 037/215] PMM-13129 Fix mongo tests. --- managed/services/agents/mongodb_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/managed/services/agents/mongodb_test.go b/managed/services/agents/mongodb_test.go index cc723d483d..e47afe9be6 100644 --- a/managed/services/agents/mongodb_test.go +++ b/managed/services/agents/mongodb_test.go @@ -30,6 +30,9 @@ import ( ) func TestMongodbExporterConfig225(t *testing.T) { + err := encryption.Init(encryption.DefaultEncryptionKeyPath) + require.NoError(t, err) + pmmAgentVersion := version.MustParse("2.25.0") node := &models.Node{ Address: "1.2.3.4", @@ -88,6 +91,9 @@ func TestMongodbExporterConfig225(t *testing.T) { } func TestMongodbExporterConfig226(t *testing.T) { + err := encryption.Init(encryption.DefaultEncryptionKeyPath) + require.NoError(t, err) + pmmAgentVersion := version.MustParse("2.26.0") node := &models.Node{ Address: "1.2.3.4", @@ -233,6 +239,9 @@ func TestMongodbExporterConfig226(t *testing.T) { } func TestMongodbExporterConfig2411(t *testing.T) { + err := encryption.Init(encryption.DefaultEncryptionKeyPath) + require.NoError(t, err) + pmmAgentVersion := version.MustParse("2.41.1") node := &models.Node{ Address: "1.2.3.4", @@ -569,6 +578,9 @@ func TestNewMongodbExporterConfig(t *testing.T) { func TestMongodbExporterConfig228_WebConfigAuth(t *testing.T) { t.Parallel() + err := encryption.Init(encryption.DefaultEncryptionKeyPath) + require.NoError(t, err) + pmmAgentVersion := version.MustParse("2.28.0") node := &models.Node{ From a03ed0d396f4a36c59a12a9bf228be3ac315c3f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 24 Jun 2024 12:58:38 +0200 Subject: [PATCH 038/215] PMM-13129 Fix another test to expect encrypted username. --- managed/services/management/agent_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/managed/services/management/agent_test.go b/managed/services/management/agent_test.go index 6a8c34e3ee..e13c924057 100644 --- a/managed/services/management/agent_test.go +++ b/managed/services/management/agent_test.go @@ -127,7 +127,7 @@ func TestAgentService(t *testing.T) { IsConnected: false, CreatedAt: timestamppb.New(now), UpdatedAt: timestamppb.New(now), - Username: "postgres", + Username: response.GetAgents()[0].Username, PostgresqlOptions: &agentv1beta1.UniversalAgent_PostgreSQLOptions{ IsSslKeySet: false, }, @@ -143,7 +143,7 @@ func TestAgentService(t *testing.T) { IsConnected: false, CreatedAt: timestamppb.New(now), UpdatedAt: timestamppb.New(now), - Username: "postgres", + Username: response.GetAgents()[1].Username, PostgresqlOptions: &agentv1beta1.UniversalAgent_PostgreSQLOptions{ IsSslKeySet: false, }, From 08f4f8fa283c2ae5dbe7cc9ebc6143aeaed74b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 24 Jun 2024 13:06:08 +0200 Subject: [PATCH 039/215] PMM-13129 Another fix for tests to expect encrypted username. --- managed/services/management/agent_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/managed/services/management/agent_test.go b/managed/services/management/agent_test.go index e13c924057..f8becefd51 100644 --- a/managed/services/management/agent_test.go +++ b/managed/services/management/agent_test.go @@ -206,6 +206,7 @@ func TestAgentService(t *testing.T) { AgentType: "rds_exporter", PmmAgentId: "/agent_id/00000000-0000-4000-8000-000000000007", IsConnected: false, + Username: response.GetAgents()[0].Username, CreatedAt: timestamppb.New(now), UpdatedAt: timestamppb.New(now), ServiceId: "/service_id/00000000-0000-4000-8000-000000000006", @@ -256,6 +257,7 @@ func TestAgentService(t *testing.T) { AgentType: "azure_database_exporter", PmmAgentId: "/agent_id/00000000-0000-4000-8000-000000000007", IsConnected: false, + Username: response.GetAgents()[0].Username, CreatedAt: timestamppb.New(now), UpdatedAt: timestamppb.New(now), ServiceId: "/service_id/00000000-0000-4000-8000-000000000006", From 2bf51de998d35b25b0444429a626264c1f27aec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 24 Jun 2024 13:26:04 +0200 Subject: [PATCH 040/215] PMM-13129 Fix for DecryptDB. --- managed/utils/encryption/encryption.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 4835f3708b..b66de922ed 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -171,11 +171,13 @@ func DecryptDB(ctx context.Context, c *DatabaseConnection) error { for k, v := range res.SetValues { for i, val := range v { - decoded, err := base64.StdEncoding.DecodeString(val.(*sql.NullString).String) - if err != nil { - return err + value := val.(*sql.NullString) + if !value.Valid { + res.SetValues[k][i] = "" + continue } - decrypted, err := Decrypt(string(decoded)) + + decrypted, err := Decrypt(value.String) if err != nil { return err } From 16b9b40470428b7cc9b91f88f99d21ba55d02659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 24 Jun 2024 13:47:08 +0200 Subject: [PATCH 041/215] PMM-13129 Err if encryption is not initialized. --- managed/utils/encryption/encryption.go | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index b66de922ed..2bff4a0dca 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -12,8 +12,9 @@ import ( const DefaultEncryptionKeyPath = "/srv/pmm-encryption.key" var ( - config *Encryption - configMtx sync.RWMutex + config *Encryption + configMtx sync.RWMutex + ErrEncryptionNotInitialized = errors.New("encryption is not initialized") ) func Init(keyPath string) error { @@ -57,8 +58,13 @@ func create(keyPath string) error { func Encrypt(secret string) (string, error) { configMtx.RLock() + if config == nil { + configMtx.RUnlock() + return "", ErrEncryptionNotInitialized + } primitive := config.Primitive configMtx.RUnlock() + cipherText, err := primitive.Encrypt([]byte(secret), []byte("")) if err != nil { return secret, err @@ -129,14 +135,18 @@ func EncryptDB(ctx context.Context, c *DatabaseConnection) error { } func Decrypt(cipherText string) (string, error) { + configMtx.RLock() + if config == nil { + configMtx.RUnlock() + return "", ErrEncryptionNotInitialized + } + primitive := config.Primitive + configMtx.RUnlock() + decoded, err := base64.StdEncoding.DecodeString(cipherText) if err != nil { return cipherText, err } - - configMtx.RLock() - primitive := config.Primitive - configMtx.RUnlock() secret, err := primitive.Decrypt([]byte(decoded), []byte("")) if err != nil { return cipherText, err From ec3f3914c6530552044137ce8fa6cc5d3eb96694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 24 Jun 2024 13:47:47 +0200 Subject: [PATCH 042/215] PMM-13129 Delimiter fix. --- managed/models/agent_model.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/managed/models/agent_model.go b/managed/models/agent_model.go index 6b097dcfe2..e34dd16c9f 100644 --- a/managed/models/agent_model.go +++ b/managed/models/agent_model.go @@ -687,6 +687,10 @@ func (s Agent) Files() map[string]string { // TemplateDelimiters returns a pair of safe template delimiters that are not present in agent parameters. func (s Agent) TemplateDelimiters(svc *Service) *DelimiterPair { + decryptedUsername, err := encryption.Decrypt(pointer.GetString(s.Username)) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } decryptedPassword, err := encryption.Decrypt(pointer.GetString(s.Password)) if err != nil { logrus.Warningf("Encryption: %v", err) @@ -694,7 +698,7 @@ func (s Agent) TemplateDelimiters(svc *Service) *DelimiterPair { templateParams := []string{ pointer.GetString(svc.Address), - pointer.GetString(s.Username), + decryptedUsername, decryptedPassword, pointer.GetString(s.MetricsPath), } From e799003d9da240312f69a545766862c679a88206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 24 Jun 2024 14:04:49 +0200 Subject: [PATCH 043/215] PMM-13129 Fix DecryptDB. --- managed/utils/encryption/encryption.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 2bff4a0dca..affbafbd88 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -183,7 +183,7 @@ func DecryptDB(ctx context.Context, c *DatabaseConnection) error { for i, val := range v { value := val.(*sql.NullString) if !value.Valid { - res.SetValues[k][i] = "" + res.SetValues[k][i] = sql.NullString{} continue } @@ -191,6 +191,10 @@ func DecryptDB(ctx context.Context, c *DatabaseConnection) error { if err != nil { return err } + if decrypted == "" { + res.SetValues[k][i] = sql.NullString{} + continue + } res.SetValues[k][i] = decrypted } data := append([]any{}, v...) From 9e255f2d5be45422b1ec15ee84860bf0c58cfcbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 24 Jun 2024 14:32:15 +0200 Subject: [PATCH 044/215] PMM-13129 Small change in agent test. --- managed/models/agent_helpers_test.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/managed/models/agent_helpers_test.go b/managed/models/agent_helpers_test.go index 6fbe4491c5..fd63f7d4d3 100644 --- a/managed/models/agent_helpers_test.go +++ b/managed/models/agent_helpers_test.go @@ -468,17 +468,14 @@ func TestAgentHelpers(t *testing.T) { }) require.NoError(t, err) - assert.NotEmpty(t, agent.Username) - assert.NotEmpty(t, agent.Password) - agent.Username = nil - agent.Password = nil - assert.Equal(t, &models.Agent{ AgentID: agent.AgentID, AgentType: models.ExternalExporterType, RunsOnNodeID: pointer.ToString("N1"), ServiceID: pointer.ToString("S1"), ListenPort: pointer.ToUint16(9104), + Username: agent.Username, + Password: agent.Password, MetricsPath: pointer.ToString("/metrics"), MetricsScheme: pointer.ToString("http"), CreatedAt: now, From fbd3ee6ff3920512209611af34b3442faa76e582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 24 Jun 2024 14:37:00 +0200 Subject: [PATCH 045/215] PMM-13129 Fix non related test to make it green for now. --- managed/services/server/updater_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/managed/services/server/updater_test.go b/managed/services/server/updater_test.go index 68bb6e69f4..963c139e2b 100644 --- a/managed/services/server/updater_test.go +++ b/managed/services/server/updater_test.go @@ -247,12 +247,12 @@ func TestUpdater(t *testing.T) { t.Run("TestLatest", func(t *testing.T) { // Used PMM 2, because PMM 3 is not released yet. - version.Version = "2.41.0" + version.Version = "2.42.0" u := NewUpdater(watchtowerURL, gRPCMessageMaxSize) latest, err := u.latest(context.Background()) require.NoError(t, err) assert.NotNil(t, latest) - assert.True(t, strings.HasPrefix(latest.Version.String(), "2.41."), "latest version of PMM 2 should have prefix 2.41.") + assert.True(t, strings.HasPrefix(latest.Version.String(), "2.42."), "latest version of PMM 2 should have prefix 2.42.") }) t.Run("TestParseFile", func(t *testing.T) { From 06314c2948011057dbe0c296d8a519a7f6382b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 25 Jun 2024 09:34:17 +0200 Subject: [PATCH 046/215] PMM-13129 Add license headers. --- managed/utils/encryption/database.go | 15 +++++++++++++++ managed/utils/encryption/encryption.go | 15 +++++++++++++++ managed/utils/encryption/encryption_test.go | 15 +++++++++++++++ managed/utils/encryption/helpers.go | 15 +++++++++++++++ managed/utils/encryption/models.go | 15 +++++++++++++++ 5 files changed, 75 insertions(+) diff --git a/managed/utils/encryption/database.go b/managed/utils/encryption/database.go index 67e7787d15..ea01cbd5d8 100644 --- a/managed/utils/encryption/database.go +++ b/managed/utils/encryption/database.go @@ -1,3 +1,18 @@ +// Copyright (C) 2024 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + package encryption import ( diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index affbafbd88..8aba063022 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -1,3 +1,18 @@ +// Copyright (C) 2024 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + package encryption import ( diff --git a/managed/utils/encryption/encryption_test.go b/managed/utils/encryption/encryption_test.go index bfca95ae46..5cbd4f19ff 100644 --- a/managed/utils/encryption/encryption_test.go +++ b/managed/utils/encryption/encryption_test.go @@ -1,3 +1,18 @@ +// Copyright (C) 2024 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + package encryption import ( diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index 081d01f51a..b778eac052 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -1,3 +1,18 @@ +// Copyright (C) 2024 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + package encryption import ( diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go index 39a0825208..aefef51d90 100644 --- a/managed/utils/encryption/models.go +++ b/managed/utils/encryption/models.go @@ -1,3 +1,18 @@ +// Copyright (C) 2024 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + package encryption import "github.com/google/tink/go/tink" From f94745b3ca64872962f9d1a085241e855fce393d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 25 Jun 2024 09:42:33 +0200 Subject: [PATCH 047/215] PMM-13129 License. --- managed/utils/encryption/database.go | 2 +- managed/utils/encryption/encryption.go | 2 +- managed/utils/encryption/encryption_test.go | 2 +- managed/utils/encryption/helpers.go | 2 +- managed/utils/encryption/models.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/managed/utils/encryption/database.go b/managed/utils/encryption/database.go index ea01cbd5d8..b880722d9b 100644 --- a/managed/utils/encryption/database.go +++ b/managed/utils/encryption/database.go @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Percona LLC +// Copyright (C) 2023 Percona LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 8aba063022..356e8b8a8c 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Percona LLC +// Copyright (C) 2023 Percona LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by diff --git a/managed/utils/encryption/encryption_test.go b/managed/utils/encryption/encryption_test.go index 5cbd4f19ff..5f563c58c1 100644 --- a/managed/utils/encryption/encryption_test.go +++ b/managed/utils/encryption/encryption_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Percona LLC +// Copyright (C) 2023 Percona LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index b778eac052..05cea02b6b 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Percona LLC +// Copyright (C) 2023 Percona LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go index aefef51d90..16a5c8a5ba 100644 --- a/managed/utils/encryption/models.go +++ b/managed/utils/encryption/models.go @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Percona LLC +// Copyright (C) 2023 Percona LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by From 3f3391ba72d4839e7181acf7a94716bb9206a5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 25 Jun 2024 10:48:37 +0200 Subject: [PATCH 048/215] PMM-13129 Lint. --- managed/utils/encryption/database.go | 8 ++++++-- managed/utils/encryption/encryption.go | 23 +++++++++++++++-------- managed/utils/encryption/helpers.go | 2 +- managed/utils/encryption/models.go | 4 ++++ 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/managed/utils/encryption/database.go b/managed/utils/encryption/database.go index b880722d9b..3ef2a22a5e 100644 --- a/managed/utils/encryption/database.go +++ b/managed/utils/encryption/database.go @@ -18,11 +18,13 @@ package encryption import ( "database/sql" "fmt" + "slices" "strings" _ "github.com/lib/pq" ) +// DatabaseConnection.Connect open connection to DB. func (c DatabaseConnection) Connect() (*sql.DB, error) { db, err := sql.Open("postgres", c.DSN()) if err != nil { @@ -37,6 +39,7 @@ func (c DatabaseConnection) Connect() (*sql.DB, error) { return db, nil } +// DatabaseConnection.DSN returns formatted connection string to PG. func (c DatabaseConnection) DSN() string { if c.SSLMode == "" { c.SSLMode = "disable" @@ -52,8 +55,9 @@ func (c DatabaseConnection) DSN() string { ) } +// EncryptedItem.Read returns query and it's values based on input. func (item EncryptedItem) Read(tx *sql.Tx) (*QueryValues, error) { - what := append(item.Identificators, item.Columns...) + what := slices.Concat(item.Identificators, item.Columns) query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ", "), item.Table) rows, err := tx.Query(query) if err != nil { @@ -94,7 +98,7 @@ func (item EncryptedItem) Read(tx *sql.Tx) (*QueryValues, error) { q.Query = fmt.Sprintf("UPDATE %s %s %s", item.Table, setSQL, whereSQL) } - err = rows.Close() + err = rows.Close() //nolint:errcheck if err != nil { return nil, err } diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 356e8b8a8c..9a5a4688ce 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -21,9 +21,11 @@ import ( "encoding/base64" "errors" "os" + "slices" "sync" ) +// DefaultEncryptionKeyPath contains default PMM encryption key path. const DefaultEncryptionKeyPath = "/srv/pmm-encryption.key" var ( @@ -32,6 +34,7 @@ var ( ErrEncryptionNotInitialized = errors.New("encryption is not initialized") ) +// Init initialize encryption. func Init(keyPath string) error { err := create(keyPath) if err != nil { @@ -71,6 +74,7 @@ func create(keyPath string) error { return nil } +// Encrypt returns input string encrypted. func Encrypt(secret string) (string, error) { configMtx.RLock() if config == nil { @@ -88,6 +92,7 @@ func Encrypt(secret string) (string, error) { return base64.StdEncoding.EncodeToString(cipherText), nil } +// EncryptDB will encrypt all columns provided in DB connection. func EncryptDB(ctx context.Context, c *DatabaseConnection) error { for _, item := range c.EncryptedItems { c.DBName = item.Database @@ -95,7 +100,7 @@ func EncryptDB(ctx context.Context, c *DatabaseConnection) error { if err != nil { return err } - defer db.Close() + defer db.Close() //nolint:errcheck if len(c.EncryptedItems) == 0 { return errors.New("DB Connection: Database target tables/columns not defined") @@ -105,7 +110,7 @@ func EncryptDB(ctx context.Context, c *DatabaseConnection) error { if err != nil { return err } - defer tx.Rollback() + defer tx.Rollback() //nolint:errcheck res, err := item.Read(tx) if err != nil { @@ -132,8 +137,8 @@ func EncryptDB(ctx context.Context, c *DatabaseConnection) error { } res.SetValues[k][i] = encrypted } - data := append([]any{}, v...) - data = append(data, res.WhereValues[k]...) + data := slices.Concat([]any{}, v) + data = slices.Concat(data, res.WhereValues[k]) _, err := tx.Exec(res.Query, data...) if err != nil { return err @@ -149,6 +154,7 @@ func EncryptDB(ctx context.Context, c *DatabaseConnection) error { return nil } +// Encrypt returns input string decrypted. func Decrypt(cipherText string) (string, error) { configMtx.RLock() if config == nil { @@ -170,6 +176,7 @@ func Decrypt(cipherText string) (string, error) { return string(secret), nil } +// DecryptDB will decrypt all columns provided in DB connection. func DecryptDB(ctx context.Context, c *DatabaseConnection) error { for _, item := range c.EncryptedItems { c.DBName = item.Database @@ -177,7 +184,7 @@ func DecryptDB(ctx context.Context, c *DatabaseConnection) error { if err != nil { return err } - defer db.Close() + defer db.Close() //nolint:errcheck if len(c.EncryptedItems) == 0 { return errors.New("DB Connection: Database target tables/columns not defined") @@ -187,7 +194,7 @@ func DecryptDB(ctx context.Context, c *DatabaseConnection) error { if err != nil { return err } - defer tx.Rollback() + defer tx.Rollback() //nolint:errcheck res, err := item.Read(tx) if err != nil { @@ -212,8 +219,8 @@ func DecryptDB(ctx context.Context, c *DatabaseConnection) error { } res.SetValues[k][i] = decrypted } - data := append([]any{}, v...) - data = append(data, res.WhereValues[k]...) + data := slices.Concat([]any{}, v) + data = slices.Concat(data, res.WhereValues[k]) _, err := tx.Exec(res.Query, data...) if err != nil { return err diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index 05cea02b6b..a044c08e21 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -84,5 +84,5 @@ func (e *Encryption) generateKey() error { } func (e *Encryption) saveKeyToFile() error { - return os.WriteFile(e.Path, []byte(e.Key), 0o644) + return os.WriteFile(e.Path, []byte(e.Key), 0o644) //nolint:gosec } diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go index 16a5c8a5ba..1f5cd00940 100644 --- a/managed/utils/encryption/models.go +++ b/managed/utils/encryption/models.go @@ -17,12 +17,14 @@ package encryption import "github.com/google/tink/go/tink" +// Encryption contains fields required for encryption. type Encryption struct { Path string Key string Primitive tink.AEAD } +// DatabaseConnection represents DB connection and it's encrypted items. type DatabaseConnection struct { Host, User, Password string Port int16 @@ -34,12 +36,14 @@ type DatabaseConnection struct { EncryptedItems []EncryptedItem } +// EncryptedItem resresents DB name, table, encrypted columns and it's identificators. type EncryptedItem struct { Database, Table string Identificators []string Columns []string } +// QueryValues represents query to update row after encrypt/decrypt. type QueryValues struct { Query string SetValues [][]any From bc8146d7c119ef1c254545414789be5cf4a5f294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 25 Jun 2024 11:10:14 +0200 Subject: [PATCH 049/215] PMM-13129 Another lint. --- managed/utils/encryption/database.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/managed/utils/encryption/database.go b/managed/utils/encryption/database.go index 3ef2a22a5e..13d37d8bd7 100644 --- a/managed/utils/encryption/database.go +++ b/managed/utils/encryption/database.go @@ -21,10 +21,10 @@ import ( "slices" "strings" - _ "github.com/lib/pq" + _ "github.com/lib/pq" // register SQL driver ) -// DatabaseConnection.Connect open connection to DB. +// Connect open connection to DB. func (c DatabaseConnection) Connect() (*sql.DB, error) { db, err := sql.Open("postgres", c.DSN()) if err != nil { @@ -39,7 +39,7 @@ func (c DatabaseConnection) Connect() (*sql.DB, error) { return db, nil } -// DatabaseConnection.DSN returns formatted connection string to PG. +// DSN returns formatted connection string to PG. func (c DatabaseConnection) DSN() string { if c.SSLMode == "" { c.SSLMode = "disable" @@ -55,10 +55,10 @@ func (c DatabaseConnection) DSN() string { ) } -// EncryptedItem.Read returns query and it's values based on input. +// Read returns query and it's values based on input. func (item EncryptedItem) Read(tx *sql.Tx) (*QueryValues, error) { what := slices.Concat(item.Identificators, item.Columns) - query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ", "), item.Table) + query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ", "), item.Table) //nolint:gosec rows, err := tx.Query(query) if err != nil { return nil, err From 40196bf322b25c6f73ebc51a5e0ab90233560d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 25 Jun 2024 11:38:23 +0200 Subject: [PATCH 050/215] PMM-13129 Lint. --- managed/utils/encryption/database.go | 2 +- managed/utils/encryption/encryption.go | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/managed/utils/encryption/database.go b/managed/utils/encryption/database.go index 13d37d8bd7..f32ead4b70 100644 --- a/managed/utils/encryption/database.go +++ b/managed/utils/encryption/database.go @@ -98,7 +98,7 @@ func (item EncryptedItem) Read(tx *sql.Tx) (*QueryValues, error) { q.Query = fmt.Sprintf("UPDATE %s %s %s", item.Table, setSQL, whereSQL) } - err = rows.Close() //nolint:errcheck + err = rows.Close() //nolint:errcheck,sqlclosecheck if err != nil { return nil, err } diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 9a5a4688ce..88cf5f58a4 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -29,8 +29,9 @@ import ( const DefaultEncryptionKeyPath = "/srv/pmm-encryption.key" var ( - config *Encryption - configMtx sync.RWMutex + config *Encryption + configMtx sync.RWMutex + // ErrEncryptionNotInitialized is error in case of encryption is not initialized. ErrEncryptionNotInitialized = errors.New("encryption is not initialized") ) @@ -154,7 +155,7 @@ func EncryptDB(ctx context.Context, c *DatabaseConnection) error { return nil } -// Encrypt returns input string decrypted. +// Decrypt returns input string decrypted. func Decrypt(cipherText string) (string, error) { configMtx.RLock() if config == nil { @@ -168,7 +169,7 @@ func Decrypt(cipherText string) (string, error) { if err != nil { return cipherText, err } - secret, err := primitive.Decrypt([]byte(decoded), []byte("")) + secret, err := primitive.Decrypt(decoded, []byte("")) if err != nil { return cipherText, err } From d9910779171c8d0873dbbe85fa98d4c88df0c22a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 25 Jun 2024 13:53:14 +0200 Subject: [PATCH 051/215] PMM-13129 Default encryption changes. --- managed/models/database.go | 5 -- managed/services/agents/mongodb_test.go | 19 ------ managed/utils/encryption/database.go | 2 +- managed/utils/encryption/encryption.go | 76 ++++++++++----------- managed/utils/encryption/encryption_test.go | 1 - managed/utils/testdb/db.go | 4 -- 6 files changed, 39 insertions(+), 68 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index 3aca17f110..f19330b52b 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1094,11 +1094,6 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. }, } - err = encryption.Init(encryption.DefaultEncryptionKeyPath) - if err != nil { - return nil, err - } - if err := encryption.EncryptDB(ctx, c); err != nil { return nil, err } diff --git a/managed/services/agents/mongodb_test.go b/managed/services/agents/mongodb_test.go index e47afe9be6..8accc04e9e 100644 --- a/managed/services/agents/mongodb_test.go +++ b/managed/services/agents/mongodb_test.go @@ -25,14 +25,10 @@ import ( "github.com/percona/pmm/api/agentpb" "github.com/percona/pmm/api/inventorypb" "github.com/percona/pmm/managed/models" - "github.com/percona/pmm/managed/utils/encryption" "github.com/percona/pmm/version" ) func TestMongodbExporterConfig225(t *testing.T) { - err := encryption.Init(encryption.DefaultEncryptionKeyPath) - require.NoError(t, err) - pmmAgentVersion := version.MustParse("2.25.0") node := &models.Node{ Address: "1.2.3.4", @@ -91,9 +87,6 @@ func TestMongodbExporterConfig225(t *testing.T) { } func TestMongodbExporterConfig226(t *testing.T) { - err := encryption.Init(encryption.DefaultEncryptionKeyPath) - require.NoError(t, err) - pmmAgentVersion := version.MustParse("2.26.0") node := &models.Node{ Address: "1.2.3.4", @@ -239,9 +232,6 @@ func TestMongodbExporterConfig226(t *testing.T) { } func TestMongodbExporterConfig2411(t *testing.T) { - err := encryption.Init(encryption.DefaultEncryptionKeyPath) - require.NoError(t, err) - pmmAgentVersion := version.MustParse("2.41.1") node := &models.Node{ Address: "1.2.3.4", @@ -397,9 +387,6 @@ func TestMongodbExporterConfig2411(t *testing.T) { } func TestMongodbExporterConfig(t *testing.T) { - err := encryption.Init(encryption.DefaultEncryptionKeyPath) - require.NoError(t, err) - pmmAgentVersion := version.MustParse("2.0.0") node := &models.Node{ Address: "1.2.3.4", @@ -521,9 +508,6 @@ func TestMongodbExporterConfig(t *testing.T) { } func TestNewMongodbExporterConfig(t *testing.T) { - err := encryption.Init(encryption.DefaultEncryptionKeyPath) - require.NoError(t, err) - pmmAgentVersion := version.MustParse("2.10.0") node := &models.Node{ Address: "1.2.3.4", @@ -578,9 +562,6 @@ func TestNewMongodbExporterConfig(t *testing.T) { func TestMongodbExporterConfig228_WebConfigAuth(t *testing.T) { t.Parallel() - err := encryption.Init(encryption.DefaultEncryptionKeyPath) - require.NoError(t, err) - pmmAgentVersion := version.MustParse("2.28.0") node := &models.Node{ diff --git a/managed/utils/encryption/database.go b/managed/utils/encryption/database.go index f32ead4b70..18904924a9 100644 --- a/managed/utils/encryption/database.go +++ b/managed/utils/encryption/database.go @@ -98,7 +98,7 @@ func (item EncryptedItem) Read(tx *sql.Tx) (*QueryValues, error) { q.Query = fmt.Sprintf("UPDATE %s %s %s", item.Table, setSQL, whereSQL) } - err = rows.Close() //nolint:errcheck,sqlclosecheck + err = rows.Close() //nolint:sqlclosecheck if err != nil { return nil, err } diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 88cf5f58a4..2ce89be95a 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -22,30 +22,22 @@ import ( "errors" "os" "slices" - "sync" + + "github.com/sirupsen/logrus" ) // DefaultEncryptionKeyPath contains default PMM encryption key path. const DefaultEncryptionKeyPath = "/srv/pmm-encryption.key" var ( - config *Encryption - configMtx sync.RWMutex // ErrEncryptionNotInitialized is error in case of encryption is not initialized. ErrEncryptionNotInitialized = errors.New("encryption is not initialized") ) -// Init initialize encryption. -func Init(keyPath string) error { - err := create(keyPath) - if err != nil { - return err - } - - return nil -} +var DefaultEncryption = New(DefaultEncryptionKeyPath) -func create(keyPath string) error { +// New initialize encryption. +func New(keyPath string) *Encryption { e := new(Encryption) e.Path = keyPath @@ -54,38 +46,35 @@ func create(keyPath string) error { case os.IsNotExist(err): err = e.generateKey() if err != nil { - return err + logrus.Errorf("Encryption: %v", err) } case err != nil: - return err + logrus.Errorf("Encryption: %v", err) default: e.Key = string(bytes) } primitive, err := e.getPrimitive() if err != nil { - return err + logrus.Errorf("Encryption: %v", err) } e.Primitive = primitive - configMtx.Lock() - config = e - configMtx.Unlock() + return e +} - return nil +// Encrypt is wrapper around DefaultEncryption.Encrypt. +func Encrypt(secret string) (string, error) { + return DefaultEncryption.Encrypt(secret) } // Encrypt returns input string encrypted. -func Encrypt(secret string) (string, error) { - configMtx.RLock() - if config == nil { - configMtx.RUnlock() +func (e *Encryption) Encrypt(secret string) (string, error) { + if e == nil || e.Primitive == nil { return "", ErrEncryptionNotInitialized } - primitive := config.Primitive - configMtx.RUnlock() - cipherText, err := primitive.Encrypt([]byte(secret), []byte("")) + cipherText, err := e.Primitive.Encrypt([]byte(secret), []byte("")) if err != nil { return secret, err } @@ -93,8 +82,13 @@ func Encrypt(secret string) (string, error) { return base64.StdEncoding.EncodeToString(cipherText), nil } -// EncryptDB will encrypt all columns provided in DB connection. +// EncryptDB is wrapper around DefaultEncryption.EncryptDB. func EncryptDB(ctx context.Context, c *DatabaseConnection) error { + return DefaultEncryption.EncryptDB(ctx, c) +} + +// EncryptDB will encrypt all columns provided in DB connection. +func (e *Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection) error { for _, item := range c.EncryptedItems { c.DBName = item.Database db, err := c.Connect() @@ -132,7 +126,7 @@ func EncryptDB(ctx context.Context, c *DatabaseConnection) error { } } - encrypted, err := Encrypt(value) + encrypted, err := e.Encrypt(value) if err != nil { return err } @@ -155,21 +149,22 @@ func EncryptDB(ctx context.Context, c *DatabaseConnection) error { return nil } -// Decrypt returns input string decrypted. +// Decrypt is wrapper around DefaultEncryption.Decrypt. func Decrypt(cipherText string) (string, error) { - configMtx.RLock() - if config == nil { - configMtx.RUnlock() + return DefaultEncryption.Decrypt(cipherText) +} + +// Decrypt returns input string decrypted. +func (e *Encryption) Decrypt(cipherText string) (string, error) { + if e == nil || e.Primitive == nil { return "", ErrEncryptionNotInitialized } - primitive := config.Primitive - configMtx.RUnlock() decoded, err := base64.StdEncoding.DecodeString(cipherText) if err != nil { return cipherText, err } - secret, err := primitive.Decrypt(decoded, []byte("")) + secret, err := e.Primitive.Decrypt(decoded, []byte("")) if err != nil { return cipherText, err } @@ -177,8 +172,13 @@ func Decrypt(cipherText string) (string, error) { return string(secret), nil } -// DecryptDB will decrypt all columns provided in DB connection. +// DecryptDB is wrapper around DefaultEncryption.DecryptDB. func DecryptDB(ctx context.Context, c *DatabaseConnection) error { + return DefaultEncryption.DecryptDB(ctx, c) +} + +// DecryptDB will decrypt all columns provided in DB connection. +func (e *Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection) error { for _, item := range c.EncryptedItems { c.DBName = item.Database db, err := c.Connect() @@ -210,7 +210,7 @@ func DecryptDB(ctx context.Context, c *DatabaseConnection) error { continue } - decrypted, err := Decrypt(value.String) + decrypted, err := e.Decrypt(value.String) if err != nil { return err } diff --git a/managed/utils/encryption/encryption_test.go b/managed/utils/encryption/encryption_test.go index 5f563c58c1..ae65cae410 100644 --- a/managed/utils/encryption/encryption_test.go +++ b/managed/utils/encryption/encryption_test.go @@ -25,7 +25,6 @@ import ( func TestEncryption(t *testing.T) { secret := "password1" - require.NoError(t, create(DefaultEncryptionKeyPath)) cipherText, err := Encrypt(secret) require.NoError(t, err) require.NotEmpty(t, cipherText) diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index 1591028e39..d5707e367a 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -26,7 +26,6 @@ import ( "github.com/stretchr/testify/require" "github.com/percona/pmm/managed/models" - "github.com/percona/pmm/managed/utils/encryption" ) const ( @@ -38,9 +37,6 @@ const ( func Open(tb testing.TB, setupFixtures models.SetupFixturesMode, migrationVersion *int) *sql.DB { tb.Helper() - err := encryption.Init(encryption.DefaultEncryptionKeyPath) - require.NoError(tb, err) - setupParams := models.SetupDBParams{ Address: "127.0.0.1:5432", Username: username, From f8f7368ce945b8e6413f830a9dacc0622c4b65d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 25 Jun 2024 15:43:58 +0200 Subject: [PATCH 052/215] PMM-13129 Encrypt, decrypt all other secret, credentials in agents. --- managed/models/agent_helpers.go | 102 ++++++++++++++++++++++--- managed/models/agent_model.go | 60 +++++++++++---- managed/utils/encryption/encryption.go | 2 +- 3 files changed, 138 insertions(+), 26 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index cf6eac03e1..e1ec7feee2 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -628,11 +628,23 @@ func CreateNodeExporter(q *reform.Querier, return nil, status.Errorf(codes.FailedPrecondition, "cannot use push_metrics_enabled with pmm_agent version=%q,"+ " it doesn't support it, minimum supported version=%q", pointer.GetString(pmmAgent.Version), PMMAgentWithPushMetricsSupport.String()) } + + encryptedUsername, err := encryption.Encrypt("") + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + encryptedPassword, err := encryption.Encrypt("") + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + row := &Agent{ AgentID: id, AgentType: NodeExporterType, PMMAgentID: &pmmAgentID, NodeID: pmmAgent.RunsOnNodeID, + Username: &encryptedUsername, + Password: &encryptedPassword, PushMetrics: pushMetrics, DisabledCollectors: disableCollectors, AgentPassword: agentPassword, @@ -894,13 +906,9 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara } } - encryptedUsername, err := encryption.Encrypt(params.Username) + encryptedParams, err := encryptCreateAgentParams(params) if err != nil { - logrus.Warningf("Encryption: %v", err) - } - encryptedPassword, err := encryption.Encrypt(params.Password) - if err != nil { - logrus.Warningf("Encryption: %v", err) + return nil, err } row := &Agent{ @@ -909,24 +917,24 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara PMMAgentID: ¶ms.PMMAgentID, ServiceID: pointer.ToStringOrNil(params.ServiceID), NodeID: pointer.ToStringOrNil(params.NodeID), - Username: &encryptedUsername, - Password: &encryptedPassword, + Username: &encryptedParams.Username, + Password: &encryptedParams.Password, AgentPassword: pointer.ToStringOrNil(params.AgentPassword), TLS: params.TLS, TLSSkipVerify: params.TLSSkipVerify, MySQLOptions: params.MySQLOptions, - MongoDBOptions: params.MongoDBOptions, + MongoDBOptions: encryptedParams.MongoDBOptions, PostgreSQLOptions: params.PostgreSQLOptions, TableCountTablestatsGroupLimit: params.TableCountTablestatsGroupLimit, MaxQueryLength: params.MaxQueryLength, QueryExamplesDisabled: params.QueryExamplesDisabled, CommentsParsingDisabled: params.CommentsParsingDisabled, MaxQueryLogSize: params.MaxQueryLogSize, - AWSAccessKey: pointer.ToStringOrNil(params.AWSAccessKey), - AWSSecretKey: pointer.ToStringOrNil(params.AWSSecretKey), + AWSAccessKey: &encryptedParams.AWSAccessKey, + AWSSecretKey: &encryptedParams.AWSSecretKey, RDSBasicMetricsDisabled: params.RDSBasicMetricsDisabled, RDSEnhancedMetricsDisabled: params.RDSEnhancedMetricsDisabled, - AzureOptions: params.AzureOptions, + AzureOptions: encryptedParams.AzureOptions, PushMetrics: params.PushMetrics, ExposeExporter: params.ExposeExporter, DisabledCollectors: params.DisableCollectors, @@ -943,6 +951,76 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara return row, nil } +func encryptCreateAgentParams(params *CreateAgentParams) (*CreateAgentParams, error) { + encryptedUsername, err := encryption.Encrypt(params.Username) + if err != nil { + return nil, err + } + encryptedPassword, err := encryption.Encrypt(params.Password) + if err != nil { + return nil, err + } + encryptedAgentPassword, err := encryption.Encrypt(params.AgentPassword) + if err != nil { + return nil, err + } + encryptedAWSAccessKey, err := encryption.Encrypt(params.AWSAccessKey) + if err != nil { + return nil, err + } + encryptedAWSSecretKey, err := encryption.Encrypt(params.AWSSecretKey) + if err != nil { + return nil, err + } + + encryptedMongoDBOptions := &MongoDBOptions{} + if params.MongoDBOptions != nil { + encryptedTLSCertificateKeyFilePassword, err := encryption.Encrypt(params.MongoDBOptions.TLSCertificateKeyFilePassword) + if err != nil { + return nil, err + } + *encryptedMongoDBOptions = *params.MongoDBOptions + encryptedMongoDBOptions.TLSCertificateKeyFilePassword = encryptedTLSCertificateKeyFilePassword + } + + var encryptedAzureOptions *AzureOptions + if params.AzureOptions != nil { + encryptedClientID, err := encryption.Encrypt(params.AzureOptions.ClientID) + if err != nil { + return nil, err + } + encryptedClientSecret, err := encryption.Encrypt(params.AzureOptions.ClientSecret) + if err != nil { + return nil, err + } + encryptedSubscriptionID, err := encryption.Encrypt(params.AzureOptions.SubscriptionID) + if err != nil { + return nil, err + } + encryptedTenantID, err := encryption.Encrypt(params.AzureOptions.TenantID) + if err != nil { + return nil, err + } + encryptedAzureOptions = &AzureOptions{ + SubscriptionID: encryptedSubscriptionID, + ClientID: encryptedClientID, + ClientSecret: encryptedClientSecret, + TenantID: encryptedTenantID, + ResourceGroup: params.AzureOptions.ResourceGroup, + } + } + + return &CreateAgentParams{ + Username: encryptedUsername, + Password: encryptedPassword, + AgentPassword: encryptedAgentPassword, + AWSAccessKey: encryptedAWSAccessKey, + AWSSecretKey: encryptedAWSSecretKey, + MongoDBOptions: encryptedMongoDBOptions, + AzureOptions: encryptedAzureOptions, + }, nil +} + // ChangeCommonAgentParams contains parameters that can be changed for all Agents. type ChangeCommonAgentParams struct { Disabled *bool // true - disable, false - enable, nil - do not change diff --git a/managed/models/agent_model.go b/managed/models/agent_model.go index e34dd16c9f..e6aa272c1c 100644 --- a/managed/models/agent_model.go +++ b/managed/models/agent_model.go @@ -256,7 +256,11 @@ func (s *Agent) SetCustomLabels(m map[string]string) error { func (s *Agent) GetAgentPassword() string { password := s.AgentID if pointer.GetString(s.AgentPassword) != "" { - password = *s.AgentPassword + decryptedAgentPassword, err := encryption.Decrypt(*s.AgentPassword) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + password = decryptedAgentPassword } return password @@ -299,9 +303,21 @@ func (c *DBConfig) Valid() bool { // DBConfig returns DBConfig for given Service with this agent. func (s *Agent) DBConfig(service *Service) *DBConfig { + username := pointer.GetString(s.Username) + password := pointer.GetString(s.Password) + + decryptedUsername, err := encryption.Decrypt(username) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + decryptedPassword, err := encryption.Decrypt(password) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + return &DBConfig{ - User: pointer.GetString(s.Username), - Password: pointer.GetString(s.Password), + User: decryptedUsername, + Password: decryptedPassword, Address: pointer.GetString(service.Address), Port: int(pointer.GetUint16(service.Port)), Socket: pointer.GetString(service.Socket), @@ -470,7 +486,12 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p q.Add("tlsCertificateKeyFile", tdp.Left+".TextFiles."+certificateKeyFilePlaceholder+tdp.Right) } if s.MongoDBOptions.TLSCertificateKeyFilePassword != "" { - q.Add("tlsCertificateKeyFilePassword", s.MongoDBOptions.TLSCertificateKeyFilePassword) + decryptedTLSCertificateKeyFilePassword, err := encryption.Decrypt(s.MongoDBOptions.TLSCertificateKeyFilePassword) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + + q.Add("tlsCertificateKeyFilePassword", decryptedTLSCertificateKeyFilePassword) } if s.MongoDBOptions.TLSCa != "" { q.Add("tlsCaFile", tdp.Left+".TextFiles."+caFilePlaceholder+tdp.Right) @@ -495,9 +516,9 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p RawQuery: q.Encode(), } switch { - case password != "": + case decryptedPassword != "": u.User = url.UserPassword(decryptedUsername, decryptedPassword) - case username != "": + case decryptedUsername != "": u.User = url.User(decryptedUsername) } dsn := u.String() @@ -557,9 +578,9 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p RawQuery: q.Encode(), } switch { - case password != "": + case decryptedPassword != "": u.User = url.UserPassword(decryptedUsername, decryptedPassword) - case username != "": + case decryptedUsername != "": u.User = url.User(decryptedUsername) } @@ -581,6 +602,15 @@ func (s *Agent) ExporterURL(q *reform.Querier) (string, error) { username := pointer.GetString(s.Username) password := pointer.GetString(s.Password) + decryptedUsername, err := encryption.Decrypt(username) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + decryptedPassword, err := encryption.Decrypt(password) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + host := "127.0.0.1" if !s.PushMetrics { node, err := FindNodeByID(q, *s.RunsOnNodeID) @@ -606,10 +636,10 @@ func (s *Agent) ExporterURL(q *reform.Querier) (string, error) { } switch { - case password != "": - u.User = url.UserPassword(username, password) - case username != "": - u.User = url.User(username) + case decryptedPassword != "": + u.User = url.UserPassword(decryptedUsername, decryptedPassword) + case decryptedUsername != "": + u.User = url.User(decryptedUsername) } return u.String(), nil } @@ -710,7 +740,11 @@ func (s Agent) TemplateDelimiters(svc *Service) *DelimiterPair { } case MongoDBServiceType: if s.MongoDBOptions != nil { - templateParams = append(templateParams, s.MongoDBOptions.TLSCertificateKeyFilePassword) + decryptedTLSCertificateKeyFilePassword, err := encryption.Decrypt(s.MongoDBOptions.TLSCertificateKeyFilePassword) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + templateParams = append(templateParams, decryptedTLSCertificateKeyFilePassword) } case PostgreSQLServiceType: if s.PostgreSQLOptions != nil { diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 2ce89be95a..410cae1407 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -36,7 +36,7 @@ var ( var DefaultEncryption = New(DefaultEncryptionKeyPath) -// New initialize encryption. +// New create encryption, if key on path doesnt exists will be generated. func New(keyPath string) *Encryption { e := new(Encryption) e.Path = keyPath From bba01fd401e6bdc2115026767a1596893aaab7f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 26 Jun 2024 12:39:42 +0200 Subject: [PATCH 053/215] PMM-13129 Changes, some refactors. --- managed/models/agent_helpers.go | 107 ++++++++++++---------- managed/services/management/agent.go | 76 ++++++++++++--- managed/services/management/agent_test.go | 1 - managed/utils/encryption/encryption.go | 12 +++ 4 files changed, 135 insertions(+), 61 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index e1ec7feee2..f1b2fab667 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -581,10 +581,21 @@ func createPMMAgentWithID(q *reform.Querier, id, runsOnNodeID string, customLabe // TODO https://jira.percona.com/browse/PMM-4496 // Check that Node is not remote. + encryptedUsername, err := encryption.Encrypt("") + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + encryptedPassword, err := encryption.Encrypt("") + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + agent := &Agent{ AgentID: id, AgentType: PMMAgentType, RunsOnNodeID: &runsOnNodeID, + Username: &encryptedUsername, + Password: &encryptedPassword, } if err := agent.SetCustomLabels(customLabels); err != nil { return nil, err @@ -906,7 +917,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara } } - encryptedParams, err := encryptCreateAgentParams(params) + err = encryptCreateAgentParams(params) if err != nil { return nil, err } @@ -917,24 +928,24 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara PMMAgentID: ¶ms.PMMAgentID, ServiceID: pointer.ToStringOrNil(params.ServiceID), NodeID: pointer.ToStringOrNil(params.NodeID), - Username: &encryptedParams.Username, - Password: &encryptedParams.Password, + Username: ¶ms.Username, + Password: ¶ms.Password, AgentPassword: pointer.ToStringOrNil(params.AgentPassword), TLS: params.TLS, TLSSkipVerify: params.TLSSkipVerify, MySQLOptions: params.MySQLOptions, - MongoDBOptions: encryptedParams.MongoDBOptions, + MongoDBOptions: params.MongoDBOptions, PostgreSQLOptions: params.PostgreSQLOptions, TableCountTablestatsGroupLimit: params.TableCountTablestatsGroupLimit, MaxQueryLength: params.MaxQueryLength, QueryExamplesDisabled: params.QueryExamplesDisabled, CommentsParsingDisabled: params.CommentsParsingDisabled, MaxQueryLogSize: params.MaxQueryLogSize, - AWSAccessKey: &encryptedParams.AWSAccessKey, - AWSSecretKey: &encryptedParams.AWSSecretKey, + AWSAccessKey: ¶ms.AWSAccessKey, + AWSSecretKey: ¶ms.AWSSecretKey, RDSBasicMetricsDisabled: params.RDSBasicMetricsDisabled, RDSEnhancedMetricsDisabled: params.RDSEnhancedMetricsDisabled, - AzureOptions: encryptedParams.AzureOptions, + AzureOptions: params.AzureOptions, PushMetrics: params.PushMetrics, ExposeExporter: params.ExposeExporter, DisabledCollectors: params.DisableCollectors, @@ -951,74 +962,74 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara return row, nil } -func encryptCreateAgentParams(params *CreateAgentParams) (*CreateAgentParams, error) { - encryptedUsername, err := encryption.Encrypt(params.Username) +func encryptCreateAgentParams(params *CreateAgentParams) error { + var err error + params.Username, err = encryption.Encrypt(params.Username) if err != nil { - return nil, err + return err } - encryptedPassword, err := encryption.Encrypt(params.Password) + params.Password, err = encryption.Encrypt(params.Password) if err != nil { - return nil, err + return err } - encryptedAgentPassword, err := encryption.Encrypt(params.AgentPassword) + params.AgentPassword, err = encryption.Encrypt(params.AgentPassword) if err != nil { - return nil, err + return err } - encryptedAWSAccessKey, err := encryption.Encrypt(params.AWSAccessKey) + params.AWSAccessKey, err = encryption.Encrypt(params.AWSAccessKey) if err != nil { - return nil, err + return err } - encryptedAWSSecretKey, err := encryption.Encrypt(params.AWSSecretKey) + params.AWSSecretKey, err = encryption.Encrypt(params.AWSSecretKey) if err != nil { - return nil, err + return err + } + + if params.MySQLOptions != nil { + params.MySQLOptions.TLSKey, err = encryption.Encrypt(params.MySQLOptions.TLSKey) + if err != nil { + return err + } + } + + if params.PostgreSQLOptions != nil { + params.PostgreSQLOptions.SSLKey, err = encryption.Encrypt(params.PostgreSQLOptions.SSLKey) + if err != nil { + return err + } } - encryptedMongoDBOptions := &MongoDBOptions{} if params.MongoDBOptions != nil { - encryptedTLSCertificateKeyFilePassword, err := encryption.Encrypt(params.MongoDBOptions.TLSCertificateKeyFilePassword) + params.MongoDBOptions.TLSCertificateKey, err = encryption.Encrypt(params.MongoDBOptions.TLSCertificateKey) if err != nil { - return nil, err + return err + } + params.MongoDBOptions.TLSCertificateKeyFilePassword, err = encryption.Encrypt(params.MongoDBOptions.TLSCertificateKeyFilePassword) + if err != nil { + return err } - *encryptedMongoDBOptions = *params.MongoDBOptions - encryptedMongoDBOptions.TLSCertificateKeyFilePassword = encryptedTLSCertificateKeyFilePassword } - var encryptedAzureOptions *AzureOptions if params.AzureOptions != nil { - encryptedClientID, err := encryption.Encrypt(params.AzureOptions.ClientID) + params.AzureOptions.ClientID, err = encryption.Encrypt(params.AzureOptions.ClientID) if err != nil { - return nil, err + return err } - encryptedClientSecret, err := encryption.Encrypt(params.AzureOptions.ClientSecret) + params.AzureOptions.ClientSecret, err = encryption.Encrypt(params.AzureOptions.ClientSecret) if err != nil { - return nil, err + return err } - encryptedSubscriptionID, err := encryption.Encrypt(params.AzureOptions.SubscriptionID) + params.AzureOptions.SubscriptionID, err = encryption.Encrypt(params.AzureOptions.SubscriptionID) if err != nil { - return nil, err + return err } - encryptedTenantID, err := encryption.Encrypt(params.AzureOptions.TenantID) + params.AzureOptions.TenantID, err = encryption.Encrypt(params.AzureOptions.TenantID) if err != nil { - return nil, err - } - encryptedAzureOptions = &AzureOptions{ - SubscriptionID: encryptedSubscriptionID, - ClientID: encryptedClientID, - ClientSecret: encryptedClientSecret, - TenantID: encryptedTenantID, - ResourceGroup: params.AzureOptions.ResourceGroup, + return err } } - return &CreateAgentParams{ - Username: encryptedUsername, - Password: encryptedPassword, - AgentPassword: encryptedAgentPassword, - AWSAccessKey: encryptedAWSAccessKey, - AWSSecretKey: encryptedAWSSecretKey, - MongoDBOptions: encryptedMongoDBOptions, - AzureOptions: encryptedAzureOptions, - }, nil + return nil } // ChangeCommonAgentParams contains parameters that can be changed for all Agents. diff --git a/managed/services/management/agent.go b/managed/services/management/agent.go index 4e6c14a244..4e3d47a6fa 100644 --- a/managed/services/management/agent.go +++ b/managed/services/management/agent.go @@ -136,22 +136,38 @@ func (s *AgentService) agentToAPI(agent *models.Agent) (*agentv1beta1.UniversalA return nil, err } + decryptedUsername, err := encryption.Decrypt(pointer.GetString(agent.Username)) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } decryptedPassword, err := encryption.Decrypt(pointer.GetString(agent.Password)) if err != nil { logrus.Warningf("Encryption: %v", err) } + decryptedAWSAccessKey, err := encryption.Decrypt(pointer.GetString(agent.AWSAccessKey)) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + decryptedAgentPassword, err := encryption.Decrypt(pointer.GetString(agent.AgentPassword)) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + decryptedAWSSecretKey, err := encryption.Decrypt(pointer.GetString(agent.AWSSecretKey)) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } ua := &agentv1beta1.UniversalAgent{ AgentId: agent.AgentID, AgentType: string(agent.AgentType), - AwsAccessKey: pointer.GetString(agent.AWSAccessKey), + AwsAccessKey: decryptedAWSAccessKey, CreatedAt: timestamppb.New(agent.CreatedAt), CustomLabels: labels, Disabled: agent.Disabled, DisabledCollectors: agent.DisabledCollectors, IsConnected: s.r.IsConnected(agent.AgentID), - IsAgentPasswordSet: agent.AgentPassword != nil, - IsAwsSecretKeySet: agent.AWSSecretKey != nil, + IsAgentPasswordSet: decryptedAgentPassword != "", + IsAwsSecretKeySet: decryptedAWSSecretKey != "", IsPasswordSet: decryptedPassword != "", ListenPort: uint32(pointer.GetUint16(agent.ListenPort)), LogLevel: pointer.GetString(agent.LogLevel), @@ -175,44 +191,80 @@ func (s *AgentService) agentToAPI(agent *models.Agent) (*agentv1beta1.UniversalA TableCountTablestatsGroupLimit: agent.TableCountTablestatsGroupLimit, Tls: agent.TLS, TlsSkipVerify: agent.TLSSkipVerify, - Username: pointer.GetString(agent.Username), + Username: decryptedUsername, UpdatedAt: timestamppb.New(agent.UpdatedAt), Version: pointer.GetString(agent.Version), } if agent.AzureOptions != nil { + decryptedClientID, err := encryption.Decrypt(agent.AzureOptions.ClientID) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + decryptedClientSecret, err := encryption.Decrypt(agent.AzureOptions.ClientSecret) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + decryptedTenantID, err := encryption.Decrypt(agent.AzureOptions.TenantID) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + decryptedSubscriptionID, err := encryption.Decrypt(agent.AzureOptions.SubscriptionID) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + ua.AzureOptions = &agentv1beta1.UniversalAgent_AzureOptions{ - ClientId: agent.AzureOptions.ClientID, - IsClientSecretSet: agent.AzureOptions.ClientSecret != "", - TenantId: agent.AzureOptions.TenantID, - SubscriptionId: agent.AzureOptions.SubscriptionID, + ClientId: decryptedClientID, + IsClientSecretSet: decryptedClientSecret != "", + TenantId: decryptedTenantID, + SubscriptionId: decryptedSubscriptionID, ResourceGroup: agent.AzureOptions.ResourceGroup, } } if agent.MySQLOptions != nil { + decryptedTLSKey, err := encryption.Decrypt(agent.MySQLOptions.TLSKey) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + ua.MysqlOptions = &agentv1beta1.UniversalAgent_MySQLOptions{ - IsTlsKeySet: agent.MySQLOptions.TLSKey != "", + IsTlsKeySet: decryptedTLSKey != "", } } if agent.PostgreSQLOptions != nil { + decryptedSSLKey, err := encryption.Decrypt(agent.PostgreSQLOptions.SSLKey) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + ua.PostgresqlOptions = &agentv1beta1.UniversalAgent_PostgreSQLOptions{ - IsSslKeySet: agent.PostgreSQLOptions.SSLKey != "", + IsSslKeySet: decryptedSSLKey != "", AutoDiscoveryLimit: agent.PostgreSQLOptions.AutoDiscoveryLimit, MaxExporterConnections: agent.PostgreSQLOptions.MaxExporterConnections, } } if agent.MongoDBOptions != nil { + decryptedTLSCertificateKey, err := encryption.Decrypt(agent.MongoDBOptions.TLSCertificateKey) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + decryptedTLSCertificateKeyFilePassword, err := encryption.Decrypt(agent.MongoDBOptions.TLSCertificateKeyFilePassword) + if err != nil { + logrus.Warningf("Encryption: %v", err) + } + ua.MongoDbOptions = &agentv1beta1.UniversalAgent_MongoDBOptions{ AuthenticationMechanism: agent.MongoDBOptions.AuthenticationMechanism, AuthenticationDatabase: agent.MongoDBOptions.AuthenticationDatabase, CollectionsLimit: agent.MongoDBOptions.CollectionsLimit, EnableAllCollectors: agent.MongoDBOptions.EnableAllCollectors, StatsCollections: agent.MongoDBOptions.StatsCollections, - IsTlsCertificateKeySet: agent.MongoDBOptions.TLSCertificateKey != "", - IsTlsCertificateKeyFilePasswordSet: agent.MongoDBOptions.TLSCertificateKeyFilePassword != "", + IsTlsCertificateKeySet: decryptedTLSCertificateKey != "", + IsTlsCertificateKeyFilePasswordSet: decryptedTLSCertificateKeyFilePassword != "", } } diff --git a/managed/services/management/agent_test.go b/managed/services/management/agent_test.go index f8becefd51..61477dde01 100644 --- a/managed/services/management/agent_test.go +++ b/managed/services/management/agent_test.go @@ -257,7 +257,6 @@ func TestAgentService(t *testing.T) { AgentType: "azure_database_exporter", PmmAgentId: "/agent_id/00000000-0000-4000-8000-000000000007", IsConnected: false, - Username: response.GetAgents()[0].Username, CreatedAt: timestamppb.New(now), UpdatedAt: timestamppb.New(now), ServiceId: "/service_id/00000000-0000-4000-8000-000000000006", diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 410cae1407..9adb7fcc2f 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -74,6 +74,13 @@ func (e *Encryption) Encrypt(secret string) (string, error) { return "", ErrEncryptionNotInitialized } + if secret != "" { + _, err := base64.StdEncoding.DecodeString(secret) + if err == nil { + return secret, nil + } + } + cipherText, err := e.Primitive.Encrypt([]byte(secret), []byte("")) if err != nil { return secret, err @@ -122,6 +129,7 @@ func (e *Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection) error if value != "" { _, err := base64.StdEncoding.DecodeString(value) if err == nil { + res.SetValues[k][i] = value continue } } @@ -160,6 +168,10 @@ func (e *Encryption) Decrypt(cipherText string) (string, error) { return "", ErrEncryptionNotInitialized } + if cipherText == "" { + return "", nil + } + decoded, err := base64.StdEncoding.DecodeString(cipherText) if err != nil { return cipherText, err From c423e1d4a746c6d852eee414a72b2c7634b37016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 26 Jun 2024 12:53:59 +0200 Subject: [PATCH 054/215] PMM-13129 Another changes. --- managed/models/agent_helpers.go | 2 +- managed/services/inventory/agents_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index f1b2fab667..f9dac2cb5e 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -930,7 +930,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara NodeID: pointer.ToStringOrNil(params.NodeID), Username: ¶ms.Username, Password: ¶ms.Password, - AgentPassword: pointer.ToStringOrNil(params.AgentPassword), + AgentPassword: ¶ms.AgentPassword, TLS: params.TLS, TLSSkipVerify: params.TLSSkipVerify, MySQLOptions: params.MySQLOptions, diff --git a/managed/services/inventory/agents_test.go b/managed/services/inventory/agents_test.go index fa064c608b..1f7438e3c4 100644 --- a/managed/services/inventory/agents_test.go +++ b/managed/services/inventory/agents_test.go @@ -383,7 +383,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000006", PmmAgentId: "pmm-server", NodeId: "/node_id/00000000-0000-4000-8000-000000000005", - AwsAccessKey: "EXAMPLE_ACCESS_KEY", + AwsAccessKey: agent.AwsAccessKey, CustomLabels: map[string]string{"baz": "qux"}, Status: inventorypb.AgentStatus_UNKNOWN, } @@ -692,7 +692,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000006", PmmAgentId: "pmm-server", NodeId: "/node_id/00000000-0000-4000-8000-000000000005", - AwsAccessKey: "EXAMPLE_ACCESS_KEY", + AwsAccessKey: agent.AwsAccessKey, CustomLabels: map[string]string{"baz": "qux"}, PushMetricsEnabled: true, Status: inventorypb.AgentStatus_UNKNOWN, From 5ab0a81bf86e3ec762df040c5604da1464927067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 26 Jun 2024 13:23:33 +0200 Subject: [PATCH 055/215] PMM-13129 Refactor. --- managed/models/agent_helpers.go | 106 ++++++++++++++++++++++++++- managed/models/agent_model.go | 80 ++++---------------- managed/services/management/agent.go | 85 ++++----------------- 3 files changed, 128 insertions(+), 143 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index f9dac2cb5e..2a448e5a4d 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -230,7 +230,9 @@ func FindAgents(q *reform.Querier, filters AgentFilters) ([]*Agent, error) { agents := make([]*Agent, len(structs)) for i, s := range structs { - agents[i] = s.(*Agent) //nolint:forcetypeassert + agent := s.(*Agent) //nolint:forcetypeassert + decryptAgent(agent) + agents[i] = agent } return agents, nil @@ -251,6 +253,8 @@ func FindAgentByID(q *reform.Querier, id string) (*Agent, error) { return nil, errors.WithStack(err) } + decryptAgent(agent) + return agent, nil } @@ -273,7 +277,9 @@ func FindAgentsByIDs(q *reform.Querier, ids []string) ([]*Agent, error) { res := make([]*Agent, len(structs)) for i, s := range structs { - res[i] = s.(*Agent) //nolint:forcetypeassert + agent := s.(*Agent) //nolint:forcetypeassert + decryptAgent(agent) + res[i] = agent } return res, nil } @@ -324,7 +330,9 @@ func FindDBConfigForService(q *reform.Querier, serviceID string) (*DBConfig, err res := make([]*Agent, len(structs)) for i, s := range structs { - res[i] = s.(*Agent) //nolint:forcetypeassert + agent := s.(*Agent) //nolint:forcetypeassert + decryptAgent(agent) + res[i] = agent } if len(res) == 0 { @@ -352,6 +360,7 @@ func FindPMMAgentsRunningOnNode(q *reform.Querier, nodeID string) ([]*Agent, err res := make([]*Agent, 0, len(structs)) for _, str := range structs { row := str.(*Agent) //nolint:forcetypeassert + decryptAgent(row) res = append(res, row) } @@ -397,6 +406,7 @@ func FindPMMAgentsForService(q *reform.Querier, serviceID string) ([]*Agent, err res := make([]*Agent, 0, len(pmmAgentRecords)) for _, str := range pmmAgentRecords { row := str.(*Agent) //nolint:forcetypeassert + decryptAgent(row) res = append(res, row) } @@ -478,7 +488,9 @@ func FindAgentsForScrapeConfig(q *reform.Querier, pmmAgentID *string, pushMetric res := make([]*Agent, len(allAgents)) for i, s := range allAgents { - res[i] = s.(*Agent) //nolint:forcetypeassert + agent := s.(*Agent) //nolint:forcetypeassert + decryptAgent(agent) + res[i] = agent } return res, nil } @@ -605,6 +617,8 @@ func createPMMAgentWithID(q *reform.Querier, id, runsOnNodeID string, customLabe return nil, errors.WithStack(err) } + decryptAgent(agent) + return agent, nil } @@ -669,6 +683,8 @@ func CreateNodeExporter(q *reform.Querier, return nil, errors.WithStack(err) } + decryptAgent(row) + return row, nil } @@ -763,6 +779,8 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar return nil, errors.WithStack(err) } + decryptAgent(row) + return row, nil } @@ -959,6 +977,8 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara return nil, errors.WithStack(err) } + decryptAgent(row) + return row, nil } @@ -1032,6 +1052,84 @@ func encryptCreateAgentParams(params *CreateAgentParams) error { return nil } +func decryptAgent(agent *Agent) error { + username, err := encryption.Decrypt(pointer.GetString(agent.Username)) + if err != nil { + return err + } + agent.Username = &username + + password, err := encryption.Encrypt(pointer.GetString(agent.Password)) + if err != nil { + return err + } + agent.Password = &password + + agentPassword, err := encryption.Encrypt(pointer.GetString(agent.AgentPassword)) + if err != nil { + return err + } + agent.AgentPassword = &agentPassword + + awsAccessKey, err := encryption.Encrypt(pointer.GetString(agent.AWSAccessKey)) + if err != nil { + return err + } + agent.AWSAccessKey = &awsAccessKey + + awsSecretKey, err := encryption.Encrypt(pointer.GetString(agent.AWSSecretKey)) + if err != nil { + return err + } + agent.AWSSecretKey = &awsSecretKey + + if agent.MySQLOptions != nil { + agent.MySQLOptions.TLSKey, err = encryption.Encrypt(agent.MySQLOptions.TLSKey) + if err != nil { + return err + } + } + + if agent.PostgreSQLOptions != nil { + agent.PostgreSQLOptions.SSLKey, err = encryption.Encrypt(agent.PostgreSQLOptions.SSLKey) + if err != nil { + return err + } + } + + if agent.MongoDBOptions != nil { + agent.MongoDBOptions.TLSCertificateKey, err = encryption.Encrypt(agent.MongoDBOptions.TLSCertificateKey) + if err != nil { + return err + } + agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = encryption.Encrypt(agent.MongoDBOptions.TLSCertificateKeyFilePassword) + if err != nil { + return err + } + } + + if agent.AzureOptions != nil { + agent.AzureOptions.ClientID, err = encryption.Encrypt(agent.AzureOptions.ClientID) + if err != nil { + return err + } + agent.AzureOptions.ClientSecret, err = encryption.Encrypt(agent.AzureOptions.ClientSecret) + if err != nil { + return err + } + agent.AzureOptions.SubscriptionID, err = encryption.Encrypt(agent.AzureOptions.SubscriptionID) + if err != nil { + return err + } + agent.AzureOptions.TenantID, err = encryption.Encrypt(agent.AzureOptions.TenantID) + if err != nil { + return err + } + } + + return nil +} + // ChangeCommonAgentParams contains parameters that can be changed for all Agents. type ChangeCommonAgentParams struct { Disabled *bool // true - disable, false - enable, nil - do not change diff --git a/managed/models/agent_model.go b/managed/models/agent_model.go index e6aa272c1c..5caf2c562c 100644 --- a/managed/models/agent_model.go +++ b/managed/models/agent_model.go @@ -30,11 +30,9 @@ import ( "github.com/go-sql-driver/mysql" "github.com/lib/pq" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "golang.org/x/crypto/bcrypt" "gopkg.in/reform.v1" - "github.com/percona/pmm/managed/utils/encryption" "github.com/percona/pmm/version" ) @@ -256,11 +254,7 @@ func (s *Agent) SetCustomLabels(m map[string]string) error { func (s *Agent) GetAgentPassword() string { password := s.AgentID if pointer.GetString(s.AgentPassword) != "" { - decryptedAgentPassword, err := encryption.Decrypt(*s.AgentPassword) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - password = decryptedAgentPassword + password = *s.AgentPassword } return password @@ -303,21 +297,9 @@ func (c *DBConfig) Valid() bool { // DBConfig returns DBConfig for given Service with this agent. func (s *Agent) DBConfig(service *Service) *DBConfig { - username := pointer.GetString(s.Username) - password := pointer.GetString(s.Password) - - decryptedUsername, err := encryption.Decrypt(username) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - decryptedPassword, err := encryption.Decrypt(password) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - return &DBConfig{ - User: decryptedUsername, - Password: decryptedPassword, + User: pointer.GetString(s.Username), + Password: pointer.GetString(s.Password), Address: pointer.GetString(service.Address), Port: int(pointer.GetUint16(service.Port)), Socket: pointer.GetString(service.Socket), @@ -340,15 +322,6 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p username := pointer.GetString(s.Username) password := pointer.GetString(s.Password) - decryptedUsername, err := encryption.Decrypt(username) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - decryptedPassword, err := encryption.Decrypt(password) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - if tdp == nil { tdp = s.TemplateDelimiters(service) } @@ -356,8 +329,8 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p switch s.AgentType { case MySQLdExporterType: cfg := mysql.NewConfig() - cfg.User = decryptedUsername - cfg.Passwd = decryptedPassword + cfg.User = username + cfg.Passwd = password cfg.Net = unix cfg.Addr = socket if socket == "" { @@ -486,12 +459,7 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p q.Add("tlsCertificateKeyFile", tdp.Left+".TextFiles."+certificateKeyFilePlaceholder+tdp.Right) } if s.MongoDBOptions.TLSCertificateKeyFilePassword != "" { - decryptedTLSCertificateKeyFilePassword, err := encryption.Decrypt(s.MongoDBOptions.TLSCertificateKeyFilePassword) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - - q.Add("tlsCertificateKeyFilePassword", decryptedTLSCertificateKeyFilePassword) + q.Add("tlsCertificateKeyFilePassword", s.MongoDBOptions.TLSCertificateKeyFilePassword) } if s.MongoDBOptions.TLSCa != "" { q.Add("tlsCaFile", tdp.Left+".TextFiles."+caFilePlaceholder+tdp.Right) @@ -602,15 +570,6 @@ func (s *Agent) ExporterURL(q *reform.Querier) (string, error) { username := pointer.GetString(s.Username) password := pointer.GetString(s.Password) - decryptedUsername, err := encryption.Decrypt(username) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - decryptedPassword, err := encryption.Decrypt(password) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - host := "127.0.0.1" if !s.PushMetrics { node, err := FindNodeByID(q, *s.RunsOnNodeID) @@ -636,10 +595,10 @@ func (s *Agent) ExporterURL(q *reform.Querier) (string, error) { } switch { - case decryptedPassword != "": - u.User = url.UserPassword(decryptedUsername, decryptedPassword) - case decryptedUsername != "": - u.User = url.User(decryptedUsername) + case password != "": + u.User = url.UserPassword(username, password) + case username != "": + u.User = url.User(username) } return u.String(), nil } @@ -717,19 +676,10 @@ func (s Agent) Files() map[string]string { // TemplateDelimiters returns a pair of safe template delimiters that are not present in agent parameters. func (s Agent) TemplateDelimiters(svc *Service) *DelimiterPair { - decryptedUsername, err := encryption.Decrypt(pointer.GetString(s.Username)) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - decryptedPassword, err := encryption.Decrypt(pointer.GetString(s.Password)) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - templateParams := []string{ pointer.GetString(svc.Address), - decryptedUsername, - decryptedPassword, + pointer.GetString(s.Username), + pointer.GetString(s.Password), pointer.GetString(s.MetricsPath), } @@ -740,11 +690,7 @@ func (s Agent) TemplateDelimiters(svc *Service) *DelimiterPair { } case MongoDBServiceType: if s.MongoDBOptions != nil { - decryptedTLSCertificateKeyFilePassword, err := encryption.Decrypt(s.MongoDBOptions.TLSCertificateKeyFilePassword) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - templateParams = append(templateParams, decryptedTLSCertificateKeyFilePassword) + templateParams = append(templateParams, s.MongoDBOptions.TLSCertificateKeyFilePassword) } case PostgreSQLServiceType: if s.PostgreSQLOptions != nil { diff --git a/managed/services/management/agent.go b/managed/services/management/agent.go index 4e3d47a6fa..644c9d6b1c 100644 --- a/managed/services/management/agent.go +++ b/managed/services/management/agent.go @@ -19,7 +19,6 @@ import ( "context" "github.com/AlekSi/pointer" - "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" @@ -27,7 +26,6 @@ import ( agentv1beta1 "github.com/percona/pmm/api/managementpb/agent" "github.com/percona/pmm/managed/models" - "github.com/percona/pmm/managed/utils/encryption" ) // AgentService represents service for working with agents. @@ -136,39 +134,18 @@ func (s *AgentService) agentToAPI(agent *models.Agent) (*agentv1beta1.UniversalA return nil, err } - decryptedUsername, err := encryption.Decrypt(pointer.GetString(agent.Username)) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - decryptedPassword, err := encryption.Decrypt(pointer.GetString(agent.Password)) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - decryptedAWSAccessKey, err := encryption.Decrypt(pointer.GetString(agent.AWSAccessKey)) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - decryptedAgentPassword, err := encryption.Decrypt(pointer.GetString(agent.AgentPassword)) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - decryptedAWSSecretKey, err := encryption.Decrypt(pointer.GetString(agent.AWSSecretKey)) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - ua := &agentv1beta1.UniversalAgent{ AgentId: agent.AgentID, AgentType: string(agent.AgentType), - AwsAccessKey: decryptedAWSAccessKey, + AwsAccessKey: pointer.GetString(agent.AWSAccessKey), CreatedAt: timestamppb.New(agent.CreatedAt), CustomLabels: labels, Disabled: agent.Disabled, DisabledCollectors: agent.DisabledCollectors, IsConnected: s.r.IsConnected(agent.AgentID), - IsAgentPasswordSet: decryptedAgentPassword != "", - IsAwsSecretKeySet: decryptedAWSSecretKey != "", - IsPasswordSet: decryptedPassword != "", + IsAgentPasswordSet: pointer.GetString(agent.AgentPassword) != "", + IsAwsSecretKeySet: pointer.GetString(agent.AWSSecretKey) != "", + IsPasswordSet: pointer.GetString(agent.Password) != "", ListenPort: uint32(pointer.GetUint16(agent.ListenPort)), LogLevel: pointer.GetString(agent.LogLevel), MaxQueryLength: agent.MaxQueryLength, @@ -191,80 +168,44 @@ func (s *AgentService) agentToAPI(agent *models.Agent) (*agentv1beta1.UniversalA TableCountTablestatsGroupLimit: agent.TableCountTablestatsGroupLimit, Tls: agent.TLS, TlsSkipVerify: agent.TLSSkipVerify, - Username: decryptedUsername, + Username: pointer.GetString(agent.Username), UpdatedAt: timestamppb.New(agent.UpdatedAt), Version: pointer.GetString(agent.Version), } if agent.AzureOptions != nil { - decryptedClientID, err := encryption.Decrypt(agent.AzureOptions.ClientID) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - decryptedClientSecret, err := encryption.Decrypt(agent.AzureOptions.ClientSecret) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - decryptedTenantID, err := encryption.Decrypt(agent.AzureOptions.TenantID) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - decryptedSubscriptionID, err := encryption.Decrypt(agent.AzureOptions.SubscriptionID) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - ua.AzureOptions = &agentv1beta1.UniversalAgent_AzureOptions{ - ClientId: decryptedClientID, - IsClientSecretSet: decryptedClientSecret != "", - TenantId: decryptedTenantID, - SubscriptionId: decryptedSubscriptionID, + ClientId: agent.AzureOptions.ClientID, + IsClientSecretSet: agent.AzureOptions.ClientSecret != "", + TenantId: agent.AzureOptions.TenantID, + SubscriptionId: agent.AzureOptions.SubscriptionID, ResourceGroup: agent.AzureOptions.ResourceGroup, } } if agent.MySQLOptions != nil { - decryptedTLSKey, err := encryption.Decrypt(agent.MySQLOptions.TLSKey) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - ua.MysqlOptions = &agentv1beta1.UniversalAgent_MySQLOptions{ - IsTlsKeySet: decryptedTLSKey != "", + IsTlsKeySet: agent.MySQLOptions.TLSKey != "", } } if agent.PostgreSQLOptions != nil { - decryptedSSLKey, err := encryption.Decrypt(agent.PostgreSQLOptions.SSLKey) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - ua.PostgresqlOptions = &agentv1beta1.UniversalAgent_PostgreSQLOptions{ - IsSslKeySet: decryptedSSLKey != "", + IsSslKeySet: agent.PostgreSQLOptions.SSLKey != "", AutoDiscoveryLimit: agent.PostgreSQLOptions.AutoDiscoveryLimit, MaxExporterConnections: agent.PostgreSQLOptions.MaxExporterConnections, } } if agent.MongoDBOptions != nil { - decryptedTLSCertificateKey, err := encryption.Decrypt(agent.MongoDBOptions.TLSCertificateKey) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - decryptedTLSCertificateKeyFilePassword, err := encryption.Decrypt(agent.MongoDBOptions.TLSCertificateKeyFilePassword) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - ua.MongoDbOptions = &agentv1beta1.UniversalAgent_MongoDBOptions{ AuthenticationMechanism: agent.MongoDBOptions.AuthenticationMechanism, AuthenticationDatabase: agent.MongoDBOptions.AuthenticationDatabase, CollectionsLimit: agent.MongoDBOptions.CollectionsLimit, EnableAllCollectors: agent.MongoDBOptions.EnableAllCollectors, StatsCollections: agent.MongoDBOptions.StatsCollections, - IsTlsCertificateKeySet: decryptedTLSCertificateKey != "", - IsTlsCertificateKeyFilePasswordSet: decryptedTLSCertificateKeyFilePassword != "", + IsTlsCertificateKeySet: agent.MongoDBOptions.TLSCertificateKey != "", + IsTlsCertificateKeyFilePasswordSet: agent.MongoDBOptions.TLSCertificateKeyFilePassword != "", } } From bc249ffda5abb1eb97027e6153a7eea687bf423f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 26 Jun 2024 13:31:50 +0200 Subject: [PATCH 056/215] PMM-13129 Fix. --- managed/models/agent_model.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/managed/models/agent_model.go b/managed/models/agent_model.go index 5caf2c562c..6a1ae3f369 100644 --- a/managed/models/agent_model.go +++ b/managed/models/agent_model.go @@ -364,8 +364,8 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p case QANMySQLPerfSchemaAgentType, QANMySQLSlowlogAgentType: cfg := mysql.NewConfig() - cfg.User = decryptedUsername - cfg.Passwd = decryptedPassword + cfg.User = username + cfg.Passwd = password cfg.Net = unix cfg.Addr = socket if socket == "" { @@ -403,8 +403,8 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p case ProxySQLExporterType: cfg := mysql.NewConfig() - cfg.User = decryptedUsername - cfg.Passwd = decryptedPassword + cfg.User = username + cfg.Passwd = password cfg.Net = unix cfg.Addr = socket if socket == "" { @@ -484,10 +484,10 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p RawQuery: q.Encode(), } switch { - case decryptedPassword != "": - u.User = url.UserPassword(decryptedUsername, decryptedPassword) - case decryptedUsername != "": - u.User = url.User(decryptedUsername) + case password != "": + u.User = url.UserPassword(username, password) + case username != "": + u.User = url.User(username) } dsn := u.String() dsn = strings.ReplaceAll(dsn, url.QueryEscape(tdp.Left), tdp.Left) @@ -546,10 +546,10 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p RawQuery: q.Encode(), } switch { - case decryptedPassword != "": - u.User = url.UserPassword(decryptedUsername, decryptedPassword) - case decryptedUsername != "": - u.User = url.User(decryptedUsername) + case password != "": + u.User = url.UserPassword(username, password) + case username != "": + u.User = url.User(username) } dsn := u.String() From 8c9d21abd06e9ad42852fc75c7dd961d601aa2c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 26 Jun 2024 14:41:32 +0200 Subject: [PATCH 057/215] PMM-13129 Changes. --- managed/models/agent_helpers.go | 68 +++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 2a448e5a4d..12b20b4035 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -1053,75 +1053,87 @@ func encryptCreateAgentParams(params *CreateAgentParams) error { } func decryptAgent(agent *Agent) error { - username, err := encryption.Decrypt(pointer.GetString(agent.Username)) - if err != nil { - return err + if agent.Username != nil { + username, err := encryption.Decrypt(*agent.Username) + if err != nil { + return err + } + agent.Username = &username } - agent.Username = &username - password, err := encryption.Encrypt(pointer.GetString(agent.Password)) - if err != nil { - return err + if agent.Password != nil { + password, err := encryption.Decrypt(*agent.Password) + if err != nil { + return err + } + agent.Password = &password } - agent.Password = &password - agentPassword, err := encryption.Encrypt(pointer.GetString(agent.AgentPassword)) - if err != nil { - return err + if agent.AgentPassword != nil { + agentPassword, err := encryption.Decrypt(*agent.AgentPassword) + if err != nil { + return err + } + agent.AgentPassword = &agentPassword } - agent.AgentPassword = &agentPassword - awsAccessKey, err := encryption.Encrypt(pointer.GetString(agent.AWSAccessKey)) - if err != nil { - return err + if agent.AWSAccessKey != nil { + awsAccessKey, err := encryption.Decrypt(*agent.AWSAccessKey) + if err != nil { + return err + } + agent.AWSAccessKey = &awsAccessKey } - agent.AWSAccessKey = &awsAccessKey - awsSecretKey, err := encryption.Encrypt(pointer.GetString(agent.AWSSecretKey)) - if err != nil { - return err + if agent.AWSSecretKey != nil { + awsSecretKey, err := encryption.Decrypt(*agent.AWSSecretKey) + if err != nil { + return err + } + agent.AWSSecretKey = &awsSecretKey } - agent.AWSSecretKey = &awsSecretKey + var err error if agent.MySQLOptions != nil { - agent.MySQLOptions.TLSKey, err = encryption.Encrypt(agent.MySQLOptions.TLSKey) + agent.MySQLOptions.TLSKey, err = encryption.Decrypt(agent.MySQLOptions.TLSKey) if err != nil { return err } + } if agent.PostgreSQLOptions != nil { - agent.PostgreSQLOptions.SSLKey, err = encryption.Encrypt(agent.PostgreSQLOptions.SSLKey) + agent.PostgreSQLOptions.SSLKey, err = encryption.Decrypt(agent.PostgreSQLOptions.SSLKey) if err != nil { return err } } if agent.MongoDBOptions != nil { - agent.MongoDBOptions.TLSCertificateKey, err = encryption.Encrypt(agent.MongoDBOptions.TLSCertificateKey) + agent.MongoDBOptions.TLSCertificateKey, err = encryption.Decrypt(agent.MongoDBOptions.TLSCertificateKey) if err != nil { return err } - agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = encryption.Encrypt(agent.MongoDBOptions.TLSCertificateKeyFilePassword) + agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = encryption.Decrypt(agent.MongoDBOptions.TLSCertificateKeyFilePassword) if err != nil { return err } } if agent.AzureOptions != nil { - agent.AzureOptions.ClientID, err = encryption.Encrypt(agent.AzureOptions.ClientID) + agent.AzureOptions.ClientID, err = encryption.Decrypt(agent.AzureOptions.ClientID) if err != nil { return err } - agent.AzureOptions.ClientSecret, err = encryption.Encrypt(agent.AzureOptions.ClientSecret) + agent.AzureOptions.ClientSecret, err = encryption.Decrypt(agent.AzureOptions.ClientSecret) if err != nil { return err } - agent.AzureOptions.SubscriptionID, err = encryption.Encrypt(agent.AzureOptions.SubscriptionID) + agent.AzureOptions.SubscriptionID, err = encryption.Decrypt(agent.AzureOptions.SubscriptionID) if err != nil { return err } - agent.AzureOptions.TenantID, err = encryption.Encrypt(agent.AzureOptions.TenantID) + agent.AzureOptions.TenantID, err = encryption.Decrypt(agent.AzureOptions.TenantID) if err != nil { return err } From f35ad6751c1ff9c9059c5aa35e77cd054d5e6c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 26 Jun 2024 14:56:54 +0200 Subject: [PATCH 058/215] PMM-13129 Changes. --- managed/models/agent_helpers.go | 33 -------------------------- managed/models/agent_helpers_test.go | 2 -- managed/utils/encryption/encryption.go | 21 ++++------------ 3 files changed, 5 insertions(+), 51 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 12b20b4035..463fde0457 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -593,21 +593,10 @@ func createPMMAgentWithID(q *reform.Querier, id, runsOnNodeID string, customLabe // TODO https://jira.percona.com/browse/PMM-4496 // Check that Node is not remote. - encryptedUsername, err := encryption.Encrypt("") - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - encryptedPassword, err := encryption.Encrypt("") - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - agent := &Agent{ AgentID: id, AgentType: PMMAgentType, RunsOnNodeID: &runsOnNodeID, - Username: &encryptedUsername, - Password: &encryptedPassword, } if err := agent.SetCustomLabels(customLabels); err != nil { return nil, err @@ -654,22 +643,11 @@ func CreateNodeExporter(q *reform.Querier, " it doesn't support it, minimum supported version=%q", pointer.GetString(pmmAgent.Version), PMMAgentWithPushMetricsSupport.String()) } - encryptedUsername, err := encryption.Encrypt("") - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - encryptedPassword, err := encryption.Encrypt("") - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - row := &Agent{ AgentID: id, AgentType: NodeExporterType, PMMAgentID: &pmmAgentID, NodeID: pmmAgent.RunsOnNodeID, - Username: &encryptedUsername, - Password: &encryptedPassword, PushMetrics: pushMetrics, DisabledCollectors: disableCollectors, AgentPassword: agentPassword, @@ -750,23 +728,12 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar metricsPath = "/metrics" } - encryptedUsername, err := encryption.Encrypt(params.Username) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - encryptedPassword, err := encryption.Encrypt(params.Password) - if err != nil { - logrus.Warningf("Encryption: %v", err) - } - row := &Agent{ PMMAgentID: pmmAgentID, AgentID: id, AgentType: ExternalExporterType, RunsOnNodeID: runsOnNodeID, ServiceID: pointer.ToStringOrNil(params.ServiceID), - Username: &encryptedUsername, - Password: &encryptedPassword, MetricsScheme: &scheme, MetricsPath: &metricsPath, ListenPort: pointer.ToUint16(uint16(params.ListenPort)), diff --git a/managed/models/agent_helpers_test.go b/managed/models/agent_helpers_test.go index fd63f7d4d3..4ec50ede52 100644 --- a/managed/models/agent_helpers_test.go +++ b/managed/models/agent_helpers_test.go @@ -474,8 +474,6 @@ func TestAgentHelpers(t *testing.T) { RunsOnNodeID: pointer.ToString("N1"), ServiceID: pointer.ToString("S1"), ListenPort: pointer.ToUint16(9104), - Username: agent.Username, - Password: agent.Password, MetricsPath: pointer.ToString("/metrics"), MetricsScheme: pointer.ToString("http"), CreatedAt: now, diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 9adb7fcc2f..1cabefd184 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -121,20 +121,13 @@ func (e *Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection) error for k, v := range res.SetValues { for i, val := range v { - var value string - if v, ok := val.(*sql.NullString); ok { - value = v.String - } - - if value != "" { - _, err := base64.StdEncoding.DecodeString(value) - if err == nil { - res.SetValues[k][i] = value - continue - } + value := val.(*sql.NullString) + if !value.Valid { + res.SetValues[k][i] = sql.NullString{} + continue } - encrypted, err := e.Encrypt(value) + encrypted, err := e.Encrypt(value.String) if err != nil { return err } @@ -226,10 +219,6 @@ func (e *Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection) error if err != nil { return err } - if decrypted == "" { - res.SetValues[k][i] = sql.NullString{} - continue - } res.SetValues[k][i] = decrypted } data := slices.Concat([]any{}, v) From d1abf77dcad99dc5b729e93e627ff0aa5d2634b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 26 Jun 2024 20:46:17 +0200 Subject: [PATCH 059/215] PMM-13129 Save. --- managed/models/agent_helpers.go | 38 ++++++++++++----------- managed/services/management/agent_test.go | 5 ++- managed/utils/encryption/encryption.go | 10 +++--- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 463fde0457..38f5cb66f4 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -231,7 +231,7 @@ func FindAgents(q *reform.Querier, filters AgentFilters) ([]*Agent, error) { agents := make([]*Agent, len(structs)) for i, s := range structs { agent := s.(*Agent) //nolint:forcetypeassert - decryptAgent(agent) + fmt.Println(decryptAgent(agent)) agents[i] = agent } @@ -734,6 +734,8 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar AgentType: ExternalExporterType, RunsOnNodeID: runsOnNodeID, ServiceID: pointer.ToStringOrNil(params.ServiceID), + Username: pointer.ToStringOrNil(params.Username), + Password: pointer.ToStringOrNil(params.Password), MetricsScheme: &scheme, MetricsPath: &metricsPath, ListenPort: pointer.ToUint16(uint16(params.ListenPort)), @@ -913,9 +915,9 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara PMMAgentID: ¶ms.PMMAgentID, ServiceID: pointer.ToStringOrNil(params.ServiceID), NodeID: pointer.ToStringOrNil(params.NodeID), - Username: ¶ms.Username, - Password: ¶ms.Password, - AgentPassword: ¶ms.AgentPassword, + Username: pointer.ToStringOrNil(params.Username), + Password: pointer.ToStringOrNil(params.Password), + AgentPassword: pointer.ToStringOrNil(params.AgentPassword), TLS: params.TLS, TLSSkipVerify: params.TLSSkipVerify, MySQLOptions: params.MySQLOptions, @@ -926,8 +928,8 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara QueryExamplesDisabled: params.QueryExamplesDisabled, CommentsParsingDisabled: params.CommentsParsingDisabled, MaxQueryLogSize: params.MaxQueryLogSize, - AWSAccessKey: ¶ms.AWSAccessKey, - AWSSecretKey: ¶ms.AWSSecretKey, + AWSAccessKey: pointer.ToStringOrNil(params.AWSAccessKey), + AWSSecretKey: pointer.ToStringOrNil(params.AWSSecretKey), RDSBasicMetricsDisabled: params.RDSBasicMetricsDisabled, RDSEnhancedMetricsDisabled: params.RDSEnhancedMetricsDisabled, AzureOptions: params.AzureOptions, @@ -951,10 +953,10 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara func encryptCreateAgentParams(params *CreateAgentParams) error { var err error - params.Username, err = encryption.Encrypt(params.Username) - if err != nil { - return err - } + // params.Username, err = encryption.Encrypt(params.Username) + // if err != nil { + // return err + // } params.Password, err = encryption.Encrypt(params.Password) if err != nil { return err @@ -1015,18 +1017,18 @@ func encryptCreateAgentParams(params *CreateAgentParams) error { return err } } - + fmt.Println(params) return nil } func decryptAgent(agent *Agent) error { - if agent.Username != nil { - username, err := encryption.Decrypt(*agent.Username) - if err != nil { - return err - } - agent.Username = &username - } + // if agent.Username != nil { + // username, err := encryption.Decrypt(*agent.Username) + // if err != nil { + // return err + // } + // agent.Username = &username + // } if agent.Password != nil { password, err := encryption.Decrypt(*agent.Password) diff --git a/managed/services/management/agent_test.go b/managed/services/management/agent_test.go index 61477dde01..6a8c34e3ee 100644 --- a/managed/services/management/agent_test.go +++ b/managed/services/management/agent_test.go @@ -127,7 +127,7 @@ func TestAgentService(t *testing.T) { IsConnected: false, CreatedAt: timestamppb.New(now), UpdatedAt: timestamppb.New(now), - Username: response.GetAgents()[0].Username, + Username: "postgres", PostgresqlOptions: &agentv1beta1.UniversalAgent_PostgreSQLOptions{ IsSslKeySet: false, }, @@ -143,7 +143,7 @@ func TestAgentService(t *testing.T) { IsConnected: false, CreatedAt: timestamppb.New(now), UpdatedAt: timestamppb.New(now), - Username: response.GetAgents()[1].Username, + Username: "postgres", PostgresqlOptions: &agentv1beta1.UniversalAgent_PostgreSQLOptions{ IsSslKeySet: false, }, @@ -206,7 +206,6 @@ func TestAgentService(t *testing.T) { AgentType: "rds_exporter", PmmAgentId: "/agent_id/00000000-0000-4000-8000-000000000007", IsConnected: false, - Username: response.GetAgents()[0].Username, CreatedAt: timestamppb.New(now), UpdatedAt: timestamppb.New(now), ServiceId: "/service_id/00000000-0000-4000-8000-000000000006", diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 1cabefd184..8150425ec3 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -20,6 +20,7 @@ import ( "database/sql" "encoding/base64" "errors" + "fmt" "os" "slices" @@ -71,7 +72,7 @@ func Encrypt(secret string) (string, error) { // Encrypt returns input string encrypted. func (e *Encryption) Encrypt(secret string) (string, error) { if e == nil || e.Primitive == nil { - return "", ErrEncryptionNotInitialized + return secret, ErrEncryptionNotInitialized } if secret != "" { @@ -158,11 +159,11 @@ func Decrypt(cipherText string) (string, error) { // Decrypt returns input string decrypted. func (e *Encryption) Decrypt(cipherText string) (string, error) { if e == nil || e.Primitive == nil { - return "", ErrEncryptionNotInitialized + return cipherText, ErrEncryptionNotInitialized } if cipherText == "" { - return "", nil + return cipherText, nil } decoded, err := base64.StdEncoding.DecodeString(cipherText) @@ -171,6 +172,7 @@ func (e *Encryption) Decrypt(cipherText string) (string, error) { } secret, err := e.Primitive.Decrypt(decoded, []byte("")) if err != nil { + fmt.Printf("%s failed %v \n\n", string(cipherText), err) return cipherText, err } @@ -211,7 +213,7 @@ func (e *Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection) error for i, val := range v { value := val.(*sql.NullString) if !value.Valid { - res.SetValues[k][i] = sql.NullString{} + res.SetValues[k][i] = nil continue } From 88271bbd1afab837f23734941e04da285bb959b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 27 Jun 2024 11:23:30 +0200 Subject: [PATCH 060/215] PMM-13129 Changes. --- managed/models/agent_helpers.go | 84 +++++++++++++++------------------ 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 38f5cb66f4..f111de7c1c 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -231,7 +231,7 @@ func FindAgents(q *reform.Querier, filters AgentFilters) ([]*Agent, error) { agents := make([]*Agent, len(structs)) for i, s := range structs { agent := s.(*Agent) //nolint:forcetypeassert - fmt.Println(decryptAgent(agent)) + decryptAgent(agent) agents[i] = agent } @@ -904,10 +904,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara } } - err = encryptCreateAgentParams(params) - if err != nil { - return nil, err - } + encryptCreateAgentParams(params) row := &Agent{ AgentID: id, @@ -951,89 +948,88 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara return row, nil } -func encryptCreateAgentParams(params *CreateAgentParams) error { +func encryptCreateAgentParams(params *CreateAgentParams) { var err error - // params.Username, err = encryption.Encrypt(params.Username) - // if err != nil { - // return err - // } + params.Username, err = encryption.Encrypt(params.Username) + if err != nil { + logrus.Warning(err) + } params.Password, err = encryption.Encrypt(params.Password) if err != nil { - return err + logrus.Warning(err) } params.AgentPassword, err = encryption.Encrypt(params.AgentPassword) if err != nil { - return err + logrus.Warning(err) } params.AWSAccessKey, err = encryption.Encrypt(params.AWSAccessKey) if err != nil { - return err + logrus.Warning(err) } params.AWSSecretKey, err = encryption.Encrypt(params.AWSSecretKey) if err != nil { - return err + logrus.Warning(err) } if params.MySQLOptions != nil { params.MySQLOptions.TLSKey, err = encryption.Encrypt(params.MySQLOptions.TLSKey) if err != nil { - return err + logrus.Warning(err) } } if params.PostgreSQLOptions != nil { params.PostgreSQLOptions.SSLKey, err = encryption.Encrypt(params.PostgreSQLOptions.SSLKey) if err != nil { - return err + logrus.Warning(err) } } if params.MongoDBOptions != nil { params.MongoDBOptions.TLSCertificateKey, err = encryption.Encrypt(params.MongoDBOptions.TLSCertificateKey) if err != nil { - return err + logrus.Warning(err) } params.MongoDBOptions.TLSCertificateKeyFilePassword, err = encryption.Encrypt(params.MongoDBOptions.TLSCertificateKeyFilePassword) if err != nil { - return err + logrus.Warning(err) } } if params.AzureOptions != nil { params.AzureOptions.ClientID, err = encryption.Encrypt(params.AzureOptions.ClientID) if err != nil { - return err + logrus.Warning(err) } params.AzureOptions.ClientSecret, err = encryption.Encrypt(params.AzureOptions.ClientSecret) if err != nil { - return err + logrus.Warning(err) } params.AzureOptions.SubscriptionID, err = encryption.Encrypt(params.AzureOptions.SubscriptionID) if err != nil { - return err + logrus.Warning(err) } params.AzureOptions.TenantID, err = encryption.Encrypt(params.AzureOptions.TenantID) if err != nil { - return err + logrus.Warning(err) } } fmt.Println(params) - return nil } -func decryptAgent(agent *Agent) error { - // if agent.Username != nil { - // username, err := encryption.Decrypt(*agent.Username) - // if err != nil { - // return err - // } - // agent.Username = &username - // } +func decryptAgent(agent *Agent) { + if agent.Username != nil { + username, err := encryption.Decrypt(*agent.Username) + if err != nil { + logrus.Warning(err) + } + agent.Username = &username + } if agent.Password != nil { password, err := encryption.Decrypt(*agent.Password) if err != nil { - return err + logrus.Warning(err) } agent.Password = &password } @@ -1041,7 +1037,7 @@ func decryptAgent(agent *Agent) error { if agent.AgentPassword != nil { agentPassword, err := encryption.Decrypt(*agent.AgentPassword) if err != nil { - return err + logrus.Warning(err) } agent.AgentPassword = &agentPassword } @@ -1049,7 +1045,7 @@ func decryptAgent(agent *Agent) error { if agent.AWSAccessKey != nil { awsAccessKey, err := encryption.Decrypt(*agent.AWSAccessKey) if err != nil { - return err + logrus.Warning(err) } agent.AWSAccessKey = &awsAccessKey } @@ -1057,7 +1053,7 @@ func decryptAgent(agent *Agent) error { if agent.AWSSecretKey != nil { awsSecretKey, err := encryption.Decrypt(*agent.AWSSecretKey) if err != nil { - return err + logrus.Warning(err) } agent.AWSSecretKey = &awsSecretKey } @@ -1066,7 +1062,7 @@ func decryptAgent(agent *Agent) error { if agent.MySQLOptions != nil { agent.MySQLOptions.TLSKey, err = encryption.Decrypt(agent.MySQLOptions.TLSKey) if err != nil { - return err + logrus.Warning(err) } } @@ -1074,41 +1070,39 @@ func decryptAgent(agent *Agent) error { if agent.PostgreSQLOptions != nil { agent.PostgreSQLOptions.SSLKey, err = encryption.Decrypt(agent.PostgreSQLOptions.SSLKey) if err != nil { - return err + logrus.Warning(err) } } if agent.MongoDBOptions != nil { agent.MongoDBOptions.TLSCertificateKey, err = encryption.Decrypt(agent.MongoDBOptions.TLSCertificateKey) if err != nil { - return err + logrus.Warning(err) } agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = encryption.Decrypt(agent.MongoDBOptions.TLSCertificateKeyFilePassword) if err != nil { - return err + logrus.Warning(err) } } if agent.AzureOptions != nil { agent.AzureOptions.ClientID, err = encryption.Decrypt(agent.AzureOptions.ClientID) if err != nil { - return err + logrus.Warning(err) } agent.AzureOptions.ClientSecret, err = encryption.Decrypt(agent.AzureOptions.ClientSecret) if err != nil { - return err + logrus.Warning(err) } agent.AzureOptions.SubscriptionID, err = encryption.Decrypt(agent.AzureOptions.SubscriptionID) if err != nil { - return err + logrus.Warning(err) } agent.AzureOptions.TenantID, err = encryption.Decrypt(agent.AzureOptions.TenantID) if err != nil { - return err + logrus.Warning(err) } } - - return nil } // ChangeCommonAgentParams contains parameters that can be changed for all Agents. From 9bb9b8aed8eac3f77c4c8865b994ecbab4db0d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 27 Jun 2024 11:35:29 +0200 Subject: [PATCH 061/215] PMM-13129 Another changes. --- managed/models/agent_helpers_test.go | 1 - managed/models/agent_model.go | 1 - managed/services/inventory/agents_test.go | 24 +++++++++++------------ managed/services/management/rds_test.go | 8 ++++---- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/managed/models/agent_helpers_test.go b/managed/models/agent_helpers_test.go index 4ec50ede52..0285a76485 100644 --- a/managed/models/agent_helpers_test.go +++ b/managed/models/agent_helpers_test.go @@ -467,7 +467,6 @@ func TestAgentHelpers(t *testing.T) { ListenPort: 9104, }) require.NoError(t, err) - assert.Equal(t, &models.Agent{ AgentID: agent.AgentID, AgentType: models.ExternalExporterType, diff --git a/managed/models/agent_model.go b/managed/models/agent_model.go index 6a1ae3f369..ab838e423f 100644 --- a/managed/models/agent_model.go +++ b/managed/models/agent_model.go @@ -492,7 +492,6 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, p dsn := u.String() dsn = strings.ReplaceAll(dsn, url.QueryEscape(tdp.Left), tdp.Left) dsn = strings.ReplaceAll(dsn, url.QueryEscape(tdp.Right), tdp.Right) - return dsn case PostgresExporterType, QANPostgreSQLPgStatementsAgentType, QANPostgreSQLPgStatMonitorAgentType: diff --git a/managed/services/inventory/agents_test.go b/managed/services/inventory/agents_test.go index 1f7438e3c4..24dd2e2bb3 100644 --- a/managed/services/inventory/agents_test.go +++ b/managed/services/inventory/agents_test.go @@ -125,7 +125,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000008", PmmAgentId: "/agent_id/00000000-0000-4000-8000-000000000005", ServiceId: s.ServiceId, - Username: actualAgent.(*inventorypb.MySQLdExporter).Username, + Username: "username", Status: inventorypb.AgentStatus_UNKNOWN, } assert.Equal(t, expectedMySQLdExporter, actualAgent) @@ -154,7 +154,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-00000000000a", PmmAgentId: pmmAgent.AgentId, ServiceId: ms.ServiceId, - Username: actualAgent.(*inventorypb.MongoDBExporter).Username, + Username: "username", Status: inventorypb.AgentStatus_UNKNOWN, } assert.Equal(t, expectedMongoDBExporter, actualAgent) @@ -173,7 +173,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-00000000000b", PmmAgentId: pmmAgent.AgentId, ServiceId: s.ServiceId, - Username: actualAgent.(*inventorypb.QANMySQLSlowlogAgent).Username, + Username: "username", Status: inventorypb.AgentStatus_UNKNOWN, } assert.Equal(t, expectedQANMySQLSlowlogAgent, actualAgent) @@ -200,7 +200,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-00000000000d", PmmAgentId: pmmAgent.AgentId, ServiceId: ps.ServiceId, - Username: actualAgent.(*inventorypb.PostgresExporter).Username, + Username: "username", Status: inventorypb.AgentStatus_UNKNOWN, } assert.Equal(t, expectedPostgresExporter, actualAgent) @@ -220,7 +220,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-00000000000e", RunsOnNodeId: models.PMMServerNodeID, ServiceId: ps.ServiceId, - Username: actualAgent.(*inventorypb.ExternalExporter).Username, + Username: "username", Scheme: "http", MetricsPath: "/metrics", ListenPort: 9222, @@ -383,7 +383,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000006", PmmAgentId: "pmm-server", NodeId: "/node_id/00000000-0000-4000-8000-000000000005", - AwsAccessKey: agent.AwsAccessKey, + AwsAccessKey: "EXAMPLE_ACCESS_KEY", CustomLabels: map[string]string{"baz": "qux"}, Status: inventorypb.AgentStatus_UNKNOWN, } @@ -415,7 +415,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000006", RunsOnNodeId: models.PMMServerNodeID, ServiceId: service.ServiceId, - Username: agent.Username, + Username: "username", Scheme: "http", MetricsPath: "/metrics", ListenPort: 12345, @@ -499,7 +499,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000007", PmmAgentId: pmmAgent.AgentId, ServiceId: ms.ServiceId, - Username: actualAgent.Username, + Username: "username", PushMetricsEnabled: true, Status: inventorypb.AgentStatus_UNKNOWN, } @@ -590,7 +590,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000007", PmmAgentId: pmmAgent.AgentId, ServiceId: ps.ServiceId, - Username: actualAgent.Username, + Username: "username", PushMetricsEnabled: true, Status: inventorypb.AgentStatus_UNKNOWN, } @@ -647,7 +647,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000007", PmmAgentId: "/agent_id/00000000-0000-4000-8000-000000000005", ServiceId: s.ServiceId, - Username: actualAgent.Username, + Username: "username", PushMetricsEnabled: true, Status: inventorypb.AgentStatus_UNKNOWN, } @@ -692,7 +692,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000006", PmmAgentId: "pmm-server", NodeId: "/node_id/00000000-0000-4000-8000-000000000005", - AwsAccessKey: agent.AwsAccessKey, + AwsAccessKey: "EXAMPLE_ACCESS_KEY", CustomLabels: map[string]string{"baz": "qux"}, PushMetricsEnabled: true, Status: inventorypb.AgentStatus_UNKNOWN, @@ -724,7 +724,7 @@ func TestAgents(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000006", RunsOnNodeId: models.PMMServerNodeID, ServiceId: service.ServiceId, - Username: agent.Username, + Username: "username", Scheme: "http", MetricsPath: "/metrics", ListenPort: 12345, diff --git a/managed/services/management/rds_test.go b/managed/services/management/rds_test.go index b3f435ab20..c103825de1 100644 --- a/managed/services/management/rds_test.go +++ b/managed/services/management/rds_test.go @@ -305,7 +305,7 @@ func TestRDSService(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000008", PmmAgentId: "pmm-server", ServiceId: "/service_id/00000000-0000-4000-8000-000000000007", - Username: resp.MysqldExporter.Username, + Username: "username", TablestatsGroupTableLimit: 1000, Status: inventorypb.AgentStatus_UNKNOWN, }, @@ -313,7 +313,7 @@ func TestRDSService(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-000000000009", PmmAgentId: "pmm-server", ServiceId: "/service_id/00000000-0000-4000-8000-000000000007", - Username: resp.QanMysqlPerfschema.Username, + Username: "username", QueryExamplesDisabled: true, Status: inventorypb.AgentStatus_UNKNOWN, }, @@ -395,7 +395,7 @@ func TestRDSService(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-00000000000d", PmmAgentId: "pmm-server", ServiceId: "/service_id/00000000-0000-4000-8000-00000000000c", - Username: resp.PostgresqlExporter.Username, + Username: "username", Status: inventorypb.AgentStatus_UNKNOWN, AutoDiscoveryLimit: 10, MaxExporterConnections: 15, @@ -404,7 +404,7 @@ func TestRDSService(t *testing.T) { AgentId: "/agent_id/00000000-0000-4000-8000-00000000000e", PmmAgentId: "pmm-server", ServiceId: "/service_id/00000000-0000-4000-8000-00000000000c", - Username: resp.QanPostgresqlPgstatements.Username, + Username: "username", Status: inventorypb.AgentStatus_UNKNOWN, }, } From 92a1b593dec6708a975bd0d40cdfdea1cbfd9109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 27 Jun 2024 11:40:57 +0200 Subject: [PATCH 062/215] PMM-13129 Refactor, another changes. --- managed/models/agent_helpers.go | 90 +++++++++++++++----------- managed/utils/encryption/encryption.go | 17 +---- 2 files changed, 56 insertions(+), 51 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index f111de7c1c..8b5505aa21 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -606,8 +606,6 @@ func createPMMAgentWithID(q *reform.Querier, id, runsOnNodeID string, customLabe return nil, errors.WithStack(err) } - decryptAgent(agent) - return agent, nil } @@ -642,7 +640,6 @@ func CreateNodeExporter(q *reform.Querier, return nil, status.Errorf(codes.FailedPrecondition, "cannot use push_metrics_enabled with pmm_agent version=%q,"+ " it doesn't support it, minimum supported version=%q", pointer.GetString(pmmAgent.Version), PMMAgentWithPushMetricsSupport.String()) } - row := &Agent{ AgentID: id, AgentType: NodeExporterType, @@ -654,6 +651,8 @@ func CreateNodeExporter(q *reform.Querier, LogLevel: pointer.ToStringOrNil(logLevel), ExposeExporter: exposeExporter, } + encryptAgent(row) + if err := row.SetCustomLabels(customLabels); err != nil { return nil, err } @@ -727,7 +726,6 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar if metricsPath == "" { metricsPath = "/metrics" } - row := &Agent{ PMMAgentID: pmmAgentID, AgentID: id, @@ -741,6 +739,8 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar ListenPort: pointer.ToUint16(uint16(params.ListenPort)), PushMetrics: params.PushMetrics, } + encryptAgent(row) + if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err } @@ -904,8 +904,6 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara } } - encryptCreateAgentParams(params) - row := &Agent{ AgentID: id, AgentType: agentType, @@ -935,6 +933,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara DisabledCollectors: params.DisableCollectors, LogLevel: pointer.ToStringOrNil(params.LogLevel), } + encryptAgent(row) if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err @@ -948,73 +947,92 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara return row, nil } -func encryptCreateAgentParams(params *CreateAgentParams) { - var err error - params.Username, err = encryption.Encrypt(params.Username) - if err != nil { - logrus.Warning(err) +func encryptAgent(agent *Agent) { + if agent.Username != nil { + username, err := encryption.Encrypt(*agent.Username) + if err != nil { + logrus.Warning(err) + } + agent.Username = &username } - params.Password, err = encryption.Encrypt(params.Password) - if err != nil { - logrus.Warning(err) + + if agent.Password != nil { + password, err := encryption.Encrypt(*agent.Password) + if err != nil { + logrus.Warning(err) + } + agent.Password = &password } - params.AgentPassword, err = encryption.Encrypt(params.AgentPassword) - if err != nil { - logrus.Warning(err) + + if agent.AgentPassword != nil { + agentPassword, err := encryption.Encrypt(*agent.AgentPassword) + if err != nil { + logrus.Warning(err) + } + agent.AgentPassword = &agentPassword } - params.AWSAccessKey, err = encryption.Encrypt(params.AWSAccessKey) - if err != nil { - logrus.Warning(err) + + if agent.AWSAccessKey != nil { + awsAccessKey, err := encryption.Encrypt(*agent.AWSAccessKey) + if err != nil { + logrus.Warning(err) + } + agent.AWSAccessKey = &awsAccessKey } - params.AWSSecretKey, err = encryption.Encrypt(params.AWSSecretKey) - if err != nil { - logrus.Warning(err) + + if agent.AWSSecretKey != nil { + awsSecretKey, err := encryption.Encrypt(*agent.AWSSecretKey) + if err != nil { + logrus.Warning(err) + } + agent.AWSSecretKey = &awsSecretKey } - if params.MySQLOptions != nil { - params.MySQLOptions.TLSKey, err = encryption.Encrypt(params.MySQLOptions.TLSKey) + var err error + if agent.MySQLOptions != nil { + agent.MySQLOptions.TLSKey, err = encryption.Encrypt(agent.MySQLOptions.TLSKey) if err != nil { logrus.Warning(err) } + } - if params.PostgreSQLOptions != nil { - params.PostgreSQLOptions.SSLKey, err = encryption.Encrypt(params.PostgreSQLOptions.SSLKey) + if agent.PostgreSQLOptions != nil { + agent.PostgreSQLOptions.SSLKey, err = encryption.Encrypt(agent.PostgreSQLOptions.SSLKey) if err != nil { logrus.Warning(err) } } - if params.MongoDBOptions != nil { - params.MongoDBOptions.TLSCertificateKey, err = encryption.Encrypt(params.MongoDBOptions.TLSCertificateKey) + if agent.MongoDBOptions != nil { + agent.MongoDBOptions.TLSCertificateKey, err = encryption.Encrypt(agent.MongoDBOptions.TLSCertificateKey) if err != nil { logrus.Warning(err) } - params.MongoDBOptions.TLSCertificateKeyFilePassword, err = encryption.Encrypt(params.MongoDBOptions.TLSCertificateKeyFilePassword) + agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = encryption.Encrypt(agent.MongoDBOptions.TLSCertificateKeyFilePassword) if err != nil { logrus.Warning(err) } } - if params.AzureOptions != nil { - params.AzureOptions.ClientID, err = encryption.Encrypt(params.AzureOptions.ClientID) + if agent.AzureOptions != nil { + agent.AzureOptions.ClientID, err = encryption.Encrypt(agent.AzureOptions.ClientID) if err != nil { logrus.Warning(err) } - params.AzureOptions.ClientSecret, err = encryption.Encrypt(params.AzureOptions.ClientSecret) + agent.AzureOptions.ClientSecret, err = encryption.Encrypt(agent.AzureOptions.ClientSecret) if err != nil { logrus.Warning(err) } - params.AzureOptions.SubscriptionID, err = encryption.Encrypt(params.AzureOptions.SubscriptionID) + agent.AzureOptions.SubscriptionID, err = encryption.Encrypt(agent.AzureOptions.SubscriptionID) if err != nil { logrus.Warning(err) } - params.AzureOptions.TenantID, err = encryption.Encrypt(params.AzureOptions.TenantID) + agent.AzureOptions.TenantID, err = encryption.Encrypt(agent.AzureOptions.TenantID) if err != nil { logrus.Warning(err) } } - fmt.Println(params) } func decryptAgent(agent *Agent) { diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 8150425ec3..ab471e76e2 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -20,7 +20,6 @@ import ( "database/sql" "encoding/base64" "errors" - "fmt" "os" "slices" @@ -30,10 +29,8 @@ import ( // DefaultEncryptionKeyPath contains default PMM encryption key path. const DefaultEncryptionKeyPath = "/srv/pmm-encryption.key" -var ( - // ErrEncryptionNotInitialized is error in case of encryption is not initialized. - ErrEncryptionNotInitialized = errors.New("encryption is not initialized") -) +// ErrEncryptionNotInitialized is error in case of encryption is not initialized. +var ErrEncryptionNotInitialized = errors.New("encryption is not initialized") var DefaultEncryption = New(DefaultEncryptionKeyPath) @@ -75,13 +72,6 @@ func (e *Encryption) Encrypt(secret string) (string, error) { return secret, ErrEncryptionNotInitialized } - if secret != "" { - _, err := base64.StdEncoding.DecodeString(secret) - if err == nil { - return secret, nil - } - } - cipherText, err := e.Primitive.Encrypt([]byte(secret), []byte("")) if err != nil { return secret, err @@ -161,18 +151,15 @@ func (e *Encryption) Decrypt(cipherText string) (string, error) { if e == nil || e.Primitive == nil { return cipherText, ErrEncryptionNotInitialized } - if cipherText == "" { return cipherText, nil } - decoded, err := base64.StdEncoding.DecodeString(cipherText) if err != nil { return cipherText, err } secret, err := e.Primitive.Decrypt(decoded, []byte("")) if err != nil { - fmt.Printf("%s failed %v \n\n", string(cipherText), err) return cipherText, err } From e3fe487f2ce37ee3a7728d7428969c426bbfffec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 27 Jun 2024 11:57:50 +0200 Subject: [PATCH 063/215] PMM-13129 Disable migration encryption until it is done. --- managed/models/database.go | 66 ++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index f19330b52b..ffb5c83833 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -36,8 +36,6 @@ import ( "google.golang.org/grpc/status" "gopkg.in/reform.v1" "gopkg.in/reform.v1/dialects/postgresql" - - "github.com/percona/pmm/managed/utils/encryption" ) const ( @@ -1065,38 +1063,38 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. return nil, err } - host, p, err := net.SplitHostPort(params.Address) - if err != nil { - return nil, err - } - - port, err := strconv.ParseInt(p, 10, 16) - if err != nil { - return nil, err - } - - c := &encryption.DatabaseConnection{ - Host: host, - Port: int16(port), - User: params.Username, - Password: params.Password, - SSLMode: params.SSLMode, - SSLCAPath: params.SSLCAPath, - SSLKeyPath: params.SSLKeyPath, - SSLCertPath: params.SSLCertPath, - EncryptedItems: []encryption.EncryptedItem{ - { - Database: "pmm-managed", - Table: "agents", - Identificators: []string{"agent_id"}, - Columns: []string{"username", "password"}, - }, - }, - } - - if err := encryption.EncryptDB(ctx, c); err != nil { - return nil, err - } + // host, p, err := net.SplitHostPort(params.Address) + // if err != nil { + // return nil, err + // } + + // port, err := strconv.ParseInt(p, 10, 16) + // if err != nil { + // return nil, err + // } + + // c := &encryption.DatabaseConnection{ + // Host: host, + // Port: int16(port), + // User: params.Username, + // Password: params.Password, + // SSLMode: params.SSLMode, + // SSLCAPath: params.SSLCAPath, + // SSLKeyPath: params.SSLKeyPath, + // SSLCertPath: params.SSLCertPath, + // EncryptedItems: []encryption.EncryptedItem{ + // { + // Database: "pmm-managed", + // Table: "agents", + // Identificators: []string{"agent_id"}, + // Columns: []string{"username", "password"}, + // }, + // }, + // } + + // if err := encryption.EncryptDB(ctx, c); err != nil { + // return nil, err + // } return db, nil } From 29d90dfaeb58af4defe48dc58a08dcdb70b17214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 27 Jun 2024 13:01:53 +0200 Subject: [PATCH 064/215] PMM-13129 Basics for settings and migration. --- .../server/change_settings_responses.go | 3 + .../client/server/get_settings_responses.go | 3 + api/serverpb/json/serverpb.json | 16 + api/serverpb/server.pb.go | 418 +++++++++--------- api/serverpb/server.proto | 2 + api/swagger/swagger-dev.json | 16 + api/swagger/swagger.json | 16 + managed/models/database.go | 81 ++-- managed/models/settings.go | 3 + managed/models/settings_helpers.go | 7 + managed/utils/encryption/encryption.go | 3 +- 11 files changed, 332 insertions(+), 236 deletions(-) diff --git a/api/serverpb/json/client/server/change_settings_responses.go b/api/serverpb/json/client/server/change_settings_responses.go index ba76893daa..275b7f67b3 100644 --- a/api/serverpb/json/client/server/change_settings_responses.go +++ b/api/serverpb/json/client/server/change_settings_responses.go @@ -700,6 +700,9 @@ type ChangeSettingsOKBodySettings struct { // Default Access Control role ID for new users. DefaultRoleID int64 `json:"default_role_id,omitempty"` + // Contains all already encrypted tables in format db.table. + EncryptedItems []string `json:"encrypted_items"` + // metrics resolutions MetricsResolutions *ChangeSettingsOKBodySettingsMetricsResolutions `json:"metrics_resolutions,omitempty"` diff --git a/api/serverpb/json/client/server/get_settings_responses.go b/api/serverpb/json/client/server/get_settings_responses.go index 44cad33ce1..4a426c4306 100644 --- a/api/serverpb/json/client/server/get_settings_responses.go +++ b/api/serverpb/json/client/server/get_settings_responses.go @@ -509,6 +509,9 @@ type GetSettingsOKBodySettings struct { // Default Access Control role ID for new users. DefaultRoleID int64 `json:"default_role_id,omitempty"` + // Contains all already encrypted tables in format db.table. + EncryptedItems []string `json:"encrypted_items"` + // metrics resolutions MetricsResolutions *GetSettingsOKBodySettingsMetricsResolutions `json:"metrics_resolutions,omitempty"` diff --git a/api/serverpb/json/serverpb.json b/api/serverpb/json/serverpb.json index f425d7fc35..40bd4a9f17 100644 --- a/api/serverpb/json/serverpb.json +++ b/api/serverpb/json/serverpb.json @@ -338,6 +338,14 @@ "type": "boolean", "x-order": 15 }, + "encrypted_items": { + "description": "Contains all already encrypted tables in format db.table.", + "type": "array", + "items": { + "type": "string" + }, + "x-order": 17 + }, "metrics_resolutions": { "description": "MetricsResolutions represents Prometheus exporters metrics resolutions.", "type": "object", @@ -531,6 +539,14 @@ "type": "boolean", "x-order": 15 }, + "encrypted_items": { + "description": "Contains all already encrypted tables in format db.table.", + "type": "array", + "items": { + "type": "string" + }, + "x-order": 17 + }, "metrics_resolutions": { "description": "MetricsResolutions represents Prometheus exporters metrics resolutions.", "type": "object", diff --git a/api/serverpb/server.pb.go b/api/serverpb/server.pb.go index 427155434a..82b62de7a8 100644 --- a/api/serverpb/server.pb.go +++ b/api/serverpb/server.pb.go @@ -1031,6 +1031,8 @@ type Settings struct { EnableAccessControl bool `protobuf:"varint,21,opt,name=enable_access_control,json=enableAccessControl,proto3" json:"enable_access_control,omitempty"` // Default Access Control role ID for new users. DefaultRoleId uint32 `protobuf:"varint,22,opt,name=default_role_id,json=defaultRoleId,proto3" json:"default_role_id,omitempty"` + // Contains all already encrypted tables in format db.table. + EncryptedItems []string `protobuf:"bytes,23,rep,name=encrypted_items,json=encryptedItems,proto3" json:"encrypted_items,omitempty"` } func (x *Settings) Reset() { @@ -1184,6 +1186,13 @@ func (x *Settings) GetDefaultRoleId() uint32 { return 0 } +func (x *Settings) GetEncryptedItems() []string { + if x != nil { + return x.EncryptedItems + } + return nil +} + type GetSettingsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1742,7 +1751,7 @@ var file_serverpb_server_proto_rawDesc = []byte{ 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x66, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22, 0xe7, 0x06, + 0x71, 0x75, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22, 0x90, 0x07, 0x0a, 0x08, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x44, 0x69, 0x73, @@ -1795,211 +1804,214 @@ var file_serverpb_server_proto_rawDesc = []byte{ 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x4a, - 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x0d, 0x10, - 0x0e, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f, 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x65, - 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, - 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x22, 0xce, 0x08, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x1c, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x65, - 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x10, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x65, 0x6c, 0x65, 0x6d, - 0x65, 0x74, 0x72, 0x79, 0x12, 0x4b, 0x0a, 0x13, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, - 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x6d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x40, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x73, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x0e, - 0x61, 0x77, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x77, 0x73, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, - 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, - 0x74, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, - 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x53, 0x74, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x6c, - 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x0a, 0x10, - 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, - 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x6d, 0x6d, 0x5f, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x13, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x6d, 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, - 0x70, 0x6d, 0x6d, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x50, 0x6d, 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x49, 0x0a, 0x13, 0x73, 0x74, 0x74, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x54, 0x54, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x11, 0x73, 0x74, 0x74, 0x43, 0x68, 0x65, - 0x63, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, - 0x76, 0x65, 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x33, - 0x0a, 0x15, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x64, - 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, - 0x76, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x18, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, - 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3a, 0x0a, - 0x19, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x17, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x4d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x34, 0x0a, - 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, - 0x04, 0x08, 0x09, 0x10, 0x0a, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x4a, 0x04, 0x08, 0x0f, 0x10, - 0x10, 0x4a, 0x04, 0x08, 0x10, 0x10, 0x11, 0x4a, 0x04, 0x08, 0x11, 0x10, 0x12, 0x4a, 0x04, 0x08, - 0x12, 0x10, 0x13, 0x22, 0x46, 0x0a, 0x16, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, + 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, + 0x27, 0x0a, 0x0f, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x74, 0x65, + 0x6d, 0x73, 0x18, 0x17, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, + 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x0d, 0x10, 0x0e, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f, + 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x43, 0x0a, 0x17, 0x41, - 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, - 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, - 0x22, 0x1a, 0x0a, 0x18, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x66, 0x0a, 0x12, - 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x49, - 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, - 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x4f, 0x43, 0x4b, 0x45, 0x52, 0x10, 0x01, 0x12, - 0x07, 0x0a, 0x03, 0x4f, 0x56, 0x46, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4d, 0x49, 0x10, - 0x03, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x5a, 0x55, 0x52, 0x45, 0x10, 0x04, 0x12, 0x06, 0x0a, 0x02, - 0x44, 0x4f, 0x10, 0x05, 0x32, 0xe5, 0x0c, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, - 0x79, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x92, 0x41, 0x27, - 0x12, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x1c, 0x52, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, - 0x76, 0x31, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x9e, 0x02, 0x0a, 0x09, 0x52, - 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x61, 0x64, - 0x69, 0x6e, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdb, 0x01, - 0x92, 0x41, 0xc5, 0x01, 0x12, 0x16, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x20, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x1a, 0xaa, 0x01, 0x52, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, - 0x77, 0x68, 0x65, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x70, - 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x72, 0x65, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, - 0x65, 0x61, 0x64, 0x79, 0x20, 0x79, 0x65, 0x74, 0x2e, 0x20, 0x55, 0x73, 0x65, 0x20, 0x74, 0x68, - 0x69, 0x73, 0x20, 0x41, 0x50, 0x49, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, - 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x20, 0x6f, - 0x66, 0x20, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x62, - 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x20, 0x72, - 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, - 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x7a, 0x12, 0xf7, 0x01, 0x0a, 0x11, - 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x12, 0x20, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4c, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4c, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9c, 0x01, 0x92, 0x41, 0x79, 0x12, 0x10, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x20, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x1a, 0x65, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x2e, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x20, 0x69, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, - 0x2f, 0x76, 0x31, 0x2f, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0xa3, 0x01, 0x0a, 0x0c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x65, - 0x63, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x58, 0x92, 0x41, 0x39, 0x12, 0x0d, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x73, 0x1a, 0x28, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x20, 0x66, 0x6f, - 0x72, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x50, 0x4d, 0x4d, 0x20, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x90, 0x01, 0x0a, 0x0b, - 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x92, 0x41, 0x29, 0x12, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, - 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x1a, 0x19, 0x53, 0x74, 0x61, 0x72, 0x74, 0x73, 0x20, - 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, - 0x2f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x9d, - 0x01, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x52, 0x92, 0x41, 0x32, 0x12, - 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x21, - 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x3a, 0x01, 0x2a, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x9a, - 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1a, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x52, 0x92, 0x41, 0x34, 0x12, 0x0c, 0x47, 0x65, - 0x74, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x1a, 0x24, 0x52, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x73, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x4d, 0x4d, 0x20, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, 0x01, 0x2a, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x53, - 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2f, 0x47, 0x65, 0x74, 0x12, 0xa1, 0x01, 0x0a, 0x0e, - 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1d, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, - 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x50, 0x92, - 0x41, 0x2f, 0x12, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x1a, 0x1c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x50, 0x4d, 0x4d, - 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x3a, 0x01, 0x2a, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2f, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, - 0xaa, 0x01, 0x0a, 0x10, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x41, 0x57, - 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x41, - 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x53, 0x92, 0x41, 0x31, 0x12, 0x12, 0x41, 0x57, - 0x53, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, - 0x1a, 0x1b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x20, 0x41, 0x57, 0x53, 0x20, 0x45, 0x43, 0x32, - 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x49, 0x44, 0x2e, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x41, 0x57, 0x53, 0x49, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x76, 0x0a, 0x0a, - 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x42, 0x0b, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x23, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x72, 0x63, 0x6f, 0x6e, 0x61, 0x2f, 0x70, 0x6d, - 0x6d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x70, 0x62, 0xa2, 0x02, - 0x03, 0x53, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0xca, 0x02, 0x06, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0xe2, 0x02, 0x12, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5c, - 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0xce, 0x08, 0x0a, 0x15, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, + 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, + 0x1d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, + 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, + 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x12, 0x4b, 0x0a, + 0x13, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, + 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x40, 0x0a, 0x0e, 0x64, 0x61, + 0x74, 0x61, 0x5f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x64, + 0x61, 0x74, 0x61, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, + 0x73, 0x73, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, + 0x73, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x77, 0x73, 0x5f, 0x70, 0x61, 0x72, + 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x61, + 0x77, 0x73, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x64, + 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x74, 0x12, 0x27, 0x0a, 0x0f, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x6c, 0x65, + 0x72, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, + 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x6d, 0x6d, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x6d, + 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x39, + 0x0a, 0x19, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x70, 0x6d, 0x6d, 0x5f, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6d, 0x6d, 0x50, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, 0x0a, 0x13, 0x73, 0x74, 0x74, + 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, + 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x53, 0x54, 0x54, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x73, 0x52, 0x11, 0x73, 0x74, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x76, 0x61, 0x6c, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, + 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x16, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x64, + 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x15, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, + 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x18, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x4d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3a, 0x0a, 0x19, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, + 0x65, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x18, 0x1e, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x34, 0x0a, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, + 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x18, 0x1f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x4a, 0x04, 0x08, 0x07, + 0x10, 0x08, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x4a, 0x04, + 0x08, 0x0a, 0x10, 0x0b, 0x4a, 0x04, 0x08, 0x0f, 0x10, 0x10, 0x4a, 0x04, 0x08, 0x10, 0x10, 0x11, + 0x4a, 0x04, 0x08, 0x11, 0x10, 0x12, 0x4a, 0x04, 0x08, 0x12, 0x10, 0x13, 0x22, 0x46, 0x0a, 0x16, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x22, 0x43, 0x0a, 0x17, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x28, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x22, 0x1a, 0x0a, 0x18, 0x41, 0x57, 0x53, + 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x66, 0x0a, 0x12, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x44, + 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x54, 0x48, + 0x4f, 0x44, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, + 0x44, 0x4f, 0x43, 0x4b, 0x45, 0x52, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x56, 0x46, 0x10, + 0x02, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4d, 0x49, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x5a, + 0x55, 0x52, 0x45, 0x10, 0x04, 0x12, 0x06, 0x0a, 0x02, 0x44, 0x4f, 0x10, 0x05, 0x32, 0xe5, 0x0c, + 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x92, 0x41, 0x27, 0x12, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x1a, 0x1c, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x9e, 0x02, 0x0a, 0x09, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, + 0x73, 0x12, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x69, + 0x6e, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdb, 0x01, 0x92, 0x41, 0xc5, 0x01, 0x12, 0x16, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x72, 0x65, 0x61, 0x64, + 0x69, 0x6e, 0x65, 0x73, 0x73, 0x1a, 0xaa, 0x01, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, + 0x61, 0x6e, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x20, + 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x20, + 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x79, 0x65, + 0x74, 0x2e, 0x20, 0x55, 0x73, 0x65, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x41, 0x50, 0x49, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x44, 0x6f, 0x63, 0x6b, 0x65, + 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x62, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x75, 0x62, + 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x20, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, + 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, + 0x61, 0x64, 0x79, 0x7a, 0x12, 0xf7, 0x01, 0x0a, 0x11, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, + 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x20, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x65, 0x61, 0x6c, + 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x9c, 0x01, 0x92, 0x41, 0x79, 0x12, 0x10, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x4c, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x1a, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x20, + 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, + 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x20, 0x69, 0x6e, + 0x20, 0x61, 0x20, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x20, 0x52, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x69, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x69, 0x73, 0x6e, + 0x27, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x6c, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0xa3, + 0x01, 0x0a, 0x0c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, + 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x58, 0x92, 0x41, 0x39, 0x12, + 0x0d, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x1a, 0x28, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, + 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x12, 0x90, 0x01, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x92, + 0x41, 0x29, 0x12, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x1a, 0x19, 0x53, 0x74, 0x61, 0x72, 0x74, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x16, 0x3a, 0x01, 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x73, 0x2f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x9d, 0x01, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x52, 0x92, 0x41, 0x32, 0x12, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x21, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, + 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, + 0x3a, 0x01, 0x2a, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, + 0x2f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x52, 0x92, 0x41, 0x34, 0x12, 0x0c, 0x47, 0x65, 0x74, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x1a, 0x24, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, + 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, + 0x01, 0x2a, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x2f, 0x47, 0x65, 0x74, 0x12, 0xa1, 0x01, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x50, 0x92, 0x41, 0x2f, 0x12, 0x0f, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x1a, 0x1c, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, + 0x3a, 0x01, 0x2a, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x2f, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0xaa, 0x01, 0x0a, 0x10, 0x41, 0x57, 0x53, + 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1f, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x53, 0x92, 0x41, 0x31, 0x12, 0x12, 0x41, 0x57, 0x53, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x1a, 0x1b, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x73, 0x20, 0x41, 0x57, 0x53, 0x20, 0x45, 0x43, 0x32, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x20, 0x49, 0x44, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, + 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x76, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x42, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x50, 0x01, 0x5a, 0x23, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, + 0x65, 0x72, 0x63, 0x6f, 0x6e, 0x61, 0x2f, 0x70, 0x6d, 0x6d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x70, 0x62, 0xa2, 0x02, 0x03, 0x53, 0x58, 0x58, 0xaa, 0x02, 0x06, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0xca, 0x02, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0xe2, + 0x02, 0x12, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/serverpb/server.proto b/api/serverpb/server.proto index 284d2ed481..f21d3638f4 100644 --- a/api/serverpb/server.proto +++ b/api/serverpb/server.proto @@ -169,6 +169,8 @@ message Settings { bool enable_access_control = 21; // Default Access Control role ID for new users. uint32 default_role_id = 22; + // Contains all already encrypted tables in format db.table. + repeated string encrypted_items = 23; } message GetSettingsRequest {} diff --git a/api/swagger/swagger-dev.json b/api/swagger/swagger-dev.json index 054efed28a..ba27351290 100644 --- a/api/swagger/swagger-dev.json +++ b/api/swagger/swagger-dev.json @@ -3262,6 +3262,14 @@ "type": "integer", "format": "int64", "x-order": 16 + }, + "encrypted_items": { + "description": "Contains all already encrypted tables in format db.table.", + "type": "array", + "items": { + "type": "string" + }, + "x-order": 17 } }, "x-order": 0 @@ -3455,6 +3463,14 @@ "type": "integer", "format": "int64", "x-order": 16 + }, + "encrypted_items": { + "description": "Contains all already encrypted tables in format db.table.", + "type": "array", + "items": { + "type": "string" + }, + "x-order": 17 } }, "x-order": 0 diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index dd3b3b8091..b52bf35467 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -418,6 +418,14 @@ "type": "integer", "format": "int64", "x-order": 16 + }, + "encrypted_items": { + "description": "Contains all already encrypted tables in format db.table.", + "type": "array", + "items": { + "type": "string" + }, + "x-order": 17 } }, "x-order": 0 @@ -611,6 +619,14 @@ "type": "integer", "format": "int64", "x-order": 16 + }, + "encrypted_items": { + "description": "Contains all already encrypted tables in format db.table.", + "type": "array", + "items": { + "type": "string" + }, + "x-order": 17 } }, "x-order": 0 diff --git a/managed/models/database.go b/managed/models/database.go index ffb5c83833..71574cfe54 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -31,6 +31,7 @@ import ( "strings" "github.com/lib/pq" + "github.com/percona/pmm/managed/utils/encryption" "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -1063,38 +1064,54 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. return nil, err } - // host, p, err := net.SplitHostPort(params.Address) - // if err != nil { - // return nil, err - // } - - // port, err := strconv.ParseInt(p, 10, 16) - // if err != nil { - // return nil, err - // } - - // c := &encryption.DatabaseConnection{ - // Host: host, - // Port: int16(port), - // User: params.Username, - // Password: params.Password, - // SSLMode: params.SSLMode, - // SSLCAPath: params.SSLCAPath, - // SSLKeyPath: params.SSLKeyPath, - // SSLCertPath: params.SSLCertPath, - // EncryptedItems: []encryption.EncryptedItem{ - // { - // Database: "pmm-managed", - // Table: "agents", - // Identificators: []string{"agent_id"}, - // Columns: []string{"username", "password"}, - // }, - // }, - // } - - // if err := encryption.EncryptDB(ctx, c); err != nil { - // return nil, err - // } + settings, err := GetSettings(sqlDB) + if err != nil { + return nil, err + } + + if len(settings.EncryptedItems) > 0 { + return db, nil + } + + host, p, err := net.SplitHostPort(params.Address) + if err != nil { + return nil, err + } + + port, err := strconv.ParseInt(p, 10, 16) + if err != nil { + return nil, err + } + + c := &encryption.DatabaseConnection{ + Host: host, + Port: int16(port), + User: params.Username, + Password: params.Password, + SSLMode: params.SSLMode, + SSLCAPath: params.SSLCAPath, + SSLKeyPath: params.SSLKeyPath, + SSLCertPath: params.SSLCertPath, + EncryptedItems: []encryption.EncryptedItem{ + { + Database: "pmm-managed", + Table: "agents", + Identificators: []string{"agent_id"}, + Columns: []string{"username", "password"}, + }, + }, + } + + if err := encryption.EncryptDB(ctx, c); err != nil { + return nil, err + } + + _, err = UpdateSettings(sqlDB, &ChangeSettingsParams{ + EncryptedItems: []string{"pmm-managed.agents"}, + }) + if err != nil { + return nil, err + } return db, nil } diff --git a/managed/models/settings.go b/managed/models/settings.go index 19d7877e85..8ff9ea9c21 100644 --- a/managed/models/settings.go +++ b/managed/models/settings.go @@ -100,6 +100,9 @@ type Settings struct { // Enabled is true if access control is enabled. Enabled bool `json:"enabled"` } `json:"access_control"` + + // Contains all already encrypted tables in format db.table. + EncryptedItems []string `json:"encrypted_items"` } // STTCheckIntervals represents intervals between STT checks. diff --git a/managed/models/settings_helpers.go b/managed/models/settings_helpers.go index 933d1eefd8..8c37974652 100644 --- a/managed/models/settings_helpers.go +++ b/managed/models/settings_helpers.go @@ -105,6 +105,9 @@ type ChangeSettingsParams struct { // DefaultRoleID sets a default role to be assigned to new users. DefaultRoleID int + + // List of tables in format db.table to be encrypted. + EncryptedItems []string } // SetPMMServerID should be run on start up to generate unique PMM Server ID. @@ -262,6 +265,10 @@ func UpdateSettings(q reform.DBTX, params *ChangeSettingsParams) (*Settings, err settings.DefaultRoleID = params.DefaultRoleID } + if len(params.EncryptedItems) != 0 { + settings.EncryptedItems = params.EncryptedItems + } + err = SaveSettings(q, settings) if err != nil { return nil, err diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index ab471e76e2..ed5a4b6023 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -20,6 +20,7 @@ import ( "database/sql" "encoding/base64" "errors" + "fmt" "os" "slices" @@ -156,7 +157,7 @@ func (e *Encryption) Decrypt(cipherText string) (string, error) { } decoded, err := base64.StdEncoding.DecodeString(cipherText) if err != nil { - return cipherText, err + return cipherText, fmt.Errorf("value %s is probably not encrypted, error: %v", cipherText, err) } secret, err := e.Primitive.Decrypt(decoded, []byte("")) if err != nil { From 952ecef29875f1119c9c16caaa4b4ad13056e477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 27 Jun 2024 13:32:41 +0200 Subject: [PATCH 065/215] PMM-13129 Original code for isPasswordSet. --- managed/services/management/agent.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/managed/services/management/agent.go b/managed/services/management/agent.go index 644c9d6b1c..6e55fecac7 100644 --- a/managed/services/management/agent.go +++ b/managed/services/management/agent.go @@ -143,9 +143,9 @@ func (s *AgentService) agentToAPI(agent *models.Agent) (*agentv1beta1.UniversalA Disabled: agent.Disabled, DisabledCollectors: agent.DisabledCollectors, IsConnected: s.r.IsConnected(agent.AgentID), - IsAgentPasswordSet: pointer.GetString(agent.AgentPassword) != "", - IsAwsSecretKeySet: pointer.GetString(agent.AWSSecretKey) != "", - IsPasswordSet: pointer.GetString(agent.Password) != "", + IsAgentPasswordSet: agent.AgentPassword != nil, + IsAwsSecretKeySet: agent.AWSSecretKey != nil, + IsPasswordSet: agent.Password != nil, ListenPort: uint32(pointer.GetUint16(agent.ListenPort)), LogLevel: pointer.GetString(agent.LogLevel), MaxQueryLength: agent.MaxQueryLength, From 8467292221009491f40f81a1b68629da2d73396d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 27 Jun 2024 18:28:50 +0200 Subject: [PATCH 066/215] PMM-13129 Fix current settings test. --- managed/models/settings_helpers_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/managed/models/settings_helpers_test.go b/managed/models/settings_helpers_test.go index 654fd226ae..9eff36ece9 100644 --- a/managed/models/settings_helpers_test.go +++ b/managed/models/settings_helpers_test.go @@ -51,7 +51,8 @@ func TestSettings(t *testing.T) { FrequentInterval: 4 * time.Hour, }, }, - DefaultRoleID: 1, + DefaultRoleID: 1, + EncryptedItems: []string{"pmm-managed.agents"}, } assert.Equal(t, expected, actual) }) From a5d0d3523595aeda066e17a9ea51e360057d5645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 1 Jul 2024 10:48:38 +0200 Subject: [PATCH 067/215] PMM-13129 Basic changes to be able pass custom handlers. --- managed/utils/encryption/database.go | 15 +++- managed/utils/encryption/encryption.go | 80 +++++++++++---------- managed/utils/encryption/encryption_test.go | 17 +++-- managed/utils/encryption/models.go | 20 ++++-- 4 files changed, 81 insertions(+), 51 deletions(-) diff --git a/managed/utils/encryption/database.go b/managed/utils/encryption/database.go index 18904924a9..2045e92b22 100644 --- a/managed/utils/encryption/database.go +++ b/managed/utils/encryption/database.go @@ -55,9 +55,18 @@ func (c DatabaseConnection) DSN() string { ) } +func (item EncryptedTable) ColumnsList() []string { + res := []string{} + for _, c := range item.Columns { + res = append(res, c.Column) + } + + return res +} + // Read returns query and it's values based on input. -func (item EncryptedItem) Read(tx *sql.Tx) (*QueryValues, error) { - what := slices.Concat(item.Identificators, item.Columns) +func (item EncryptedTable) Read(tx *sql.Tx) (*QueryValues, error) { + what := slices.Concat(item.Identificators, item.ColumnsList()) query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ", "), item.Table) //nolint:gosec rows, err := tx.Query(query) if err != nil { @@ -79,7 +88,7 @@ func (item EncryptedItem) Read(tx *sql.Tx) (*QueryValues, error) { set := []string{} setValues := []any{} for k, v := range row[len(item.Identificators):] { - set = append(set, fmt.Sprintf("%s = $%d", item.Columns[k], i)) + set = append(set, fmt.Sprintf("%s = $%d", item.Columns[k].Column, i)) setValues = append(setValues, v) i++ } diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index ed5a4b6023..288f733d2d 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -106,30 +106,32 @@ func (e *Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection) error } defer tx.Rollback() //nolint:errcheck - res, err := item.Read(tx) - if err != nil { - return err - } + for _, table := range item.Tables { + res, err := table.Read(tx) + if err != nil { + return err + } - for k, v := range res.SetValues { - for i, val := range v { - value := val.(*sql.NullString) - if !value.Valid { - res.SetValues[k][i] = sql.NullString{} - continue + for k, v := range res.SetValues { + for i, val := range v { + value := val.(*sql.NullString) + if !value.Valid { + res.SetValues[k][i] = sql.NullString{} + continue + } + + encrypted, err := e.Encrypt(value.String) + if err != nil { + return err + } + res.SetValues[k][i] = encrypted } - - encrypted, err := e.Encrypt(value.String) + data := slices.Concat([]any{}, v) + data = slices.Concat(data, res.WhereValues[k]) + _, err := tx.Exec(res.Query, data...) if err != nil { return err } - res.SetValues[k][i] = encrypted - } - data := slices.Concat([]any{}, v) - data = slices.Concat(data, res.WhereValues[k]) - _, err := tx.Exec(res.Query, data...) - if err != nil { - return err } } @@ -192,30 +194,32 @@ func (e *Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection) error } defer tx.Rollback() //nolint:errcheck - res, err := item.Read(tx) - if err != nil { - return err - } + for _, table := range item.Tables { + res, err := table.Read(tx) + if err != nil { + return err + } - for k, v := range res.SetValues { - for i, val := range v { - value := val.(*sql.NullString) - if !value.Valid { - res.SetValues[k][i] = nil - continue + for k, v := range res.SetValues { + for i, val := range v { + value := val.(*sql.NullString) + if !value.Valid { + res.SetValues[k][i] = nil + continue + } + + decrypted, err := e.Decrypt(value.String) + if err != nil { + return err + } + res.SetValues[k][i] = decrypted } - - decrypted, err := e.Decrypt(value.String) + data := slices.Concat([]any{}, v) + data = slices.Concat(data, res.WhereValues[k]) + _, err := tx.Exec(res.Query, data...) if err != nil { return err } - res.SetValues[k][i] = decrypted - } - data := slices.Concat([]any{}, v) - data = slices.Concat(data, res.WhereValues[k]) - _, err := tx.Exec(res.Query, data...) - if err != nil { - return err } } diff --git a/managed/utils/encryption/encryption_test.go b/managed/utils/encryption/encryption_test.go index ae65cae410..f53ff08a79 100644 --- a/managed/utils/encryption/encryption_test.go +++ b/managed/utils/encryption/encryption_test.go @@ -37,12 +37,19 @@ func TestEncryption(t *testing.T) { Port: 5432, User: "postgres", Password: "", - EncryptedItems: []EncryptedItem{ + EncryptedItems: []EncryptedDatabase{ { - Database: "pmm-managed", - Table: "agents", - Identificators: []string{"agent_id"}, - Columns: []string{"username", "password"}, + Database: "pmm-managed", + Tables: []EncryptedTable{ + { + Table: "agents", + Identificators: []string{"agent_id"}, + Columns: []EncryptedColumn{ + {Column: "username", Handler: func() {}}, + {Column: "password", Handler: func() {}}, + }, + }, + }, }, }, } diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go index 1f5cd00940..837eecf87f 100644 --- a/managed/utils/encryption/models.go +++ b/managed/utils/encryption/models.go @@ -33,14 +33,24 @@ type DatabaseConnection struct { SSLCAPath string SSLKeyPath string SSLCertPath string - EncryptedItems []EncryptedItem + EncryptedItems []EncryptedDatabase } // EncryptedItem resresents DB name, table, encrypted columns and it's identificators. -type EncryptedItem struct { - Database, Table string - Identificators []string - Columns []string +type EncryptedDatabase struct { + Database string + Tables []EncryptedTable +} + +type EncryptedTable struct { + Table string + Identificators []string + Columns []EncryptedColumn +} + +type EncryptedColumn struct { + Column string + Handler func() } // QueryValues represents query to update row after encrypt/decrypt. From 3ea748ae6719d13dcedf47078e6edc0543218377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 1 Jul 2024 14:15:45 +0200 Subject: [PATCH 068/215] PMM-13129 Handlers, PG handler. --- managed/models/agent_helpers.go | 177 -------------- managed/models/database.go | 17 +- managed/models/encryption_helpers.go | 218 ++++++++++++++++++ managed/utils/encryption/encryption.go | 13 +- managed/utils/encryption/helpers.go | 16 +- managed/utils/encryption/models.go | 2 +- .../encryption_test.go | 25 +- 7 files changed, 268 insertions(+), 200 deletions(-) create mode 100644 managed/models/encryption_helpers.go rename managed/utils/{encryption => encryption_test}/encryption_test.go (65%) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 8b5505aa21..5390cf24ac 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -28,7 +28,6 @@ import ( "google.golang.org/grpc/status" "gopkg.in/reform.v1" - "github.com/percona/pmm/managed/utils/encryption" "github.com/percona/pmm/version" ) @@ -947,182 +946,6 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara return row, nil } -func encryptAgent(agent *Agent) { - if agent.Username != nil { - username, err := encryption.Encrypt(*agent.Username) - if err != nil { - logrus.Warning(err) - } - agent.Username = &username - } - - if agent.Password != nil { - password, err := encryption.Encrypt(*agent.Password) - if err != nil { - logrus.Warning(err) - } - agent.Password = &password - } - - if agent.AgentPassword != nil { - agentPassword, err := encryption.Encrypt(*agent.AgentPassword) - if err != nil { - logrus.Warning(err) - } - agent.AgentPassword = &agentPassword - } - - if agent.AWSAccessKey != nil { - awsAccessKey, err := encryption.Encrypt(*agent.AWSAccessKey) - if err != nil { - logrus.Warning(err) - } - agent.AWSAccessKey = &awsAccessKey - } - - if agent.AWSSecretKey != nil { - awsSecretKey, err := encryption.Encrypt(*agent.AWSSecretKey) - if err != nil { - logrus.Warning(err) - } - agent.AWSSecretKey = &awsSecretKey - } - - var err error - if agent.MySQLOptions != nil { - agent.MySQLOptions.TLSKey, err = encryption.Encrypt(agent.MySQLOptions.TLSKey) - if err != nil { - logrus.Warning(err) - } - - } - - if agent.PostgreSQLOptions != nil { - agent.PostgreSQLOptions.SSLKey, err = encryption.Encrypt(agent.PostgreSQLOptions.SSLKey) - if err != nil { - logrus.Warning(err) - } - } - - if agent.MongoDBOptions != nil { - agent.MongoDBOptions.TLSCertificateKey, err = encryption.Encrypt(agent.MongoDBOptions.TLSCertificateKey) - if err != nil { - logrus.Warning(err) - } - agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = encryption.Encrypt(agent.MongoDBOptions.TLSCertificateKeyFilePassword) - if err != nil { - logrus.Warning(err) - } - } - - if agent.AzureOptions != nil { - agent.AzureOptions.ClientID, err = encryption.Encrypt(agent.AzureOptions.ClientID) - if err != nil { - logrus.Warning(err) - } - agent.AzureOptions.ClientSecret, err = encryption.Encrypt(agent.AzureOptions.ClientSecret) - if err != nil { - logrus.Warning(err) - } - agent.AzureOptions.SubscriptionID, err = encryption.Encrypt(agent.AzureOptions.SubscriptionID) - if err != nil { - logrus.Warning(err) - } - agent.AzureOptions.TenantID, err = encryption.Encrypt(agent.AzureOptions.TenantID) - if err != nil { - logrus.Warning(err) - } - } -} - -func decryptAgent(agent *Agent) { - if agent.Username != nil { - username, err := encryption.Decrypt(*agent.Username) - if err != nil { - logrus.Warning(err) - } - agent.Username = &username - } - - if agent.Password != nil { - password, err := encryption.Decrypt(*agent.Password) - if err != nil { - logrus.Warning(err) - } - agent.Password = &password - } - - if agent.AgentPassword != nil { - agentPassword, err := encryption.Decrypt(*agent.AgentPassword) - if err != nil { - logrus.Warning(err) - } - agent.AgentPassword = &agentPassword - } - - if agent.AWSAccessKey != nil { - awsAccessKey, err := encryption.Decrypt(*agent.AWSAccessKey) - if err != nil { - logrus.Warning(err) - } - agent.AWSAccessKey = &awsAccessKey - } - - if agent.AWSSecretKey != nil { - awsSecretKey, err := encryption.Decrypt(*agent.AWSSecretKey) - if err != nil { - logrus.Warning(err) - } - agent.AWSSecretKey = &awsSecretKey - } - - var err error - if agent.MySQLOptions != nil { - agent.MySQLOptions.TLSKey, err = encryption.Decrypt(agent.MySQLOptions.TLSKey) - if err != nil { - logrus.Warning(err) - } - - } - - if agent.PostgreSQLOptions != nil { - agent.PostgreSQLOptions.SSLKey, err = encryption.Decrypt(agent.PostgreSQLOptions.SSLKey) - if err != nil { - logrus.Warning(err) - } - } - - if agent.MongoDBOptions != nil { - agent.MongoDBOptions.TLSCertificateKey, err = encryption.Decrypt(agent.MongoDBOptions.TLSCertificateKey) - if err != nil { - logrus.Warning(err) - } - agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = encryption.Decrypt(agent.MongoDBOptions.TLSCertificateKeyFilePassword) - if err != nil { - logrus.Warning(err) - } - } - - if agent.AzureOptions != nil { - agent.AzureOptions.ClientID, err = encryption.Decrypt(agent.AzureOptions.ClientID) - if err != nil { - logrus.Warning(err) - } - agent.AzureOptions.ClientSecret, err = encryption.Decrypt(agent.AzureOptions.ClientSecret) - if err != nil { - logrus.Warning(err) - } - agent.AzureOptions.SubscriptionID, err = encryption.Decrypt(agent.AzureOptions.SubscriptionID) - if err != nil { - logrus.Warning(err) - } - agent.AzureOptions.TenantID, err = encryption.Decrypt(agent.AzureOptions.TenantID) - if err != nil { - logrus.Warning(err) - } - } -} - // ChangeCommonAgentParams contains parameters that can be changed for all Agents. type ChangeCommonAgentParams struct { Disabled *bool // true - disable, false - enable, nil - do not change diff --git a/managed/models/database.go b/managed/models/database.go index 71574cfe54..347a7a1ae9 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1092,12 +1092,19 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. SSLCAPath: params.SSLCAPath, SSLKeyPath: params.SSLKeyPath, SSLCertPath: params.SSLCertPath, - EncryptedItems: []encryption.EncryptedItem{ + EncryptedItems: []encryption.EncryptedDatabase{ { - Database: "pmm-managed", - Table: "agents", - Identificators: []string{"agent_id"}, - Columns: []string{"username", "password"}, + Database: "pmm-managed", + Tables: []encryption.EncryptedTable{ + { + Table: "agents", + Identificators: []string{"agent_id"}, + Columns: []encryption.EncryptedColumn{ + {Column: "username"}, + {Column: "password"}, + }, + }, + }, }, }, } diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go new file mode 100644 index 0000000000..b05f54c1e6 --- /dev/null +++ b/managed/models/encryption_helpers.go @@ -0,0 +1,218 @@ +package models + +import ( + "database/sql" + "encoding/json" + + "github.com/percona/pmm/managed/utils/encryption" + "github.com/sirupsen/logrus" +) + +func encryptAgent(agent *Agent) { + if agent.Username != nil { + username, err := encryption.Encrypt(*agent.Username) + if err != nil { + logrus.Warning(err) + } + agent.Username = &username + } + + if agent.Password != nil { + password, err := encryption.Encrypt(*agent.Password) + if err != nil { + logrus.Warning(err) + } + agent.Password = &password + } + + if agent.AgentPassword != nil { + agentPassword, err := encryption.Encrypt(*agent.AgentPassword) + if err != nil { + logrus.Warning(err) + } + agent.AgentPassword = &agentPassword + } + + if agent.AWSAccessKey != nil { + awsAccessKey, err := encryption.Encrypt(*agent.AWSAccessKey) + if err != nil { + logrus.Warning(err) + } + agent.AWSAccessKey = &awsAccessKey + } + + if agent.AWSSecretKey != nil { + awsSecretKey, err := encryption.Encrypt(*agent.AWSSecretKey) + if err != nil { + logrus.Warning(err) + } + agent.AWSSecretKey = &awsSecretKey + } + + var err error + if agent.MySQLOptions != nil { + agent.MySQLOptions.TLSKey, err = encryption.Encrypt(agent.MySQLOptions.TLSKey) + if err != nil { + logrus.Warning(err) + } + + } + + if agent.PostgreSQLOptions != nil { + agent.PostgreSQLOptions.SSLKey, err = encryption.Encrypt(agent.PostgreSQLOptions.SSLKey) + if err != nil { + logrus.Warning(err) + } + } + + if agent.MongoDBOptions != nil { + agent.MongoDBOptions.TLSCertificateKey, err = encryption.Encrypt(agent.MongoDBOptions.TLSCertificateKey) + if err != nil { + logrus.Warning(err) + } + agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = encryption.Encrypt(agent.MongoDBOptions.TLSCertificateKeyFilePassword) + if err != nil { + logrus.Warning(err) + } + } + + if agent.AzureOptions != nil { + agent.AzureOptions.ClientID, err = encryption.Encrypt(agent.AzureOptions.ClientID) + if err != nil { + logrus.Warning(err) + } + agent.AzureOptions.ClientSecret, err = encryption.Encrypt(agent.AzureOptions.ClientSecret) + if err != nil { + logrus.Warning(err) + } + agent.AzureOptions.SubscriptionID, err = encryption.Encrypt(agent.AzureOptions.SubscriptionID) + if err != nil { + logrus.Warning(err) + } + agent.AzureOptions.TenantID, err = encryption.Encrypt(agent.AzureOptions.TenantID) + if err != nil { + logrus.Warning(err) + } + } +} + +func decryptAgent(agent *Agent) { + if agent.Username != nil { + username, err := encryption.Decrypt(*agent.Username) + if err != nil { + logrus.Warning(err) + } + agent.Username = &username + } + + if agent.Password != nil { + password, err := encryption.Decrypt(*agent.Password) + if err != nil { + logrus.Warning(err) + } + agent.Password = &password + } + + if agent.AgentPassword != nil { + agentPassword, err := encryption.Decrypt(*agent.AgentPassword) + if err != nil { + logrus.Warning(err) + } + agent.AgentPassword = &agentPassword + } + + if agent.AWSAccessKey != nil { + awsAccessKey, err := encryption.Decrypt(*agent.AWSAccessKey) + if err != nil { + logrus.Warning(err) + } + agent.AWSAccessKey = &awsAccessKey + } + + if agent.AWSSecretKey != nil { + awsSecretKey, err := encryption.Decrypt(*agent.AWSSecretKey) + if err != nil { + logrus.Warning(err) + } + agent.AWSSecretKey = &awsSecretKey + } + + var err error + if agent.MySQLOptions != nil { + agent.MySQLOptions.TLSKey, err = encryption.Decrypt(agent.MySQLOptions.TLSKey) + if err != nil { + logrus.Warning(err) + } + + } + + if agent.PostgreSQLOptions != nil { + agent.PostgreSQLOptions.SSLKey, err = encryption.Decrypt(agent.PostgreSQLOptions.SSLKey) + if err != nil { + logrus.Warning(err) + } + } + + if agent.MongoDBOptions != nil { + agent.MongoDBOptions.TLSCertificateKey, err = encryption.Decrypt(agent.MongoDBOptions.TLSCertificateKey) + if err != nil { + logrus.Warning(err) + } + agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = encryption.Decrypt(agent.MongoDBOptions.TLSCertificateKeyFilePassword) + if err != nil { + logrus.Warning(err) + } + } + + if agent.AzureOptions != nil { + agent.AzureOptions.ClientID, err = encryption.Decrypt(agent.AzureOptions.ClientID) + if err != nil { + logrus.Warning(err) + } + agent.AzureOptions.ClientSecret, err = encryption.Decrypt(agent.AzureOptions.ClientSecret) + if err != nil { + logrus.Warning(err) + } + agent.AzureOptions.SubscriptionID, err = encryption.Decrypt(agent.AzureOptions.SubscriptionID) + if err != nil { + logrus.Warning(err) + } + agent.AzureOptions.TenantID, err = encryption.Decrypt(agent.AzureOptions.TenantID) + if err != nil { + logrus.Warning(err) + } + } +} + +func EncryptedColumnPostgreSQLOptionsHandler(e *encryption.Encryption, val any) (any, error) { + o := PostgreSQLOptions{} + value := val.(*sql.NullString) + if !value.Valid { + return sql.NullString{}, nil + } + + err := json.Unmarshal([]byte(value.String), &o) + if err != nil { + return nil, err + } + + o.SSLCa, err = e.Encrypt(o.SSLCa) + if err != nil { + return nil, err + } + o.SSLCert, err = e.Encrypt(o.SSLCert) + if err != nil { + return nil, err + } + o.SSLKey, err = encryption.Encrypt(o.SSLKey) + if err != nil { + return nil, err + } + + res, err := json.Marshal(o) + if err != nil { + return nil, err + } + + return res, nil +} diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 288f733d2d..fe7b3b6335 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -114,17 +114,20 @@ func (e *Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection) error for k, v := range res.SetValues { for i, val := range v { - value := val.(*sql.NullString) - if !value.Valid { - res.SetValues[k][i] = sql.NullString{} - continue + var encrypted any + var err error + switch table.Columns[i].Handler { + case nil: + encrypted, err = encryptedColumnStringHandler(e, val) + default: + encrypted, err = table.Columns[i].Handler(e, val) } - encrypted, err := e.Encrypt(value.String) if err != nil { return err } res.SetValues[k][i] = encrypted + } data := slices.Concat([]any{}, v) data = slices.Concat(data, res.WhereValues[k]) diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index a044c08e21..c9af049b4c 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -41,7 +41,7 @@ func prepareRowPointers(rows *sql.Rows) ([]any, error) { row := []any{} for _, t := range columns { switch t { - case "VARCHAR": + case "VARCHAR", "JSONB": row = append(row, new(sql.NullString)) default: // TODO support more identificators types @@ -52,6 +52,20 @@ func prepareRowPointers(rows *sql.Rows) ([]any, error) { return row, nil } +func encryptedColumnStringHandler(e *Encryption, val any) (any, error) { + value := val.(*sql.NullString) + if !value.Valid { + return sql.NullString{}, nil + } + + encrypted, err := e.Encrypt(value.String) + if err != nil { + return nil, err + } + + return encrypted, nil +} + func (e *Encryption) getPrimitive() (tink.AEAD, error) { serializedKeyset, err := base64.StdEncoding.DecodeString(e.Key) if err != nil { diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go index 837eecf87f..5e59092330 100644 --- a/managed/utils/encryption/models.go +++ b/managed/utils/encryption/models.go @@ -50,7 +50,7 @@ type EncryptedTable struct { type EncryptedColumn struct { Column string - Handler func() + Handler func(e *Encryption, val any) (any, error) } // QueryValues represents query to update row after encrypt/decrypt. diff --git a/managed/utils/encryption/encryption_test.go b/managed/utils/encryption_test/encryption_test.go similarity index 65% rename from managed/utils/encryption/encryption_test.go rename to managed/utils/encryption_test/encryption_test.go index f53ff08a79..254e1be011 100644 --- a/managed/utils/encryption/encryption_test.go +++ b/managed/utils/encryption_test/encryption_test.go @@ -13,40 +13,43 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package encryption +package encryption_test import ( "context" "testing" + "github.com/percona/pmm/managed/models" + "github.com/percona/pmm/managed/utils/encryption" "github.com/stretchr/testify/require" ) func TestEncryption(t *testing.T) { secret := "password1" - cipherText, err := Encrypt(secret) + cipherText, err := encryption.Encrypt(secret) require.NoError(t, err) require.NotEmpty(t, cipherText) - decryptedSecret, err := Decrypt(cipherText) + decryptedSecret, err := encryption.Decrypt(cipherText) require.NoError(t, err) require.Equal(t, secret, decryptedSecret) - c := &DatabaseConnection{ + c := &encryption.DatabaseConnection{ Host: "127.0.0.1", Port: 5432, User: "postgres", Password: "", - EncryptedItems: []EncryptedDatabase{ + EncryptedItems: []encryption.EncryptedDatabase{ { Database: "pmm-managed", - Tables: []EncryptedTable{ + Tables: []encryption.EncryptedTable{ { Table: "agents", Identificators: []string{"agent_id"}, - Columns: []EncryptedColumn{ - {Column: "username", Handler: func() {}}, - {Column: "password", Handler: func() {}}, + Columns: []encryption.EncryptedColumn{ + {Column: "username"}, + {Column: "password"}, + {Column: "postgresql_options", Handler: models.EncryptedColumnPostgreSQLOptionsHandler}, }, }, }, @@ -55,6 +58,6 @@ func TestEncryption(t *testing.T) { } ctx := context.Background() - require.NoError(t, EncryptDB(ctx, c)) - require.NoError(t, DecryptDB(ctx, c)) + require.NoError(t, encryption.EncryptDB(ctx, c)) + //require.NoError(t, encryption.DecryptDB(ctx, c)) } From 639a567d9e57a6084d4ce48fd1d0c8630f5a84be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 1 Jul 2024 14:25:16 +0200 Subject: [PATCH 069/215] PMM-13129 Refactor. --- managed/models/encryption_helpers.go | 2 +- managed/utils/encryption/encryption.go | 15 ++++++++------- managed/utils/encryption/helpers.go | 16 +++++++++++++++- managed/utils/encryption_test/encryption_test.go | 4 ++-- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index b05f54c1e6..4eb1504a38 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -184,7 +184,7 @@ func decryptAgent(agent *Agent) { } } -func EncryptedColumnPostgreSQLOptionsHandler(e *encryption.Encryption, val any) (any, error) { +func EncryptColumnPostgreSQLOptionsHandler(e *encryption.Encryption, val any) (any, error) { o := PostgreSQLOptions{} value := val.(*sql.NullString) if !value.Valid { diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index fe7b3b6335..088bc8e2ba 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -17,7 +17,6 @@ package encryption import ( "context" - "database/sql" "encoding/base64" "errors" "fmt" @@ -118,7 +117,7 @@ func (e *Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection) error var err error switch table.Columns[i].Handler { case nil: - encrypted, err = encryptedColumnStringHandler(e, val) + encrypted, err = encryptColumnStringHandler(e, val) default: encrypted, err = table.Columns[i].Handler(e, val) } @@ -205,13 +204,15 @@ func (e *Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection) error for k, v := range res.SetValues { for i, val := range v { - value := val.(*sql.NullString) - if !value.Valid { - res.SetValues[k][i] = nil - continue + var decrypted any + var err error + switch table.Columns[i].Handler { + case nil: + decrypted, err = decryptColumnStringHandler(e, val) + default: + decrypted, err = table.Columns[i].Handler(e, val) } - decrypted, err := e.Decrypt(value.String) if err != nil { return err } diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index c9af049b4c..7139513893 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -52,7 +52,7 @@ func prepareRowPointers(rows *sql.Rows) ([]any, error) { return row, nil } -func encryptedColumnStringHandler(e *Encryption, val any) (any, error) { +func encryptColumnStringHandler(e *Encryption, val any) (any, error) { value := val.(*sql.NullString) if !value.Valid { return sql.NullString{}, nil @@ -66,6 +66,20 @@ func encryptedColumnStringHandler(e *Encryption, val any) (any, error) { return encrypted, nil } +func decryptColumnStringHandler(e *Encryption, val any) (any, error) { + value := val.(*sql.NullString) + if !value.Valid { + return nil, nil + } + + decrypted, err := e.Decrypt(value.String) + if err != nil { + return nil, err + } + + return decrypted, nil +} + func (e *Encryption) getPrimitive() (tink.AEAD, error) { serializedKeyset, err := base64.StdEncoding.DecodeString(e.Key) if err != nil { diff --git a/managed/utils/encryption_test/encryption_test.go b/managed/utils/encryption_test/encryption_test.go index 254e1be011..a702c31283 100644 --- a/managed/utils/encryption_test/encryption_test.go +++ b/managed/utils/encryption_test/encryption_test.go @@ -49,7 +49,7 @@ func TestEncryption(t *testing.T) { Columns: []encryption.EncryptedColumn{ {Column: "username"}, {Column: "password"}, - {Column: "postgresql_options", Handler: models.EncryptedColumnPostgreSQLOptionsHandler}, + {Column: "postgresql_options", Handler: models.EncryptColumnPostgreSQLOptionsHandler}, }, }, }, @@ -59,5 +59,5 @@ func TestEncryption(t *testing.T) { ctx := context.Background() require.NoError(t, encryption.EncryptDB(ctx, c)) - //require.NoError(t, encryption.DecryptDB(ctx, c)) + require.NoError(t, encryption.DecryptDB(ctx, c)) } From 80e42d048a684f4a35e15a874c95e48b42e413ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 1 Jul 2024 15:18:32 +0200 Subject: [PATCH 070/215] PMM-13129 Changes, refactor. --- managed/models/database.go | 26 +- managed/models/encryption_helpers.go | 248 ++++++++++++------ managed/utils/encryption/database.go | 4 +- managed/utils/encryption/encryption.go | 49 ++-- managed/utils/encryption/models.go | 15 +- .../utils/encryption_test/encryption_test.go | 47 ++-- 6 files changed, 241 insertions(+), 148 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index 347a7a1ae9..01a7e36547 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1092,24 +1092,26 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. SSLCAPath: params.SSLCAPath, SSLKeyPath: params.SSLKeyPath, SSLCertPath: params.SSLCertPath, - EncryptedItems: []encryption.EncryptedDatabase{ - { - Database: "pmm-managed", - Tables: []encryption.EncryptedTable{ - { - Table: "agents", - Identificators: []string{"agent_id"}, - Columns: []encryption.EncryptedColumn{ - {Column: "username"}, - {Column: "password"}, - }, + } + + itemsToEncrypt := []encryption.Database{ + { + Database: "pmm-managed", + Tables: []encryption.Table{ + { + Table: "agents", + Identificators: []string{"agent_id"}, + Columns: []encryption.Column{ + {Column: "username"}, + {Column: "password"}, + {Column: "postgresql_options", CustomHandler: EncryptPostgreSQLOptionsHandler}, }, }, }, }, } - if err := encryption.EncryptDB(ctx, c); err != nil { + if err := encryption.EncryptDB(ctx, c, itemsToEncrypt); err != nil { return nil, err } diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index 4eb1504a38..29d9855256 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -9,8 +9,16 @@ import ( ) func encryptAgent(agent *Agent) { + agentEncryption(agent, encryption.Encrypt) +} + +func decryptAgent(agent *Agent) { + agentEncryption(agent, encryption.Decrypt) +} + +func agentEncryption(agent *Agent, handler func(string) (string, error)) { if agent.Username != nil { - username, err := encryption.Encrypt(*agent.Username) + username, err := handler(*agent.Username) if err != nil { logrus.Warning(err) } @@ -18,7 +26,7 @@ func encryptAgent(agent *Agent) { } if agent.Password != nil { - password, err := encryption.Encrypt(*agent.Password) + password, err := handler(*agent.Password) if err != nil { logrus.Warning(err) } @@ -26,7 +34,7 @@ func encryptAgent(agent *Agent) { } if agent.AgentPassword != nil { - agentPassword, err := encryption.Encrypt(*agent.AgentPassword) + agentPassword, err := handler(*agent.AgentPassword) if err != nil { logrus.Warning(err) } @@ -34,7 +42,7 @@ func encryptAgent(agent *Agent) { } if agent.AWSAccessKey != nil { - awsAccessKey, err := encryption.Encrypt(*agent.AWSAccessKey) + awsAccessKey, err := handler(*agent.AWSAccessKey) if err != nil { logrus.Warning(err) } @@ -42,7 +50,7 @@ func encryptAgent(agent *Agent) { } if agent.AWSSecretKey != nil { - awsSecretKey, err := encryption.Encrypt(*agent.AWSSecretKey) + awsSecretKey, err := handler(*agent.AWSSecretKey) if err != nil { logrus.Warning(err) } @@ -51,141 +59,203 @@ func encryptAgent(agent *Agent) { var err error if agent.MySQLOptions != nil { - agent.MySQLOptions.TLSKey, err = encryption.Encrypt(agent.MySQLOptions.TLSKey) + agent.MySQLOptions.TLSCa, err = handler(agent.MySQLOptions.TLSCa) + if err != nil { + logrus.Warning(err) + } + agent.MySQLOptions.TLSCert, err = handler(agent.MySQLOptions.TLSCert) + if err != nil { + logrus.Warning(err) + } + agent.MySQLOptions.TLSKey, err = handler(agent.MySQLOptions.TLSKey) if err != nil { logrus.Warning(err) } - } if agent.PostgreSQLOptions != nil { - agent.PostgreSQLOptions.SSLKey, err = encryption.Encrypt(agent.PostgreSQLOptions.SSLKey) + agent.PostgreSQLOptions.SSLCa, err = handler(agent.PostgreSQLOptions.SSLCa) + if err != nil { + logrus.Warning(err) + } + agent.PostgreSQLOptions.SSLCert, err = handler(agent.PostgreSQLOptions.SSLCert) + if err != nil { + logrus.Warning(err) + } + agent.PostgreSQLOptions.SSLKey, err = handler(agent.PostgreSQLOptions.SSLKey) if err != nil { logrus.Warning(err) } } if agent.MongoDBOptions != nil { - agent.MongoDBOptions.TLSCertificateKey, err = encryption.Encrypt(agent.MongoDBOptions.TLSCertificateKey) + agent.MongoDBOptions.TLSCa, err = handler(agent.MongoDBOptions.TLSCa) + if err != nil { + logrus.Warning(err) + } + agent.MongoDBOptions.TLSCertificateKey, err = handler(agent.MongoDBOptions.TLSCertificateKey) if err != nil { logrus.Warning(err) } - agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = encryption.Encrypt(agent.MongoDBOptions.TLSCertificateKeyFilePassword) + agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = handler(agent.MongoDBOptions.TLSCertificateKeyFilePassword) if err != nil { logrus.Warning(err) } } if agent.AzureOptions != nil { - agent.AzureOptions.ClientID, err = encryption.Encrypt(agent.AzureOptions.ClientID) + agent.AzureOptions.ClientID, err = handler(agent.AzureOptions.ClientID) if err != nil { logrus.Warning(err) } - agent.AzureOptions.ClientSecret, err = encryption.Encrypt(agent.AzureOptions.ClientSecret) + agent.AzureOptions.ClientSecret, err = handler(agent.AzureOptions.ClientSecret) if err != nil { logrus.Warning(err) } - agent.AzureOptions.SubscriptionID, err = encryption.Encrypt(agent.AzureOptions.SubscriptionID) + agent.AzureOptions.SubscriptionID, err = handler(agent.AzureOptions.SubscriptionID) if err != nil { logrus.Warning(err) } - agent.AzureOptions.TenantID, err = encryption.Encrypt(agent.AzureOptions.TenantID) + agent.AzureOptions.TenantID, err = handler(agent.AzureOptions.TenantID) if err != nil { logrus.Warning(err) } } } -func decryptAgent(agent *Agent) { - if agent.Username != nil { - username, err := encryption.Decrypt(*agent.Username) - if err != nil { - logrus.Warning(err) - } - agent.Username = &username +func EncryptMySQLOptionsHandler(e *encryption.Encryption, val any) (any, error) { + return mySQLOptionsHandler(val, e.Encrypt) +} + +func DecryptMySQLOptionsHandler(e *encryption.Encryption, val any) (any, error) { + return mySQLOptionsHandler(val, e.Decrypt) +} + +func mySQLOptionsHandler(val any, handler func(string) (string, error)) (any, error) { + o := MySQLOptions{} + value := val.(*sql.NullString) + if !value.Valid { + return sql.NullString{}, nil } - if agent.Password != nil { - password, err := encryption.Decrypt(*agent.Password) - if err != nil { - logrus.Warning(err) - } - agent.Password = &password + err := json.Unmarshal([]byte(value.String), &o) + if err != nil { + return nil, err } - if agent.AgentPassword != nil { - agentPassword, err := encryption.Decrypt(*agent.AgentPassword) - if err != nil { - logrus.Warning(err) - } - agent.AgentPassword = &agentPassword + o.TLSCa, err = handler(o.TLSCa) + if err != nil { + return nil, err + } + o.TLSCert, err = handler(o.TLSCert) + if err != nil { + return nil, err + } + o.TLSKey, err = handler(o.TLSKey) + if err != nil { + return nil, err } - if agent.AWSAccessKey != nil { - awsAccessKey, err := encryption.Decrypt(*agent.AWSAccessKey) - if err != nil { - logrus.Warning(err) - } - agent.AWSAccessKey = &awsAccessKey + res, err := json.Marshal(o) + if err != nil { + return nil, err } - if agent.AWSSecretKey != nil { - awsSecretKey, err := encryption.Decrypt(*agent.AWSSecretKey) - if err != nil { - logrus.Warning(err) - } - agent.AWSSecretKey = &awsSecretKey + return res, nil +} + +func EncryptPostgreSQLOptionsHandler(e *encryption.Encryption, val any) (any, error) { + return postgreSQLOptionsHandler(val, e.Encrypt) +} + +func DecryptPostgreSQLOptionsHandler(e *encryption.Encryption, val any) (any, error) { + return postgreSQLOptionsHandler(val, e.Decrypt) +} + +func postgreSQLOptionsHandler(val any, handler func(string) (string, error)) (any, error) { + o := PostgreSQLOptions{} + value := val.(*sql.NullString) + if !value.Valid { + return sql.NullString{}, nil } - var err error - if agent.MySQLOptions != nil { - agent.MySQLOptions.TLSKey, err = encryption.Decrypt(agent.MySQLOptions.TLSKey) - if err != nil { - logrus.Warning(err) - } + err := json.Unmarshal([]byte(value.String), &o) + if err != nil { + return nil, err + } + o.SSLCa, err = handler(o.SSLCa) + if err != nil { + return nil, err + } + o.SSLCert, err = handler(o.SSLCert) + if err != nil { + return nil, err + } + o.SSLKey, err = handler(o.SSLKey) + if err != nil { + return nil, err } - if agent.PostgreSQLOptions != nil { - agent.PostgreSQLOptions.SSLKey, err = encryption.Decrypt(agent.PostgreSQLOptions.SSLKey) - if err != nil { - logrus.Warning(err) - } + res, err := json.Marshal(o) + if err != nil { + return nil, err } - if agent.MongoDBOptions != nil { - agent.MongoDBOptions.TLSCertificateKey, err = encryption.Decrypt(agent.MongoDBOptions.TLSCertificateKey) - if err != nil { - logrus.Warning(err) - } - agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = encryption.Decrypt(agent.MongoDBOptions.TLSCertificateKeyFilePassword) - if err != nil { - logrus.Warning(err) - } + return res, nil +} + +func EncryptMongoDBOptionsHandler(e *encryption.Encryption, val any) (any, error) { + return mongoDBOptionsHandler(val, e.Encrypt) +} + +func DecryptMongoDBOptionsHandler(e *encryption.Encryption, val any) (any, error) { + return mongoDBOptionsHandler(val, e.Decrypt) +} + +func mongoDBOptionsHandler(val any, handler func(string) (string, error)) (any, error) { + o := MongoDBOptions{} + value := val.(*sql.NullString) + if !value.Valid { + return sql.NullString{}, nil } - if agent.AzureOptions != nil { - agent.AzureOptions.ClientID, err = encryption.Decrypt(agent.AzureOptions.ClientID) - if err != nil { - logrus.Warning(err) - } - agent.AzureOptions.ClientSecret, err = encryption.Decrypt(agent.AzureOptions.ClientSecret) - if err != nil { - logrus.Warning(err) - } - agent.AzureOptions.SubscriptionID, err = encryption.Decrypt(agent.AzureOptions.SubscriptionID) - if err != nil { - logrus.Warning(err) - } - agent.AzureOptions.TenantID, err = encryption.Decrypt(agent.AzureOptions.TenantID) - if err != nil { - logrus.Warning(err) - } + err := json.Unmarshal([]byte(value.String), &o) + if err != nil { + return nil, err + } + + o.TLSCa, err = handler(o.TLSCa) + if err != nil { + return nil, err + } + o.TLSCertificateKey, err = handler(o.TLSCertificateKey) + if err != nil { + return nil, err + } + o.TLSCertificateKeyFilePassword, err = handler(o.TLSCertificateKeyFilePassword) + if err != nil { + return nil, err + } + + res, err := json.Marshal(o) + if err != nil { + return nil, err } + + return res, nil } -func EncryptColumnPostgreSQLOptionsHandler(e *encryption.Encryption, val any) (any, error) { - o := PostgreSQLOptions{} +func EncryptAzureOptionsHandler(e *encryption.Encryption, val any) (any, error) { + return azureOptionsHandler(val, e.Encrypt) +} + +func DecryptAzureOptionsHandler(e *encryption.Encryption, val any) (any, error) { + return azureOptionsHandler(val, e.Decrypt) +} + +func azureOptionsHandler(val any, handler func(string) (string, error)) (any, error) { + o := AzureOptions{} value := val.(*sql.NullString) if !value.Valid { return sql.NullString{}, nil @@ -196,15 +266,19 @@ func EncryptColumnPostgreSQLOptionsHandler(e *encryption.Encryption, val any) (a return nil, err } - o.SSLCa, err = e.Encrypt(o.SSLCa) + o.ClientID, err = handler(o.ClientID) + if err != nil { + return nil, err + } + o.ClientSecret, err = handler(o.ClientSecret) if err != nil { return nil, err } - o.SSLCert, err = e.Encrypt(o.SSLCert) + o.SubscriptionID, err = handler(o.SubscriptionID) if err != nil { return nil, err } - o.SSLKey, err = encryption.Encrypt(o.SSLKey) + o.TenantID, err = handler(o.TenantID) if err != nil { return nil, err } diff --git a/managed/utils/encryption/database.go b/managed/utils/encryption/database.go index 2045e92b22..d08d710a08 100644 --- a/managed/utils/encryption/database.go +++ b/managed/utils/encryption/database.go @@ -55,7 +55,7 @@ func (c DatabaseConnection) DSN() string { ) } -func (item EncryptedTable) ColumnsList() []string { +func (item Table) ColumnsList() []string { res := []string{} for _, c := range item.Columns { res = append(res, c.Column) @@ -65,7 +65,7 @@ func (item EncryptedTable) ColumnsList() []string { } // Read returns query and it's values based on input. -func (item EncryptedTable) Read(tx *sql.Tx) (*QueryValues, error) { +func (item Table) Read(tx *sql.Tx) (*QueryValues, error) { what := slices.Concat(item.Identificators, item.ColumnsList()) query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ", "), item.Table) //nolint:gosec rows, err := tx.Query(query) diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 088bc8e2ba..0e5c5940b9 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -29,10 +29,11 @@ import ( // DefaultEncryptionKeyPath contains default PMM encryption key path. const DefaultEncryptionKeyPath = "/srv/pmm-encryption.key" -// ErrEncryptionNotInitialized is error in case of encryption is not initialized. -var ErrEncryptionNotInitialized = errors.New("encryption is not initialized") - -var DefaultEncryption = New(DefaultEncryptionKeyPath) +var ( + // ErrEncryptionNotInitialized is error in case of encryption is not initialized. + ErrEncryptionNotInitialized = errors.New("encryption is not initialized") + DefaultEncryption = New(DefaultEncryptionKeyPath) +) // New create encryption, if key on path doesnt exists will be generated. func New(keyPath string) *Encryption { @@ -81,13 +82,17 @@ func (e *Encryption) Encrypt(secret string) (string, error) { } // EncryptDB is wrapper around DefaultEncryption.EncryptDB. -func EncryptDB(ctx context.Context, c *DatabaseConnection) error { - return DefaultEncryption.EncryptDB(ctx, c) +func EncryptDB(ctx context.Context, c *DatabaseConnection, items []Database) error { + return DefaultEncryption.EncryptDB(ctx, c, items) } // EncryptDB will encrypt all columns provided in DB connection. -func (e *Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection) error { - for _, item := range c.EncryptedItems { +func (e *Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection, items []Database) error { + if len(items) == 0 { + return errors.New("DB Connection: Database target tables/columns not defined") + } + + for _, item := range items { c.DBName = item.Database db, err := c.Connect() if err != nil { @@ -95,10 +100,6 @@ func (e *Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection) error } defer db.Close() //nolint:errcheck - if len(c.EncryptedItems) == 0 { - return errors.New("DB Connection: Database target tables/columns not defined") - } - tx, err := db.BeginTx(ctx, nil) if err != nil { return err @@ -115,11 +116,11 @@ func (e *Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection) error for i, val := range v { var encrypted any var err error - switch table.Columns[i].Handler { + switch table.Columns[i].CustomHandler { case nil: encrypted, err = encryptColumnStringHandler(e, val) default: - encrypted, err = table.Columns[i].Handler(e, val) + encrypted, err = table.Columns[i].CustomHandler(e, val) } if err != nil { @@ -172,13 +173,17 @@ func (e *Encryption) Decrypt(cipherText string) (string, error) { } // DecryptDB is wrapper around DefaultEncryption.DecryptDB. -func DecryptDB(ctx context.Context, c *DatabaseConnection) error { - return DefaultEncryption.DecryptDB(ctx, c) +func DecryptDB(ctx context.Context, c *DatabaseConnection, items []Database) error { + return DefaultEncryption.DecryptDB(ctx, c, items) } // DecryptDB will decrypt all columns provided in DB connection. -func (e *Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection) error { - for _, item := range c.EncryptedItems { +func (e *Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection, items []Database) error { + if len(items) == 0 { + return errors.New("DB Connection: Database target tables/columns not defined") + } + + for _, item := range items { c.DBName = item.Database db, err := c.Connect() if err != nil { @@ -186,10 +191,6 @@ func (e *Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection) error } defer db.Close() //nolint:errcheck - if len(c.EncryptedItems) == 0 { - return errors.New("DB Connection: Database target tables/columns not defined") - } - tx, err := db.BeginTx(ctx, nil) if err != nil { return err @@ -206,11 +207,11 @@ func (e *Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection) error for i, val := range v { var decrypted any var err error - switch table.Columns[i].Handler { + switch table.Columns[i].CustomHandler { case nil: decrypted, err = decryptColumnStringHandler(e, val) default: - decrypted, err = table.Columns[i].Handler(e, val) + decrypted, err = table.Columns[i].CustomHandler(e, val) } if err != nil { diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go index 5e59092330..862ee33e3f 100644 --- a/managed/utils/encryption/models.go +++ b/managed/utils/encryption/models.go @@ -33,24 +33,23 @@ type DatabaseConnection struct { SSLCAPath string SSLKeyPath string SSLCertPath string - EncryptedItems []EncryptedDatabase } // EncryptedItem resresents DB name, table, encrypted columns and it's identificators. -type EncryptedDatabase struct { +type Database struct { Database string - Tables []EncryptedTable + Tables []Table } -type EncryptedTable struct { +type Table struct { Table string Identificators []string - Columns []EncryptedColumn + Columns []Column } -type EncryptedColumn struct { - Column string - Handler func(e *Encryption, val any) (any, error) +type Column struct { + Column string + CustomHandler func(e *Encryption, val any) (any, error) } // QueryValues represents query to update row after encrypt/decrypt. diff --git a/managed/utils/encryption_test/encryption_test.go b/managed/utils/encryption_test/encryption_test.go index a702c31283..55ee92489d 100644 --- a/managed/utils/encryption_test/encryption_test.go +++ b/managed/utils/encryption_test/encryption_test.go @@ -39,25 +39,42 @@ func TestEncryption(t *testing.T) { Port: 5432, User: "postgres", Password: "", - EncryptedItems: []encryption.EncryptedDatabase{ - { - Database: "pmm-managed", - Tables: []encryption.EncryptedTable{ - { - Table: "agents", - Identificators: []string{"agent_id"}, - Columns: []encryption.EncryptedColumn{ - {Column: "username"}, - {Column: "password"}, - {Column: "postgresql_options", Handler: models.EncryptColumnPostgreSQLOptionsHandler}, - }, + } + ctx := context.Background() + + itemsToEncrypt := []encryption.Database{ + { + Database: "pmm-managed", + Tables: []encryption.Table{ + { + Table: "agents", + Identificators: []string{"agent_id"}, + Columns: []encryption.Column{ + {Column: "username"}, + {Column: "password"}, + {Column: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, }, }, }, }, } + require.NoError(t, encryption.EncryptDB(ctx, c, itemsToEncrypt)) - ctx := context.Background() - require.NoError(t, encryption.EncryptDB(ctx, c)) - require.NoError(t, encryption.DecryptDB(ctx, c)) + itemsToDecrypt := []encryption.Database{ + { + Database: "pmm-managed", + Tables: []encryption.Table{ + { + Table: "agents", + Identificators: []string{"agent_id"}, + Columns: []encryption.Column{ + {Column: "username"}, + {Column: "password"}, + {Column: "postgresql_options", CustomHandler: models.DecryptPostgreSQLOptionsHandler}, + }, + }, + }, + }, + } + require.NoError(t, encryption.DecryptDB(ctx, c, itemsToDecrypt)) } From 21b74e4d448e8d10b95d18e3f0184bb026b3a8e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 1 Jul 2024 15:26:58 +0200 Subject: [PATCH 071/215] PMM-13129 Migrate and encrypt all possible fields. --- managed/models/database.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/managed/models/database.go b/managed/models/database.go index 01a7e36547..29b14589d9 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1104,7 +1104,13 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. Columns: []encryption.Column{ {Column: "username"}, {Column: "password"}, + {Column: "agent_password"}, + {Column: "aws_access_key"}, + {Column: "aws_secret_key "}, + {Column: "mysql_options", CustomHandler: EncryptMySQLOptionsHandler}, {Column: "postgresql_options", CustomHandler: EncryptPostgreSQLOptionsHandler}, + {Column: "mongo_db_tls_options", CustomHandler: EncryptMongoDBOptionsHandler}, + {Column: "azure_options", CustomHandler: EncryptAzureOptionsHandler}, }, }, }, From 14786955ca80d529fd8862d53f6469a869f4377a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 1 Jul 2024 16:17:25 +0200 Subject: [PATCH 072/215] PMM-13129 Fix for service info broker. --- managed/models/agent_helpers.go | 26 +++++++++---------- managed/models/encryption_helpers.go | 4 +-- .../services/agents/service_info_broker.go | 2 ++ 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 5390cf24ac..482c9a76b7 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -230,7 +230,7 @@ func FindAgents(q *reform.Querier, filters AgentFilters) ([]*Agent, error) { agents := make([]*Agent, len(structs)) for i, s := range structs { agent := s.(*Agent) //nolint:forcetypeassert - decryptAgent(agent) + DecryptAgent(agent) agents[i] = agent } @@ -252,7 +252,7 @@ func FindAgentByID(q *reform.Querier, id string) (*Agent, error) { return nil, errors.WithStack(err) } - decryptAgent(agent) + DecryptAgent(agent) return agent, nil } @@ -277,7 +277,7 @@ func FindAgentsByIDs(q *reform.Querier, ids []string) ([]*Agent, error) { res := make([]*Agent, len(structs)) for i, s := range structs { agent := s.(*Agent) //nolint:forcetypeassert - decryptAgent(agent) + DecryptAgent(agent) res[i] = agent } return res, nil @@ -330,7 +330,7 @@ func FindDBConfigForService(q *reform.Querier, serviceID string) (*DBConfig, err res := make([]*Agent, len(structs)) for i, s := range structs { agent := s.(*Agent) //nolint:forcetypeassert - decryptAgent(agent) + DecryptAgent(agent) res[i] = agent } @@ -359,7 +359,7 @@ func FindPMMAgentsRunningOnNode(q *reform.Querier, nodeID string) ([]*Agent, err res := make([]*Agent, 0, len(structs)) for _, str := range structs { row := str.(*Agent) //nolint:forcetypeassert - decryptAgent(row) + DecryptAgent(row) res = append(res, row) } @@ -405,7 +405,7 @@ func FindPMMAgentsForService(q *reform.Querier, serviceID string) ([]*Agent, err res := make([]*Agent, 0, len(pmmAgentRecords)) for _, str := range pmmAgentRecords { row := str.(*Agent) //nolint:forcetypeassert - decryptAgent(row) + DecryptAgent(row) res = append(res, row) } @@ -488,7 +488,7 @@ func FindAgentsForScrapeConfig(q *reform.Querier, pmmAgentID *string, pushMetric res := make([]*Agent, len(allAgents)) for i, s := range allAgents { agent := s.(*Agent) //nolint:forcetypeassert - decryptAgent(agent) + DecryptAgent(agent) res[i] = agent } return res, nil @@ -650,7 +650,7 @@ func CreateNodeExporter(q *reform.Querier, LogLevel: pointer.ToStringOrNil(logLevel), ExposeExporter: exposeExporter, } - encryptAgent(row) + EncryptAgent(row) if err := row.SetCustomLabels(customLabels); err != nil { return nil, err @@ -659,7 +659,7 @@ func CreateNodeExporter(q *reform.Querier, return nil, errors.WithStack(err) } - decryptAgent(row) + DecryptAgent(row) return row, nil } @@ -738,7 +738,7 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar ListenPort: pointer.ToUint16(uint16(params.ListenPort)), PushMetrics: params.PushMetrics, } - encryptAgent(row) + EncryptAgent(row) if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err @@ -747,7 +747,7 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar return nil, errors.WithStack(err) } - decryptAgent(row) + DecryptAgent(row) return row, nil } @@ -932,7 +932,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara DisabledCollectors: params.DisableCollectors, LogLevel: pointer.ToStringOrNil(params.LogLevel), } - encryptAgent(row) + EncryptAgent(row) if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err @@ -941,7 +941,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara return nil, errors.WithStack(err) } - decryptAgent(row) + DecryptAgent(row) return row, nil } diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index 29d9855256..d66e89c288 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -8,11 +8,11 @@ import ( "github.com/sirupsen/logrus" ) -func encryptAgent(agent *Agent) { +func EncryptAgent(agent *Agent) { agentEncryption(agent, encryption.Encrypt) } -func decryptAgent(agent *Agent) { +func DecryptAgent(agent *Agent) { agentEncryption(agent, encryption.Decrypt) } diff --git a/managed/services/agents/service_info_broker.go b/managed/services/agents/service_info_broker.go index 890a647109..e1751dd5a9 100644 --- a/managed/services/agents/service_info_broker.go +++ b/managed/services/agents/service_info_broker.go @@ -194,6 +194,7 @@ func (c *ServiceInfoBroker) GetInfoFromService(ctx context.Context, q *reform.Qu case models.MySQLServiceType: agent.TableCount = &sInfo.TableCount l.Debugf("Updating table count: %d.", sInfo.TableCount) + models.EncryptAgent(agent) if err = q.Update(agent); err != nil { return errors.Wrap(err, "failed to update table count") } @@ -218,6 +219,7 @@ func (c *ServiceInfoBroker) GetInfoFromService(ctx context.Context, q *reform.Qu agent.PostgreSQLOptions.DatabaseCount = int32(databaseCount - excludedDatabaseCount) l.Debugf("Updating PostgreSQL options, database count: %d.", agent.PostgreSQLOptions.DatabaseCount) + models.EncryptAgent(agent) if err = q.Update(agent); err != nil { return errors.Wrap(err, "failed to update database count") } From 96e202699d562aa3afb1a1b0bfaa32d08dfd47ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 1 Jul 2024 16:25:44 +0200 Subject: [PATCH 073/215] PMM-13129 Fix for settings helper test. --- managed/models/settings.go | 1 + managed/models/settings_helpers_test.go | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/managed/models/settings.go b/managed/models/settings.go index 8ff9ea9c21..ae7c111ddb 100644 --- a/managed/models/settings.go +++ b/managed/models/settings.go @@ -154,4 +154,5 @@ func (s *Settings) fillDefaults() { // VictoriaMetrics CacheEnable is false by default // PMMPublicAddress is empty by default // Azurediscover.Enabled is false by default + // EncryptedItems are empty by default } diff --git a/managed/models/settings_helpers_test.go b/managed/models/settings_helpers_test.go index 9eff36ece9..654fd226ae 100644 --- a/managed/models/settings_helpers_test.go +++ b/managed/models/settings_helpers_test.go @@ -51,8 +51,7 @@ func TestSettings(t *testing.T) { FrequentInterval: 4 * time.Hour, }, }, - DefaultRoleID: 1, - EncryptedItems: []string{"pmm-managed.agents"}, + DefaultRoleID: 1, } assert.Equal(t, expected, actual) }) From 6a2d4f25104b6a711e6744d6e0a09dfab4264edf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 1 Jul 2024 16:26:55 +0200 Subject: [PATCH 074/215] PMM-13129 Refactor. --- .../utils/{encryption_test => encryption}/encryption_test.go | 3 --- 1 file changed, 3 deletions(-) rename managed/utils/{encryption_test => encryption}/encryption_test.go (90%) diff --git a/managed/utils/encryption_test/encryption_test.go b/managed/utils/encryption/encryption_test.go similarity index 90% rename from managed/utils/encryption_test/encryption_test.go rename to managed/utils/encryption/encryption_test.go index 55ee92489d..336b3bb9b2 100644 --- a/managed/utils/encryption_test/encryption_test.go +++ b/managed/utils/encryption/encryption_test.go @@ -19,7 +19,6 @@ import ( "context" "testing" - "github.com/percona/pmm/managed/models" "github.com/percona/pmm/managed/utils/encryption" "github.com/stretchr/testify/require" ) @@ -52,7 +51,6 @@ func TestEncryption(t *testing.T) { Columns: []encryption.Column{ {Column: "username"}, {Column: "password"}, - {Column: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, }, }, }, @@ -70,7 +68,6 @@ func TestEncryption(t *testing.T) { Columns: []encryption.Column{ {Column: "username"}, {Column: "password"}, - {Column: "postgresql_options", CustomHandler: models.DecryptPostgreSQLOptionsHandler}, }, }, }, From 32fa22fb3c979e5165773e40e711f7888e04a8ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 1 Jul 2024 16:38:35 +0200 Subject: [PATCH 075/215] PMM-13129 Lint. --- managed/models/encryption_helpers.go | 10 ++++++++++ managed/utils/encryption/encryption.go | 4 +++- managed/utils/encryption/models.go | 4 +++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index d66e89c288..7e98fb3a89 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -8,10 +8,12 @@ import ( "github.com/sirupsen/logrus" ) +// EncryptAgent returns agent after it is encrypted. func EncryptAgent(agent *Agent) { agentEncryption(agent, encryption.Encrypt) } +// DecryptAgent returns agent after it is decrypted. func DecryptAgent(agent *Agent) { agentEncryption(agent, encryption.Decrypt) } @@ -123,10 +125,12 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { } } +// EncryptMySQLOptionsHandler returns encrypted MySQL Options. func EncryptMySQLOptionsHandler(e *encryption.Encryption, val any) (any, error) { return mySQLOptionsHandler(val, e.Encrypt) } +// DecryptMySQLOptionsHandler returns decrypted MySQL Options. func DecryptMySQLOptionsHandler(e *encryption.Encryption, val any) (any, error) { return mySQLOptionsHandler(val, e.Decrypt) } @@ -164,10 +168,12 @@ func mySQLOptionsHandler(val any, handler func(string) (string, error)) (any, er return res, nil } +// EncryptPostgreSQLOptionsHandler returns encrypted PostgreSQL Options. func EncryptPostgreSQLOptionsHandler(e *encryption.Encryption, val any) (any, error) { return postgreSQLOptionsHandler(val, e.Encrypt) } +// DecryptPostgreSQLOptionsHandler returns decrypted PostgreSQL Options. func DecryptPostgreSQLOptionsHandler(e *encryption.Encryption, val any) (any, error) { return postgreSQLOptionsHandler(val, e.Decrypt) } @@ -205,10 +211,12 @@ func postgreSQLOptionsHandler(val any, handler func(string) (string, error)) (an return res, nil } +// EncryptMongoDBOptionsHandler returns encrypted MongoDB Options. func EncryptMongoDBOptionsHandler(e *encryption.Encryption, val any) (any, error) { return mongoDBOptionsHandler(val, e.Encrypt) } +// DecryptMongoDBOptionsHandler returns decrypted MongoDB Options. func DecryptMongoDBOptionsHandler(e *encryption.Encryption, val any) (any, error) { return mongoDBOptionsHandler(val, e.Decrypt) } @@ -246,10 +254,12 @@ func mongoDBOptionsHandler(val any, handler func(string) (string, error)) (any, return res, nil } +// EncryptAzureOptionsHandler returns encrypted Azure Options. func EncryptAzureOptionsHandler(e *encryption.Encryption, val any) (any, error) { return azureOptionsHandler(val, e.Encrypt) } +// DecryptAzureOptionsHandler returns decrypted Azure Options. func DecryptAzureOptionsHandler(e *encryption.Encryption, val any) (any, error) { return azureOptionsHandler(val, e.Decrypt) } diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 0e5c5940b9..a703795a5b 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -13,6 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +// Package collectors contains functions to encrypt/decrypt items or DB. package encryption import ( @@ -32,7 +33,8 @@ const DefaultEncryptionKeyPath = "/srv/pmm-encryption.key" var ( // ErrEncryptionNotInitialized is error in case of encryption is not initialized. ErrEncryptionNotInitialized = errors.New("encryption is not initialized") - DefaultEncryption = New(DefaultEncryptionKeyPath) + // DefaultEncryption is the default implementation of encryption. + DefaultEncryption = New(DefaultEncryptionKeyPath) ) // New create encryption, if key on path doesnt exists will be generated. diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go index 862ee33e3f..dda95e318f 100644 --- a/managed/utils/encryption/models.go +++ b/managed/utils/encryption/models.go @@ -35,18 +35,20 @@ type DatabaseConnection struct { SSLCertPath string } -// EncryptedItem resresents DB name, table, encrypted columns and it's identificators. +// Database resresents database name and tables to be encrypted/decrypted. type Database struct { Database string Tables []Table } +// Table represents table name, it's identificators and columns to be encrypted/decrypted. type Table struct { Table string Identificators []string Columns []Column } +// Column represents column name and column's custom handler (if needed). type Column struct { Column string CustomHandler func(e *Encryption, val any) (any, error) From 2ff1602d296e16857e541a18e0a81a765b73f12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 1 Jul 2024 16:41:42 +0200 Subject: [PATCH 076/215] PMM-13129 Lint. --- managed/utils/encryption/helpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index 7139513893..4ad7a32eb9 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -80,7 +80,7 @@ func decryptColumnStringHandler(e *Encryption, val any) (any, error) { return decrypted, nil } -func (e *Encryption) getPrimitive() (tink.AEAD, error) { +func (e *Encryption) getPrimitive() (tink.AEAD, error) { //nolint:ireturn serializedKeyset, err := base64.StdEncoding.DecodeString(e.Key) if err != nil { return nil, err From 206e7a027b38a474b412d6ed2045dc6f5e0b903b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 1 Jul 2024 16:45:44 +0200 Subject: [PATCH 077/215] PMM-13129 Format. --- managed/models/database.go | 3 ++- managed/models/encryption_helpers.go | 3 ++- managed/utils/encryption/encryption_test.go | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index 29b14589d9..1c05554fe3 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -31,12 +31,13 @@ import ( "strings" "github.com/lib/pq" - "github.com/percona/pmm/managed/utils/encryption" "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "gopkg.in/reform.v1" "gopkg.in/reform.v1/dialects/postgresql" + + "github.com/percona/pmm/managed/utils/encryption" ) const ( diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index 7e98fb3a89..c0a70d69cf 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -4,8 +4,9 @@ import ( "database/sql" "encoding/json" - "github.com/percona/pmm/managed/utils/encryption" "github.com/sirupsen/logrus" + + "github.com/percona/pmm/managed/utils/encryption" ) // EncryptAgent returns agent after it is encrypted. diff --git a/managed/utils/encryption/encryption_test.go b/managed/utils/encryption/encryption_test.go index 336b3bb9b2..ea5e85981a 100644 --- a/managed/utils/encryption/encryption_test.go +++ b/managed/utils/encryption/encryption_test.go @@ -19,8 +19,9 @@ import ( "context" "testing" - "github.com/percona/pmm/managed/utils/encryption" "github.com/stretchr/testify/require" + + "github.com/percona/pmm/managed/utils/encryption" ) func TestEncryption(t *testing.T) { From e20d52a2d10158dbb7174870aa56db519ba2eb38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 1 Jul 2024 16:51:44 +0200 Subject: [PATCH 078/215] PMM-13129 Fix settings helpers test. --- managed/models/settings_helpers_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/managed/models/settings_helpers_test.go b/managed/models/settings_helpers_test.go index 654fd226ae..9eff36ece9 100644 --- a/managed/models/settings_helpers_test.go +++ b/managed/models/settings_helpers_test.go @@ -51,7 +51,8 @@ func TestSettings(t *testing.T) { FrequentInterval: 4 * time.Hour, }, }, - DefaultRoleID: 1, + DefaultRoleID: 1, + EncryptedItems: []string{"pmm-managed.agents"}, } assert.Equal(t, expected, actual) }) From c7edac809885066942db581f0c44c7089b4392d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 1 Jul 2024 16:52:50 +0200 Subject: [PATCH 079/215] PMM-13129 License header. --- managed/models/encryption_helpers.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index c0a70d69cf..dad86edcc6 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -1,3 +1,18 @@ +// Copyright (C) 2023 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + package models import ( From ec6e6feb5cfccdfbd15e19fbef228b473f071668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 1 Jul 2024 17:06:47 +0200 Subject: [PATCH 080/215] PMM-13129 Another lint. --- managed/models/encryption_helpers.go | 8 ++++---- managed/utils/encryption/database.go | 1 + managed/utils/encryption/encryption.go | 7 +++---- managed/utils/encryption/helpers.go | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index dad86edcc6..6296cc995e 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -153,7 +153,7 @@ func DecryptMySQLOptionsHandler(e *encryption.Encryption, val any) (any, error) func mySQLOptionsHandler(val any, handler func(string) (string, error)) (any, error) { o := MySQLOptions{} - value := val.(*sql.NullString) + value := val.(*sql.NullString) //nolint:forcetypeassert if !value.Valid { return sql.NullString{}, nil } @@ -196,7 +196,7 @@ func DecryptPostgreSQLOptionsHandler(e *encryption.Encryption, val any) (any, er func postgreSQLOptionsHandler(val any, handler func(string) (string, error)) (any, error) { o := PostgreSQLOptions{} - value := val.(*sql.NullString) + value := val.(*sql.NullString) //nolint:forcetypeassert if !value.Valid { return sql.NullString{}, nil } @@ -239,7 +239,7 @@ func DecryptMongoDBOptionsHandler(e *encryption.Encryption, val any) (any, error func mongoDBOptionsHandler(val any, handler func(string) (string, error)) (any, error) { o := MongoDBOptions{} - value := val.(*sql.NullString) + value := val.(*sql.NullString) //nolint:forcetypeassert if !value.Valid { return sql.NullString{}, nil } @@ -282,7 +282,7 @@ func DecryptAzureOptionsHandler(e *encryption.Encryption, val any) (any, error) func azureOptionsHandler(val any, handler func(string) (string, error)) (any, error) { o := AzureOptions{} - value := val.(*sql.NullString) + value := val.(*sql.NullString) //nolint:forcetypeassert if !value.Valid { return sql.NullString{}, nil } diff --git a/managed/utils/encryption/database.go b/managed/utils/encryption/database.go index d08d710a08..c5f90ea516 100644 --- a/managed/utils/encryption/database.go +++ b/managed/utils/encryption/database.go @@ -55,6 +55,7 @@ func (c DatabaseConnection) DSN() string { ) } +// ColumnsList returns array of table columns. func (item Table) ColumnsList() []string { res := []string{} for _, c := range item.Columns { diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index a703795a5b..57144fbdeb 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -13,17 +13,17 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -// Package collectors contains functions to encrypt/decrypt items or DB. +// Package encryption contains functions to encrypt/decrypt items or DB. package encryption import ( "context" "encoding/base64" - "errors" "fmt" "os" "slices" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -129,7 +129,6 @@ func (e *Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection, items return err } res.SetValues[k][i] = encrypted - } data := slices.Concat([]any{}, v) data = slices.Concat(data, res.WhereValues[k]) @@ -164,7 +163,7 @@ func (e *Encryption) Decrypt(cipherText string) (string, error) { } decoded, err := base64.StdEncoding.DecodeString(cipherText) if err != nil { - return cipherText, fmt.Errorf("value %s is probably not encrypted, error: %v", cipherText, err) + return cipherText, fmt.Errorf("value %s is probably not encrypted, error: %w", cipherText, err) } secret, err := e.Primitive.Decrypt(decoded, []byte("")) if err != nil { diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index 4ad7a32eb9..468ac8ced3 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -53,7 +53,7 @@ func prepareRowPointers(rows *sql.Rows) ([]any, error) { } func encryptColumnStringHandler(e *Encryption, val any) (any, error) { - value := val.(*sql.NullString) + value := val.(*sql.NullString) //nolint:forcetypeassert if !value.Valid { return sql.NullString{}, nil } @@ -67,9 +67,9 @@ func encryptColumnStringHandler(e *Encryption, val any) (any, error) { } func decryptColumnStringHandler(e *Encryption, val any) (any, error) { - value := val.(*sql.NullString) + value := val.(*sql.NullString) //nolint:forcetypeassert if !value.Valid { - return nil, nil + return nil, nil //nolint:nilnil } decrypted, err := e.Decrypt(value.String) From 5d379a345e92ff33ba248d840e92bb8aeab1a882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 1 Jul 2024 17:17:33 +0200 Subject: [PATCH 081/215] PMM-13129 Lint. --- managed/models/database.go | 2 +- managed/utils/encryption/database.go | 8 +++----- managed/utils/encryption/encryption.go | 2 +- managed/utils/encryption/helpers.go | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index 1c05554fe3..d237295e11 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1070,7 +1070,7 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. return nil, err } - if len(settings.EncryptedItems) > 0 { + if len(settings.EncryptedItems) != 0 { return db, nil } diff --git a/managed/utils/encryption/database.go b/managed/utils/encryption/database.go index c5f90ea516..aaa29f50e0 100644 --- a/managed/utils/encryption/database.go +++ b/managed/utils/encryption/database.go @@ -49,10 +49,8 @@ func (c DatabaseConnection) DSN() string { c.Password = fmt.Sprintf("password=%s", c.Password) } - return fmt.Sprintf( - "host=%s port=%d dbname=%s user=%s %s sslmode=%s sslrootcert=%s sslkey=%s sslcert=%s", - c.Host, c.Port, c.DBName, c.User, c.Password, c.SSLMode, c.SSLCAPath, c.SSLKeyPath, c.SSLCertPath, - ) + return fmt.Sprintf("host=%s port=%d dbname=%s user=%s %s sslmode=%s sslrootcert=%s sslkey=%s sslcert=%s", + c.Host, c.Port, c.DBName, c.User, c.Password, c.SSLMode, c.SSLCAPath, c.SSLKeyPath, c.SSLCertPath) } // ColumnsList returns array of table columns. @@ -74,7 +72,7 @@ func (item Table) Read(tx *sql.Tx) (*QueryValues, error) { return nil, err } - q := new(QueryValues) + q := &QueryValues{} for rows.Next() { row, err := prepareRowPointers(rows) if err != nil { diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 57144fbdeb..0ee41051f7 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -39,7 +39,7 @@ var ( // New create encryption, if key on path doesnt exists will be generated. func New(keyPath string) *Encryption { - e := new(Encryption) + e := &Encryption{} e.Path = keyPath bytes, err := os.ReadFile(e.Path) diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index 468ac8ced3..2eab8a2b52 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -42,7 +42,7 @@ func prepareRowPointers(rows *sql.Rows) ([]any, error) { for _, t := range columns { switch t { case "VARCHAR", "JSONB": - row = append(row, new(sql.NullString)) + row = append(row, &sql.NullString{}) default: // TODO support more identificators types return nil, fmt.Errorf("unsupported identificator type %s", t) From 252741b45a9c410de5285328f962df756444850b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 11:51:57 +0200 Subject: [PATCH 082/215] PMM-13129 Changes to fix tests. Refactor. --- managed/cmd/pmm-managed/main.go | 31 ++++++- managed/models/database.go | 91 +++++++++++---------- managed/utils/encryption/database.go | 24 ++++-- managed/utils/encryption/encryption.go | 58 +++++++------ managed/utils/encryption/encryption_test.go | 6 +- managed/utils/testdb/db.go | 32 +++++++- 6 files changed, 165 insertions(+), 77 deletions(-) diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index 9268365c25..9216cff9ff 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -104,6 +104,7 @@ import ( "github.com/percona/pmm/managed/services/victoriametrics" "github.com/percona/pmm/managed/services/vmalert" "github.com/percona/pmm/managed/utils/clean" + "github.com/percona/pmm/managed/utils/encryption" "github.com/percona/pmm/managed/utils/envvars" "github.com/percona/pmm/managed/utils/interceptors" platformClient "github.com/percona/pmm/managed/utils/platform" @@ -615,9 +616,37 @@ func migrateDB(ctx context.Context, sqlDB *sql.DB, params models.SetupDBParams) if err == nil { return } - l.Warnf("Failed to migrate database: %s.", err) time.Sleep(time.Second) + + itemsToEncrypt := []encryption.Database{ + { + Database: "pmm-managed", + Tables: []encryption.Table{ + { + Table: "agents", + Identificators: []string{"agent_id"}, + Columns: []encryption.Column{ + {Column: "username"}, + {Column: "password"}, + {Column: "agent_password"}, + {Column: "aws_access_key"}, + {Column: "aws_secret_key "}, + {Column: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}, + {Column: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, + {Column: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}, + {Column: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}, + }, + }, + }, + }, + } + l.Infof("Encrypting database...") + if err := models.EncryptDB(context.TODO(), sqlDB, params, itemsToEncrypt); err != nil { + return + } + l.Warnf("Failed to encrypt database: %s.", err) + time.Sleep(time.Second) } } diff --git a/managed/models/database.go b/managed/models/database.go index d237295e11..f65d790f0d 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -27,17 +27,17 @@ import ( "net" "net/url" "os" + "slices" "strconv" "strings" "github.com/lib/pq" + "github.com/percona/pmm/managed/utils/encryption" "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "gopkg.in/reform.v1" "gopkg.in/reform.v1/dialects/postgresql" - - "github.com/percona/pmm/managed/utils/encryption" ) const ( @@ -1065,20 +1065,63 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. return nil, err } + return db, nil +} + +// EncryptDB encrypt all provided columns in specific database and table. +func EncryptDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsToEncrypt []encryption.Database) error { settings, err := GetSettings(sqlDB) if err != nil { - return nil, err + return err + } + alreadyEncrypted := map[string]bool{} + for _, v := range settings.EncryptedItems { + alreadyEncrypted[v] = true } - if len(settings.EncryptedItems) != 0 { - return db, nil + c, err := connectionFromSetupDBParams(params) + if err != nil { + return err } + notEncrypted := []encryption.Database{} + for _, item := range itemsToEncrypt { + database := encryption.Database{ + Database: item.Database, + } + for _, table := range item.Tables { + dbWithTable := fmt.Sprintf("%s.%s", item.Database, table.Table) + if alreadyEncrypted[dbWithTable] { + continue + } + + database.Tables = append(database.Tables, table) + } + + if len(database.Tables) != 0 { + notEncrypted = append(notEncrypted, database) + } + } + + newlyEncryptedItems, err := encryption.EncryptItems(ctx, c, notEncrypted) + if err != nil { + return err + } + _, err = UpdateSettings(sqlDB, &ChangeSettingsParams{ + EncryptedItems: slices.Concat(settings.EncryptedItems, newlyEncryptedItems), + }) + if err != nil { + return err + } + + return nil +} + +func connectionFromSetupDBParams(params SetupDBParams) (*encryption.DatabaseConnection, error) { host, p, err := net.SplitHostPort(params.Address) if err != nil { return nil, err } - port, err := strconv.ParseInt(p, 10, 16) if err != nil { return nil, err @@ -1095,41 +1138,7 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. SSLCertPath: params.SSLCertPath, } - itemsToEncrypt := []encryption.Database{ - { - Database: "pmm-managed", - Tables: []encryption.Table{ - { - Table: "agents", - Identificators: []string{"agent_id"}, - Columns: []encryption.Column{ - {Column: "username"}, - {Column: "password"}, - {Column: "agent_password"}, - {Column: "aws_access_key"}, - {Column: "aws_secret_key "}, - {Column: "mysql_options", CustomHandler: EncryptMySQLOptionsHandler}, - {Column: "postgresql_options", CustomHandler: EncryptPostgreSQLOptionsHandler}, - {Column: "mongo_db_tls_options", CustomHandler: EncryptMongoDBOptionsHandler}, - {Column: "azure_options", CustomHandler: EncryptAzureOptionsHandler}, - }, - }, - }, - }, - } - - if err := encryption.EncryptDB(ctx, c, itemsToEncrypt); err != nil { - return nil, err - } - - _, err = UpdateSettings(sqlDB, &ChangeSettingsParams{ - EncryptedItems: []string{"pmm-managed.agents"}, - }) - if err != nil { - return nil, err - } - - return db, nil + return c, nil } // checkVersion checks minimal required PostgreSQL server version. diff --git a/managed/utils/encryption/database.go b/managed/utils/encryption/database.go index aaa29f50e0..91ac451685 100644 --- a/managed/utils/encryption/database.go +++ b/managed/utils/encryption/database.go @@ -41,22 +41,36 @@ func (c DatabaseConnection) Connect() (*sql.DB, error) { // DSN returns formatted connection string to PG. func (c DatabaseConnection) DSN() string { - if c.SSLMode == "" { - c.SSLMode = "disable" + if c.DBName == "" { + c.DBName = "postgres" } if c.Password != "" { c.Password = fmt.Sprintf("password=%s", c.Password) } + if c.SSLMode == "" { + c.SSLMode = "disable" + } + return fmt.Sprintf("host=%s port=%d dbname=%s user=%s %s sslmode=%s sslrootcert=%s sslkey=%s sslcert=%s", c.Host, c.Port, c.DBName, c.User, c.Password, c.SSLMode, c.SSLCAPath, c.SSLKeyPath, c.SSLCertPath) } -// ColumnsList returns array of table columns. -func (item Table) ColumnsList() []string { +// List returns list of database and tables in database.table format. +func (db Database) List() []string { + var list []string + for _, table := range db.Tables { + list = append(list, fmt.Sprintf("%s.%s", db.Database, table.Table)) + } + + return list +} + +// ColumnsList returns list of table columns. +func (table Table) ColumnsList() []string { res := []string{} - for _, c := range item.Columns { + for _, c := range table.Columns { res = append(res, c.Column) } diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 0ee41051f7..92c4c2ea70 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -83,35 +83,36 @@ func (e *Encryption) Encrypt(secret string) (string, error) { return base64.StdEncoding.EncodeToString(cipherText), nil } -// EncryptDB is wrapper around DefaultEncryption.EncryptDB. -func EncryptDB(ctx context.Context, c *DatabaseConnection, items []Database) error { - return DefaultEncryption.EncryptDB(ctx, c, items) +// EncryptItems is wrapper around DefaultEncryption.EncryptItems. +func EncryptItems(ctx context.Context, c *DatabaseConnection, items []Database) ([]string, error) { + return DefaultEncryption.EncryptItems(ctx, c, items) } -// EncryptDB will encrypt all columns provided in DB connection. -func (e *Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection, items []Database) error { +// EncryptItems will encrypt all columns provided in DB connection. +func (e *Encryption) EncryptItems(ctx context.Context, c *DatabaseConnection, items []Database) ([]string, error) { if len(items) == 0 { - return errors.New("DB Connection: Database target tables/columns not defined") + return nil, errors.New("DB Connection: Database target tables/columns not defined") } + encryptedItems := []string{} for _, item := range items { c.DBName = item.Database db, err := c.Connect() if err != nil { - return err + return encryptedItems, err } defer db.Close() //nolint:errcheck tx, err := db.BeginTx(ctx, nil) if err != nil { - return err + return encryptedItems, err } defer tx.Rollback() //nolint:errcheck for _, table := range item.Tables { res, err := table.Read(tx) if err != nil { - return err + return encryptedItems, err } for k, v := range res.SetValues { @@ -126,7 +127,7 @@ func (e *Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection, items } if err != nil { - return err + return encryptedItems, err } res.SetValues[k][i] = encrypted } @@ -134,18 +135,20 @@ func (e *Encryption) EncryptDB(ctx context.Context, c *DatabaseConnection, items data = slices.Concat(data, res.WhereValues[k]) _, err := tx.Exec(res.Query, data...) if err != nil { - return err + return encryptedItems, err } } } err = tx.Commit() if err != nil { - return err + return encryptedItems, err } + + encryptedItems = slices.Concat(encryptedItems, item.List()) } - return nil + return encryptedItems, nil } // Decrypt is wrapper around DefaultEncryption.Decrypt. @@ -173,35 +176,36 @@ func (e *Encryption) Decrypt(cipherText string) (string, error) { return string(secret), nil } -// DecryptDB is wrapper around DefaultEncryption.DecryptDB. -func DecryptDB(ctx context.Context, c *DatabaseConnection, items []Database) error { - return DefaultEncryption.DecryptDB(ctx, c, items) +// DecryptItems is wrapper around DefaultEncryption.DecryptItems. +func DecryptItems(ctx context.Context, c *DatabaseConnection, items []Database) ([]string, error) { + return DefaultEncryption.DecryptItems(ctx, c, items) } -// DecryptDB will decrypt all columns provided in DB connection. -func (e *Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection, items []Database) error { +// DecryptItems will decrypt all columns provided in DB connection. +func (e *Encryption) DecryptItems(ctx context.Context, c *DatabaseConnection, items []Database) ([]string, error) { if len(items) == 0 { - return errors.New("DB Connection: Database target tables/columns not defined") + return nil, errors.New("DB Connection: Database target tables/columns not defined") } + decryptedItems := []string{} for _, item := range items { c.DBName = item.Database db, err := c.Connect() if err != nil { - return err + return decryptedItems, err } defer db.Close() //nolint:errcheck tx, err := db.BeginTx(ctx, nil) if err != nil { - return err + return decryptedItems, err } defer tx.Rollback() //nolint:errcheck for _, table := range item.Tables { res, err := table.Read(tx) if err != nil { - return err + return decryptedItems, err } for k, v := range res.SetValues { @@ -216,7 +220,7 @@ func (e *Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection, items } if err != nil { - return err + return decryptedItems, err } res.SetValues[k][i] = decrypted } @@ -224,16 +228,18 @@ func (e *Encryption) DecryptDB(ctx context.Context, c *DatabaseConnection, items data = slices.Concat(data, res.WhereValues[k]) _, err := tx.Exec(res.Query, data...) if err != nil { - return err + return decryptedItems, err } } } err = tx.Commit() if err != nil { - return err + return decryptedItems, err } + + decryptedItems = slices.Concat(decryptedItems, item.List()) } - return nil + return decryptedItems, nil } diff --git a/managed/utils/encryption/encryption_test.go b/managed/utils/encryption/encryption_test.go index ea5e85981a..2a54d1a5a3 100644 --- a/managed/utils/encryption/encryption_test.go +++ b/managed/utils/encryption/encryption_test.go @@ -57,7 +57,8 @@ func TestEncryption(t *testing.T) { }, }, } - require.NoError(t, encryption.EncryptDB(ctx, c, itemsToEncrypt)) + _, err = encryption.EncryptItems(ctx, c, itemsToEncrypt) + require.NoError(t, err) itemsToDecrypt := []encryption.Database{ { @@ -74,5 +75,6 @@ func TestEncryption(t *testing.T) { }, }, } - require.NoError(t, encryption.DecryptDB(ctx, c, itemsToDecrypt)) + _, err = encryption.DecryptItems(ctx, c, itemsToDecrypt) + require.NoError(t, err) } diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index d5707e367a..e50eb681ec 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/require" "github.com/percona/pmm/managed/models" + "github.com/percona/pmm/managed/utils/encryption" ) const ( @@ -70,7 +71,8 @@ func Open(tb testing.TB, setupFixtures models.SetupFixturesMode, migrationVersio // Please use Open method to recreate DB for each test if you don't need to control migrations. func SetupDB(tb testing.TB, db *sql.DB, setupFixtures models.SetupFixturesMode, migrationVersion *int) { tb.Helper() - _, err := models.SetupDB(context.TODO(), db, models.SetupDBParams{ + ctx := context.TODO() + params := models.SetupDBParams{ // Uncomment to see all setup queries: // Logf: tb.Logf, Address: models.DefaultPostgreSQLAddr, @@ -79,7 +81,33 @@ func SetupDB(tb testing.TB, db *sql.DB, setupFixtures models.SetupFixturesMode, Password: password, SetupFixtures: setupFixtures, MigrationVersion: migrationVersion, - }) + } + _, err := models.SetupDB(ctx, db, params) + require.NoError(tb, err) + + itemsToEncrypt := []encryption.Database{ + { + Database: testDatabase, + Tables: []encryption.Table{ + { + Table: "agents", + Identificators: []string{"agent_id"}, + Columns: []encryption.Column{ + {Column: "username"}, + {Column: "password"}, + {Column: "agent_password"}, + {Column: "aws_access_key"}, + {Column: "aws_secret_key "}, + {Column: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}, + {Column: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, + {Column: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}, + {Column: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}, + }, + }, + }, + }, + } + err = models.EncryptDB(ctx, db, params, itemsToEncrypt) require.NoError(tb, err) } From cfeb84ecb66cd7090aa43ce7dad0d04a2acc500e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 11:56:47 +0200 Subject: [PATCH 083/215] PMM-13129 Format. --- managed/models/database.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/managed/models/database.go b/managed/models/database.go index f65d790f0d..91b8ad7414 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -32,12 +32,13 @@ import ( "strings" "github.com/lib/pq" - "github.com/percona/pmm/managed/utils/encryption" "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "gopkg.in/reform.v1" "gopkg.in/reform.v1/dialects/postgresql" + + "github.com/percona/pmm/managed/utils/encryption" ) const ( From 7854d50e0b824769aa141f7df45bc299ddcfd4fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 12:28:37 +0200 Subject: [PATCH 084/215] PMM-13129 Fix. --- managed/cmd/pmm-managed/main.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index 9216cff9ff..f0521bce51 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -613,11 +613,11 @@ func migrateDB(ctx context.Context, sqlDB *sql.DB, params models.SetupDBParams) } l.Infof("Migrating database...") _, err := models.SetupDB(timeoutCtx, sqlDB, params) - if err == nil { - return + if err != nil { + l.Warnf("Failed to migrate database: %s.", err) + time.Sleep(time.Second) + continue } - l.Warnf("Failed to migrate database: %s.", err) - time.Sleep(time.Second) itemsToEncrypt := []encryption.Database{ { @@ -642,7 +642,8 @@ func migrateDB(ctx context.Context, sqlDB *sql.DB, params models.SetupDBParams) }, } l.Infof("Encrypting database...") - if err := models.EncryptDB(context.TODO(), sqlDB, params, itemsToEncrypt); err != nil { + err = models.EncryptDB(context.TODO(), sqlDB, params, itemsToEncrypt) + if err == nil { return } l.Warnf("Failed to encrypt database: %s.", err) From 5f0094d9986abc38449d7aa01bfd5bb23cc90014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 13:14:22 +0200 Subject: [PATCH 085/215] PMM-13129 Encrypt items now receive opened DB connection, refactor. --- managed/cmd/pmm-managed/main.go | 31 ++-- managed/models/database.go | 58 ++----- managed/utils/encryption/database.go | 129 --------------- managed/utils/encryption/encryption.go | 169 +++++++++----------- managed/utils/encryption/encryption_test.go | 80 --------- managed/utils/encryption/helpers.go | 62 +++++++ managed/utils/encryption/models.go | 10 +- managed/utils/testdb/db.go | 31 ++-- 8 files changed, 174 insertions(+), 396 deletions(-) delete mode 100644 managed/utils/encryption/database.go delete mode 100644 managed/utils/encryption/encryption_test.go diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index f0521bce51..d5d2dab92d 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -619,25 +619,20 @@ func migrateDB(ctx context.Context, sqlDB *sql.DB, params models.SetupDBParams) continue } - itemsToEncrypt := []encryption.Database{ + itemsToEncrypt := []encryption.Table{ { - Database: "pmm-managed", - Tables: []encryption.Table{ - { - Table: "agents", - Identificators: []string{"agent_id"}, - Columns: []encryption.Column{ - {Column: "username"}, - {Column: "password"}, - {Column: "agent_password"}, - {Column: "aws_access_key"}, - {Column: "aws_secret_key "}, - {Column: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}, - {Column: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, - {Column: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}, - {Column: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}, - }, - }, + Name: "agents", + Identificators: []string{"agent_id"}, + Columns: []encryption.Column{ + {Name: "username"}, + {Name: "password"}, + {Name: "agent_password"}, + {Name: "aws_access_key"}, + {Name: "aws_secret_key "}, + {Name: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}, + {Name: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, + {Name: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}, + {Name: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}, }, }, } diff --git a/managed/models/database.go b/managed/models/database.go index 91b8ad7414..fb16d25a8f 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1070,7 +1070,7 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. } // EncryptDB encrypt all provided columns in specific database and table. -func EncryptDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsToEncrypt []encryption.Database) error { +func EncryptDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsToEncrypt []encryption.Table) error { settings, err := GetSettings(sqlDB) if err != nil { return err @@ -1080,36 +1080,24 @@ func EncryptDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsTo alreadyEncrypted[v] = true } - c, err := connectionFromSetupDBParams(params) - if err != nil { - return err - } - - notEncrypted := []encryption.Database{} - for _, item := range itemsToEncrypt { - database := encryption.Database{ - Database: item.Database, - } - for _, table := range item.Tables { - dbWithTable := fmt.Sprintf("%s.%s", item.Database, table.Table) - if alreadyEncrypted[dbWithTable] { - continue - } - - database.Tables = append(database.Tables, table) + notEncrypted := []encryption.Table{} + newlyEncrypted := []string{} + for _, table := range itemsToEncrypt { + dbWithTable := fmt.Sprintf("%s.%s", params.Name, table.Name) + if alreadyEncrypted[dbWithTable] { + continue } - if len(database.Tables) != 0 { - notEncrypted = append(notEncrypted, database) - } + notEncrypted = append(notEncrypted, table) + newlyEncrypted = append(newlyEncrypted, dbWithTable) } - newlyEncryptedItems, err := encryption.EncryptItems(ctx, c, notEncrypted) + err = encryption.EncryptItems(ctx, sqlDB, notEncrypted) if err != nil { return err } _, err = UpdateSettings(sqlDB, &ChangeSettingsParams{ - EncryptedItems: slices.Concat(settings.EncryptedItems, newlyEncryptedItems), + EncryptedItems: slices.Concat(settings.EncryptedItems, newlyEncrypted), }) if err != nil { return err @@ -1118,30 +1106,6 @@ func EncryptDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsTo return nil } -func connectionFromSetupDBParams(params SetupDBParams) (*encryption.DatabaseConnection, error) { - host, p, err := net.SplitHostPort(params.Address) - if err != nil { - return nil, err - } - port, err := strconv.ParseInt(p, 10, 16) - if err != nil { - return nil, err - } - - c := &encryption.DatabaseConnection{ - Host: host, - Port: int16(port), - User: params.Username, - Password: params.Password, - SSLMode: params.SSLMode, - SSLCAPath: params.SSLCAPath, - SSLKeyPath: params.SSLKeyPath, - SSLCertPath: params.SSLCertPath, - } - - return c, nil -} - // checkVersion checks minimal required PostgreSQL server version. func checkVersion(ctx context.Context, db reform.DBTXContext) error { PGVersion, err := GetPostgreSQLVersion(ctx, db) diff --git a/managed/utils/encryption/database.go b/managed/utils/encryption/database.go deleted file mode 100644 index 91ac451685..0000000000 --- a/managed/utils/encryption/database.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (C) 2023 Percona LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package encryption - -import ( - "database/sql" - "fmt" - "slices" - "strings" - - _ "github.com/lib/pq" // register SQL driver -) - -// Connect open connection to DB. -func (c DatabaseConnection) Connect() (*sql.DB, error) { - db, err := sql.Open("postgres", c.DSN()) - if err != nil { - return nil, err - } - - err = db.Ping() - if err != nil { - return nil, err - } - - return db, nil -} - -// DSN returns formatted connection string to PG. -func (c DatabaseConnection) DSN() string { - if c.DBName == "" { - c.DBName = "postgres" - } - - if c.Password != "" { - c.Password = fmt.Sprintf("password=%s", c.Password) - } - - if c.SSLMode == "" { - c.SSLMode = "disable" - } - - return fmt.Sprintf("host=%s port=%d dbname=%s user=%s %s sslmode=%s sslrootcert=%s sslkey=%s sslcert=%s", - c.Host, c.Port, c.DBName, c.User, c.Password, c.SSLMode, c.SSLCAPath, c.SSLKeyPath, c.SSLCertPath) -} - -// List returns list of database and tables in database.table format. -func (db Database) List() []string { - var list []string - for _, table := range db.Tables { - list = append(list, fmt.Sprintf("%s.%s", db.Database, table.Table)) - } - - return list -} - -// ColumnsList returns list of table columns. -func (table Table) ColumnsList() []string { - res := []string{} - for _, c := range table.Columns { - res = append(res, c.Column) - } - - return res -} - -// Read returns query and it's values based on input. -func (item Table) Read(tx *sql.Tx) (*QueryValues, error) { - what := slices.Concat(item.Identificators, item.ColumnsList()) - query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ", "), item.Table) //nolint:gosec - rows, err := tx.Query(query) - if err != nil { - return nil, err - } - - q := &QueryValues{} - for rows.Next() { - row, err := prepareRowPointers(rows) - if err != nil { - return nil, err - } - err = rows.Scan(row...) - if err != nil { - return nil, err - } - - i := 1 - set := []string{} - setValues := []any{} - for k, v := range row[len(item.Identificators):] { - set = append(set, fmt.Sprintf("%s = $%d", item.Columns[k].Column, i)) - setValues = append(setValues, v) - i++ - } - setSQL := fmt.Sprintf("SET %s", strings.Join(set, ", ")) - q.SetValues = append(q.SetValues, setValues) - - where := []string{} - whereValues := []any{} - for k, id := range item.Identificators { - where = append(where, fmt.Sprintf("%s = $%d", id, i)) - whereValues = append(whereValues, row[k]) - i++ - } - whereSQL := fmt.Sprintf("WHERE %s", strings.Join(where, " AND ")) - q.WhereValues = append(q.WhereValues, whereValues) - - q.Query = fmt.Sprintf("UPDATE %s %s %s", item.Table, setSQL, whereSQL) - } - err = rows.Close() //nolint:sqlclosecheck - if err != nil { - return nil, err - } - - return q, nil -} diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 92c4c2ea70..3624f88103 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -18,6 +18,7 @@ package encryption import ( "context" + "database/sql" "encoding/base64" "fmt" "os" @@ -84,71 +85,59 @@ func (e *Encryption) Encrypt(secret string) (string, error) { } // EncryptItems is wrapper around DefaultEncryption.EncryptItems. -func EncryptItems(ctx context.Context, c *DatabaseConnection, items []Database) ([]string, error) { - return DefaultEncryption.EncryptItems(ctx, c, items) +func EncryptItems(ctx context.Context, db *sql.DB, tables []Table) error { + return DefaultEncryption.EncryptItems(ctx, db, tables) } // EncryptItems will encrypt all columns provided in DB connection. -func (e *Encryption) EncryptItems(ctx context.Context, c *DatabaseConnection, items []Database) ([]string, error) { - if len(items) == 0 { - return nil, errors.New("DB Connection: Database target tables/columns not defined") +func (e *Encryption) EncryptItems(ctx context.Context, db *sql.DB, tables []Table) error { + if len(tables) == 0 { + return errors.New("target tables/columns not defined") } - encryptedItems := []string{} - for _, item := range items { - c.DBName = item.Database - db, err := c.Connect() - if err != nil { - return encryptedItems, err - } - defer db.Close() //nolint:errcheck + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer tx.Rollback() //nolint:errcheck - tx, err := db.BeginTx(ctx, nil) + for _, table := range tables { + res, err := table.Read(tx) if err != nil { - return encryptedItems, err + return err } - defer tx.Rollback() //nolint:errcheck - for _, table := range item.Tables { - res, err := table.Read(tx) - if err != nil { - return encryptedItems, err - } - - for k, v := range res.SetValues { - for i, val := range v { - var encrypted any - var err error - switch table.Columns[i].CustomHandler { - case nil: - encrypted, err = encryptColumnStringHandler(e, val) - default: - encrypted, err = table.Columns[i].CustomHandler(e, val) - } - - if err != nil { - return encryptedItems, err - } - res.SetValues[k][i] = encrypted + for k, v := range res.SetValues { + for i, val := range v { + var encrypted any + var err error + switch table.Columns[i].CustomHandler { + case nil: + encrypted, err = encryptColumnStringHandler(e, val) + default: + encrypted, err = table.Columns[i].CustomHandler(e, val) } - data := slices.Concat([]any{}, v) - data = slices.Concat(data, res.WhereValues[k]) - _, err := tx.Exec(res.Query, data...) + if err != nil { - return encryptedItems, err + return err } + res.SetValues[k][i] = encrypted + } + data := slices.Concat([]any{}, v) + data = slices.Concat(data, res.WhereValues[k]) + _, err := tx.Exec(res.Query, data...) + if err != nil { + return err } } + } - err = tx.Commit() - if err != nil { - return encryptedItems, err - } - - encryptedItems = slices.Concat(encryptedItems, item.List()) + err = tx.Commit() + if err != nil { + return err } - return encryptedItems, nil + return nil } // Decrypt is wrapper around DefaultEncryption.Decrypt. @@ -177,69 +166,57 @@ func (e *Encryption) Decrypt(cipherText string) (string, error) { } // DecryptItems is wrapper around DefaultEncryption.DecryptItems. -func DecryptItems(ctx context.Context, c *DatabaseConnection, items []Database) ([]string, error) { - return DefaultEncryption.DecryptItems(ctx, c, items) +func DecryptItems(ctx context.Context, db *sql.DB, tables []Table) error { + return DefaultEncryption.DecryptItems(ctx, db, tables) } // DecryptItems will decrypt all columns provided in DB connection. -func (e *Encryption) DecryptItems(ctx context.Context, c *DatabaseConnection, items []Database) ([]string, error) { - if len(items) == 0 { - return nil, errors.New("DB Connection: Database target tables/columns not defined") +func (e *Encryption) DecryptItems(ctx context.Context, db *sql.DB, tables []Table) error { + if len(tables) == 0 { + return errors.New("target tables/columns not defined") } - decryptedItems := []string{} - for _, item := range items { - c.DBName = item.Database - db, err := c.Connect() - if err != nil { - return decryptedItems, err - } - defer db.Close() //nolint:errcheck + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer tx.Rollback() //nolint:errcheck - tx, err := db.BeginTx(ctx, nil) + for _, table := range tables { + res, err := table.Read(tx) if err != nil { - return decryptedItems, err + return err } - defer tx.Rollback() //nolint:errcheck - for _, table := range item.Tables { - res, err := table.Read(tx) - if err != nil { - return decryptedItems, err - } - - for k, v := range res.SetValues { - for i, val := range v { - var decrypted any - var err error - switch table.Columns[i].CustomHandler { - case nil: - decrypted, err = decryptColumnStringHandler(e, val) - default: - decrypted, err = table.Columns[i].CustomHandler(e, val) - } - - if err != nil { - return decryptedItems, err - } - res.SetValues[k][i] = decrypted + for k, v := range res.SetValues { + for i, val := range v { + var decrypted any + var err error + switch table.Columns[i].CustomHandler { + case nil: + decrypted, err = decryptColumnStringHandler(e, val) + default: + decrypted, err = table.Columns[i].CustomHandler(e, val) } - data := slices.Concat([]any{}, v) - data = slices.Concat(data, res.WhereValues[k]) - _, err := tx.Exec(res.Query, data...) + if err != nil { - return decryptedItems, err + return err } + res.SetValues[k][i] = decrypted + } + data := slices.Concat([]any{}, v) + data = slices.Concat(data, res.WhereValues[k]) + _, err := tx.Exec(res.Query, data...) + if err != nil { + return err } } + } - err = tx.Commit() - if err != nil { - return decryptedItems, err - } - - decryptedItems = slices.Concat(decryptedItems, item.List()) + err = tx.Commit() + if err != nil { + return err } - return decryptedItems, nil + return nil } diff --git a/managed/utils/encryption/encryption_test.go b/managed/utils/encryption/encryption_test.go deleted file mode 100644 index 2a54d1a5a3..0000000000 --- a/managed/utils/encryption/encryption_test.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 2023 Percona LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package encryption_test - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/percona/pmm/managed/utils/encryption" -) - -func TestEncryption(t *testing.T) { - secret := "password1" - - cipherText, err := encryption.Encrypt(secret) - require.NoError(t, err) - require.NotEmpty(t, cipherText) - decryptedSecret, err := encryption.Decrypt(cipherText) - require.NoError(t, err) - require.Equal(t, secret, decryptedSecret) - - c := &encryption.DatabaseConnection{ - Host: "127.0.0.1", - Port: 5432, - User: "postgres", - Password: "", - } - ctx := context.Background() - - itemsToEncrypt := []encryption.Database{ - { - Database: "pmm-managed", - Tables: []encryption.Table{ - { - Table: "agents", - Identificators: []string{"agent_id"}, - Columns: []encryption.Column{ - {Column: "username"}, - {Column: "password"}, - }, - }, - }, - }, - } - _, err = encryption.EncryptItems(ctx, c, itemsToEncrypt) - require.NoError(t, err) - - itemsToDecrypt := []encryption.Database{ - { - Database: "pmm-managed", - Tables: []encryption.Table{ - { - Table: "agents", - Identificators: []string{"agent_id"}, - Columns: []encryption.Column{ - {Column: "username"}, - {Column: "password"}, - }, - }, - }, - }, - } - _, err = encryption.DecryptItems(ctx, c, itemsToDecrypt) - require.NoError(t, err) -} diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index 2eab8a2b52..27f76f96f7 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -21,6 +21,8 @@ import ( "encoding/base64" "fmt" "os" + "slices" + "strings" "github.com/google/tink/go/aead" "github.com/google/tink/go/insecurecleartextkeyset" @@ -114,3 +116,63 @@ func (e *Encryption) generateKey() error { func (e *Encryption) saveKeyToFile() error { return os.WriteFile(e.Path, []byte(e.Key), 0o644) //nolint:gosec } + +func (table Table) ColumnsList() []string { + res := []string{} + for _, c := range table.Columns { + res = append(res, c.Name) + } + + return res +} + +// Read returns query and it's values based on input. +func (table Table) Read(tx *sql.Tx) (*QueryValues, error) { + what := slices.Concat(table.Identificators, table.ColumnsList()) + query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ", "), table.Name) //nolint:gosec + rows, err := tx.Query(query) + if err != nil { + return nil, err + } + + q := &QueryValues{} + for rows.Next() { + row, err := prepareRowPointers(rows) + if err != nil { + return nil, err + } + err = rows.Scan(row...) + if err != nil { + return nil, err + } + + i := 1 + set := []string{} + setValues := []any{} + for k, v := range row[len(table.Identificators):] { + set = append(set, fmt.Sprintf("%s = $%d", table.Columns[k].Name, i)) + setValues = append(setValues, v) + i++ + } + setSQL := fmt.Sprintf("SET %s", strings.Join(set, ", ")) + q.SetValues = append(q.SetValues, setValues) + + where := []string{} + whereValues := []any{} + for k, id := range table.Identificators { + where = append(where, fmt.Sprintf("%s = $%d", id, i)) + whereValues = append(whereValues, row[k]) + i++ + } + whereSQL := fmt.Sprintf("WHERE %s", strings.Join(where, " AND ")) + q.WhereValues = append(q.WhereValues, whereValues) + + q.Query = fmt.Sprintf("UPDATE %s %s %s", table.Name, setSQL, whereSQL) + } + err = rows.Close() //nolint:sqlclosecheck + if err != nil { + return nil, err + } + + return q, nil +} diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go index dda95e318f..2407e25c53 100644 --- a/managed/utils/encryption/models.go +++ b/managed/utils/encryption/models.go @@ -35,22 +35,16 @@ type DatabaseConnection struct { SSLCertPath string } -// Database resresents database name and tables to be encrypted/decrypted. -type Database struct { - Database string - Tables []Table -} - // Table represents table name, it's identificators and columns to be encrypted/decrypted. type Table struct { - Table string + Name string Identificators []string Columns []Column } // Column represents column name and column's custom handler (if needed). type Column struct { - Column string + Name string CustomHandler func(e *Encryption, val any) (any, error) } diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index e50eb681ec..af3989db5d 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -85,25 +85,20 @@ func SetupDB(tb testing.TB, db *sql.DB, setupFixtures models.SetupFixturesMode, _, err := models.SetupDB(ctx, db, params) require.NoError(tb, err) - itemsToEncrypt := []encryption.Database{ + itemsToEncrypt := []encryption.Table{ { - Database: testDatabase, - Tables: []encryption.Table{ - { - Table: "agents", - Identificators: []string{"agent_id"}, - Columns: []encryption.Column{ - {Column: "username"}, - {Column: "password"}, - {Column: "agent_password"}, - {Column: "aws_access_key"}, - {Column: "aws_secret_key "}, - {Column: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}, - {Column: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, - {Column: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}, - {Column: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}, - }, - }, + Name: "agents", + Identificators: []string{"agent_id"}, + Columns: []encryption.Column{ + {Name: "username"}, + {Name: "password"}, + {Name: "agent_password"}, + {Name: "aws_access_key"}, + {Name: "aws_secret_key "}, + {Name: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}, + {Name: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, + {Name: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}, + {Name: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}, }, }, } From 12c3bb13004915d80fea402ece43074fbc7db01d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 13:19:51 +0200 Subject: [PATCH 086/215] PMM-13129 Lint (correct ctx). --- managed/cmd/pmm-managed/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index d5d2dab92d..44b6e9bd9a 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -637,7 +637,7 @@ func migrateDB(ctx context.Context, sqlDB *sql.DB, params models.SetupDBParams) }, } l.Infof("Encrypting database...") - err = models.EncryptDB(context.TODO(), sqlDB, params, itemsToEncrypt) + err = models.EncryptDB(timeoutCtx, sqlDB, params, itemsToEncrypt) if err == nil { return } From 0d0bef4b4bc6b071d8398e2df41b099b7a85a875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 13:23:21 +0200 Subject: [PATCH 087/215] PMM-13129 Refactor, lint. --- managed/utils/encryption/encryption.go | 4 ++-- managed/utils/encryption/helpers.go | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 3624f88103..b1b943f783 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -102,7 +102,7 @@ func (e *Encryption) EncryptItems(ctx context.Context, db *sql.DB, tables []Tabl defer tx.Rollback() //nolint:errcheck for _, table := range tables { - res, err := table.Read(tx) + res, err := table.read(tx) if err != nil { return err } @@ -183,7 +183,7 @@ func (e *Encryption) DecryptItems(ctx context.Context, db *sql.DB, tables []Tabl defer tx.Rollback() //nolint:errcheck for _, table := range tables { - res, err := table.Read(tx) + res, err := table.read(tx) if err != nil { return err } diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index 27f76f96f7..f79c5aebbd 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -117,7 +117,7 @@ func (e *Encryption) saveKeyToFile() error { return os.WriteFile(e.Path, []byte(e.Key), 0o644) //nolint:gosec } -func (table Table) ColumnsList() []string { +func (table Table) columnsList() []string { res := []string{} for _, c := range table.Columns { res = append(res, c.Name) @@ -126,9 +126,8 @@ func (table Table) ColumnsList() []string { return res } -// Read returns query and it's values based on input. -func (table Table) Read(tx *sql.Tx) (*QueryValues, error) { - what := slices.Concat(table.Identificators, table.ColumnsList()) +func (table Table) read(tx *sql.Tx) (*QueryValues, error) { + what := slices.Concat(table.Identificators, table.columnsList()) query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ", "), table.Name) //nolint:gosec rows, err := tx.Query(query) if err != nil { From 3a5bd605b8b0b0ea0804d99309fe0e72db9fb87d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 13:44:51 +0200 Subject: [PATCH 088/215] PMM-13129 Check. --- managed/models/database.go | 4 ++++ managed/utils/testdb/db.go | 7 ------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index fb16d25a8f..9c65100c0d 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1092,6 +1092,10 @@ func EncryptDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsTo newlyEncrypted = append(newlyEncrypted, dbWithTable) } + if len(notEncrypted) == 0 { + return nil + } + err = encryption.EncryptItems(ctx, sqlDB, notEncrypted) if err != nil { return err diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index af3989db5d..f0a564105d 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -92,13 +92,6 @@ func SetupDB(tb testing.TB, db *sql.DB, setupFixtures models.SetupFixturesMode, Columns: []encryption.Column{ {Name: "username"}, {Name: "password"}, - {Name: "agent_password"}, - {Name: "aws_access_key"}, - {Name: "aws_secret_key "}, - {Name: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}, - {Name: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, - {Name: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}, - {Name: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}, }, }, } From 7fbe25735de2df9f040700c16ee9140aca465839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 14:05:51 +0200 Subject: [PATCH 089/215] PMM-13129 Lint. --- managed/models/database.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/models/database.go b/managed/models/database.go index 9c65100c0d..4cbc379972 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1075,7 +1075,7 @@ func EncryptDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsTo if err != nil { return err } - alreadyEncrypted := map[string]bool{} + alreadyEncrypted := make(map[string]bool) for _, v := range settings.EncryptedItems { alreadyEncrypted[v] = true } From 586b75acd05081002711fce370d902324b302c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 14:59:29 +0200 Subject: [PATCH 090/215] PMM-13129 Fix settings test. --- managed/models/settings_helpers_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/managed/models/settings_helpers_test.go b/managed/models/settings_helpers_test.go index 9eff36ece9..f3658e68f4 100644 --- a/managed/models/settings_helpers_test.go +++ b/managed/models/settings_helpers_test.go @@ -36,6 +36,7 @@ func TestSettings(t *testing.T) { t.Run("Defaults", func(t *testing.T) { actual, err := models.GetSettings(sqlDB) require.NoError(t, err) + require.NotEmpty(t, actual.EncryptedItems) expected := &models.Settings{ MetricsResolutions: models.MetricsResolutions{ HR: 5 * time.Second, @@ -52,7 +53,7 @@ func TestSettings(t *testing.T) { }, }, DefaultRoleID: 1, - EncryptedItems: []string{"pmm-managed.agents"}, + EncryptedItems: actual.EncryptedItems, } assert.Equal(t, expected, actual) }) From 15244cb3ae4b3153230183bcaec583a6dc9fe86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 18:13:56 +0200 Subject: [PATCH 091/215] PMM-13129 Fix to prevent double encryption on setup fixtures. --- managed/models/database.go | 36 ++++++++++++++++++---------- managed/models/encryption_helpers.go | 4 ++-- managed/utils/testdb/db.go | 14 ----------- 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index 4cbc379972..25f776806f 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1066,6 +1066,28 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. return nil, err } + itemsToEncrypt := []encryption.Table{ + { + Name: "agents", + Identificators: []string{"agent_id"}, + Columns: []encryption.Column{ + {Name: "username"}, + {Name: "password"}, + }, + }, + } + err := EncryptDB(ctx, sqlDB, params, itemsToEncrypt) + if err != nil { + return nil, err + } + + // add PMM Server postgres_exporter and pg_stat_statements + // once db is already encrypted to prevent double encryption + // during adding by CreateAgent methods. + if err = setupPMMServerAgents(db.Querier, params); err != nil { + return nil, err + } + return db, nil } @@ -1218,17 +1240,11 @@ func migrateDB(db *reform.DB, params SetupDBParams) error { return err } - if err = setupFixture1(tx.Querier, params); err != nil { - return err - } - if err = setupFixture2(tx.Querier, params.Username, params.Password); err != nil { - return err - } return nil }) } -func setupFixture1(q *reform.Querier, params SetupDBParams) error { +func setupPMMServerAgents(q *reform.Querier, params SetupDBParams) error { // create PMM Server Node and associated Agents node, err := createNodeWithID(q, PMMServerNodeID, GenericNodeType, &CreateNodeParams{ NodeName: "pmm-server", @@ -1311,12 +1327,6 @@ func setupFixture1(q *reform.Querier, params SetupDBParams) error { return nil } -func setupFixture2(q *reform.Querier, username, password string) error { //nolint:revive - // TODO add clickhouse_exporter - - return nil -} - // parsePGAddress parses PostgreSQL address into address:port; if no port specified returns default port number. func parsePGAddress(address string) (string, uint16, error) { if !strings.Contains(address, ":") { diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index 6296cc995e..0c53fa6da3 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -24,12 +24,12 @@ import ( "github.com/percona/pmm/managed/utils/encryption" ) -// EncryptAgent returns agent after it is encrypted. +// EncryptAgent encrypt agent. func EncryptAgent(agent *Agent) { agentEncryption(agent, encryption.Encrypt) } -// DecryptAgent returns agent after it is decrypted. +// DecryptAgent decrypt agent. func DecryptAgent(agent *Agent) { agentEncryption(agent, encryption.Decrypt) } diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index f0a564105d..79ae30438d 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -26,7 +26,6 @@ import ( "github.com/stretchr/testify/require" "github.com/percona/pmm/managed/models" - "github.com/percona/pmm/managed/utils/encryption" ) const ( @@ -84,19 +83,6 @@ func SetupDB(tb testing.TB, db *sql.DB, setupFixtures models.SetupFixturesMode, } _, err := models.SetupDB(ctx, db, params) require.NoError(tb, err) - - itemsToEncrypt := []encryption.Table{ - { - Name: "agents", - Identificators: []string{"agent_id"}, - Columns: []encryption.Column{ - {Name: "username"}, - {Name: "password"}, - }, - }, - } - err = models.EncryptDB(ctx, db, params, itemsToEncrypt) - require.NoError(tb, err) } func newName(length int) string { From e2c720e065760a090bb9a8a265d1b06c6f59491e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 18:21:31 +0200 Subject: [PATCH 092/215] PMM-13129 Changes. --- managed/cmd/pmm-managed/main.go | 14 ++++---------- managed/models/database.go | 12 +----------- managed/utils/testdb/db.go | 22 +++++++++++++++++++++- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index 44b6e9bd9a..67d1b934f9 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -611,13 +611,6 @@ func migrateDB(ctx context.Context, sqlDB *sql.DB, params models.SetupDBParams) l.Fatalf("Could not migrate DB: timeout") default: } - l.Infof("Migrating database...") - _, err := models.SetupDB(timeoutCtx, sqlDB, params) - if err != nil { - l.Warnf("Failed to migrate database: %s.", err) - time.Sleep(time.Second) - continue - } itemsToEncrypt := []encryption.Table{ { @@ -636,12 +629,13 @@ func migrateDB(ctx context.Context, sqlDB *sql.DB, params models.SetupDBParams) }, }, } - l.Infof("Encrypting database...") - err = models.EncryptDB(timeoutCtx, sqlDB, params, itemsToEncrypt) + + l.Infof("Migrating database...") + _, err := models.SetupDB(timeoutCtx, sqlDB, params, itemsToEncrypt) if err == nil { return } - l.Warnf("Failed to encrypt database: %s.", err) + l.Warnf("Failed to migrate database: %s.", err) time.Sleep(time.Second) } } diff --git a/managed/models/database.go b/managed/models/database.go index 25f776806f..c6d69e8633 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1042,7 +1042,7 @@ type SetupDBParams struct { } // SetupDB checks minimal required PostgreSQL version and runs database migrations. Optionally creates database and adds initial data. -func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform.DB, error) { +func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsToEncrypt []encryption.Table) (*reform.DB, error) { var logger reform.Logger if params.Logf != nil { logger = reform.NewPrintfLogger(params.Logf) @@ -1066,16 +1066,6 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. return nil, err } - itemsToEncrypt := []encryption.Table{ - { - Name: "agents", - Identificators: []string{"agent_id"}, - Columns: []encryption.Column{ - {Name: "username"}, - {Name: "password"}, - }, - }, - } err := EncryptDB(ctx, sqlDB, params, itemsToEncrypt) if err != nil { return nil, err diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index 79ae30438d..1807f0c5c2 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/require" "github.com/percona/pmm/managed/models" + "github.com/percona/pmm/managed/utils/encryption" ) const ( @@ -81,7 +82,26 @@ func SetupDB(tb testing.TB, db *sql.DB, setupFixtures models.SetupFixturesMode, SetupFixtures: setupFixtures, MigrationVersion: migrationVersion, } - _, err := models.SetupDB(ctx, db, params) + + itemsToEncrypt := []encryption.Table{ + { + Name: "agents", + Identificators: []string{"agent_id"}, + Columns: []encryption.Column{ + {Name: "username"}, + {Name: "password"}, + {Name: "agent_password"}, + {Name: "aws_access_key"}, + {Name: "aws_secret_key "}, + {Name: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}, + {Name: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, + {Name: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}, + {Name: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}, + }, + }, + } + + _, err := models.SetupDB(ctx, db, params, itemsToEncrypt) require.NoError(tb, err) } From d0a454ce07c3579433124744e83a03eb03807a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 18:28:58 +0200 Subject: [PATCH 093/215] PMM-13129 Encrypt only basic fields in tests (migration). --- managed/utils/testdb/db.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index 1807f0c5c2..fb88b8eec0 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -90,13 +90,6 @@ func SetupDB(tb testing.TB, db *sql.DB, setupFixtures models.SetupFixturesMode, Columns: []encryption.Column{ {Name: "username"}, {Name: "password"}, - {Name: "agent_password"}, - {Name: "aws_access_key"}, - {Name: "aws_secret_key "}, - {Name: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}, - {Name: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, - {Name: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}, - {Name: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}, }, }, } From 64f8b13c6ffe597613184b1d5e7f8308c32d53b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 19:26:40 +0200 Subject: [PATCH 094/215] PMM-13129 Test. --- managed/models/database.go | 39 ++++++++++++++------------ managed/utils/encryption/encryption.go | 36 +++++------------------- managed/utils/encryption/helpers.go | 3 +- 3 files changed, 30 insertions(+), 48 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index c6d69e8633..fa49a64507 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1062,19 +1062,7 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsToEn return nil, errCV } - if err := migrateDB(db, params); err != nil { - return nil, err - } - - err := EncryptDB(ctx, sqlDB, params, itemsToEncrypt) - if err != nil { - return nil, err - } - - // add PMM Server postgres_exporter and pg_stat_statements - // once db is already encrypted to prevent double encryption - // during adding by CreateAgent methods. - if err = setupPMMServerAgents(db.Querier, params); err != nil { + if err := migrateDB(db, params, itemsToEncrypt); err != nil { return nil, err } @@ -1082,8 +1070,8 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsToEn } // EncryptDB encrypt all provided columns in specific database and table. -func EncryptDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsToEncrypt []encryption.Table) error { - settings, err := GetSettings(sqlDB) +func EncryptDB(ctx context.Context, tx *reform.TX, params SetupDBParams, itemsToEncrypt []encryption.Table) error { + settings, err := GetSettings(tx) if err != nil { return err } @@ -1108,11 +1096,11 @@ func EncryptDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsTo return nil } - err = encryption.EncryptItems(ctx, sqlDB, notEncrypted) + err = encryption.EncryptItems(ctx, tx, notEncrypted) if err != nil { return err } - _, err = UpdateSettings(sqlDB, &ChangeSettingsParams{ + _, err = UpdateSettings(tx, &ChangeSettingsParams{ EncryptedItems: slices.Concat(settings.EncryptedItems, newlyEncrypted), }) if err != nil { @@ -1181,7 +1169,7 @@ func initWithRoot(params SetupDBParams) error { } // migrateDB runs PostgreSQL database migrations. -func migrateDB(db *reform.DB, params SetupDBParams) error { +func migrateDB(db *reform.DB, params SetupDBParams, itemsToEncrypt []encryption.Table) error { var currentVersion int errDB := db.QueryRow("SELECT id FROM schema_migrations ORDER BY id DESC LIMIT 1").Scan(¤tVersion) // undefined_table (see https://www.postgresql.org/docs/current/errcodes-appendix.html) @@ -1218,6 +1206,11 @@ func migrateDB(db *reform.DB, params SetupDBParams) error { } if params.SetupFixtures == SkipFixtures { + err := EncryptDB(context.TODO(), tx, params, itemsToEncrypt) + if err != nil { + return err + } + return nil } @@ -1230,6 +1223,16 @@ func migrateDB(db *reform.DB, params SetupDBParams) error { return err } + err = EncryptDB(context.TODO(), tx, params, itemsToEncrypt) + if err != nil { + return err + } + + err = setupPMMServerAgents(tx.Querier, params) + if err != nil { + return err + } + return nil }) } diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index b1b943f783..c4694d5a98 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -18,7 +18,6 @@ package encryption import ( "context" - "database/sql" "encoding/base64" "fmt" "os" @@ -26,6 +25,7 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" + "gopkg.in/reform.v1" ) // DefaultEncryptionKeyPath contains default PMM encryption key path. @@ -85,22 +85,16 @@ func (e *Encryption) Encrypt(secret string) (string, error) { } // EncryptItems is wrapper around DefaultEncryption.EncryptItems. -func EncryptItems(ctx context.Context, db *sql.DB, tables []Table) error { - return DefaultEncryption.EncryptItems(ctx, db, tables) +func EncryptItems(ctx context.Context, tx *reform.TX, tables []Table) error { + return DefaultEncryption.EncryptItems(ctx, tx, tables) } // EncryptItems will encrypt all columns provided in DB connection. -func (e *Encryption) EncryptItems(ctx context.Context, db *sql.DB, tables []Table) error { +func (e *Encryption) EncryptItems(ctx context.Context, tx *reform.TX, tables []Table) error { if len(tables) == 0 { return errors.New("target tables/columns not defined") } - tx, err := db.BeginTx(ctx, nil) - if err != nil { - return err - } - defer tx.Rollback() //nolint:errcheck - for _, table := range tables { res, err := table.read(tx) if err != nil { @@ -132,11 +126,6 @@ func (e *Encryption) EncryptItems(ctx context.Context, db *sql.DB, tables []Tabl } } - err = tx.Commit() - if err != nil { - return err - } - return nil } @@ -166,22 +155,16 @@ func (e *Encryption) Decrypt(cipherText string) (string, error) { } // DecryptItems is wrapper around DefaultEncryption.DecryptItems. -func DecryptItems(ctx context.Context, db *sql.DB, tables []Table) error { - return DefaultEncryption.DecryptItems(ctx, db, tables) +func DecryptItems(ctx context.Context, tx *reform.TX, tables []Table) error { + return DefaultEncryption.DecryptItems(ctx, tx, tables) } // DecryptItems will decrypt all columns provided in DB connection. -func (e *Encryption) DecryptItems(ctx context.Context, db *sql.DB, tables []Table) error { +func (e *Encryption) DecryptItems(ctx context.Context, tx *reform.TX, tables []Table) error { if len(tables) == 0 { return errors.New("target tables/columns not defined") } - tx, err := db.BeginTx(ctx, nil) - if err != nil { - return err - } - defer tx.Rollback() //nolint:errcheck - for _, table := range tables { res, err := table.read(tx) if err != nil { @@ -213,10 +196,5 @@ func (e *Encryption) DecryptItems(ctx context.Context, db *sql.DB, tables []Tabl } } - err = tx.Commit() - if err != nil { - return err - } - return nil } diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index f79c5aebbd..c53ab5162f 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -28,6 +28,7 @@ import ( "github.com/google/tink/go/insecurecleartextkeyset" "github.com/google/tink/go/keyset" "github.com/google/tink/go/tink" + "gopkg.in/reform.v1" ) func prepareRowPointers(rows *sql.Rows) ([]any, error) { @@ -126,7 +127,7 @@ func (table Table) columnsList() []string { return res } -func (table Table) read(tx *sql.Tx) (*QueryValues, error) { +func (table Table) read(tx *reform.TX) (*QueryValues, error) { what := slices.Concat(table.Identificators, table.columnsList()) query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ", "), table.Name) //nolint:gosec rows, err := tx.Query(query) From b55d5597b8ec101f9fbef7770c5fec8d43f92e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 19:37:05 +0200 Subject: [PATCH 095/215] PMM-13129 Lint. --- managed/models/database.go | 8 ++++---- managed/utils/encryption/encryption.go | 13 ++++++------- managed/utils/encryption/helpers.go | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index fa49a64507..0e84e21a02 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1070,7 +1070,7 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsToEn } // EncryptDB encrypt all provided columns in specific database and table. -func EncryptDB(ctx context.Context, tx *reform.TX, params SetupDBParams, itemsToEncrypt []encryption.Table) error { +func EncryptDB(tx *reform.TX, params SetupDBParams, itemsToEncrypt []encryption.Table) error { settings, err := GetSettings(tx) if err != nil { return err @@ -1096,7 +1096,7 @@ func EncryptDB(ctx context.Context, tx *reform.TX, params SetupDBParams, itemsTo return nil } - err = encryption.EncryptItems(ctx, tx, notEncrypted) + err = encryption.EncryptItems(tx, notEncrypted) if err != nil { return err } @@ -1206,7 +1206,7 @@ func migrateDB(db *reform.DB, params SetupDBParams, itemsToEncrypt []encryption. } if params.SetupFixtures == SkipFixtures { - err := EncryptDB(context.TODO(), tx, params, itemsToEncrypt) + err := EncryptDB(tx, params, itemsToEncrypt) if err != nil { return err } @@ -1223,7 +1223,7 @@ func migrateDB(db *reform.DB, params SetupDBParams, itemsToEncrypt []encryption. return err } - err = EncryptDB(context.TODO(), tx, params, itemsToEncrypt) + err = EncryptDB(tx, params, itemsToEncrypt) if err != nil { return err } diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index c4694d5a98..d7ce0b58a3 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -17,7 +17,6 @@ package encryption import ( - "context" "encoding/base64" "fmt" "os" @@ -85,12 +84,12 @@ func (e *Encryption) Encrypt(secret string) (string, error) { } // EncryptItems is wrapper around DefaultEncryption.EncryptItems. -func EncryptItems(ctx context.Context, tx *reform.TX, tables []Table) error { - return DefaultEncryption.EncryptItems(ctx, tx, tables) +func EncryptItems(tx *reform.TX, tables []Table) error { + return DefaultEncryption.EncryptItems(tx, tables) } // EncryptItems will encrypt all columns provided in DB connection. -func (e *Encryption) EncryptItems(ctx context.Context, tx *reform.TX, tables []Table) error { +func (e *Encryption) EncryptItems(tx *reform.TX, tables []Table) error { if len(tables) == 0 { return errors.New("target tables/columns not defined") } @@ -155,12 +154,12 @@ func (e *Encryption) Decrypt(cipherText string) (string, error) { } // DecryptItems is wrapper around DefaultEncryption.DecryptItems. -func DecryptItems(ctx context.Context, tx *reform.TX, tables []Table) error { - return DefaultEncryption.DecryptItems(ctx, tx, tables) +func DecryptItems(tx *reform.TX, tables []Table) error { + return DefaultEncryption.DecryptItems(tx, tables) } // DecryptItems will decrypt all columns provided in DB connection. -func (e *Encryption) DecryptItems(ctx context.Context, tx *reform.TX, tables []Table) error { +func (e *Encryption) DecryptItems(tx *reform.TX, tables []Table) error { if len(tables) == 0 { return errors.New("target tables/columns not defined") } diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index c53ab5162f..24e32d91aa 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -129,7 +129,7 @@ func (table Table) columnsList() []string { func (table Table) read(tx *reform.TX) (*QueryValues, error) { what := slices.Concat(table.Identificators, table.columnsList()) - query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ", "), table.Name) //nolint:gosec + query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ", "), table.Name) rows, err := tx.Query(query) if err != nil { return nil, err From 8861cbeffadd348e28af3df4b046d5a5105530a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 20:23:48 +0200 Subject: [PATCH 096/215] PMM-13129 Different encrypted columns for different migration versions. --- managed/utils/testdb/db.go | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index fb88b8eec0..e639089b92 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -20,6 +20,7 @@ import ( "context" "database/sql" "math/rand" + "slices" "testing" "time" @@ -83,14 +84,40 @@ func SetupDB(tb testing.TB, db *sql.DB, setupFixtures models.SetupFixturesMode, MigrationVersion: migrationVersion, } + columnsToEncrypt := []encryption.Column{ + {Name: "username"}, + {Name: "password"}, + } + if *migrationVersion >= 9 { + columnsToEncrypt = slices.Concat( + columnsToEncrypt, []encryption.Column{{Name: "aws_access_key"}, {Name: "aws_secret_key "}}) + } + if *migrationVersion >= 25 { + columnsToEncrypt = append( + columnsToEncrypt, encryption.Column{Name: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}) + } + if *migrationVersion >= 31 { + columnsToEncrypt = append( + columnsToEncrypt, encryption.Column{Name: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}) + } + if *migrationVersion >= 36 { + columnsToEncrypt = append( + columnsToEncrypt, encryption.Column{Name: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}) + } + if *migrationVersion >= 41 { + columnsToEncrypt = append( + columnsToEncrypt, encryption.Column{Name: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}) + } + if *migrationVersion >= 42 { + columnsToEncrypt = append( + columnsToEncrypt, encryption.Column{Name: "agent_password"}) + } + itemsToEncrypt := []encryption.Table{ { Name: "agents", Identificators: []string{"agent_id"}, - Columns: []encryption.Column{ - {Name: "username"}, - {Name: "password"}, - }, + Columns: columnsToEncrypt, }, } From 98bf78f4eb1818e02688aa7e990a197de4a21848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 20:51:59 +0200 Subject: [PATCH 097/215] PMM-13129 Fix. --- managed/utils/testdb/db.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index e639089b92..7d3b02a9ac 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -24,6 +24,7 @@ import ( "testing" "time" + "github.com/AlekSi/pointer" "github.com/stretchr/testify/require" "github.com/percona/pmm/managed/models" @@ -88,27 +89,27 @@ func SetupDB(tb testing.TB, db *sql.DB, setupFixtures models.SetupFixturesMode, {Name: "username"}, {Name: "password"}, } - if *migrationVersion >= 9 { + if pointer.GetInt(migrationVersion) >= 9 { columnsToEncrypt = slices.Concat( columnsToEncrypt, []encryption.Column{{Name: "aws_access_key"}, {Name: "aws_secret_key "}}) } - if *migrationVersion >= 25 { + if pointer.GetInt(migrationVersion) >= 25 { columnsToEncrypt = append( columnsToEncrypt, encryption.Column{Name: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}) } - if *migrationVersion >= 31 { + if pointer.GetInt(migrationVersion) >= 31 { columnsToEncrypt = append( columnsToEncrypt, encryption.Column{Name: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}) } - if *migrationVersion >= 36 { + if pointer.GetInt(migrationVersion) >= 36 { columnsToEncrypt = append( columnsToEncrypt, encryption.Column{Name: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}) } - if *migrationVersion >= 41 { + if pointer.GetInt(migrationVersion) >= 41 { columnsToEncrypt = append( columnsToEncrypt, encryption.Column{Name: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}) } - if *migrationVersion >= 42 { + if pointer.GetInt(migrationVersion) >= 42 { columnsToEncrypt = append( columnsToEncrypt, encryption.Column{Name: "agent_password"}) } From 9544ea0e072c674fcd14d55c5611a68fe6389cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 2 Jul 2024 20:52:33 +0200 Subject: [PATCH 098/215] PMM-13129 TODO. --- managed/cmd/pmm-managed/main.go | 14 +++++++------- managed/models/agent_helpers.go | 7 +++++++ managed/models/database.go | 19 ++++++++++++++----- managed/utils/encryption/encryption.go | 6 +++--- managed/utils/testdb/db.go | 3 ++- 5 files changed, 33 insertions(+), 16 deletions(-) diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index 67d1b934f9..c2ab096437 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -619,13 +619,13 @@ func migrateDB(ctx context.Context, sqlDB *sql.DB, params models.SetupDBParams) Columns: []encryption.Column{ {Name: "username"}, {Name: "password"}, - {Name: "agent_password"}, - {Name: "aws_access_key"}, - {Name: "aws_secret_key "}, - {Name: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}, - {Name: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, - {Name: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}, - {Name: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}, + // {Name: "agent_password"}, + // {Name: "aws_access_key"}, + // {Name: "aws_secret_key "}, + // {Name: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}, + // {Name: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, + // {Name: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}, + // {Name: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}, }, }, } diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 482c9a76b7..1a51414c89 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -932,6 +932,13 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara DisabledCollectors: params.DisableCollectors, LogLevel: pointer.ToStringOrNil(params.LogLevel), } + + // TODO + // settings, err := GetSettings(q) + // if err != nil { + // return err + // } + EncryptAgent(row) if err := row.SetCustomLabels(params.CustomLabels); err != nil { diff --git a/managed/models/database.go b/managed/models/database.go index 0e84e21a02..966f536b1b 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1071,6 +1071,10 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsToEn // EncryptDB encrypt all provided columns in specific database and table. func EncryptDB(tx *reform.TX, params SetupDBParams, itemsToEncrypt []encryption.Table) error { + if len(itemsToEncrypt) == 0 { + return nil + } + settings, err := GetSettings(tx) if err != nil { return err @@ -1083,13 +1087,18 @@ func EncryptDB(tx *reform.TX, params SetupDBParams, itemsToEncrypt []encryption. notEncrypted := []encryption.Table{} newlyEncrypted := []string{} for _, table := range itemsToEncrypt { - dbWithTable := fmt.Sprintf("%s.%s", params.Name, table.Name) - if alreadyEncrypted[dbWithTable] { - continue - } + columns := []encryption.Column{} + for _, column := range table.Columns { + dbTableColumn := fmt.Sprintf("%s.%s.%s", params.Name, table.Name, column.Name) + if alreadyEncrypted[dbTableColumn] { + continue + } + columns = append(columns, column) + newlyEncrypted = append(newlyEncrypted, dbTableColumn) + } + table.Columns = columns notEncrypted = append(notEncrypted, table) - newlyEncrypted = append(newlyEncrypted, dbWithTable) } if len(notEncrypted) == 0 { diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index d7ce0b58a3..792c83b289 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -28,7 +28,7 @@ import ( ) // DefaultEncryptionKeyPath contains default PMM encryption key path. -const DefaultEncryptionKeyPath = "/srv/pmm-encryption.key" +const DefaultEncryptionKeyPath = "/Users/jiri.ctvrtka/pmm-encryption.key" var ( // ErrEncryptionNotInitialized is error in case of encryption is not initialized. @@ -91,7 +91,7 @@ func EncryptItems(tx *reform.TX, tables []Table) error { // EncryptItems will encrypt all columns provided in DB connection. func (e *Encryption) EncryptItems(tx *reform.TX, tables []Table) error { if len(tables) == 0 { - return errors.New("target tables/columns not defined") + return nil } for _, table := range tables { @@ -161,7 +161,7 @@ func DecryptItems(tx *reform.TX, tables []Table) error { // DecryptItems will decrypt all columns provided in DB connection. func (e *Encryption) DecryptItems(tx *reform.TX, tables []Table) error { if len(tables) == 0 { - return errors.New("target tables/columns not defined") + return nil } for _, table := range tables { diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index 7d3b02a9ac..2dd5d6e3c2 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -32,7 +32,7 @@ import ( ) const ( - username, password = "postgres", "" + username, password = "pmm-agent", "pmm-agent-password" testDatabase = "pmm-managed-dev" ) @@ -85,6 +85,7 @@ func SetupDB(tb testing.TB, db *sql.DB, setupFixtures models.SetupFixturesMode, MigrationVersion: migrationVersion, } + // TODO columnsToEncrypt := []encryption.Column{ {Name: "username"}, {Name: "password"}, From a4cad29e1bdf7229263e2f0d91daf35de1ed695c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 3 Jul 2024 10:19:37 +0200 Subject: [PATCH 099/215] PMM-13129 TODO. --- managed/cmd/pmm-managed/main.go | 14 ++--- managed/models/agent_helpers.go | 32 +++++------ managed/models/encryption_helpers.go | 54 +++++++++++++------ .../services/agents/service_info_broker.go | 4 +- managed/utils/encryption/encryption.go | 2 +- managed/utils/testdb/db.go | 2 +- 6 files changed, 63 insertions(+), 45 deletions(-) diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index c2ab096437..126a8c8070 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -619,13 +619,13 @@ func migrateDB(ctx context.Context, sqlDB *sql.DB, params models.SetupDBParams) Columns: []encryption.Column{ {Name: "username"}, {Name: "password"}, - // {Name: "agent_password"}, - // {Name: "aws_access_key"}, - // {Name: "aws_secret_key "}, - // {Name: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}, - // {Name: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, - // {Name: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}, - // {Name: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}, + {Name: "agent_password"}, + {Name: "aws_access_key"}, + {Name: "aws_secret_key"}, + {Name: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}, + {Name: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, + {Name: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}, + {Name: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}, }, }, } diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 1a51414c89..ae03bcaee0 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -230,7 +230,7 @@ func FindAgents(q *reform.Querier, filters AgentFilters) ([]*Agent, error) { agents := make([]*Agent, len(structs)) for i, s := range structs { agent := s.(*Agent) //nolint:forcetypeassert - DecryptAgent(agent) + DecryptAgent(q, agent) agents[i] = agent } @@ -252,7 +252,7 @@ func FindAgentByID(q *reform.Querier, id string) (*Agent, error) { return nil, errors.WithStack(err) } - DecryptAgent(agent) + DecryptAgent(q, agent) return agent, nil } @@ -277,7 +277,7 @@ func FindAgentsByIDs(q *reform.Querier, ids []string) ([]*Agent, error) { res := make([]*Agent, len(structs)) for i, s := range structs { agent := s.(*Agent) //nolint:forcetypeassert - DecryptAgent(agent) + DecryptAgent(q, agent) res[i] = agent } return res, nil @@ -330,7 +330,7 @@ func FindDBConfigForService(q *reform.Querier, serviceID string) (*DBConfig, err res := make([]*Agent, len(structs)) for i, s := range structs { agent := s.(*Agent) //nolint:forcetypeassert - DecryptAgent(agent) + DecryptAgent(q, agent) res[i] = agent } @@ -359,7 +359,7 @@ func FindPMMAgentsRunningOnNode(q *reform.Querier, nodeID string) ([]*Agent, err res := make([]*Agent, 0, len(structs)) for _, str := range structs { row := str.(*Agent) //nolint:forcetypeassert - DecryptAgent(row) + DecryptAgent(q, row) res = append(res, row) } @@ -405,7 +405,7 @@ func FindPMMAgentsForService(q *reform.Querier, serviceID string) ([]*Agent, err res := make([]*Agent, 0, len(pmmAgentRecords)) for _, str := range pmmAgentRecords { row := str.(*Agent) //nolint:forcetypeassert - DecryptAgent(row) + DecryptAgent(q, row) res = append(res, row) } @@ -488,7 +488,7 @@ func FindAgentsForScrapeConfig(q *reform.Querier, pmmAgentID *string, pushMetric res := make([]*Agent, len(allAgents)) for i, s := range allAgents { agent := s.(*Agent) //nolint:forcetypeassert - DecryptAgent(agent) + DecryptAgent(q, agent) res[i] = agent } return res, nil @@ -650,7 +650,7 @@ func CreateNodeExporter(q *reform.Querier, LogLevel: pointer.ToStringOrNil(logLevel), ExposeExporter: exposeExporter, } - EncryptAgent(row) + EncryptAgent(q, row) if err := row.SetCustomLabels(customLabels); err != nil { return nil, err @@ -659,7 +659,7 @@ func CreateNodeExporter(q *reform.Querier, return nil, errors.WithStack(err) } - DecryptAgent(row) + DecryptAgent(q, row) return row, nil } @@ -738,7 +738,7 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar ListenPort: pointer.ToUint16(uint16(params.ListenPort)), PushMetrics: params.PushMetrics, } - EncryptAgent(row) + EncryptAgent(q, row) if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err @@ -747,7 +747,7 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar return nil, errors.WithStack(err) } - DecryptAgent(row) + DecryptAgent(q, row) return row, nil } @@ -933,13 +933,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara LogLevel: pointer.ToStringOrNil(params.LogLevel), } - // TODO - // settings, err := GetSettings(q) - // if err != nil { - // return err - // } - - EncryptAgent(row) + EncryptAgent(q, row) if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err @@ -948,7 +942,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara return nil, errors.WithStack(err) } - DecryptAgent(row) + DecryptAgent(q, row) return row, nil } diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index 0c53fa6da3..f0adb45746 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -18,24 +18,49 @@ package models import ( "database/sql" "encoding/json" + "fmt" "github.com/sirupsen/logrus" + "gopkg.in/reform.v1" "github.com/percona/pmm/managed/utils/encryption" ) +func agentColumnPath(column string) string { + prefix := "pmm-managed.agents." + return fmt.Sprintf("%s%s", prefix, column) +} + +func isEncrypted(encrypted map[string]bool, column string) bool { + return encrypted[agentColumnPath(column)] +} + +func notEncrypted(encrypted map[string]bool, column string) bool { + return !encrypted[agentColumnPath(column)] +} + // EncryptAgent encrypt agent. -func EncryptAgent(agent *Agent) { - agentEncryption(agent, encryption.Encrypt) +func EncryptAgent(q *reform.Querier, agent *Agent) { + agentEncryption(q, agent, encryption.Encrypt, notEncrypted) } // DecryptAgent decrypt agent. -func DecryptAgent(agent *Agent) { - agentEncryption(agent, encryption.Decrypt) +func DecryptAgent(q *reform.Querier, agent *Agent) { + agentEncryption(q, agent, encryption.Decrypt, isEncrypted) } -func agentEncryption(agent *Agent, handler func(string) (string, error)) { - if agent.Username != nil { +func agentEncryption(q *reform.Querier, agent *Agent, handler func(string) (string, error), check func(encrypted map[string]bool, column string) bool) { + settings, err := GetSettings(q) + if err != nil { + logrus.Warning(err) + return + } + encrypted := make(map[string]bool) + for _, v := range settings.EncryptedItems { + encrypted[v] = true + } + + if check(encrypted, "username") && agent.Username != nil { username, err := handler(*agent.Username) if err != nil { logrus.Warning(err) @@ -43,7 +68,7 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { agent.Username = &username } - if agent.Password != nil { + if check(encrypted, "password") && agent.Password != nil { password, err := handler(*agent.Password) if err != nil { logrus.Warning(err) @@ -51,7 +76,7 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { agent.Password = &password } - if agent.AgentPassword != nil { + if check(encrypted, "agent_password") && agent.AgentPassword != nil { agentPassword, err := handler(*agent.AgentPassword) if err != nil { logrus.Warning(err) @@ -59,7 +84,7 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { agent.AgentPassword = &agentPassword } - if agent.AWSAccessKey != nil { + if check(encrypted, "aws_access_key") && agent.AWSAccessKey != nil { awsAccessKey, err := handler(*agent.AWSAccessKey) if err != nil { logrus.Warning(err) @@ -67,7 +92,7 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { agent.AWSAccessKey = &awsAccessKey } - if agent.AWSSecretKey != nil { + if check(encrypted, "aws_secret_key") && agent.AWSSecretKey != nil { awsSecretKey, err := handler(*agent.AWSSecretKey) if err != nil { logrus.Warning(err) @@ -75,8 +100,7 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { agent.AWSSecretKey = &awsSecretKey } - var err error - if agent.MySQLOptions != nil { + if check(encrypted, "mysql_options") && agent.MySQLOptions != nil { agent.MySQLOptions.TLSCa, err = handler(agent.MySQLOptions.TLSCa) if err != nil { logrus.Warning(err) @@ -91,7 +115,7 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { } } - if agent.PostgreSQLOptions != nil { + if check(encrypted, "postgresql_options") && agent.PostgreSQLOptions != nil { agent.PostgreSQLOptions.SSLCa, err = handler(agent.PostgreSQLOptions.SSLCa) if err != nil { logrus.Warning(err) @@ -106,7 +130,7 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { } } - if agent.MongoDBOptions != nil { + if check(encrypted, "mongo_db_tls_options") && agent.MongoDBOptions != nil { agent.MongoDBOptions.TLSCa, err = handler(agent.MongoDBOptions.TLSCa) if err != nil { logrus.Warning(err) @@ -121,7 +145,7 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { } } - if agent.AzureOptions != nil { + if check(encrypted, "azure_options") && agent.AzureOptions != nil { agent.AzureOptions.ClientID, err = handler(agent.AzureOptions.ClientID) if err != nil { logrus.Warning(err) diff --git a/managed/services/agents/service_info_broker.go b/managed/services/agents/service_info_broker.go index e1751dd5a9..0970f8083c 100644 --- a/managed/services/agents/service_info_broker.go +++ b/managed/services/agents/service_info_broker.go @@ -194,7 +194,7 @@ func (c *ServiceInfoBroker) GetInfoFromService(ctx context.Context, q *reform.Qu case models.MySQLServiceType: agent.TableCount = &sInfo.TableCount l.Debugf("Updating table count: %d.", sInfo.TableCount) - models.EncryptAgent(agent) + models.EncryptAgent(q, agent) if err = q.Update(agent); err != nil { return errors.Wrap(err, "failed to update table count") } @@ -219,7 +219,7 @@ func (c *ServiceInfoBroker) GetInfoFromService(ctx context.Context, q *reform.Qu agent.PostgreSQLOptions.DatabaseCount = int32(databaseCount - excludedDatabaseCount) l.Debugf("Updating PostgreSQL options, database count: %d.", agent.PostgreSQLOptions.DatabaseCount) - models.EncryptAgent(agent) + models.EncryptAgent(q, agent) if err = q.Update(agent); err != nil { return errors.Wrap(err, "failed to update database count") } diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 792c83b289..e11127556f 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -28,7 +28,7 @@ import ( ) // DefaultEncryptionKeyPath contains default PMM encryption key path. -const DefaultEncryptionKeyPath = "/Users/jiri.ctvrtka/pmm-encryption.key" +const DefaultEncryptionKeyPath = "/srv/pmm-encryption.key" var ( // ErrEncryptionNotInitialized is error in case of encryption is not initialized. diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index 2dd5d6e3c2..0bdbdae6f0 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -32,7 +32,7 @@ import ( ) const ( - username, password = "pmm-agent", "pmm-agent-password" + username, password = "postgres", "" testDatabase = "pmm-managed-dev" ) From 98dee60d02bfbd61a068640b67996abcebbcfcb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 3 Jul 2024 10:27:51 +0200 Subject: [PATCH 100/215] PMM-13129 Check for nothing to encrypt. --- managed/models/database.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/managed/models/database.go b/managed/models/database.go index 966f536b1b..9d1a7eaab9 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1097,6 +1097,10 @@ func EncryptDB(tx *reform.TX, params SetupDBParams, itemsToEncrypt []encryption. columns = append(columns, column) newlyEncrypted = append(newlyEncrypted, dbTableColumn) } + if len(columns) == 0 { + continue + } + table.Columns = columns notEncrypted = append(notEncrypted, table) } From b4714bff03d0e43fc5297ffe8d9a5c9809c52340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 3 Jul 2024 10:36:32 +0200 Subject: [PATCH 101/215] PMM-13129 Encrypted fields based on migration version. --- managed/cmd/pmm-managed/main.go | 21 +--------------- managed/models/database.go | 40 +++++++++++++++++++++++++++++- managed/utils/testdb/db.go | 43 +-------------------------------- 3 files changed, 41 insertions(+), 63 deletions(-) diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index 126a8c8070..7cb207a472 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -104,7 +104,6 @@ import ( "github.com/percona/pmm/managed/services/victoriametrics" "github.com/percona/pmm/managed/services/vmalert" "github.com/percona/pmm/managed/utils/clean" - "github.com/percona/pmm/managed/utils/encryption" "github.com/percona/pmm/managed/utils/envvars" "github.com/percona/pmm/managed/utils/interceptors" platformClient "github.com/percona/pmm/managed/utils/platform" @@ -612,26 +611,8 @@ func migrateDB(ctx context.Context, sqlDB *sql.DB, params models.SetupDBParams) default: } - itemsToEncrypt := []encryption.Table{ - { - Name: "agents", - Identificators: []string{"agent_id"}, - Columns: []encryption.Column{ - {Name: "username"}, - {Name: "password"}, - {Name: "agent_password"}, - {Name: "aws_access_key"}, - {Name: "aws_secret_key"}, - {Name: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}, - {Name: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}, - {Name: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}, - {Name: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}, - }, - }, - } - l.Infof("Migrating database...") - _, err := models.SetupDB(timeoutCtx, sqlDB, params, itemsToEncrypt) + _, err := models.SetupDB(timeoutCtx, sqlDB, params) if err == nil { return } diff --git a/managed/models/database.go b/managed/models/database.go index 9d1a7eaab9..22fc3077d8 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -31,6 +31,7 @@ import ( "strconv" "strings" + "github.com/AlekSi/pointer" "github.com/lib/pq" "github.com/pkg/errors" "google.golang.org/grpc/codes" @@ -1042,7 +1043,7 @@ type SetupDBParams struct { } // SetupDB checks minimal required PostgreSQL version and runs database migrations. Optionally creates database and adds initial data. -func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsToEncrypt []encryption.Table) (*reform.DB, error) { +func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform.DB, error) { var logger reform.Logger if params.Logf != nil { logger = reform.NewPrintfLogger(params.Logf) @@ -1062,6 +1063,43 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams, itemsToEn return nil, errCV } + columnsToEncrypt := []encryption.Column{ + {Name: "username"}, + {Name: "password"}, + } + if pointer.GetInt(params.MigrationVersion) >= 9 { + columnsToEncrypt = slices.Concat( + columnsToEncrypt, []encryption.Column{{Name: "aws_access_key"}, {Name: "aws_secret_key "}}) + } + if pointer.GetInt(params.MigrationVersion) >= 25 { + columnsToEncrypt = append( + columnsToEncrypt, encryption.Column{Name: "mongo_db_tls_options", CustomHandler: EncryptMongoDBOptionsHandler}) + } + if pointer.GetInt(params.MigrationVersion) >= 31 { + columnsToEncrypt = append( + columnsToEncrypt, encryption.Column{Name: "azure_options", CustomHandler: EncryptAzureOptionsHandler}) + } + if pointer.GetInt(params.MigrationVersion) >= 36 { + columnsToEncrypt = append( + columnsToEncrypt, encryption.Column{Name: "mysql_options", CustomHandler: EncryptMySQLOptionsHandler}) + } + if pointer.GetInt(params.MigrationVersion) >= 41 { + columnsToEncrypt = append( + columnsToEncrypt, encryption.Column{Name: "postgresql_options", CustomHandler: EncryptPostgreSQLOptionsHandler}) + } + if pointer.GetInt(params.MigrationVersion) >= 42 { + columnsToEncrypt = append( + columnsToEncrypt, encryption.Column{Name: "agent_password"}) + } + + itemsToEncrypt := []encryption.Table{ + { + Name: "agents", + Identificators: []string{"agent_id"}, + Columns: columnsToEncrypt, + }, + } + if err := migrateDB(db, params, itemsToEncrypt); err != nil { return nil, err } diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index 0bdbdae6f0..2f6ee84cc7 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -20,15 +20,12 @@ import ( "context" "database/sql" "math/rand" - "slices" "testing" "time" - "github.com/AlekSi/pointer" "github.com/stretchr/testify/require" "github.com/percona/pmm/managed/models" - "github.com/percona/pmm/managed/utils/encryption" ) const ( @@ -85,45 +82,7 @@ func SetupDB(tb testing.TB, db *sql.DB, setupFixtures models.SetupFixturesMode, MigrationVersion: migrationVersion, } - // TODO - columnsToEncrypt := []encryption.Column{ - {Name: "username"}, - {Name: "password"}, - } - if pointer.GetInt(migrationVersion) >= 9 { - columnsToEncrypt = slices.Concat( - columnsToEncrypt, []encryption.Column{{Name: "aws_access_key"}, {Name: "aws_secret_key "}}) - } - if pointer.GetInt(migrationVersion) >= 25 { - columnsToEncrypt = append( - columnsToEncrypt, encryption.Column{Name: "mongo_db_tls_options", CustomHandler: models.EncryptMongoDBOptionsHandler}) - } - if pointer.GetInt(migrationVersion) >= 31 { - columnsToEncrypt = append( - columnsToEncrypt, encryption.Column{Name: "azure_options", CustomHandler: models.EncryptAzureOptionsHandler}) - } - if pointer.GetInt(migrationVersion) >= 36 { - columnsToEncrypt = append( - columnsToEncrypt, encryption.Column{Name: "mysql_options", CustomHandler: models.EncryptMySQLOptionsHandler}) - } - if pointer.GetInt(migrationVersion) >= 41 { - columnsToEncrypt = append( - columnsToEncrypt, encryption.Column{Name: "postgresql_options", CustomHandler: models.EncryptPostgreSQLOptionsHandler}) - } - if pointer.GetInt(migrationVersion) >= 42 { - columnsToEncrypt = append( - columnsToEncrypt, encryption.Column{Name: "agent_password"}) - } - - itemsToEncrypt := []encryption.Table{ - { - Name: "agents", - Identificators: []string{"agent_id"}, - Columns: columnsToEncrypt, - }, - } - - _, err := models.SetupDB(ctx, db, params, itemsToEncrypt) + _, err := models.SetupDB(ctx, db, params) require.NoError(tb, err) } From a0cea8b154798732f80f9ab7c3c53e9c31e3b83a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 3 Jul 2024 10:42:58 +0200 Subject: [PATCH 102/215] PMM-13129 Better debug. --- managed/models/encryption_helpers.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index f0adb45746..0c745e1337 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -63,6 +63,7 @@ func agentEncryption(q *reform.Querier, agent *Agent, handler func(string) (stri if check(encrypted, "username") && agent.Username != nil { username, err := handler(*agent.Username) if err != nil { + logrus.Debugln(username) logrus.Warning(err) } agent.Username = &username @@ -71,6 +72,7 @@ func agentEncryption(q *reform.Querier, agent *Agent, handler func(string) (stri if check(encrypted, "password") && agent.Password != nil { password, err := handler(*agent.Password) if err != nil { + logrus.Debugln(password) logrus.Warning(err) } agent.Password = &password @@ -79,6 +81,7 @@ func agentEncryption(q *reform.Querier, agent *Agent, handler func(string) (stri if check(encrypted, "agent_password") && agent.AgentPassword != nil { agentPassword, err := handler(*agent.AgentPassword) if err != nil { + logrus.Debugln(agentPassword) logrus.Warning(err) } agent.AgentPassword = &agentPassword @@ -87,6 +90,7 @@ func agentEncryption(q *reform.Querier, agent *Agent, handler func(string) (stri if check(encrypted, "aws_access_key") && agent.AWSAccessKey != nil { awsAccessKey, err := handler(*agent.AWSAccessKey) if err != nil { + logrus.Debugln(awsAccessKey) logrus.Warning(err) } agent.AWSAccessKey = &awsAccessKey @@ -95,6 +99,7 @@ func agentEncryption(q *reform.Querier, agent *Agent, handler func(string) (stri if check(encrypted, "aws_secret_key") && agent.AWSSecretKey != nil { awsSecretKey, err := handler(*agent.AWSSecretKey) if err != nil { + logrus.Debugln(awsSecretKey) logrus.Warning(err) } agent.AWSSecretKey = &awsSecretKey @@ -103,14 +108,17 @@ func agentEncryption(q *reform.Querier, agent *Agent, handler func(string) (stri if check(encrypted, "mysql_options") && agent.MySQLOptions != nil { agent.MySQLOptions.TLSCa, err = handler(agent.MySQLOptions.TLSCa) if err != nil { + logrus.Debugln(agent.MySQLOptions.TLSCa) logrus.Warning(err) } agent.MySQLOptions.TLSCert, err = handler(agent.MySQLOptions.TLSCert) if err != nil { + logrus.Debugln(agent.MySQLOptions.TLSCert) logrus.Warning(err) } agent.MySQLOptions.TLSKey, err = handler(agent.MySQLOptions.TLSKey) if err != nil { + logrus.Debugln(agent.MySQLOptions.TLSKey) logrus.Warning(err) } } @@ -118,14 +126,17 @@ func agentEncryption(q *reform.Querier, agent *Agent, handler func(string) (stri if check(encrypted, "postgresql_options") && agent.PostgreSQLOptions != nil { agent.PostgreSQLOptions.SSLCa, err = handler(agent.PostgreSQLOptions.SSLCa) if err != nil { + logrus.Debugln(agent.PostgreSQLOptions.SSLCa) logrus.Warning(err) } agent.PostgreSQLOptions.SSLCert, err = handler(agent.PostgreSQLOptions.SSLCert) if err != nil { + logrus.Debugln(agent.PostgreSQLOptions.SSLCert) logrus.Warning(err) } agent.PostgreSQLOptions.SSLKey, err = handler(agent.PostgreSQLOptions.SSLKey) if err != nil { + logrus.Debugln(agent.PostgreSQLOptions.SSLKey) logrus.Warning(err) } } @@ -133,14 +144,17 @@ func agentEncryption(q *reform.Querier, agent *Agent, handler func(string) (stri if check(encrypted, "mongo_db_tls_options") && agent.MongoDBOptions != nil { agent.MongoDBOptions.TLSCa, err = handler(agent.MongoDBOptions.TLSCa) if err != nil { + logrus.Debugln(agent.MongoDBOptions.TLSCa) logrus.Warning(err) } agent.MongoDBOptions.TLSCertificateKey, err = handler(agent.MongoDBOptions.TLSCertificateKey) if err != nil { + logrus.Debugln(agent.MongoDBOptions.TLSCertificateKey) logrus.Warning(err) } agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = handler(agent.MongoDBOptions.TLSCertificateKeyFilePassword) if err != nil { + logrus.Debugln(agent.MongoDBOptions.TLSCertificateKeyFilePassword) logrus.Warning(err) } } @@ -148,18 +162,22 @@ func agentEncryption(q *reform.Querier, agent *Agent, handler func(string) (stri if check(encrypted, "azure_options") && agent.AzureOptions != nil { agent.AzureOptions.ClientID, err = handler(agent.AzureOptions.ClientID) if err != nil { + logrus.Debugln(agent.AzureOptions.ClientID) logrus.Warning(err) } agent.AzureOptions.ClientSecret, err = handler(agent.AzureOptions.ClientSecret) if err != nil { + logrus.Debugln(agent.AzureOptions.ClientSecret) logrus.Warning(err) } agent.AzureOptions.SubscriptionID, err = handler(agent.AzureOptions.SubscriptionID) if err != nil { + logrus.Debugln(agent.AzureOptions.SubscriptionID) logrus.Warning(err) } agent.AzureOptions.TenantID, err = handler(agent.AzureOptions.TenantID) if err != nil { + logrus.Debugln(agent.AzureOptions.TenantID) logrus.Warning(err) } } From a1df8c870d755b3b22843f497734cb0c0df6e8fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 3 Jul 2024 10:53:41 +0200 Subject: [PATCH 103/215] PMM-13129 Lint. --- managed/models/encryption_helpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index 0c745e1337..56512da6d0 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -49,7 +49,7 @@ func DecryptAgent(q *reform.Querier, agent *Agent) { agentEncryption(q, agent, encryption.Decrypt, isEncrypted) } -func agentEncryption(q *reform.Querier, agent *Agent, handler func(string) (string, error), check func(encrypted map[string]bool, column string) bool) { +func agentEncryption(q *reform.Querier, agent *Agent, handler func(string) (string, error), check func(encrypted map[string]bool, column string) bool) { //nolint:cyclop settings, err := GetSettings(q) if err != nil { logrus.Warning(err) From a55f422d6adcc5109a63b4a7174382a4add4d9ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 3 Jul 2024 11:48:00 +0200 Subject: [PATCH 104/215] PMM-13129 Fix, better debug. --- managed/models/agent_helpers.go | 26 +++--- managed/models/encryption_helpers.go | 90 +++++++------------ .../services/agents/service_info_broker.go | 4 +- 3 files changed, 48 insertions(+), 72 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index ae03bcaee0..efa8bd184a 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -230,7 +230,7 @@ func FindAgents(q *reform.Querier, filters AgentFilters) ([]*Agent, error) { agents := make([]*Agent, len(structs)) for i, s := range structs { agent := s.(*Agent) //nolint:forcetypeassert - DecryptAgent(q, agent) + DecryptAgent(agent) agents[i] = agent } @@ -252,7 +252,7 @@ func FindAgentByID(q *reform.Querier, id string) (*Agent, error) { return nil, errors.WithStack(err) } - DecryptAgent(q, agent) + DecryptAgent(agent) return agent, nil } @@ -277,7 +277,7 @@ func FindAgentsByIDs(q *reform.Querier, ids []string) ([]*Agent, error) { res := make([]*Agent, len(structs)) for i, s := range structs { agent := s.(*Agent) //nolint:forcetypeassert - DecryptAgent(q, agent) + DecryptAgent(agent) res[i] = agent } return res, nil @@ -330,7 +330,7 @@ func FindDBConfigForService(q *reform.Querier, serviceID string) (*DBConfig, err res := make([]*Agent, len(structs)) for i, s := range structs { agent := s.(*Agent) //nolint:forcetypeassert - DecryptAgent(q, agent) + DecryptAgent(agent) res[i] = agent } @@ -359,7 +359,7 @@ func FindPMMAgentsRunningOnNode(q *reform.Querier, nodeID string) ([]*Agent, err res := make([]*Agent, 0, len(structs)) for _, str := range structs { row := str.(*Agent) //nolint:forcetypeassert - DecryptAgent(q, row) + DecryptAgent(row) res = append(res, row) } @@ -405,7 +405,7 @@ func FindPMMAgentsForService(q *reform.Querier, serviceID string) ([]*Agent, err res := make([]*Agent, 0, len(pmmAgentRecords)) for _, str := range pmmAgentRecords { row := str.(*Agent) //nolint:forcetypeassert - DecryptAgent(q, row) + DecryptAgent(row) res = append(res, row) } @@ -488,7 +488,7 @@ func FindAgentsForScrapeConfig(q *reform.Querier, pmmAgentID *string, pushMetric res := make([]*Agent, len(allAgents)) for i, s := range allAgents { agent := s.(*Agent) //nolint:forcetypeassert - DecryptAgent(q, agent) + DecryptAgent(agent) res[i] = agent } return res, nil @@ -650,7 +650,7 @@ func CreateNodeExporter(q *reform.Querier, LogLevel: pointer.ToStringOrNil(logLevel), ExposeExporter: exposeExporter, } - EncryptAgent(q, row) + EncryptAgent(row) if err := row.SetCustomLabels(customLabels); err != nil { return nil, err @@ -659,7 +659,7 @@ func CreateNodeExporter(q *reform.Querier, return nil, errors.WithStack(err) } - DecryptAgent(q, row) + DecryptAgent(row) return row, nil } @@ -738,7 +738,7 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar ListenPort: pointer.ToUint16(uint16(params.ListenPort)), PushMetrics: params.PushMetrics, } - EncryptAgent(q, row) + EncryptAgent(row) if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err @@ -747,7 +747,7 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar return nil, errors.WithStack(err) } - DecryptAgent(q, row) + DecryptAgent(row) return row, nil } @@ -933,7 +933,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara LogLevel: pointer.ToStringOrNil(params.LogLevel), } - EncryptAgent(q, row) + EncryptAgent(row) if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err @@ -942,7 +942,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara return nil, errors.WithStack(err) } - DecryptAgent(q, row) + DecryptAgent(row) return row, nil } diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index 56512da6d0..1a7d3b99a8 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -18,166 +18,142 @@ package models import ( "database/sql" "encoding/json" - "fmt" "github.com/sirupsen/logrus" - "gopkg.in/reform.v1" "github.com/percona/pmm/managed/utils/encryption" ) -func agentColumnPath(column string) string { - prefix := "pmm-managed.agents." - return fmt.Sprintf("%s%s", prefix, column) -} - -func isEncrypted(encrypted map[string]bool, column string) bool { - return encrypted[agentColumnPath(column)] -} - -func notEncrypted(encrypted map[string]bool, column string) bool { - return !encrypted[agentColumnPath(column)] -} - // EncryptAgent encrypt agent. -func EncryptAgent(q *reform.Querier, agent *Agent) { - agentEncryption(q, agent, encryption.Encrypt, notEncrypted) +func EncryptAgent(agent *Agent) { + agentEncryption(agent, encryption.Encrypt) } // DecryptAgent decrypt agent. -func DecryptAgent(q *reform.Querier, agent *Agent) { - agentEncryption(q, agent, encryption.Decrypt, isEncrypted) +func DecryptAgent(agent *Agent) { + agentEncryption(agent, encryption.Decrypt) } -func agentEncryption(q *reform.Querier, agent *Agent, handler func(string) (string, error), check func(encrypted map[string]bool, column string) bool) { //nolint:cyclop - settings, err := GetSettings(q) - if err != nil { - logrus.Warning(err) - return - } - encrypted := make(map[string]bool) - for _, v := range settings.EncryptedItems { - encrypted[v] = true - } - - if check(encrypted, "username") && agent.Username != nil { +func agentEncryption(agent *Agent, handler func(string) (string, error)) { + if agent.Username != nil { username, err := handler(*agent.Username) if err != nil { - logrus.Debugln(username) + logrus.Debugf("username:%s", username) logrus.Warning(err) } agent.Username = &username } - if check(encrypted, "password") && agent.Password != nil { + if agent.Password != nil { password, err := handler(*agent.Password) if err != nil { - logrus.Debugln(password) + logrus.Debugf("password:%s", password) logrus.Warning(err) } agent.Password = &password } - if check(encrypted, "agent_password") && agent.AgentPassword != nil { + if agent.AgentPassword != nil { agentPassword, err := handler(*agent.AgentPassword) if err != nil { - logrus.Debugln(agentPassword) + logrus.Debugf("agent_password:%s", agentPassword) logrus.Warning(err) } agent.AgentPassword = &agentPassword } - if check(encrypted, "aws_access_key") && agent.AWSAccessKey != nil { + if agent.AWSAccessKey != nil { awsAccessKey, err := handler(*agent.AWSAccessKey) if err != nil { - logrus.Debugln(awsAccessKey) + logrus.Debugf("aws_access_key:%s", awsAccessKey) logrus.Warning(err) } agent.AWSAccessKey = &awsAccessKey } - if check(encrypted, "aws_secret_key") && agent.AWSSecretKey != nil { + if agent.AWSSecretKey != nil { awsSecretKey, err := handler(*agent.AWSSecretKey) if err != nil { - logrus.Debugln(awsSecretKey) + logrus.Debugf("aws_secret_key:%s", awsSecretKey) logrus.Warning(err) } agent.AWSSecretKey = &awsSecretKey } - if check(encrypted, "mysql_options") && agent.MySQLOptions != nil { + var err error + if agent.MySQLOptions != nil { agent.MySQLOptions.TLSCa, err = handler(agent.MySQLOptions.TLSCa) if err != nil { - logrus.Debugln(agent.MySQLOptions.TLSCa) + logrus.Debugf("mysql_options.tls_ca:%s", agent.MySQLOptions.TLSCa) logrus.Warning(err) } agent.MySQLOptions.TLSCert, err = handler(agent.MySQLOptions.TLSCert) if err != nil { - logrus.Debugln(agent.MySQLOptions.TLSCert) + logrus.Debugf("mysql_options.tls_cert:%s", agent.MySQLOptions.TLSCert) logrus.Warning(err) } agent.MySQLOptions.TLSKey, err = handler(agent.MySQLOptions.TLSKey) if err != nil { - logrus.Debugln(agent.MySQLOptions.TLSKey) + logrus.Debugf("mysql_options.tls_key:%s", agent.MySQLOptions.TLSKey) logrus.Warning(err) } } - if check(encrypted, "postgresql_options") && agent.PostgreSQLOptions != nil { + if agent.PostgreSQLOptions != nil { agent.PostgreSQLOptions.SSLCa, err = handler(agent.PostgreSQLOptions.SSLCa) if err != nil { - logrus.Debugln(agent.PostgreSQLOptions.SSLCa) + logrus.Debugf("postgresql_options.ssl_ca:%s", agent.PostgreSQLOptions.SSLCa) logrus.Warning(err) } agent.PostgreSQLOptions.SSLCert, err = handler(agent.PostgreSQLOptions.SSLCert) if err != nil { - logrus.Debugln(agent.PostgreSQLOptions.SSLCert) + logrus.Debugf("postgresql_options.ssl_cert:%s", agent.PostgreSQLOptions.SSLCert) logrus.Warning(err) } agent.PostgreSQLOptions.SSLKey, err = handler(agent.PostgreSQLOptions.SSLKey) if err != nil { - logrus.Debugln(agent.PostgreSQLOptions.SSLKey) + logrus.Debugf("postgresql_options.ssl_key:%s", agent.PostgreSQLOptions.SSLKey) logrus.Warning(err) } } - if check(encrypted, "mongo_db_tls_options") && agent.MongoDBOptions != nil { + if agent.MongoDBOptions != nil { agent.MongoDBOptions.TLSCa, err = handler(agent.MongoDBOptions.TLSCa) if err != nil { - logrus.Debugln(agent.MongoDBOptions.TLSCa) + logrus.Debugf("mongo_db_tls_options.tls_ca:%s", agent.MongoDBOptions.TLSCa) logrus.Warning(err) } agent.MongoDBOptions.TLSCertificateKey, err = handler(agent.MongoDBOptions.TLSCertificateKey) if err != nil { - logrus.Debugln(agent.MongoDBOptions.TLSCertificateKey) + logrus.Debugf("mongo_db_tls_options.tls_certificate_key:%s", agent.MongoDBOptions.TLSCertificateKey) logrus.Warning(err) } agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = handler(agent.MongoDBOptions.TLSCertificateKeyFilePassword) if err != nil { - logrus.Debugln(agent.MongoDBOptions.TLSCertificateKeyFilePassword) + logrus.Debugf("mongo_db_tls_options.tls_certificate_key_file_password:%s", agent.MongoDBOptions.TLSCertificateKeyFilePassword) logrus.Warning(err) } } - if check(encrypted, "azure_options") && agent.AzureOptions != nil { + if agent.AzureOptions != nil { agent.AzureOptions.ClientID, err = handler(agent.AzureOptions.ClientID) if err != nil { - logrus.Debugln(agent.AzureOptions.ClientID) + logrus.Debugf("azure_options.client_id:%s", agent.AzureOptions.ClientID) logrus.Warning(err) } agent.AzureOptions.ClientSecret, err = handler(agent.AzureOptions.ClientSecret) if err != nil { - logrus.Debugln(agent.AzureOptions.ClientSecret) + logrus.Debugf("azure_options.client_secret:%s", agent.AzureOptions.ClientSecret) logrus.Warning(err) } agent.AzureOptions.SubscriptionID, err = handler(agent.AzureOptions.SubscriptionID) if err != nil { - logrus.Debugln(agent.AzureOptions.SubscriptionID) + logrus.Debugf("azure_options.subscription_id:%s", agent.AzureOptions.SubscriptionID) logrus.Warning(err) } agent.AzureOptions.TenantID, err = handler(agent.AzureOptions.TenantID) if err != nil { - logrus.Debugln(agent.AzureOptions.TenantID) + logrus.Debugf("azure_options.tenant_id:%s", agent.AzureOptions.TenantID) logrus.Warning(err) } } diff --git a/managed/services/agents/service_info_broker.go b/managed/services/agents/service_info_broker.go index 0970f8083c..e1751dd5a9 100644 --- a/managed/services/agents/service_info_broker.go +++ b/managed/services/agents/service_info_broker.go @@ -194,7 +194,7 @@ func (c *ServiceInfoBroker) GetInfoFromService(ctx context.Context, q *reform.Qu case models.MySQLServiceType: agent.TableCount = &sInfo.TableCount l.Debugf("Updating table count: %d.", sInfo.TableCount) - models.EncryptAgent(q, agent) + models.EncryptAgent(agent) if err = q.Update(agent); err != nil { return errors.Wrap(err, "failed to update table count") } @@ -219,7 +219,7 @@ func (c *ServiceInfoBroker) GetInfoFromService(ctx context.Context, q *reform.Qu agent.PostgreSQLOptions.DatabaseCount = int32(databaseCount - excludedDatabaseCount) l.Debugf("Updating PostgreSQL options, database count: %d.", agent.PostgreSQLOptions.DatabaseCount) - models.EncryptAgent(q, agent) + models.EncryptAgent(agent) if err = q.Update(agent); err != nil { return errors.Wrap(err, "failed to update database count") } From 3821c7065940aadb573dcc41ef2ad1d518193dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 3 Jul 2024 12:14:31 +0200 Subject: [PATCH 105/215] PMM-13129 Exit in case of encryption initialization error. --- managed/utils/encryption/encryption.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index e11127556f..85acf91838 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -48,9 +48,11 @@ func New(keyPath string) *Encryption { err = e.generateKey() if err != nil { logrus.Errorf("Encryption: %v", err) + os.Exit(1) } case err != nil: logrus.Errorf("Encryption: %v", err) + os.Exit(1) default: e.Key = string(bytes) } From b009d0f5c90a1ea2ff98778575b8cc79ae3ba87a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 3 Jul 2024 12:14:51 +0200 Subject: [PATCH 106/215] PMM-13129 Handle nil migration version. --- managed/models/database.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index 22fc3077d8..0d3130cc72 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -31,7 +31,6 @@ import ( "strconv" "strings" - "github.com/AlekSi/pointer" "github.com/lib/pq" "github.com/pkg/errors" "google.golang.org/grpc/codes" @@ -1067,27 +1066,28 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. {Name: "username"}, {Name: "password"}, } - if pointer.GetInt(params.MigrationVersion) >= 9 { + + if params.MigrationVersion == nil || *params.MigrationVersion >= 9 { columnsToEncrypt = slices.Concat( columnsToEncrypt, []encryption.Column{{Name: "aws_access_key"}, {Name: "aws_secret_key "}}) } - if pointer.GetInt(params.MigrationVersion) >= 25 { + if params.MigrationVersion == nil || *params.MigrationVersion >= 25 { columnsToEncrypt = append( columnsToEncrypt, encryption.Column{Name: "mongo_db_tls_options", CustomHandler: EncryptMongoDBOptionsHandler}) } - if pointer.GetInt(params.MigrationVersion) >= 31 { + if params.MigrationVersion == nil || *params.MigrationVersion >= 31 { columnsToEncrypt = append( columnsToEncrypt, encryption.Column{Name: "azure_options", CustomHandler: EncryptAzureOptionsHandler}) } - if pointer.GetInt(params.MigrationVersion) >= 36 { + if params.MigrationVersion == nil || *params.MigrationVersion >= 36 { columnsToEncrypt = append( columnsToEncrypt, encryption.Column{Name: "mysql_options", CustomHandler: EncryptMySQLOptionsHandler}) } - if pointer.GetInt(params.MigrationVersion) >= 41 { + if params.MigrationVersion == nil || *params.MigrationVersion >= 41 { columnsToEncrypt = append( columnsToEncrypt, encryption.Column{Name: "postgresql_options", CustomHandler: EncryptPostgreSQLOptionsHandler}) } - if pointer.GetInt(params.MigrationVersion) >= 42 { + if params.MigrationVersion == nil || *params.MigrationVersion >= 42 { columnsToEncrypt = append( columnsToEncrypt, encryption.Column{Name: "agent_password"}) } From f4bdf3d60182789f0b53fd23965cb257744edf2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 3 Jul 2024 12:21:53 +0200 Subject: [PATCH 107/215] PMM-13129 Typo. --- managed/models/database.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/models/database.go b/managed/models/database.go index 0d3130cc72..3ab111c92a 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1069,7 +1069,7 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. if params.MigrationVersion == nil || *params.MigrationVersion >= 9 { columnsToEncrypt = slices.Concat( - columnsToEncrypt, []encryption.Column{{Name: "aws_access_key"}, {Name: "aws_secret_key "}}) + columnsToEncrypt, []encryption.Column{{Name: "aws_access_key"}, {Name: "aws_secret_key"}}) } if params.MigrationVersion == nil || *params.MigrationVersion >= 25 { columnsToEncrypt = append( From 5f67dccd82bb68e06fb7df647f4fd964dab284cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 3 Jul 2024 13:10:14 +0200 Subject: [PATCH 108/215] PMM-13129 Fix for service broker and connection check. --- managed/services/agents/service_info_broker.go | 4 ++++ managed/utils/encryption/encryption.go | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/managed/services/agents/service_info_broker.go b/managed/services/agents/service_info_broker.go index e1751dd5a9..27783fe786 100644 --- a/managed/services/agents/service_info_broker.go +++ b/managed/services/agents/service_info_broker.go @@ -198,6 +198,8 @@ func (c *ServiceInfoBroker) GetInfoFromService(ctx context.Context, q *reform.Qu if err = q.Update(agent); err != nil { return errors.Wrap(err, "failed to update table count") } + models.DecryptAgent(agent) + return updateServiceVersion(ctx, q, resp, service) case models.PostgreSQLServiceType: if agent.PostgreSQLOptions == nil { @@ -223,6 +225,8 @@ func (c *ServiceInfoBroker) GetInfoFromService(ctx context.Context, q *reform.Qu if err = q.Update(agent); err != nil { return errors.Wrap(err, "failed to update database count") } + models.DecryptAgent(agent) + return updateServiceVersion(ctx, q, resp, service) case models.MongoDBServiceType, models.ProxySQLServiceType: diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 85acf91838..e11127556f 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -48,11 +48,9 @@ func New(keyPath string) *Encryption { err = e.generateKey() if err != nil { logrus.Errorf("Encryption: %v", err) - os.Exit(1) } case err != nil: logrus.Errorf("Encryption: %v", err) - os.Exit(1) default: e.Key = string(bytes) } From e3b1341370dbc61de0eeff8aea606d8a86f714ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 3 Jul 2024 13:26:49 +0200 Subject: [PATCH 109/215] PMM-13129 Comments. --- api/serverpb/json/client/server/change_settings_responses.go | 2 +- api/serverpb/json/client/server/get_settings_responses.go | 2 +- api/serverpb/json/serverpb.json | 4 ++-- api/serverpb/server.pb.go | 2 +- api/serverpb/server.proto | 2 +- api/swagger/swagger-dev.json | 4 ++-- api/swagger/swagger.json | 4 ++-- managed/cmd/pmm-managed/main.go | 2 +- managed/models/settings.go | 2 +- managed/models/settings_helpers.go | 2 +- managed/utils/encryption/helpers.go | 1 - 11 files changed, 13 insertions(+), 14 deletions(-) diff --git a/api/serverpb/json/client/server/change_settings_responses.go b/api/serverpb/json/client/server/change_settings_responses.go index 275b7f67b3..34363d86fd 100644 --- a/api/serverpb/json/client/server/change_settings_responses.go +++ b/api/serverpb/json/client/server/change_settings_responses.go @@ -700,7 +700,7 @@ type ChangeSettingsOKBodySettings struct { // Default Access Control role ID for new users. DefaultRoleID int64 `json:"default_role_id,omitempty"` - // Contains all already encrypted tables in format db.table. + // Contains all already encrypted tables in format 'db.table.column'. EncryptedItems []string `json:"encrypted_items"` // metrics resolutions diff --git a/api/serverpb/json/client/server/get_settings_responses.go b/api/serverpb/json/client/server/get_settings_responses.go index 4a426c4306..163727e9e9 100644 --- a/api/serverpb/json/client/server/get_settings_responses.go +++ b/api/serverpb/json/client/server/get_settings_responses.go @@ -509,7 +509,7 @@ type GetSettingsOKBodySettings struct { // Default Access Control role ID for new users. DefaultRoleID int64 `json:"default_role_id,omitempty"` - // Contains all already encrypted tables in format db.table. + // Contains all already encrypted tables in format 'db.table.column'. EncryptedItems []string `json:"encrypted_items"` // metrics resolutions diff --git a/api/serverpb/json/serverpb.json b/api/serverpb/json/serverpb.json index 40bd4a9f17..e8732c6729 100644 --- a/api/serverpb/json/serverpb.json +++ b/api/serverpb/json/serverpb.json @@ -339,7 +339,7 @@ "x-order": 15 }, "encrypted_items": { - "description": "Contains all already encrypted tables in format db.table.", + "description": "Contains all already encrypted tables in format 'db.table.column'.", "type": "array", "items": { "type": "string" @@ -540,7 +540,7 @@ "x-order": 15 }, "encrypted_items": { - "description": "Contains all already encrypted tables in format db.table.", + "description": "Contains all already encrypted tables in format 'db.table.column'.", "type": "array", "items": { "type": "string" diff --git a/api/serverpb/server.pb.go b/api/serverpb/server.pb.go index 82b62de7a8..4ea9b21aa2 100644 --- a/api/serverpb/server.pb.go +++ b/api/serverpb/server.pb.go @@ -1031,7 +1031,7 @@ type Settings struct { EnableAccessControl bool `protobuf:"varint,21,opt,name=enable_access_control,json=enableAccessControl,proto3" json:"enable_access_control,omitempty"` // Default Access Control role ID for new users. DefaultRoleId uint32 `protobuf:"varint,22,opt,name=default_role_id,json=defaultRoleId,proto3" json:"default_role_id,omitempty"` - // Contains all already encrypted tables in format db.table. + // Contains all already encrypted tables in format 'db.table.column'. EncryptedItems []string `protobuf:"bytes,23,rep,name=encrypted_items,json=encryptedItems,proto3" json:"encrypted_items,omitempty"` } diff --git a/api/serverpb/server.proto b/api/serverpb/server.proto index f21d3638f4..a2fec8221a 100644 --- a/api/serverpb/server.proto +++ b/api/serverpb/server.proto @@ -169,7 +169,7 @@ message Settings { bool enable_access_control = 21; // Default Access Control role ID for new users. uint32 default_role_id = 22; - // Contains all already encrypted tables in format db.table. + // Contains all already encrypted tables in format 'db.table.column'. repeated string encrypted_items = 23; } diff --git a/api/swagger/swagger-dev.json b/api/swagger/swagger-dev.json index ba27351290..152f9f484e 100644 --- a/api/swagger/swagger-dev.json +++ b/api/swagger/swagger-dev.json @@ -3264,7 +3264,7 @@ "x-order": 16 }, "encrypted_items": { - "description": "Contains all already encrypted tables in format db.table.", + "description": "Contains all already encrypted tables in format 'db.table.column'.", "type": "array", "items": { "type": "string" @@ -3465,7 +3465,7 @@ "x-order": 16 }, "encrypted_items": { - "description": "Contains all already encrypted tables in format db.table.", + "description": "Contains all already encrypted tables in format 'db.table.column'.", "type": "array", "items": { "type": "string" diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index b52bf35467..ef0e048255 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -420,7 +420,7 @@ "x-order": 16 }, "encrypted_items": { - "description": "Contains all already encrypted tables in format db.table.", + "description": "Contains all already encrypted tables in format 'db.table.column'.", "type": "array", "items": { "type": "string" @@ -621,7 +621,7 @@ "x-order": 16 }, "encrypted_items": { - "description": "Contains all already encrypted tables in format db.table.", + "description": "Contains all already encrypted tables in format 'db.table.column'.", "type": "array", "items": { "type": "string" diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index 7cb207a472..9268365c25 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -610,12 +610,12 @@ func migrateDB(ctx context.Context, sqlDB *sql.DB, params models.SetupDBParams) l.Fatalf("Could not migrate DB: timeout") default: } - l.Infof("Migrating database...") _, err := models.SetupDB(timeoutCtx, sqlDB, params) if err == nil { return } + l.Warnf("Failed to migrate database: %s.", err) time.Sleep(time.Second) } diff --git a/managed/models/settings.go b/managed/models/settings.go index ae7c111ddb..6d0d6bbe2a 100644 --- a/managed/models/settings.go +++ b/managed/models/settings.go @@ -101,7 +101,7 @@ type Settings struct { Enabled bool `json:"enabled"` } `json:"access_control"` - // Contains all already encrypted tables in format db.table. + // Contains all already encrypted tables in format 'db.table.column'. EncryptedItems []string `json:"encrypted_items"` } diff --git a/managed/models/settings_helpers.go b/managed/models/settings_helpers.go index 8c37974652..37ea041bcf 100644 --- a/managed/models/settings_helpers.go +++ b/managed/models/settings_helpers.go @@ -106,7 +106,7 @@ type ChangeSettingsParams struct { // DefaultRoleID sets a default role to be assigned to new users. DefaultRoleID int - // List of tables in format db.table to be encrypted. + // List of items in format 'db.table.column' to be encrypted. EncryptedItems []string } diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index 24e32d91aa..d0ce0f543d 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -47,7 +47,6 @@ func prepareRowPointers(rows *sql.Rows) ([]any, error) { case "VARCHAR", "JSONB": row = append(row, &sql.NullString{}) default: - // TODO support more identificators types return nil, fmt.Errorf("unsupported identificator type %s", t) } } From 1e147a21cf0a2febdffa763c639d0d4c40f6b85a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 4 Jul 2024 12:34:02 +0200 Subject: [PATCH 110/215] PMM-13129 Remove debug logging. --- managed/models/encryption_helpers.go | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index 1a7d3b99a8..0c53fa6da3 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -38,7 +38,6 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { if agent.Username != nil { username, err := handler(*agent.Username) if err != nil { - logrus.Debugf("username:%s", username) logrus.Warning(err) } agent.Username = &username @@ -47,7 +46,6 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { if agent.Password != nil { password, err := handler(*agent.Password) if err != nil { - logrus.Debugf("password:%s", password) logrus.Warning(err) } agent.Password = &password @@ -56,7 +54,6 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { if agent.AgentPassword != nil { agentPassword, err := handler(*agent.AgentPassword) if err != nil { - logrus.Debugf("agent_password:%s", agentPassword) logrus.Warning(err) } agent.AgentPassword = &agentPassword @@ -65,7 +62,6 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { if agent.AWSAccessKey != nil { awsAccessKey, err := handler(*agent.AWSAccessKey) if err != nil { - logrus.Debugf("aws_access_key:%s", awsAccessKey) logrus.Warning(err) } agent.AWSAccessKey = &awsAccessKey @@ -74,7 +70,6 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { if agent.AWSSecretKey != nil { awsSecretKey, err := handler(*agent.AWSSecretKey) if err != nil { - logrus.Debugf("aws_secret_key:%s", awsSecretKey) logrus.Warning(err) } agent.AWSSecretKey = &awsSecretKey @@ -84,17 +79,14 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { if agent.MySQLOptions != nil { agent.MySQLOptions.TLSCa, err = handler(agent.MySQLOptions.TLSCa) if err != nil { - logrus.Debugf("mysql_options.tls_ca:%s", agent.MySQLOptions.TLSCa) logrus.Warning(err) } agent.MySQLOptions.TLSCert, err = handler(agent.MySQLOptions.TLSCert) if err != nil { - logrus.Debugf("mysql_options.tls_cert:%s", agent.MySQLOptions.TLSCert) logrus.Warning(err) } agent.MySQLOptions.TLSKey, err = handler(agent.MySQLOptions.TLSKey) if err != nil { - logrus.Debugf("mysql_options.tls_key:%s", agent.MySQLOptions.TLSKey) logrus.Warning(err) } } @@ -102,17 +94,14 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { if agent.PostgreSQLOptions != nil { agent.PostgreSQLOptions.SSLCa, err = handler(agent.PostgreSQLOptions.SSLCa) if err != nil { - logrus.Debugf("postgresql_options.ssl_ca:%s", agent.PostgreSQLOptions.SSLCa) logrus.Warning(err) } agent.PostgreSQLOptions.SSLCert, err = handler(agent.PostgreSQLOptions.SSLCert) if err != nil { - logrus.Debugf("postgresql_options.ssl_cert:%s", agent.PostgreSQLOptions.SSLCert) logrus.Warning(err) } agent.PostgreSQLOptions.SSLKey, err = handler(agent.PostgreSQLOptions.SSLKey) if err != nil { - logrus.Debugf("postgresql_options.ssl_key:%s", agent.PostgreSQLOptions.SSLKey) logrus.Warning(err) } } @@ -120,17 +109,14 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { if agent.MongoDBOptions != nil { agent.MongoDBOptions.TLSCa, err = handler(agent.MongoDBOptions.TLSCa) if err != nil { - logrus.Debugf("mongo_db_tls_options.tls_ca:%s", agent.MongoDBOptions.TLSCa) logrus.Warning(err) } agent.MongoDBOptions.TLSCertificateKey, err = handler(agent.MongoDBOptions.TLSCertificateKey) if err != nil { - logrus.Debugf("mongo_db_tls_options.tls_certificate_key:%s", agent.MongoDBOptions.TLSCertificateKey) logrus.Warning(err) } agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = handler(agent.MongoDBOptions.TLSCertificateKeyFilePassword) if err != nil { - logrus.Debugf("mongo_db_tls_options.tls_certificate_key_file_password:%s", agent.MongoDBOptions.TLSCertificateKeyFilePassword) logrus.Warning(err) } } @@ -138,22 +124,18 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { if agent.AzureOptions != nil { agent.AzureOptions.ClientID, err = handler(agent.AzureOptions.ClientID) if err != nil { - logrus.Debugf("azure_options.client_id:%s", agent.AzureOptions.ClientID) logrus.Warning(err) } agent.AzureOptions.ClientSecret, err = handler(agent.AzureOptions.ClientSecret) if err != nil { - logrus.Debugf("azure_options.client_secret:%s", agent.AzureOptions.ClientSecret) logrus.Warning(err) } agent.AzureOptions.SubscriptionID, err = handler(agent.AzureOptions.SubscriptionID) if err != nil { - logrus.Debugf("azure_options.subscription_id:%s", agent.AzureOptions.SubscriptionID) logrus.Warning(err) } agent.AzureOptions.TenantID, err = handler(agent.AzureOptions.TenantID) if err != nil { - logrus.Debugf("azure_options.tenant_id:%s", agent.AzureOptions.TenantID) logrus.Warning(err) } } From fb85645e6bc7ef5cf392cd57bf5d2b261f78055d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 4 Jul 2024 12:45:24 +0200 Subject: [PATCH 111/215] PMM-13129 Remove pointer in EncryptAgent, DecryptAgent. --- managed/models/agent_helpers.go | 48 ++++++++----------- managed/models/encryption_helpers.go | 12 +++-- .../services/agents/service_info_broker.go | 10 ++-- 3 files changed, 31 insertions(+), 39 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index efa8bd184a..ce0fcf2c9d 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -230,8 +230,8 @@ func FindAgents(q *reform.Querier, filters AgentFilters) ([]*Agent, error) { agents := make([]*Agent, len(structs)) for i, s := range structs { agent := s.(*Agent) //nolint:forcetypeassert - DecryptAgent(agent) - agents[i] = agent + decryptedAgent := DecryptAgent(*agent) + agents[i] = &decryptedAgent } return agents, nil @@ -251,10 +251,9 @@ func FindAgentByID(q *reform.Querier, id string) (*Agent, error) { } return nil, errors.WithStack(err) } + decryptedAgent := DecryptAgent(*agent) - DecryptAgent(agent) - - return agent, nil + return &decryptedAgent, nil } // FindAgentsByIDs finds Agents by IDs. @@ -277,8 +276,8 @@ func FindAgentsByIDs(q *reform.Querier, ids []string) ([]*Agent, error) { res := make([]*Agent, len(structs)) for i, s := range structs { agent := s.(*Agent) //nolint:forcetypeassert - DecryptAgent(agent) - res[i] = agent + decryptedAgent := DecryptAgent(*agent) + res[i] = &decryptedAgent } return res, nil } @@ -330,8 +329,8 @@ func FindDBConfigForService(q *reform.Querier, serviceID string) (*DBConfig, err res := make([]*Agent, len(structs)) for i, s := range structs { agent := s.(*Agent) //nolint:forcetypeassert - DecryptAgent(agent) - res[i] = agent + decryptedAgent := DecryptAgent(*agent) + res[i] = &decryptedAgent } if len(res) == 0 { @@ -359,8 +358,8 @@ func FindPMMAgentsRunningOnNode(q *reform.Querier, nodeID string) ([]*Agent, err res := make([]*Agent, 0, len(structs)) for _, str := range structs { row := str.(*Agent) //nolint:forcetypeassert - DecryptAgent(row) - res = append(res, row) + decryptedAgent := DecryptAgent(*row) + res = append(res, &decryptedAgent) } return res, nil @@ -405,8 +404,8 @@ func FindPMMAgentsForService(q *reform.Querier, serviceID string) ([]*Agent, err res := make([]*Agent, 0, len(pmmAgentRecords)) for _, str := range pmmAgentRecords { row := str.(*Agent) //nolint:forcetypeassert - DecryptAgent(row) - res = append(res, row) + decryptedAgent := DecryptAgent(*row) + res = append(res, &decryptedAgent) } return res, nil @@ -488,8 +487,8 @@ func FindAgentsForScrapeConfig(q *reform.Querier, pmmAgentID *string, pushMetric res := make([]*Agent, len(allAgents)) for i, s := range allAgents { agent := s.(*Agent) //nolint:forcetypeassert - DecryptAgent(agent) - res[i] = agent + decryptedAgent := DecryptAgent(*agent) + res[i] = &decryptedAgent } return res, nil } @@ -650,17 +649,15 @@ func CreateNodeExporter(q *reform.Querier, LogLevel: pointer.ToStringOrNil(logLevel), ExposeExporter: exposeExporter, } - EncryptAgent(row) + encryptedAgent := EncryptAgent(*row) if err := row.SetCustomLabels(customLabels); err != nil { return nil, err } - if err := q.Insert(row); err != nil { + if err := q.Insert(&encryptedAgent); err != nil { return nil, errors.WithStack(err) } - DecryptAgent(row) - return row, nil } @@ -738,17 +735,15 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar ListenPort: pointer.ToUint16(uint16(params.ListenPort)), PushMetrics: params.PushMetrics, } - EncryptAgent(row) + encryptedAgent := EncryptAgent(*row) if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err } - if err := q.Insert(row); err != nil { + if err := q.Insert(&encryptedAgent); err != nil { return nil, errors.WithStack(err) } - DecryptAgent(row) - return row, nil } @@ -932,18 +927,15 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara DisabledCollectors: params.DisableCollectors, LogLevel: pointer.ToStringOrNil(params.LogLevel), } - - EncryptAgent(row) + encryptedAgent := EncryptAgent(*row) if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err } - if err := q.Insert(row); err != nil { + if err := q.Insert(&encryptedAgent); err != nil { return nil, errors.WithStack(err) } - DecryptAgent(row) - return row, nil } diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index 0c53fa6da3..2d39ab2ffe 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -25,16 +25,16 @@ import ( ) // EncryptAgent encrypt agent. -func EncryptAgent(agent *Agent) { - agentEncryption(agent, encryption.Encrypt) +func EncryptAgent(agent Agent) Agent { + return agentEncryption(agent, encryption.Encrypt) } // DecryptAgent decrypt agent. -func DecryptAgent(agent *Agent) { - agentEncryption(agent, encryption.Decrypt) +func DecryptAgent(agent Agent) Agent { + return agentEncryption(agent, encryption.Decrypt) } -func agentEncryption(agent *Agent, handler func(string) (string, error)) { +func agentEncryption(agent Agent, handler func(string) (string, error)) Agent { if agent.Username != nil { username, err := handler(*agent.Username) if err != nil { @@ -139,6 +139,8 @@ func agentEncryption(agent *Agent, handler func(string) (string, error)) { logrus.Warning(err) } } + + return agent } // EncryptMySQLOptionsHandler returns encrypted MySQL Options. diff --git a/managed/services/agents/service_info_broker.go b/managed/services/agents/service_info_broker.go index 27783fe786..d79ae6664b 100644 --- a/managed/services/agents/service_info_broker.go +++ b/managed/services/agents/service_info_broker.go @@ -194,11 +194,10 @@ func (c *ServiceInfoBroker) GetInfoFromService(ctx context.Context, q *reform.Qu case models.MySQLServiceType: agent.TableCount = &sInfo.TableCount l.Debugf("Updating table count: %d.", sInfo.TableCount) - models.EncryptAgent(agent) - if err = q.Update(agent); err != nil { + encryptedAgent := models.EncryptAgent(*agent) + if err = q.Update(&encryptedAgent); err != nil { return errors.Wrap(err, "failed to update table count") } - models.DecryptAgent(agent) return updateServiceVersion(ctx, q, resp, service) case models.PostgreSQLServiceType: @@ -221,11 +220,10 @@ func (c *ServiceInfoBroker) GetInfoFromService(ctx context.Context, q *reform.Qu agent.PostgreSQLOptions.DatabaseCount = int32(databaseCount - excludedDatabaseCount) l.Debugf("Updating PostgreSQL options, database count: %d.", agent.PostgreSQLOptions.DatabaseCount) - models.EncryptAgent(agent) - if err = q.Update(agent); err != nil { + encryptedAgent := models.EncryptAgent(*agent) + if err = q.Update(&encryptedAgent); err != nil { return errors.Wrap(err, "failed to update database count") } - models.DecryptAgent(agent) return updateServiceVersion(ctx, q, resp, service) case models.MongoDBServiceType, From db7cff72c423bc0daeb0c7dc062c474e82b7bcbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 4 Jul 2024 13:44:10 +0200 Subject: [PATCH 112/215] PMM-13129 Fix. --- managed/models/agent_helpers.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index ce0fcf2c9d..50e9e9a4d9 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -658,7 +658,9 @@ func CreateNodeExporter(q *reform.Querier, return nil, errors.WithStack(err) } - return row, nil + decryptedAgent := DecryptAgent(encryptedAgent) + + return &decryptedAgent, nil } // CreateExternalExporterParams params for add external exporter. @@ -744,7 +746,9 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar return nil, errors.WithStack(err) } - return row, nil + decryptedAgent := DecryptAgent(encryptedAgent) + + return &decryptedAgent, nil } // CreateAgentParams params for add common exporter. @@ -936,7 +940,9 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara return nil, errors.WithStack(err) } - return row, nil + decryptedAgent := DecryptAgent(encryptedAgent) + + return &decryptedAgent, nil } // ChangeCommonAgentParams contains parameters that can be changed for all Agents. From 68be0a59f2ac9be67768d0bb6115d65951c345ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 4 Jul 2024 13:59:27 +0200 Subject: [PATCH 113/215] PMM-13129 Fix for service_info_broker. --- managed/services/agents/service_info_broker.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/managed/services/agents/service_info_broker.go b/managed/services/agents/service_info_broker.go index d79ae6664b..b353d28ca7 100644 --- a/managed/services/agents/service_info_broker.go +++ b/managed/services/agents/service_info_broker.go @@ -198,6 +198,8 @@ func (c *ServiceInfoBroker) GetInfoFromService(ctx context.Context, q *reform.Qu if err = q.Update(&encryptedAgent); err != nil { return errors.Wrap(err, "failed to update table count") } + decryptedAgent := models.DecryptAgent(encryptedAgent) + agent = &decryptedAgent return updateServiceVersion(ctx, q, resp, service) case models.PostgreSQLServiceType: @@ -224,6 +226,8 @@ func (c *ServiceInfoBroker) GetInfoFromService(ctx context.Context, q *reform.Qu if err = q.Update(&encryptedAgent); err != nil { return errors.Wrap(err, "failed to update database count") } + decryptedAgent := models.DecryptAgent(encryptedAgent) + agent = &decryptedAgent return updateServiceVersion(ctx, q, resp, service) case models.MongoDBServiceType, From 46c6cd6fe6d964449242690201ef276a628f45a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 4 Jul 2024 14:21:24 +0200 Subject: [PATCH 114/215] PMM-13129 Fix service_info_broker options pointer propagation. --- managed/services/agents/service_info_broker.go | 4 ---- managed/services/management/mongodb.go | 6 ++---- managed/services/management/postgresql.go | 7 +++---- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/managed/services/agents/service_info_broker.go b/managed/services/agents/service_info_broker.go index b353d28ca7..d79ae6664b 100644 --- a/managed/services/agents/service_info_broker.go +++ b/managed/services/agents/service_info_broker.go @@ -198,8 +198,6 @@ func (c *ServiceInfoBroker) GetInfoFromService(ctx context.Context, q *reform.Qu if err = q.Update(&encryptedAgent); err != nil { return errors.Wrap(err, "failed to update table count") } - decryptedAgent := models.DecryptAgent(encryptedAgent) - agent = &decryptedAgent return updateServiceVersion(ctx, q, resp, service) case models.PostgreSQLServiceType: @@ -226,8 +224,6 @@ func (c *ServiceInfoBroker) GetInfoFromService(ctx context.Context, q *reform.Qu if err = q.Update(&encryptedAgent); err != nil { return errors.Wrap(err, "failed to update database count") } - decryptedAgent := models.DecryptAgent(encryptedAgent) - agent = &decryptedAgent return updateServiceVersion(ctx, q, resp, service) case models.MongoDBServiceType, diff --git a/managed/services/management/mongodb.go b/managed/services/management/mongodb.go index e6f999cf76..dd5d75b2f9 100644 --- a/managed/services/management/mongodb.go +++ b/managed/services/management/mongodb.go @@ -77,8 +77,6 @@ func (s *MongoDBService) Add(ctx context.Context, req *managementpb.AddMongoDBRe } res.Service = invService.(*inventorypb.MongoDBService) //nolint:forcetypeassert - mongoDBOptions := models.MongoDBOptionsFromRequest(req) - req.MetricsMode, err = supportedMetricsMode(tx.Querier, req.MetricsMode, req.PmmAgentId) if err != nil { return err @@ -92,7 +90,7 @@ func (s *MongoDBService) Add(ctx context.Context, req *managementpb.AddMongoDBRe AgentPassword: req.AgentPassword, TLS: req.Tls, TLSSkipVerify: req.TlsSkipVerify, - MongoDBOptions: mongoDBOptions, + MongoDBOptions: models.MongoDBOptionsFromRequest(req), PushMetrics: isPushMode(req.MetricsMode), ExposeExporter: req.ExposeExporter, DisableCollectors: req.DisableCollectors, @@ -126,7 +124,7 @@ func (s *MongoDBService) Add(ctx context.Context, req *managementpb.AddMongoDBRe Password: req.Password, TLS: req.Tls, TLSSkipVerify: req.TlsSkipVerify, - MongoDBOptions: mongoDBOptions, + MongoDBOptions: models.MongoDBOptionsFromRequest(req), MaxQueryLength: req.MaxQueryLength, LogLevel: services.SpecifyLogLevel(req.LogLevel, inventorypb.LogLevel_fatal), // TODO QueryExamplesDisabled https://jira.percona.com/browse/PMM-7860 diff --git a/managed/services/management/postgresql.go b/managed/services/management/postgresql.go index 0449251eb4..70481c218d 100644 --- a/managed/services/management/postgresql.go +++ b/managed/services/management/postgresql.go @@ -85,7 +85,6 @@ func (s *PostgreSQLService) Add(ctx context.Context, req *managementpb.AddPostgr return err } - options := models.PostgreSQLOptionsFromRequest(req) row, err := models.CreateAgent(tx.Querier, models.PostgresExporterType, &models.CreateAgentParams{ PMMAgentID: req.PmmAgentId, ServiceID: service.ServiceID, @@ -97,7 +96,7 @@ func (s *PostgreSQLService) Add(ctx context.Context, req *managementpb.AddPostgr PushMetrics: isPushMode(req.MetricsMode), ExposeExporter: req.ExposeExporter, DisableCollectors: req.DisableCollectors, - PostgreSQLOptions: options, + PostgreSQLOptions: models.PostgreSQLOptionsFromRequest(req), LogLevel: services.SpecifyLogLevel(req.LogLevel, inventorypb.LogLevel_error), }) if err != nil { @@ -138,7 +137,7 @@ func (s *PostgreSQLService) Add(ctx context.Context, req *managementpb.AddPostgr CommentsParsingDisabled: req.DisableCommentsParsing, TLS: req.Tls, TLSSkipVerify: req.TlsSkipVerify, - PostgreSQLOptions: options, + PostgreSQLOptions: models.PostgreSQLOptionsFromRequest(req), LogLevel: services.SpecifyLogLevel(req.LogLevel, inventorypb.LogLevel_fatal), }) if err != nil { @@ -163,7 +162,7 @@ func (s *PostgreSQLService) Add(ctx context.Context, req *managementpb.AddPostgr CommentsParsingDisabled: req.DisableCommentsParsing, TLS: req.Tls, TLSSkipVerify: req.TlsSkipVerify, - PostgreSQLOptions: options, + PostgreSQLOptions: models.PostgreSQLOptionsFromRequest(req), LogLevel: services.SpecifyLogLevel(req.LogLevel, inventorypb.LogLevel_fatal), }) if err != nil { From b68421ab3ab908fe81c319b70e6901f74ad6922c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 4 Jul 2024 14:51:41 +0200 Subject: [PATCH 115/215] PMM-13129 Fix for custom labels after removed pointer. --- managed/models/agent_helpers.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 50e9e9a4d9..ff5b2c7cfe 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -651,7 +651,7 @@ func CreateNodeExporter(q *reform.Querier, } encryptedAgent := EncryptAgent(*row) - if err := row.SetCustomLabels(customLabels); err != nil { + if err := encryptedAgent.SetCustomLabels(customLabels); err != nil { return nil, err } if err := q.Insert(&encryptedAgent); err != nil { @@ -739,7 +739,7 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar } encryptedAgent := EncryptAgent(*row) - if err := row.SetCustomLabels(params.CustomLabels); err != nil { + if err := encryptedAgent.SetCustomLabels(params.CustomLabels); err != nil { return nil, err } if err := q.Insert(&encryptedAgent); err != nil { @@ -933,7 +933,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara } encryptedAgent := EncryptAgent(*row) - if err := row.SetCustomLabels(params.CustomLabels); err != nil { + if err := encryptedAgent.SetCustomLabels(params.CustomLabels); err != nil { return nil, err } if err := q.Insert(&encryptedAgent); err != nil { From 53ba9a96b92dff236f00c0d2dc5387a5286ed2e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Fri, 5 Jul 2024 08:28:28 +0200 Subject: [PATCH 116/215] PMM-13129 Hide cipherText in error message. --- managed/utils/encryption/encryption.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index e11127556f..89b01adf15 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -18,7 +18,6 @@ package encryption import ( "encoding/base64" - "fmt" "os" "slices" @@ -143,7 +142,7 @@ func (e *Encryption) Decrypt(cipherText string) (string, error) { } decoded, err := base64.StdEncoding.DecodeString(cipherText) if err != nil { - return cipherText, fmt.Errorf("value %s is probably not encrypted, error: %w", cipherText, err) + return cipherText, err } secret, err := e.Primitive.Decrypt(decoded, []byte("")) if err != nil { From fae5504fe7fc6c2cf34a7c621f40211cb65c6794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Fri, 5 Jul 2024 11:12:50 +0200 Subject: [PATCH 117/215] PMM-13129 Panic in case of unavailable encryption. --- managed/utils/encryption/encryption.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 89b01adf15..8e8e4e0d6a 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -46,17 +46,17 @@ func New(keyPath string) *Encryption { case os.IsNotExist(err): err = e.generateKey() if err != nil { - logrus.Errorf("Encryption: %v", err) + logrus.Panicf("Encryption: %v", err) } case err != nil: - logrus.Errorf("Encryption: %v", err) + logrus.Panicf("Encryption: %v", err) default: e.Key = string(bytes) } primitive, err := e.getPrimitive() if err != nil { - logrus.Errorf("Encryption: %v", err) + logrus.Panicf("Encryption: %v", err) } e.Primitive = primitive From db111b8c95b49ddec3e933813beb1da50db593a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Fri, 5 Jul 2024 11:13:39 +0200 Subject: [PATCH 118/215] PMM-13129 Remove CA certificates from encryption/decryption. --- managed/models/encryption_helpers.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index 2d39ab2ffe..677a79d49f 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -77,10 +77,6 @@ func agentEncryption(agent Agent, handler func(string) (string, error)) Agent { var err error if agent.MySQLOptions != nil { - agent.MySQLOptions.TLSCa, err = handler(agent.MySQLOptions.TLSCa) - if err != nil { - logrus.Warning(err) - } agent.MySQLOptions.TLSCert, err = handler(agent.MySQLOptions.TLSCert) if err != nil { logrus.Warning(err) @@ -92,10 +88,6 @@ func agentEncryption(agent Agent, handler func(string) (string, error)) Agent { } if agent.PostgreSQLOptions != nil { - agent.PostgreSQLOptions.SSLCa, err = handler(agent.PostgreSQLOptions.SSLCa) - if err != nil { - logrus.Warning(err) - } agent.PostgreSQLOptions.SSLCert, err = handler(agent.PostgreSQLOptions.SSLCert) if err != nil { logrus.Warning(err) @@ -107,10 +99,6 @@ func agentEncryption(agent Agent, handler func(string) (string, error)) Agent { } if agent.MongoDBOptions != nil { - agent.MongoDBOptions.TLSCa, err = handler(agent.MongoDBOptions.TLSCa) - if err != nil { - logrus.Warning(err) - } agent.MongoDBOptions.TLSCertificateKey, err = handler(agent.MongoDBOptions.TLSCertificateKey) if err != nil { logrus.Warning(err) From b9bf58b07d9747793ed46b4c68c6a99b9f9679a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Fri, 5 Jul 2024 11:16:05 +0200 Subject: [PATCH 119/215] PMM-13129 Required refactor. --- managed/models/agent_helpers.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index ff5b2c7cfe..a88d52c88c 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -229,8 +229,7 @@ func FindAgents(q *reform.Querier, filters AgentFilters) ([]*Agent, error) { agents := make([]*Agent, len(structs)) for i, s := range structs { - agent := s.(*Agent) //nolint:forcetypeassert - decryptedAgent := DecryptAgent(*agent) + decryptedAgent := DecryptAgent(*s.(*Agent)) //nolint:forcetypeassert agents[i] = &decryptedAgent } @@ -275,8 +274,7 @@ func FindAgentsByIDs(q *reform.Querier, ids []string) ([]*Agent, error) { res := make([]*Agent, len(structs)) for i, s := range structs { - agent := s.(*Agent) //nolint:forcetypeassert - decryptedAgent := DecryptAgent(*agent) + decryptedAgent := DecryptAgent(*s.(*Agent)) //nolint:forcetypeassert res[i] = &decryptedAgent } return res, nil @@ -328,8 +326,7 @@ func FindDBConfigForService(q *reform.Querier, serviceID string) (*DBConfig, err res := make([]*Agent, len(structs)) for i, s := range structs { - agent := s.(*Agent) //nolint:forcetypeassert - decryptedAgent := DecryptAgent(*agent) + decryptedAgent := DecryptAgent(*s.(*Agent)) //nolint:forcetypeassert res[i] = &decryptedAgent } @@ -357,8 +354,7 @@ func FindPMMAgentsRunningOnNode(q *reform.Querier, nodeID string) ([]*Agent, err res := make([]*Agent, 0, len(structs)) for _, str := range structs { - row := str.(*Agent) //nolint:forcetypeassert - decryptedAgent := DecryptAgent(*row) + decryptedAgent := DecryptAgent(*str.(*Agent)) //nolint:forcetypeassert res = append(res, &decryptedAgent) } @@ -403,8 +399,7 @@ func FindPMMAgentsForService(q *reform.Querier, serviceID string) ([]*Agent, err } res := make([]*Agent, 0, len(pmmAgentRecords)) for _, str := range pmmAgentRecords { - row := str.(*Agent) //nolint:forcetypeassert - decryptedAgent := DecryptAgent(*row) + decryptedAgent := DecryptAgent(*str.(*Agent)) //nolint:forcetypeassert res = append(res, &decryptedAgent) } @@ -486,8 +481,7 @@ func FindAgentsForScrapeConfig(q *reform.Querier, pmmAgentID *string, pushMetric res := make([]*Agent, len(allAgents)) for i, s := range allAgents { - agent := s.(*Agent) //nolint:forcetypeassert - decryptedAgent := DecryptAgent(*agent) + decryptedAgent := DecryptAgent(*s.(*Agent)) //nolint:forcetypeassert res[i] = &decryptedAgent } return res, nil From 54c215e936eb35208cd67df057a2d62eeb3066d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= <62988319+JiriCtvrtka@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:16:58 +0200 Subject: [PATCH 120/215] Update api/serverpb/server.proto Co-authored-by: Alex Demidoff --- api/serverpb/server.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/serverpb/server.proto b/api/serverpb/server.proto index a2fec8221a..05f13e1743 100644 --- a/api/serverpb/server.proto +++ b/api/serverpb/server.proto @@ -169,7 +169,7 @@ message Settings { bool enable_access_control = 21; // Default Access Control role ID for new users. uint32 default_role_id = 22; - // Contains all already encrypted tables in format 'db.table.column'. + // Contains all encrypted tables in format 'db.table.column'. repeated string encrypted_items = 23; } From 7dcabf4370167b6ec7554f32ec24b141f252c625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= <62988319+JiriCtvrtka@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:18:46 +0200 Subject: [PATCH 121/215] Update managed/models/database.go Co-authored-by: Alex Demidoff --- managed/models/database.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/models/database.go b/managed/models/database.go index 3ab111c92a..09767d0df7 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1107,7 +1107,7 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. return db, nil } -// EncryptDB encrypt all provided columns in specific database and table. +// EncryptDB encrypts a set of columns in a specific database and table. func EncryptDB(tx *reform.TX, params SetupDBParams, itemsToEncrypt []encryption.Table) error { if len(itemsToEncrypt) == 0 { return nil From 607450eaad08241b7b28efdfd0616803c0c3ba2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= <62988319+JiriCtvrtka@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:19:14 +0200 Subject: [PATCH 122/215] Update managed/utils/encryption/encryption.go Co-authored-by: Alex Demidoff --- managed/utils/encryption/encryption.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 89b01adf15..424296034d 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -36,7 +36,7 @@ var ( DefaultEncryption = New(DefaultEncryptionKeyPath) ) -// New create encryption, if key on path doesnt exists will be generated. +// New creates an encryption; if key on path doesn't exist, it will be generated. func New(keyPath string) *Encryption { e := &Encryption{} e.Path = keyPath From 5e40835ad2ddb908948e56e9e24b2179ff94d391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= <62988319+JiriCtvrtka@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:20:01 +0200 Subject: [PATCH 123/215] Update managed/utils/encryption/models.go Co-authored-by: Alex Demidoff --- managed/utils/encryption/models.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go index 2407e25c53..63bab71da4 100644 --- a/managed/utils/encryption/models.go +++ b/managed/utils/encryption/models.go @@ -38,7 +38,7 @@ type DatabaseConnection struct { // Table represents table name, it's identificators and columns to be encrypted/decrypted. type Table struct { Name string - Identificators []string + Identifiers []string Columns []Column } From ae77773c71f660ef90d8c1235ad6a4c88fd7044f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= <62988319+JiriCtvrtka@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:20:31 +0200 Subject: [PATCH 124/215] Update managed/utils/encryption/models.go Co-authored-by: Alex Demidoff --- managed/utils/encryption/models.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go index 63bab71da4..424a87eaaf 100644 --- a/managed/utils/encryption/models.go +++ b/managed/utils/encryption/models.go @@ -24,7 +24,7 @@ type Encryption struct { Primitive tink.AEAD } -// DatabaseConnection represents DB connection and it's encrypted items. +// DatabaseConnection represents DB connection and its encrypted items. type DatabaseConnection struct { Host, User, Password string Port int16 From fde529b6450c1d3a237009519e451dae6d2dd66b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= <62988319+JiriCtvrtka@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:20:55 +0200 Subject: [PATCH 125/215] Update managed/utils/encryption/helpers.go Co-authored-by: Alex Demidoff --- managed/utils/encryption/helpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index d0ce0f543d..1acbf543bf 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -163,7 +163,7 @@ func (table Table) read(tx *reform.TX) (*QueryValues, error) { whereValues = append(whereValues, row[k]) i++ } - whereSQL := fmt.Sprintf("WHERE %s", strings.Join(where, " AND ")) + whereSQL := "WHERE " + strings.Join(where, " AND ") q.WhereValues = append(q.WhereValues, whereValues) q.Query = fmt.Sprintf("UPDATE %s %s %s", table.Name, setSQL, whereSQL) From f5def78220a2ee35ad29545ec7d4194c34ce6e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Fri, 5 Jul 2024 11:31:26 +0200 Subject: [PATCH 126/215] PMM-13129 Gen. --- .../json/client/server/change_settings_responses.go | 2 +- api/serverpb/json/client/server/get_settings_responses.go | 2 +- api/serverpb/json/serverpb.json | 4 ++-- api/serverpb/server.pb.go | 2 +- api/swagger/swagger-dev.json | 4 ++-- api/swagger/swagger.json | 4 ++-- managed/models/database.go | 6 +++--- managed/utils/encryption/models.go | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/api/serverpb/json/client/server/change_settings_responses.go b/api/serverpb/json/client/server/change_settings_responses.go index 34363d86fd..03a6426982 100644 --- a/api/serverpb/json/client/server/change_settings_responses.go +++ b/api/serverpb/json/client/server/change_settings_responses.go @@ -700,7 +700,7 @@ type ChangeSettingsOKBodySettings struct { // Default Access Control role ID for new users. DefaultRoleID int64 `json:"default_role_id,omitempty"` - // Contains all already encrypted tables in format 'db.table.column'. + // Contains all encrypted tables in format 'db.table.column'. EncryptedItems []string `json:"encrypted_items"` // metrics resolutions diff --git a/api/serverpb/json/client/server/get_settings_responses.go b/api/serverpb/json/client/server/get_settings_responses.go index 163727e9e9..29b9d8dbaf 100644 --- a/api/serverpb/json/client/server/get_settings_responses.go +++ b/api/serverpb/json/client/server/get_settings_responses.go @@ -509,7 +509,7 @@ type GetSettingsOKBodySettings struct { // Default Access Control role ID for new users. DefaultRoleID int64 `json:"default_role_id,omitempty"` - // Contains all already encrypted tables in format 'db.table.column'. + // Contains all encrypted tables in format 'db.table.column'. EncryptedItems []string `json:"encrypted_items"` // metrics resolutions diff --git a/api/serverpb/json/serverpb.json b/api/serverpb/json/serverpb.json index e8732c6729..bbf363e162 100644 --- a/api/serverpb/json/serverpb.json +++ b/api/serverpb/json/serverpb.json @@ -339,7 +339,7 @@ "x-order": 15 }, "encrypted_items": { - "description": "Contains all already encrypted tables in format 'db.table.column'.", + "description": "Contains all encrypted tables in format 'db.table.column'.", "type": "array", "items": { "type": "string" @@ -540,7 +540,7 @@ "x-order": 15 }, "encrypted_items": { - "description": "Contains all already encrypted tables in format 'db.table.column'.", + "description": "Contains all encrypted tables in format 'db.table.column'.", "type": "array", "items": { "type": "string" diff --git a/api/serverpb/server.pb.go b/api/serverpb/server.pb.go index 4ea9b21aa2..fde1a23454 100644 --- a/api/serverpb/server.pb.go +++ b/api/serverpb/server.pb.go @@ -1031,7 +1031,7 @@ type Settings struct { EnableAccessControl bool `protobuf:"varint,21,opt,name=enable_access_control,json=enableAccessControl,proto3" json:"enable_access_control,omitempty"` // Default Access Control role ID for new users. DefaultRoleId uint32 `protobuf:"varint,22,opt,name=default_role_id,json=defaultRoleId,proto3" json:"default_role_id,omitempty"` - // Contains all already encrypted tables in format 'db.table.column'. + // Contains all encrypted tables in format 'db.table.column'. EncryptedItems []string `protobuf:"bytes,23,rep,name=encrypted_items,json=encryptedItems,proto3" json:"encrypted_items,omitempty"` } diff --git a/api/swagger/swagger-dev.json b/api/swagger/swagger-dev.json index 152f9f484e..24aa623790 100644 --- a/api/swagger/swagger-dev.json +++ b/api/swagger/swagger-dev.json @@ -3264,7 +3264,7 @@ "x-order": 16 }, "encrypted_items": { - "description": "Contains all already encrypted tables in format 'db.table.column'.", + "description": "Contains all encrypted tables in format 'db.table.column'.", "type": "array", "items": { "type": "string" @@ -3465,7 +3465,7 @@ "x-order": 16 }, "encrypted_items": { - "description": "Contains all already encrypted tables in format 'db.table.column'.", + "description": "Contains all encrypted tables in format 'db.table.column'.", "type": "array", "items": { "type": "string" diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index ef0e048255..f822cd7ea1 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -420,7 +420,7 @@ "x-order": 16 }, "encrypted_items": { - "description": "Contains all already encrypted tables in format 'db.table.column'.", + "description": "Contains all encrypted tables in format 'db.table.column'.", "type": "array", "items": { "type": "string" @@ -621,7 +621,7 @@ "x-order": 16 }, "encrypted_items": { - "description": "Contains all already encrypted tables in format 'db.table.column'.", + "description": "Contains all encrypted tables in format 'db.table.column'.", "type": "array", "items": { "type": "string" diff --git a/managed/models/database.go b/managed/models/database.go index 09767d0df7..11f9e9472d 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1094,9 +1094,9 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. itemsToEncrypt := []encryption.Table{ { - Name: "agents", - Identificators: []string{"agent_id"}, - Columns: columnsToEncrypt, + Name: "agents", + Identifiers: []string{"agent_id"}, + Columns: columnsToEncrypt, }, } diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go index 424a87eaaf..64ed15a262 100644 --- a/managed/utils/encryption/models.go +++ b/managed/utils/encryption/models.go @@ -37,9 +37,9 @@ type DatabaseConnection struct { // Table represents table name, it's identificators and columns to be encrypted/decrypted. type Table struct { - Name string + Name string Identifiers []string - Columns []Column + Columns []Column } // Column represents column name and column's custom handler (if needed). From fc783442f0f6ac25895f58156bf830a2bf56d046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Fri, 5 Jul 2024 11:36:06 +0200 Subject: [PATCH 127/215] PMM-13129 Identifiers word. --- managed/utils/encryption/helpers.go | 6 +++--- managed/utils/encryption/models.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index 1acbf543bf..c8c11ab4e3 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -127,7 +127,7 @@ func (table Table) columnsList() []string { } func (table Table) read(tx *reform.TX) (*QueryValues, error) { - what := slices.Concat(table.Identificators, table.columnsList()) + what := slices.Concat(table.Identifiers, table.columnsList()) query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(what, ", "), table.Name) rows, err := tx.Query(query) if err != nil { @@ -148,7 +148,7 @@ func (table Table) read(tx *reform.TX) (*QueryValues, error) { i := 1 set := []string{} setValues := []any{} - for k, v := range row[len(table.Identificators):] { + for k, v := range row[len(table.Identifiers):] { set = append(set, fmt.Sprintf("%s = $%d", table.Columns[k].Name, i)) setValues = append(setValues, v) i++ @@ -158,7 +158,7 @@ func (table Table) read(tx *reform.TX) (*QueryValues, error) { where := []string{} whereValues := []any{} - for k, id := range table.Identificators { + for k, id := range table.Identifiers { where = append(where, fmt.Sprintf("%s = $%d", id, i)) whereValues = append(whereValues, row[k]) i++ diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go index 64ed15a262..778e4c0a4c 100644 --- a/managed/utils/encryption/models.go +++ b/managed/utils/encryption/models.go @@ -35,7 +35,7 @@ type DatabaseConnection struct { SSLCertPath string } -// Table represents table name, it's identificators and columns to be encrypted/decrypted. +// Table represents table name, it's identifiers and columns to be encrypted/decrypted. type Table struct { Name string Identifiers []string From ff38d335570bc389d1dabfbf295d337b628925dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 9 Jul 2024 08:31:23 +0200 Subject: [PATCH 128/215] PMM-13129 Remove CAs from handlers. --- managed/models/encryption_helpers.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index 677a79d49f..f3ae2e01a8 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -153,10 +153,6 @@ func mySQLOptionsHandler(val any, handler func(string) (string, error)) (any, er return nil, err } - o.TLSCa, err = handler(o.TLSCa) - if err != nil { - return nil, err - } o.TLSCert, err = handler(o.TLSCert) if err != nil { return nil, err @@ -196,10 +192,6 @@ func postgreSQLOptionsHandler(val any, handler func(string) (string, error)) (an return nil, err } - o.SSLCa, err = handler(o.SSLCa) - if err != nil { - return nil, err - } o.SSLCert, err = handler(o.SSLCert) if err != nil { return nil, err @@ -239,10 +231,6 @@ func mongoDBOptionsHandler(val any, handler func(string) (string, error)) (any, return nil, err } - o.TLSCa, err = handler(o.TLSCa) - if err != nil { - return nil, err - } o.TLSCertificateKey, err = handler(o.TLSCertificateKey) if err != nil { return nil, err From 0a069f517ae3a4c5fc9f360700da3ab378a02eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= <62988319+JiriCtvrtka@users.noreply.github.com> Date: Tue, 9 Jul 2024 08:31:52 +0200 Subject: [PATCH 129/215] Update managed/models/settings.go Co-authored-by: Alex Demidoff --- managed/models/settings.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/models/settings.go b/managed/models/settings.go index 6d0d6bbe2a..018fcfba6b 100644 --- a/managed/models/settings.go +++ b/managed/models/settings.go @@ -101,7 +101,7 @@ type Settings struct { Enabled bool `json:"enabled"` } `json:"access_control"` - // Contains all already encrypted tables in format 'db.table.column'. + // Contains all encrypted tables in format 'db.table.column'. EncryptedItems []string `json:"encrypted_items"` } From dfcb8d153aa1afc250bd144d9695db6fd8442f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= <62988319+JiriCtvrtka@users.noreply.github.com> Date: Tue, 9 Jul 2024 08:31:59 +0200 Subject: [PATCH 130/215] Update managed/utils/encryption/encryption.go Co-authored-by: Alex Demidoff --- managed/utils/encryption/encryption.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 517ac9eb43..62ff4322ac 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -63,7 +63,7 @@ func New(keyPath string) *Encryption { return e } -// Encrypt is wrapper around DefaultEncryption.Encrypt. +// Encrypt is a wrapper around DefaultEncryption.Encrypt. func Encrypt(secret string) (string, error) { return DefaultEncryption.Encrypt(secret) } From 82d881f6e76247bef336c34664a8a86c40e3c07a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= <62988319+JiriCtvrtka@users.noreply.github.com> Date: Tue, 9 Jul 2024 08:32:07 +0200 Subject: [PATCH 131/215] Update managed/utils/encryption/encryption.go Co-authored-by: Alex Demidoff --- managed/utils/encryption/encryption.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 62ff4322ac..edcb519094 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -82,7 +82,7 @@ func (e *Encryption) Encrypt(secret string) (string, error) { return base64.StdEncoding.EncodeToString(cipherText), nil } -// EncryptItems is wrapper around DefaultEncryption.EncryptItems. +// EncryptItems is a wrapper around DefaultEncryption.EncryptItems. func EncryptItems(tx *reform.TX, tables []Table) error { return DefaultEncryption.EncryptItems(tx, tables) } From fe3be3149b2d75924940176c725995d5d467f2d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 9 Jul 2024 09:10:20 +0200 Subject: [PATCH 132/215] PMM-13129 Dereference all DB options on encrypt/decrypt. --- managed/models/agent_helpers.go | 12 +++-------- managed/models/encryption_helpers.go | 32 +++++++++++++++++++--------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index a88d52c88c..422531cbe9 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -652,9 +652,7 @@ func CreateNodeExporter(q *reform.Querier, return nil, errors.WithStack(err) } - decryptedAgent := DecryptAgent(encryptedAgent) - - return &decryptedAgent, nil + return row, nil } // CreateExternalExporterParams params for add external exporter. @@ -740,9 +738,7 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar return nil, errors.WithStack(err) } - decryptedAgent := DecryptAgent(encryptedAgent) - - return &decryptedAgent, nil + return row, nil } // CreateAgentParams params for add common exporter. @@ -934,9 +930,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara return nil, errors.WithStack(err) } - decryptedAgent := DecryptAgent(encryptedAgent) - - return &decryptedAgent, nil + return row, nil } // ChangeCommonAgentParams contains parameters that can be changed for all Agents. diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index f3ae2e01a8..baacefe47b 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -77,55 +77,67 @@ func agentEncryption(agent Agent, handler func(string) (string, error)) Agent { var err error if agent.MySQLOptions != nil { - agent.MySQLOptions.TLSCert, err = handler(agent.MySQLOptions.TLSCert) + options := &MySQLOptions{} + *options = *agent.MySQLOptions + options.TLSCert, err = handler(agent.MySQLOptions.TLSCert) if err != nil { logrus.Warning(err) } - agent.MySQLOptions.TLSKey, err = handler(agent.MySQLOptions.TLSKey) + options.TLSKey, err = handler(agent.MySQLOptions.TLSKey) if err != nil { logrus.Warning(err) } + agent.MySQLOptions = options } if agent.PostgreSQLOptions != nil { - agent.PostgreSQLOptions.SSLCert, err = handler(agent.PostgreSQLOptions.SSLCert) + options := &PostgreSQLOptions{} + *options = *agent.PostgreSQLOptions + options.SSLCert, err = handler(agent.PostgreSQLOptions.SSLCert) if err != nil { logrus.Warning(err) } - agent.PostgreSQLOptions.SSLKey, err = handler(agent.PostgreSQLOptions.SSLKey) + options.SSLKey, err = handler(agent.PostgreSQLOptions.SSLKey) if err != nil { logrus.Warning(err) } + agent.PostgreSQLOptions = options } if agent.MongoDBOptions != nil { - agent.MongoDBOptions.TLSCertificateKey, err = handler(agent.MongoDBOptions.TLSCertificateKey) + options := &MongoDBOptions{} + *options = *agent.MongoDBOptions + options.TLSCertificateKey, err = handler(agent.MongoDBOptions.TLSCertificateKey) if err != nil { logrus.Warning(err) } - agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = handler(agent.MongoDBOptions.TLSCertificateKeyFilePassword) + options.TLSCertificateKeyFilePassword, err = handler(agent.MongoDBOptions.TLSCertificateKeyFilePassword) if err != nil { logrus.Warning(err) } + agent.MongoDBOptions = options } if agent.AzureOptions != nil { - agent.AzureOptions.ClientID, err = handler(agent.AzureOptions.ClientID) + options := &AzureOptions{} + *options = *agent.AzureOptions + options.ClientID, err = handler(agent.AzureOptions.ClientID) if err != nil { logrus.Warning(err) } - agent.AzureOptions.ClientSecret, err = handler(agent.AzureOptions.ClientSecret) + options.ClientSecret, err = handler(agent.AzureOptions.ClientSecret) if err != nil { logrus.Warning(err) } - agent.AzureOptions.SubscriptionID, err = handler(agent.AzureOptions.SubscriptionID) + options.SubscriptionID, err = handler(agent.AzureOptions.SubscriptionID) if err != nil { logrus.Warning(err) } - agent.AzureOptions.TenantID, err = handler(agent.AzureOptions.TenantID) + options.TenantID, err = handler(agent.AzureOptions.TenantID) if err != nil { logrus.Warning(err) } + agent.AzureOptions = options } return agent From 903b4ef4aa0ff84edde087ebf60d80b42ee62b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 9 Jul 2024 09:27:58 +0200 Subject: [PATCH 133/215] PMM-13129 Custom labels. --- managed/models/agent_helpers.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 422531cbe9..3054cf12cb 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -645,7 +645,7 @@ func CreateNodeExporter(q *reform.Querier, } encryptedAgent := EncryptAgent(*row) - if err := encryptedAgent.SetCustomLabels(customLabels); err != nil { + if err := row.SetCustomLabels(customLabels); err != nil { return nil, err } if err := q.Insert(&encryptedAgent); err != nil { @@ -731,7 +731,7 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar } encryptedAgent := EncryptAgent(*row) - if err := encryptedAgent.SetCustomLabels(params.CustomLabels); err != nil { + if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err } if err := q.Insert(&encryptedAgent); err != nil { @@ -923,7 +923,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara } encryptedAgent := EncryptAgent(*row) - if err := encryptedAgent.SetCustomLabels(params.CustomLabels); err != nil { + if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err } if err := q.Insert(&encryptedAgent); err != nil { From 9fd89821fbef0d233674b20dbcde913f98027c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 9 Jul 2024 09:32:10 +0200 Subject: [PATCH 134/215] Revert "PMM-13129 Custom labels." This reverts commit 903b4ef4aa0ff84edde087ebf60d80b42ee62b1a. --- managed/models/agent_helpers.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 3054cf12cb..422531cbe9 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -645,7 +645,7 @@ func CreateNodeExporter(q *reform.Querier, } encryptedAgent := EncryptAgent(*row) - if err := row.SetCustomLabels(customLabels); err != nil { + if err := encryptedAgent.SetCustomLabels(customLabels); err != nil { return nil, err } if err := q.Insert(&encryptedAgent); err != nil { @@ -731,7 +731,7 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar } encryptedAgent := EncryptAgent(*row) - if err := row.SetCustomLabels(params.CustomLabels); err != nil { + if err := encryptedAgent.SetCustomLabels(params.CustomLabels); err != nil { return nil, err } if err := q.Insert(&encryptedAgent); err != nil { @@ -923,7 +923,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara } encryptedAgent := EncryptAgent(*row) - if err := row.SetCustomLabels(params.CustomLabels); err != nil { + if err := encryptedAgent.SetCustomLabels(params.CustomLabels); err != nil { return nil, err } if err := q.Insert(&encryptedAgent); err != nil { From f9550405f8c4084e18615897ae1c4b39c52b682a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 9 Jul 2024 09:32:21 +0200 Subject: [PATCH 135/215] Revert "PMM-13129 Dereference all DB options on encrypt/decrypt." This reverts commit fe3be3149b2d75924940176c725995d5d467f2d2. --- managed/models/agent_helpers.go | 12 ++++++++--- managed/models/encryption_helpers.go | 32 +++++++++------------------- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 422531cbe9..a88d52c88c 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -652,7 +652,9 @@ func CreateNodeExporter(q *reform.Querier, return nil, errors.WithStack(err) } - return row, nil + decryptedAgent := DecryptAgent(encryptedAgent) + + return &decryptedAgent, nil } // CreateExternalExporterParams params for add external exporter. @@ -738,7 +740,9 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar return nil, errors.WithStack(err) } - return row, nil + decryptedAgent := DecryptAgent(encryptedAgent) + + return &decryptedAgent, nil } // CreateAgentParams params for add common exporter. @@ -930,7 +934,9 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara return nil, errors.WithStack(err) } - return row, nil + decryptedAgent := DecryptAgent(encryptedAgent) + + return &decryptedAgent, nil } // ChangeCommonAgentParams contains parameters that can be changed for all Agents. diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index baacefe47b..f3ae2e01a8 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -77,67 +77,55 @@ func agentEncryption(agent Agent, handler func(string) (string, error)) Agent { var err error if agent.MySQLOptions != nil { - options := &MySQLOptions{} - *options = *agent.MySQLOptions - options.TLSCert, err = handler(agent.MySQLOptions.TLSCert) + agent.MySQLOptions.TLSCert, err = handler(agent.MySQLOptions.TLSCert) if err != nil { logrus.Warning(err) } - options.TLSKey, err = handler(agent.MySQLOptions.TLSKey) + agent.MySQLOptions.TLSKey, err = handler(agent.MySQLOptions.TLSKey) if err != nil { logrus.Warning(err) } - agent.MySQLOptions = options } if agent.PostgreSQLOptions != nil { - options := &PostgreSQLOptions{} - *options = *agent.PostgreSQLOptions - options.SSLCert, err = handler(agent.PostgreSQLOptions.SSLCert) + agent.PostgreSQLOptions.SSLCert, err = handler(agent.PostgreSQLOptions.SSLCert) if err != nil { logrus.Warning(err) } - options.SSLKey, err = handler(agent.PostgreSQLOptions.SSLKey) + agent.PostgreSQLOptions.SSLKey, err = handler(agent.PostgreSQLOptions.SSLKey) if err != nil { logrus.Warning(err) } - agent.PostgreSQLOptions = options } if agent.MongoDBOptions != nil { - options := &MongoDBOptions{} - *options = *agent.MongoDBOptions - options.TLSCertificateKey, err = handler(agent.MongoDBOptions.TLSCertificateKey) + agent.MongoDBOptions.TLSCertificateKey, err = handler(agent.MongoDBOptions.TLSCertificateKey) if err != nil { logrus.Warning(err) } - options.TLSCertificateKeyFilePassword, err = handler(agent.MongoDBOptions.TLSCertificateKeyFilePassword) + agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = handler(agent.MongoDBOptions.TLSCertificateKeyFilePassword) if err != nil { logrus.Warning(err) } - agent.MongoDBOptions = options } if agent.AzureOptions != nil { - options := &AzureOptions{} - *options = *agent.AzureOptions - options.ClientID, err = handler(agent.AzureOptions.ClientID) + agent.AzureOptions.ClientID, err = handler(agent.AzureOptions.ClientID) if err != nil { logrus.Warning(err) } - options.ClientSecret, err = handler(agent.AzureOptions.ClientSecret) + agent.AzureOptions.ClientSecret, err = handler(agent.AzureOptions.ClientSecret) if err != nil { logrus.Warning(err) } - options.SubscriptionID, err = handler(agent.AzureOptions.SubscriptionID) + agent.AzureOptions.SubscriptionID, err = handler(agent.AzureOptions.SubscriptionID) if err != nil { logrus.Warning(err) } - options.TenantID, err = handler(agent.AzureOptions.TenantID) + agent.AzureOptions.TenantID, err = handler(agent.AzureOptions.TenantID) if err != nil { logrus.Warning(err) } - agent.AzureOptions = options } return agent From 687a2e248496864a722156ead6be1adee98d89eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 9 Jul 2024 09:37:05 +0200 Subject: [PATCH 136/215] Reapply "PMM-13129 Custom labels." This reverts commit 9fd89821fbef0d233674b20dbcde913f98027c8c. --- managed/models/agent_helpers.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index a88d52c88c..a1c683985b 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -645,7 +645,7 @@ func CreateNodeExporter(q *reform.Querier, } encryptedAgent := EncryptAgent(*row) - if err := encryptedAgent.SetCustomLabels(customLabels); err != nil { + if err := row.SetCustomLabels(customLabels); err != nil { return nil, err } if err := q.Insert(&encryptedAgent); err != nil { @@ -733,7 +733,7 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar } encryptedAgent := EncryptAgent(*row) - if err := encryptedAgent.SetCustomLabels(params.CustomLabels); err != nil { + if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err } if err := q.Insert(&encryptedAgent); err != nil { @@ -927,7 +927,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara } encryptedAgent := EncryptAgent(*row) - if err := encryptedAgent.SetCustomLabels(params.CustomLabels); err != nil { + if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err } if err := q.Insert(&encryptedAgent); err != nil { From f09bef1207301238346e17ac96e454def8dab6d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 9 Jul 2024 09:37:18 +0200 Subject: [PATCH 137/215] Reapply "PMM-13129 Dereference all DB options on encrypt/decrypt." This reverts commit f9550405f8c4084e18615897ae1c4b39c52b682a. --- managed/models/agent_helpers.go | 12 +++-------- managed/models/encryption_helpers.go | 32 +++++++++++++++++++--------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index a1c683985b..3054cf12cb 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -652,9 +652,7 @@ func CreateNodeExporter(q *reform.Querier, return nil, errors.WithStack(err) } - decryptedAgent := DecryptAgent(encryptedAgent) - - return &decryptedAgent, nil + return row, nil } // CreateExternalExporterParams params for add external exporter. @@ -740,9 +738,7 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar return nil, errors.WithStack(err) } - decryptedAgent := DecryptAgent(encryptedAgent) - - return &decryptedAgent, nil + return row, nil } // CreateAgentParams params for add common exporter. @@ -934,9 +930,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara return nil, errors.WithStack(err) } - decryptedAgent := DecryptAgent(encryptedAgent) - - return &decryptedAgent, nil + return row, nil } // ChangeCommonAgentParams contains parameters that can be changed for all Agents. diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index f3ae2e01a8..baacefe47b 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -77,55 +77,67 @@ func agentEncryption(agent Agent, handler func(string) (string, error)) Agent { var err error if agent.MySQLOptions != nil { - agent.MySQLOptions.TLSCert, err = handler(agent.MySQLOptions.TLSCert) + options := &MySQLOptions{} + *options = *agent.MySQLOptions + options.TLSCert, err = handler(agent.MySQLOptions.TLSCert) if err != nil { logrus.Warning(err) } - agent.MySQLOptions.TLSKey, err = handler(agent.MySQLOptions.TLSKey) + options.TLSKey, err = handler(agent.MySQLOptions.TLSKey) if err != nil { logrus.Warning(err) } + agent.MySQLOptions = options } if agent.PostgreSQLOptions != nil { - agent.PostgreSQLOptions.SSLCert, err = handler(agent.PostgreSQLOptions.SSLCert) + options := &PostgreSQLOptions{} + *options = *agent.PostgreSQLOptions + options.SSLCert, err = handler(agent.PostgreSQLOptions.SSLCert) if err != nil { logrus.Warning(err) } - agent.PostgreSQLOptions.SSLKey, err = handler(agent.PostgreSQLOptions.SSLKey) + options.SSLKey, err = handler(agent.PostgreSQLOptions.SSLKey) if err != nil { logrus.Warning(err) } + agent.PostgreSQLOptions = options } if agent.MongoDBOptions != nil { - agent.MongoDBOptions.TLSCertificateKey, err = handler(agent.MongoDBOptions.TLSCertificateKey) + options := &MongoDBOptions{} + *options = *agent.MongoDBOptions + options.TLSCertificateKey, err = handler(agent.MongoDBOptions.TLSCertificateKey) if err != nil { logrus.Warning(err) } - agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = handler(agent.MongoDBOptions.TLSCertificateKeyFilePassword) + options.TLSCertificateKeyFilePassword, err = handler(agent.MongoDBOptions.TLSCertificateKeyFilePassword) if err != nil { logrus.Warning(err) } + agent.MongoDBOptions = options } if agent.AzureOptions != nil { - agent.AzureOptions.ClientID, err = handler(agent.AzureOptions.ClientID) + options := &AzureOptions{} + *options = *agent.AzureOptions + options.ClientID, err = handler(agent.AzureOptions.ClientID) if err != nil { logrus.Warning(err) } - agent.AzureOptions.ClientSecret, err = handler(agent.AzureOptions.ClientSecret) + options.ClientSecret, err = handler(agent.AzureOptions.ClientSecret) if err != nil { logrus.Warning(err) } - agent.AzureOptions.SubscriptionID, err = handler(agent.AzureOptions.SubscriptionID) + options.SubscriptionID, err = handler(agent.AzureOptions.SubscriptionID) if err != nil { logrus.Warning(err) } - agent.AzureOptions.TenantID, err = handler(agent.AzureOptions.TenantID) + options.TenantID, err = handler(agent.AzureOptions.TenantID) if err != nil { logrus.Warning(err) } + agent.AzureOptions = options } return agent From 0229c6598a46ce2d36b3e925c1824d261a540bbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 9 Jul 2024 12:23:46 +0200 Subject: [PATCH 138/215] PMM-13129 Remove old migrations tests, required refactor. --- managed/models/database.go | 46 +++++++-------------------- managed/models/database_test.go | 55 --------------------------------- 2 files changed, 11 insertions(+), 90 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index 11f9e9472d..85e807f987 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1065,31 +1065,12 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. columnsToEncrypt := []encryption.Column{ {Name: "username"}, {Name: "password"}, - } - - if params.MigrationVersion == nil || *params.MigrationVersion >= 9 { - columnsToEncrypt = slices.Concat( - columnsToEncrypt, []encryption.Column{{Name: "aws_access_key"}, {Name: "aws_secret_key"}}) - } - if params.MigrationVersion == nil || *params.MigrationVersion >= 25 { - columnsToEncrypt = append( - columnsToEncrypt, encryption.Column{Name: "mongo_db_tls_options", CustomHandler: EncryptMongoDBOptionsHandler}) - } - if params.MigrationVersion == nil || *params.MigrationVersion >= 31 { - columnsToEncrypt = append( - columnsToEncrypt, encryption.Column{Name: "azure_options", CustomHandler: EncryptAzureOptionsHandler}) - } - if params.MigrationVersion == nil || *params.MigrationVersion >= 36 { - columnsToEncrypt = append( - columnsToEncrypt, encryption.Column{Name: "mysql_options", CustomHandler: EncryptMySQLOptionsHandler}) - } - if params.MigrationVersion == nil || *params.MigrationVersion >= 41 { - columnsToEncrypt = append( - columnsToEncrypt, encryption.Column{Name: "postgresql_options", CustomHandler: EncryptPostgreSQLOptionsHandler}) - } - if params.MigrationVersion == nil || *params.MigrationVersion >= 42 { - columnsToEncrypt = append( - columnsToEncrypt, encryption.Column{Name: "agent_password"}) + {Name: "aws_access_key"}, {Name: "aws_secret_key"}, + {Name: "mongo_db_tls_options", CustomHandler: EncryptMongoDBOptionsHandler}, + {Name: "azure_options", CustomHandler: EncryptAzureOptionsHandler}, + {Name: "mysql_options", CustomHandler: EncryptMySQLOptionsHandler}, + {Name: "postgresql_options", CustomHandler: EncryptPostgreSQLOptionsHandler}, + {Name: "agent_password"}, } itemsToEncrypt := []encryption.Table{ @@ -1256,12 +1237,12 @@ func migrateDB(db *reform.DB, params SetupDBParams, itemsToEncrypt []encryption. } } - if params.SetupFixtures == SkipFixtures { - err := EncryptDB(tx, params, itemsToEncrypt) - if err != nil { - return err - } + err := EncryptDB(tx, params, itemsToEncrypt) + if err != nil { + return err + } + if params.SetupFixtures == SkipFixtures { return nil } @@ -1274,11 +1255,6 @@ func migrateDB(db *reform.DB, params SetupDBParams, itemsToEncrypt []encryption. return err } - err = EncryptDB(tx, params, itemsToEncrypt) - if err != nil { - return err - } - err = setupPMMServerAgents(tx.Querier, params) if err != nil { return err diff --git a/managed/models/database_test.go b/managed/models/database_test.go index f128904fe3..dd08a18095 100644 --- a/managed/models/database_test.go +++ b/managed/models/database_test.go @@ -21,7 +21,6 @@ import ( "database/sql" "fmt" "testing" - "time" "github.com/AlekSi/pointer" "github.com/lib/pq" @@ -327,60 +326,6 @@ func TestDatabaseChecks(t *testing.T) { } func TestDatabaseMigrations(t *testing.T) { - t.Run("Update metrics resolutions", func(t *testing.T) { - sqlDB := testdb.Open(t, models.SkipFixtures, pointer.ToInt(9)) - defer sqlDB.Close() //nolint:errcheck - settings, err := models.GetSettings(sqlDB) - require.NoError(t, err) - metricsResolutions := models.MetricsResolutions{ - HR: 5 * time.Second, - MR: 5 * time.Second, - LR: 60 * time.Second, - } - settings.MetricsResolutions = metricsResolutions - err = models.SaveSettings(sqlDB, settings) - require.NoError(t, err) - - settings, err = models.GetSettings(sqlDB) - require.NoError(t, err) - require.Equal(t, metricsResolutions, settings.MetricsResolutions) - - testdb.SetupDB(t, sqlDB, models.SkipFixtures, pointer.ToInt(10)) - settings, err = models.GetSettings(sqlDB) - require.NoError(t, err) - require.Equal(t, models.MetricsResolutions{ - HR: 5 * time.Second, - MR: 10 * time.Second, - LR: 60 * time.Second, - }, settings.MetricsResolutions) - }) - t.Run("Shouldn' update metrics resolutions if it's already changed", func(t *testing.T) { - sqlDB := testdb.Open(t, models.SkipFixtures, pointer.ToInt(9)) - defer sqlDB.Close() //nolint:errcheck - settings, err := models.GetSettings(sqlDB) - require.NoError(t, err) - metricsResolutions := models.MetricsResolutions{ - HR: 1 * time.Second, - MR: 5 * time.Second, - LR: 60 * time.Second, - } - settings.MetricsResolutions = metricsResolutions - err = models.SaveSettings(sqlDB, settings) - require.NoError(t, err) - - settings, err = models.GetSettings(sqlDB) - require.NoError(t, err) - require.Equal(t, metricsResolutions, settings.MetricsResolutions) - - testdb.SetupDB(t, sqlDB, models.SkipFixtures, pointer.ToInt(10)) - settings, err = models.GetSettings(sqlDB) - require.NoError(t, err) - require.Equal(t, models.MetricsResolutions{ - HR: 1 * time.Second, - MR: 5 * time.Second, - LR: 60 * time.Second, - }, settings.MetricsResolutions) - }) t.Run("stats_collections field migration: string to string array", func(t *testing.T) { sqlDB := testdb.Open(t, models.SkipFixtures, pointer.ToInt(57)) defer sqlDB.Close() //nolint:errcheck From c7dd080e799e56afaffb3c066c226b7d0c16546a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 9 Jul 2024 13:28:47 +0200 Subject: [PATCH 139/215] Revert "Reapply "PMM-13129 Custom labels."" This reverts commit 687a2e248496864a722156ead6be1adee98d89eb. --- managed/models/agent_helpers.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 3054cf12cb..422531cbe9 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -645,7 +645,7 @@ func CreateNodeExporter(q *reform.Querier, } encryptedAgent := EncryptAgent(*row) - if err := row.SetCustomLabels(customLabels); err != nil { + if err := encryptedAgent.SetCustomLabels(customLabels); err != nil { return nil, err } if err := q.Insert(&encryptedAgent); err != nil { @@ -731,7 +731,7 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar } encryptedAgent := EncryptAgent(*row) - if err := row.SetCustomLabels(params.CustomLabels); err != nil { + if err := encryptedAgent.SetCustomLabels(params.CustomLabels); err != nil { return nil, err } if err := q.Insert(&encryptedAgent); err != nil { @@ -923,7 +923,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara } encryptedAgent := EncryptAgent(*row) - if err := row.SetCustomLabels(params.CustomLabels); err != nil { + if err := encryptedAgent.SetCustomLabels(params.CustomLabels); err != nil { return nil, err } if err := q.Insert(&encryptedAgent); err != nil { From 771ca54128b389e7f40f68f97d837f325bf69990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 9 Jul 2024 13:28:54 +0200 Subject: [PATCH 140/215] Revert "Reapply "PMM-13129 Dereference all DB options on encrypt/decrypt."" This reverts commit f09bef1207301238346e17ac96e454def8dab6d2. --- managed/models/agent_helpers.go | 12 ++++++++--- managed/models/encryption_helpers.go | 32 +++++++++------------------- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 422531cbe9..a88d52c88c 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -652,7 +652,9 @@ func CreateNodeExporter(q *reform.Querier, return nil, errors.WithStack(err) } - return row, nil + decryptedAgent := DecryptAgent(encryptedAgent) + + return &decryptedAgent, nil } // CreateExternalExporterParams params for add external exporter. @@ -738,7 +740,9 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar return nil, errors.WithStack(err) } - return row, nil + decryptedAgent := DecryptAgent(encryptedAgent) + + return &decryptedAgent, nil } // CreateAgentParams params for add common exporter. @@ -930,7 +934,9 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara return nil, errors.WithStack(err) } - return row, nil + decryptedAgent := DecryptAgent(encryptedAgent) + + return &decryptedAgent, nil } // ChangeCommonAgentParams contains parameters that can be changed for all Agents. diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index baacefe47b..f3ae2e01a8 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -77,67 +77,55 @@ func agentEncryption(agent Agent, handler func(string) (string, error)) Agent { var err error if agent.MySQLOptions != nil { - options := &MySQLOptions{} - *options = *agent.MySQLOptions - options.TLSCert, err = handler(agent.MySQLOptions.TLSCert) + agent.MySQLOptions.TLSCert, err = handler(agent.MySQLOptions.TLSCert) if err != nil { logrus.Warning(err) } - options.TLSKey, err = handler(agent.MySQLOptions.TLSKey) + agent.MySQLOptions.TLSKey, err = handler(agent.MySQLOptions.TLSKey) if err != nil { logrus.Warning(err) } - agent.MySQLOptions = options } if agent.PostgreSQLOptions != nil { - options := &PostgreSQLOptions{} - *options = *agent.PostgreSQLOptions - options.SSLCert, err = handler(agent.PostgreSQLOptions.SSLCert) + agent.PostgreSQLOptions.SSLCert, err = handler(agent.PostgreSQLOptions.SSLCert) if err != nil { logrus.Warning(err) } - options.SSLKey, err = handler(agent.PostgreSQLOptions.SSLKey) + agent.PostgreSQLOptions.SSLKey, err = handler(agent.PostgreSQLOptions.SSLKey) if err != nil { logrus.Warning(err) } - agent.PostgreSQLOptions = options } if agent.MongoDBOptions != nil { - options := &MongoDBOptions{} - *options = *agent.MongoDBOptions - options.TLSCertificateKey, err = handler(agent.MongoDBOptions.TLSCertificateKey) + agent.MongoDBOptions.TLSCertificateKey, err = handler(agent.MongoDBOptions.TLSCertificateKey) if err != nil { logrus.Warning(err) } - options.TLSCertificateKeyFilePassword, err = handler(agent.MongoDBOptions.TLSCertificateKeyFilePassword) + agent.MongoDBOptions.TLSCertificateKeyFilePassword, err = handler(agent.MongoDBOptions.TLSCertificateKeyFilePassword) if err != nil { logrus.Warning(err) } - agent.MongoDBOptions = options } if agent.AzureOptions != nil { - options := &AzureOptions{} - *options = *agent.AzureOptions - options.ClientID, err = handler(agent.AzureOptions.ClientID) + agent.AzureOptions.ClientID, err = handler(agent.AzureOptions.ClientID) if err != nil { logrus.Warning(err) } - options.ClientSecret, err = handler(agent.AzureOptions.ClientSecret) + agent.AzureOptions.ClientSecret, err = handler(agent.AzureOptions.ClientSecret) if err != nil { logrus.Warning(err) } - options.SubscriptionID, err = handler(agent.AzureOptions.SubscriptionID) + agent.AzureOptions.SubscriptionID, err = handler(agent.AzureOptions.SubscriptionID) if err != nil { logrus.Warning(err) } - options.TenantID, err = handler(agent.AzureOptions.TenantID) + agent.AzureOptions.TenantID, err = handler(agent.AzureOptions.TenantID) if err != nil { logrus.Warning(err) } - agent.AzureOptions = options } return agent From 98e51bec1fa7b5107d42ea372dd881cc2dfaf003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 9 Jul 2024 13:30:39 +0200 Subject: [PATCH 141/215] PMM-13129 Logic change. --- managed/models/agent_helpers.go | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index a88d52c88c..1b2357d022 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -643,15 +643,14 @@ func CreateNodeExporter(q *reform.Querier, LogLevel: pointer.ToStringOrNil(logLevel), ExposeExporter: exposeExporter, } - encryptedAgent := EncryptAgent(*row) - - if err := encryptedAgent.SetCustomLabels(customLabels); err != nil { + if err := row.SetCustomLabels(customLabels); err != nil { return nil, err } + + encryptedAgent := EncryptAgent(*row) if err := q.Insert(&encryptedAgent); err != nil { return nil, errors.WithStack(err) } - decryptedAgent := DecryptAgent(encryptedAgent) return &decryptedAgent, nil @@ -731,15 +730,14 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar ListenPort: pointer.ToUint16(uint16(params.ListenPort)), PushMetrics: params.PushMetrics, } - encryptedAgent := EncryptAgent(*row) - - if err := encryptedAgent.SetCustomLabels(params.CustomLabels); err != nil { + if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err } + + encryptedAgent := EncryptAgent(*row) if err := q.Insert(&encryptedAgent); err != nil { return nil, errors.WithStack(err) } - decryptedAgent := DecryptAgent(encryptedAgent) return &decryptedAgent, nil @@ -925,15 +923,14 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara DisabledCollectors: params.DisableCollectors, LogLevel: pointer.ToStringOrNil(params.LogLevel), } - encryptedAgent := EncryptAgent(*row) - - if err := encryptedAgent.SetCustomLabels(params.CustomLabels); err != nil { + if err := row.SetCustomLabels(params.CustomLabels); err != nil { return nil, err } + + encryptedAgent := EncryptAgent(*row) if err := q.Insert(&encryptedAgent); err != nil { return nil, errors.WithStack(err) } - decryptedAgent := DecryptAgent(encryptedAgent) return &decryptedAgent, nil From 41d98dbbea33529b1ed624f1d3ebe9bb128f44a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 9 Jul 2024 17:15:45 +0200 Subject: [PATCH 142/215] PMM-13129 Remove username, aws_access_key, aws_secret_key from enc. --- managed/models/database.go | 2 -- managed/models/encryption_helpers.go | 24 ------------------------ 2 files changed, 26 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index 85e807f987..454dc2d3de 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1063,9 +1063,7 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. } columnsToEncrypt := []encryption.Column{ - {Name: "username"}, {Name: "password"}, - {Name: "aws_access_key"}, {Name: "aws_secret_key"}, {Name: "mongo_db_tls_options", CustomHandler: EncryptMongoDBOptionsHandler}, {Name: "azure_options", CustomHandler: EncryptAzureOptionsHandler}, {Name: "mysql_options", CustomHandler: EncryptMySQLOptionsHandler}, diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index f3ae2e01a8..fd7b92b83b 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -35,14 +35,6 @@ func DecryptAgent(agent Agent) Agent { } func agentEncryption(agent Agent, handler func(string) (string, error)) Agent { - if agent.Username != nil { - username, err := handler(*agent.Username) - if err != nil { - logrus.Warning(err) - } - agent.Username = &username - } - if agent.Password != nil { password, err := handler(*agent.Password) if err != nil { @@ -59,22 +51,6 @@ func agentEncryption(agent Agent, handler func(string) (string, error)) Agent { agent.AgentPassword = &agentPassword } - if agent.AWSAccessKey != nil { - awsAccessKey, err := handler(*agent.AWSAccessKey) - if err != nil { - logrus.Warning(err) - } - agent.AWSAccessKey = &awsAccessKey - } - - if agent.AWSSecretKey != nil { - awsSecretKey, err := handler(*agent.AWSSecretKey) - if err != nil { - logrus.Warning(err) - } - agent.AWSSecretKey = &awsSecretKey - } - var err error if agent.MySQLOptions != nil { agent.MySQLOptions.TLSCert, err = handler(agent.MySQLOptions.TLSCert) From e4fab91b99dd8b096aeeec5eed578bb15a25d263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 9 Jul 2024 17:34:57 +0200 Subject: [PATCH 143/215] PMM-13129 Env variable for custom encryption key. --- managed/utils/encryption/encryption.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index edcb519094..13d51feb88 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -39,7 +39,12 @@ var ( // New creates an encryption; if key on path doesn't exist, it will be generated. func New(keyPath string) *Encryption { e := &Encryption{} - e.Path = keyPath + customKeyPath := os.Getenv("PMM_ENCRYPTION_KEY_PATH") + if customKeyPath != "" { + e.Path = customKeyPath + } else { + e.Path = keyPath + } bytes, err := os.ReadFile(e.Path) switch { From 70bda620f7498c74ec93272d5e2646d6253e08f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 9 Jul 2024 17:38:15 +0200 Subject: [PATCH 144/215] PMM-13129 Custom key for main check. --- .github/workflows/main.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4feb0fbe66..b449b41b29 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,6 +16,9 @@ jobs: name: Checks runs-on: ubuntu-22.04 + env: + PMM_ENCRYPTION_KEY_PATH: pmm-encryption.key + steps: - name: Check out code uses: actions/checkout@v4 From 21e94f1bbe5a107af33a4abe342aaa4d41a2b83b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 9 Jul 2024 17:51:36 +0200 Subject: [PATCH 145/215] PMM-13129 Remove decrypt agent from create agent methods. --- managed/models/agent_helpers.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 1b2357d022..5213ec881b 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -651,9 +651,8 @@ func CreateNodeExporter(q *reform.Querier, if err := q.Insert(&encryptedAgent); err != nil { return nil, errors.WithStack(err) } - decryptedAgent := DecryptAgent(encryptedAgent) - return &decryptedAgent, nil + return &encryptedAgent, nil } // CreateExternalExporterParams params for add external exporter. @@ -738,9 +737,8 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar if err := q.Insert(&encryptedAgent); err != nil { return nil, errors.WithStack(err) } - decryptedAgent := DecryptAgent(encryptedAgent) - return &decryptedAgent, nil + return &encryptedAgent, nil } // CreateAgentParams params for add common exporter. @@ -931,9 +929,8 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara if err := q.Insert(&encryptedAgent); err != nil { return nil, errors.WithStack(err) } - decryptedAgent := DecryptAgent(encryptedAgent) - return &decryptedAgent, nil + return &encryptedAgent, nil } // ChangeCommonAgentParams contains parameters that can be changed for all Agents. From 36904112177b7b0ccc916fdcc9ec7ab009fcd478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 10 Jul 2024 11:34:55 +0200 Subject: [PATCH 146/215] PMM-13129 Change to skip empty values from encryption. --- managed/models/database.go | 1 + managed/models/encryption_helpers.go | 8 ++++++++ managed/services/management/agent.go | 6 +++--- managed/utils/encryption/encryption.go | 4 +++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index 454dc2d3de..13c9342fec 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1064,6 +1064,7 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. columnsToEncrypt := []encryption.Column{ {Name: "password"}, + {Name: "aws_secret_key"}, {Name: "mongo_db_tls_options", CustomHandler: EncryptMongoDBOptionsHandler}, {Name: "azure_options", CustomHandler: EncryptAzureOptionsHandler}, {Name: "mysql_options", CustomHandler: EncryptMySQLOptionsHandler}, diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index fd7b92b83b..5cf0510d5a 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -51,6 +51,14 @@ func agentEncryption(agent Agent, handler func(string) (string, error)) Agent { agent.AgentPassword = &agentPassword } + if agent.AWSSecretKey != nil { + awsSecretKey, err := handler(*agent.AWSSecretKey) + if err != nil { + logrus.Warning(err) + } + agent.AWSSecretKey = &awsSecretKey + } + var err error if agent.MySQLOptions != nil { agent.MySQLOptions.TLSCert, err = handler(agent.MySQLOptions.TLSCert) diff --git a/managed/services/management/agent.go b/managed/services/management/agent.go index 6e55fecac7..644c9d6b1c 100644 --- a/managed/services/management/agent.go +++ b/managed/services/management/agent.go @@ -143,9 +143,9 @@ func (s *AgentService) agentToAPI(agent *models.Agent) (*agentv1beta1.UniversalA Disabled: agent.Disabled, DisabledCollectors: agent.DisabledCollectors, IsConnected: s.r.IsConnected(agent.AgentID), - IsAgentPasswordSet: agent.AgentPassword != nil, - IsAwsSecretKeySet: agent.AWSSecretKey != nil, - IsPasswordSet: agent.Password != nil, + IsAgentPasswordSet: pointer.GetString(agent.AgentPassword) != "", + IsAwsSecretKeySet: pointer.GetString(agent.AWSSecretKey) != "", + IsPasswordSet: pointer.GetString(agent.Password) != "", ListenPort: uint32(pointer.GetUint16(agent.ListenPort)), LogLevel: pointer.GetString(agent.LogLevel), MaxQueryLength: agent.MaxQueryLength, diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 13d51feb88..a7cba048df 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -78,7 +78,9 @@ func (e *Encryption) Encrypt(secret string) (string, error) { if e == nil || e.Primitive == nil { return secret, ErrEncryptionNotInitialized } - + if secret == "" { + return secret, nil + } cipherText, err := e.Primitive.Encrypt([]byte(secret), []byte("")) if err != nil { return secret, err From 028312ddb9a31ec8f1cb57bfa3641fb5a8de309a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 10 Jul 2024 12:48:53 +0200 Subject: [PATCH 147/215] PMM-13129 Remove unused struct. --- managed/utils/encryption/models.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go index 778e4c0a4c..257b49b1de 100644 --- a/managed/utils/encryption/models.go +++ b/managed/utils/encryption/models.go @@ -24,17 +24,6 @@ type Encryption struct { Primitive tink.AEAD } -// DatabaseConnection represents DB connection and its encrypted items. -type DatabaseConnection struct { - Host, User, Password string - Port int16 - DBName string - SSLMode string - SSLCAPath string - SSLKeyPath string - SSLCertPath string -} - // Table represents table name, it's identifiers and columns to be encrypted/decrypted. type Table struct { Name string From 1b6ef0f432b0cd292110ae7c7de262de7d7c8b48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= <62988319+JiriCtvrtka@users.noreply.github.com> Date: Thu, 11 Jul 2024 08:27:18 +0200 Subject: [PATCH 148/215] Update managed/models/database.go Co-authored-by: Nurlan Moldomurov --- managed/models/database.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/models/database.go b/managed/models/database.go index 13c9342fec..5b11cf100c 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1062,7 +1062,7 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. return nil, errCV } - columnsToEncrypt := []encryption.Column{ + agentColumnsToEncrypt := []encryption.Column{ {Name: "password"}, {Name: "aws_secret_key"}, {Name: "mongo_db_tls_options", CustomHandler: EncryptMongoDBOptionsHandler}, From caad234360b24ec625fe1e1c504cab3ce9b9b466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 11 Jul 2024 08:29:25 +0200 Subject: [PATCH 149/215] PMM-13129 Renaming of variable. --- managed/models/database.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/models/database.go b/managed/models/database.go index 5b11cf100c..c6a532e0b8 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1076,7 +1076,7 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. { Name: "agents", Identifiers: []string{"agent_id"}, - Columns: columnsToEncrypt, + Columns: agentColumnsToEncrypt, }, } From 2ffdc3a87d85f62ea2df8493609162aa46f8392f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 11 Jul 2024 08:38:24 +0200 Subject: [PATCH 150/215] PMM-13129 Remove EncryptedItems field from settings proto. --- .../server/change_settings_responses.go | 3 - .../client/server/get_settings_responses.go | 3 - api/serverpb/json/serverpb.json | 16 - api/serverpb/server.pb.go | 418 +++++++++--------- api/serverpb/server.proto | 2 - api/swagger/swagger-dev.json | 16 - api/swagger/swagger.json | 16 - 7 files changed, 203 insertions(+), 271 deletions(-) diff --git a/api/serverpb/json/client/server/change_settings_responses.go b/api/serverpb/json/client/server/change_settings_responses.go index 03a6426982..ba76893daa 100644 --- a/api/serverpb/json/client/server/change_settings_responses.go +++ b/api/serverpb/json/client/server/change_settings_responses.go @@ -700,9 +700,6 @@ type ChangeSettingsOKBodySettings struct { // Default Access Control role ID for new users. DefaultRoleID int64 `json:"default_role_id,omitempty"` - // Contains all encrypted tables in format 'db.table.column'. - EncryptedItems []string `json:"encrypted_items"` - // metrics resolutions MetricsResolutions *ChangeSettingsOKBodySettingsMetricsResolutions `json:"metrics_resolutions,omitempty"` diff --git a/api/serverpb/json/client/server/get_settings_responses.go b/api/serverpb/json/client/server/get_settings_responses.go index 29b9d8dbaf..44cad33ce1 100644 --- a/api/serverpb/json/client/server/get_settings_responses.go +++ b/api/serverpb/json/client/server/get_settings_responses.go @@ -509,9 +509,6 @@ type GetSettingsOKBodySettings struct { // Default Access Control role ID for new users. DefaultRoleID int64 `json:"default_role_id,omitempty"` - // Contains all encrypted tables in format 'db.table.column'. - EncryptedItems []string `json:"encrypted_items"` - // metrics resolutions MetricsResolutions *GetSettingsOKBodySettingsMetricsResolutions `json:"metrics_resolutions,omitempty"` diff --git a/api/serverpb/json/serverpb.json b/api/serverpb/json/serverpb.json index bbf363e162..f425d7fc35 100644 --- a/api/serverpb/json/serverpb.json +++ b/api/serverpb/json/serverpb.json @@ -338,14 +338,6 @@ "type": "boolean", "x-order": 15 }, - "encrypted_items": { - "description": "Contains all encrypted tables in format 'db.table.column'.", - "type": "array", - "items": { - "type": "string" - }, - "x-order": 17 - }, "metrics_resolutions": { "description": "MetricsResolutions represents Prometheus exporters metrics resolutions.", "type": "object", @@ -539,14 +531,6 @@ "type": "boolean", "x-order": 15 }, - "encrypted_items": { - "description": "Contains all encrypted tables in format 'db.table.column'.", - "type": "array", - "items": { - "type": "string" - }, - "x-order": 17 - }, "metrics_resolutions": { "description": "MetricsResolutions represents Prometheus exporters metrics resolutions.", "type": "object", diff --git a/api/serverpb/server.pb.go b/api/serverpb/server.pb.go index fde1a23454..427155434a 100644 --- a/api/serverpb/server.pb.go +++ b/api/serverpb/server.pb.go @@ -1031,8 +1031,6 @@ type Settings struct { EnableAccessControl bool `protobuf:"varint,21,opt,name=enable_access_control,json=enableAccessControl,proto3" json:"enable_access_control,omitempty"` // Default Access Control role ID for new users. DefaultRoleId uint32 `protobuf:"varint,22,opt,name=default_role_id,json=defaultRoleId,proto3" json:"default_role_id,omitempty"` - // Contains all encrypted tables in format 'db.table.column'. - EncryptedItems []string `protobuf:"bytes,23,rep,name=encrypted_items,json=encryptedItems,proto3" json:"encrypted_items,omitempty"` } func (x *Settings) Reset() { @@ -1186,13 +1184,6 @@ func (x *Settings) GetDefaultRoleId() uint32 { return 0 } -func (x *Settings) GetEncryptedItems() []string { - if x != nil { - return x.EncryptedItems - } - return nil -} - type GetSettingsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1751,7 +1742,7 @@ var file_serverpb_server_proto_rawDesc = []byte{ 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x66, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22, 0x90, 0x07, + 0x71, 0x75, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22, 0xe7, 0x06, 0x0a, 0x08, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x44, 0x69, 0x73, @@ -1804,214 +1795,211 @@ var file_serverpb_server_proto_rawDesc = []byte{ 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, - 0x27, 0x0a, 0x0f, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x74, 0x65, - 0x6d, 0x73, 0x18, 0x17, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, - 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x0d, 0x10, 0x0e, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f, - 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, + 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x4a, + 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x0d, 0x10, + 0x0e, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f, 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, + 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x22, 0xce, 0x08, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x1c, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x65, + 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x10, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x65, 0x6c, 0x65, 0x6d, + 0x65, 0x74, 0x72, 0x79, 0x12, 0x4b, 0x0a, 0x13, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, + 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x40, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x73, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x0e, + 0x61, 0x77, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x77, 0x73, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, + 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, + 0x74, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, + 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x53, 0x74, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x6c, + 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x0a, 0x10, + 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, + 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x6d, 0x6d, 0x5f, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x13, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x6d, 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, + 0x70, 0x6d, 0x6d, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x50, 0x6d, 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x49, 0x0a, 0x13, 0x73, 0x74, 0x74, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x54, 0x54, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x11, 0x73, 0x74, 0x74, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, + 0x76, 0x65, 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x33, + 0x0a, 0x15, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x64, + 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, + 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, + 0x76, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x18, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, + 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3a, 0x0a, + 0x19, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x17, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x4d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x34, 0x0a, + 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, + 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, + 0x04, 0x08, 0x09, 0x10, 0x0a, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x4a, 0x04, 0x08, 0x0f, 0x10, + 0x10, 0x4a, 0x04, 0x08, 0x10, 0x10, 0x11, 0x4a, 0x04, 0x08, 0x11, 0x10, 0x12, 0x4a, 0x04, 0x08, + 0x12, 0x10, 0x13, 0x22, 0x46, 0x0a, 0x16, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0xce, 0x08, 0x0a, 0x15, - 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, - 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, - 0x1d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, - 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, - 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x65, 0x6c, 0x65, - 0x6d, 0x65, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x12, 0x4b, 0x0a, - 0x13, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, - 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, - 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x40, 0x0a, 0x0e, 0x64, 0x61, - 0x74, 0x61, 0x5f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x64, - 0x61, 0x74, 0x61, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, - 0x73, 0x73, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, - 0x73, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x77, 0x73, 0x5f, 0x70, 0x61, 0x72, - 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x61, - 0x77, 0x73, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x09, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x74, 0x12, 0x27, 0x0a, 0x0f, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x18, - 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x6c, 0x65, - 0x72, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, - 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x6d, 0x6d, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x6d, - 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x39, - 0x0a, 0x19, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x70, 0x6d, 0x6d, 0x5f, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6d, 0x6d, 0x50, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, 0x0a, 0x13, 0x73, 0x74, 0x74, - 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, - 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x53, 0x54, 0x54, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x73, 0x52, 0x11, 0x73, 0x74, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x76, 0x61, 0x6c, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, - 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x16, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x64, - 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x15, 0x64, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, - 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, - 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x18, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x4d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3a, 0x0a, 0x19, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, - 0x65, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x64, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x18, 0x1e, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x34, 0x0a, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, - 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x18, 0x1f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x4a, 0x04, 0x08, 0x07, - 0x10, 0x08, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x4a, 0x04, - 0x08, 0x0a, 0x10, 0x0b, 0x4a, 0x04, 0x08, 0x0f, 0x10, 0x10, 0x4a, 0x04, 0x08, 0x10, 0x10, 0x11, - 0x4a, 0x04, 0x08, 0x11, 0x10, 0x12, 0x4a, 0x04, 0x08, 0x12, 0x10, 0x13, 0x22, 0x46, 0x0a, 0x16, - 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x73, 0x22, 0x43, 0x0a, 0x17, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x28, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x22, 0x1a, 0x0a, 0x18, 0x41, 0x57, 0x53, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x66, 0x0a, 0x12, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x44, - 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x54, 0x48, - 0x4f, 0x44, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, - 0x44, 0x4f, 0x43, 0x4b, 0x45, 0x52, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x56, 0x46, 0x10, - 0x02, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4d, 0x49, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x5a, - 0x55, 0x52, 0x45, 0x10, 0x04, 0x12, 0x06, 0x0a, 0x02, 0x44, 0x4f, 0x10, 0x05, 0x32, 0xe5, 0x0c, - 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x92, 0x41, 0x27, 0x12, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x1a, 0x1c, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x9e, 0x02, 0x0a, 0x09, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, - 0x73, 0x12, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x69, - 0x6e, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdb, 0x01, 0x92, 0x41, 0xc5, 0x01, 0x12, 0x16, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x72, 0x65, 0x61, 0x64, - 0x69, 0x6e, 0x65, 0x73, 0x73, 0x1a, 0xaa, 0x01, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, - 0x61, 0x6e, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x20, - 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x20, - 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x79, 0x65, - 0x74, 0x2e, 0x20, 0x55, 0x73, 0x65, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x41, 0x50, 0x49, 0x20, - 0x66, 0x6f, 0x72, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x44, 0x6f, 0x63, 0x6b, 0x65, - 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, - 0x20, 0x66, 0x6f, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x62, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x75, 0x62, - 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x20, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, - 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, - 0x61, 0x64, 0x79, 0x7a, 0x12, 0xf7, 0x01, 0x0a, 0x11, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, - 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x20, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x65, 0x61, 0x6c, - 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x9c, 0x01, 0x92, 0x41, 0x79, 0x12, 0x10, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x4c, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x1a, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x20, - 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, - 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x20, 0x69, 0x6e, - 0x20, 0x61, 0x20, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x20, 0x52, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x69, 0x66, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x69, 0x73, 0x6e, - 0x27, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x6c, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0xa3, - 0x01, 0x0a, 0x0c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, - 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x58, 0x92, 0x41, 0x39, 0x12, - 0x0d, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x1a, 0x28, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, - 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x12, 0x90, 0x01, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, - 0x61, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x92, - 0x41, 0x29, 0x12, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x1a, 0x19, 0x53, 0x74, 0x61, 0x72, 0x74, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x16, 0x3a, 0x01, 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x73, 0x2f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x9d, 0x01, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x52, 0x92, 0x41, 0x32, 0x12, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x21, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, - 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, - 0x3a, 0x01, 0x2a, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, - 0x2f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, - 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x52, 0x92, 0x41, 0x34, 0x12, 0x0c, 0x47, 0x65, 0x74, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x1a, 0x24, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, - 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, - 0x01, 0x2a, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x2f, 0x47, 0x65, 0x74, 0x12, 0xa1, 0x01, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, - 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x50, 0x92, 0x41, 0x2f, 0x12, 0x0f, 0x43, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x1a, 0x1c, 0x43, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, - 0x3a, 0x01, 0x2a, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x2f, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0xaa, 0x01, 0x0a, 0x10, 0x41, 0x57, 0x53, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1f, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x53, 0x92, 0x41, 0x31, 0x12, 0x12, 0x41, 0x57, 0x53, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x1a, 0x1b, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x73, 0x20, 0x41, 0x57, 0x53, 0x20, 0x45, 0x43, 0x32, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x20, 0x49, 0x44, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, - 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x76, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x42, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x23, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, - 0x65, 0x72, 0x63, 0x6f, 0x6e, 0x61, 0x2f, 0x70, 0x6d, 0x6d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x70, 0x62, 0xa2, 0x02, 0x03, 0x53, 0x58, 0x58, 0xaa, 0x02, 0x06, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0xca, 0x02, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0xe2, - 0x02, 0x12, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x43, 0x0a, 0x17, 0x41, + 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, + 0x22, 0x1a, 0x0a, 0x18, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x66, 0x0a, 0x12, + 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, + 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x4f, 0x43, 0x4b, 0x45, 0x52, 0x10, 0x01, 0x12, + 0x07, 0x0a, 0x03, 0x4f, 0x56, 0x46, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4d, 0x49, 0x10, + 0x03, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x5a, 0x55, 0x52, 0x45, 0x10, 0x04, 0x12, 0x06, 0x0a, 0x02, + 0x44, 0x4f, 0x10, 0x05, 0x32, 0xe5, 0x0c, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, + 0x79, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x92, 0x41, 0x27, + 0x12, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x1c, 0x52, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, + 0x76, 0x31, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x9e, 0x02, 0x0a, 0x09, 0x52, + 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x61, 0x64, + 0x69, 0x6e, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdb, 0x01, + 0x92, 0x41, 0xc5, 0x01, 0x12, 0x16, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x1a, 0xaa, 0x01, 0x52, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, + 0x77, 0x68, 0x65, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x72, 0x65, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, + 0x65, 0x61, 0x64, 0x79, 0x20, 0x79, 0x65, 0x74, 0x2e, 0x20, 0x55, 0x73, 0x65, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x20, 0x41, 0x50, 0x49, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x20, 0x6f, + 0x66, 0x20, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x62, + 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x20, 0x72, + 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, + 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x7a, 0x12, 0xf7, 0x01, 0x0a, 0x11, + 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x12, 0x20, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4c, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4c, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9c, 0x01, 0x92, 0x41, 0x79, 0x12, 0x10, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x20, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x1a, 0x65, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x2e, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x20, 0x69, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, + 0x2f, 0x76, 0x31, 0x2f, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0xa3, 0x01, 0x0a, 0x0c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x58, 0x92, 0x41, 0x39, 0x12, 0x0d, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x73, 0x1a, 0x28, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x50, 0x4d, 0x4d, 0x20, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x90, 0x01, 0x0a, 0x0b, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x92, 0x41, 0x29, 0x12, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, + 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x1a, 0x19, 0x53, 0x74, 0x61, 0x72, 0x74, 0x73, 0x20, + 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, + 0x2f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x9d, + 0x01, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x52, 0x92, 0x41, 0x32, 0x12, + 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x21, + 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x3a, 0x01, 0x2a, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x9a, + 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1a, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x52, 0x92, 0x41, 0x34, 0x12, 0x0c, 0x47, 0x65, + 0x74, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x1a, 0x24, 0x52, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x73, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x4d, 0x4d, 0x20, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, 0x01, 0x2a, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2f, 0x47, 0x65, 0x74, 0x12, 0xa1, 0x01, 0x0a, 0x0e, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1d, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x50, 0x92, + 0x41, 0x2f, 0x12, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x1a, 0x1c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x50, 0x4d, 0x4d, + 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x3a, 0x01, 0x2a, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2f, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, + 0xaa, 0x01, 0x0a, 0x10, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x41, 0x57, + 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x41, + 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x53, 0x92, 0x41, 0x31, 0x12, 0x12, 0x41, 0x57, + 0x53, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x1a, 0x1b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x20, 0x41, 0x57, 0x53, 0x20, 0x45, 0x43, 0x32, + 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x49, 0x44, 0x2e, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x41, 0x57, 0x53, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x76, 0x0a, 0x0a, + 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x42, 0x0b, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x23, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x72, 0x63, 0x6f, 0x6e, 0x61, 0x2f, 0x70, 0x6d, + 0x6d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x70, 0x62, 0xa2, 0x02, + 0x03, 0x53, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0xca, 0x02, 0x06, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0xe2, 0x02, 0x12, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5c, + 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/serverpb/server.proto b/api/serverpb/server.proto index 05f13e1743..284d2ed481 100644 --- a/api/serverpb/server.proto +++ b/api/serverpb/server.proto @@ -169,8 +169,6 @@ message Settings { bool enable_access_control = 21; // Default Access Control role ID for new users. uint32 default_role_id = 22; - // Contains all encrypted tables in format 'db.table.column'. - repeated string encrypted_items = 23; } message GetSettingsRequest {} diff --git a/api/swagger/swagger-dev.json b/api/swagger/swagger-dev.json index 24aa623790..054efed28a 100644 --- a/api/swagger/swagger-dev.json +++ b/api/swagger/swagger-dev.json @@ -3262,14 +3262,6 @@ "type": "integer", "format": "int64", "x-order": 16 - }, - "encrypted_items": { - "description": "Contains all encrypted tables in format 'db.table.column'.", - "type": "array", - "items": { - "type": "string" - }, - "x-order": 17 } }, "x-order": 0 @@ -3463,14 +3455,6 @@ "type": "integer", "format": "int64", "x-order": 16 - }, - "encrypted_items": { - "description": "Contains all encrypted tables in format 'db.table.column'.", - "type": "array", - "items": { - "type": "string" - }, - "x-order": 17 } }, "x-order": 0 diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index f822cd7ea1..dd3b3b8091 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -418,14 +418,6 @@ "type": "integer", "format": "int64", "x-order": 16 - }, - "encrypted_items": { - "description": "Contains all encrypted tables in format 'db.table.column'.", - "type": "array", - "items": { - "type": "string" - }, - "x-order": 17 } }, "x-order": 0 @@ -619,14 +611,6 @@ "type": "integer", "format": "int64", "x-order": 16 - }, - "encrypted_items": { - "description": "Contains all encrypted tables in format 'db.table.column'.", - "type": "array", - "items": { - "type": "string" - }, - "x-order": 17 } }, "x-order": 0 From 5d0583b06f07ecb778f331e6098bdddb56c7a25d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 11 Jul 2024 13:03:01 +0200 Subject: [PATCH 151/215] PMM-13129 Workaround to create FB for now. Will be reverted. --- managed/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/managed/Makefile b/managed/Makefile index fa0ab06fa8..dcf35ec550 100644 --- a/managed/Makefile +++ b/managed/Makefile @@ -37,7 +37,6 @@ clean: ## Remove generated files release: ## Build pmm-managed release binaries env CGO_ENABLED=0 go build -v $(PMM_LD_FLAGS) -o $(PMM_RELEASE_PATH)/ ./cmd/... - $(PMM_RELEASE_PATH)/pmm-managed --version release-starlark: env CGO_ENABLED=0 go build -v $(PMM_LD_FLAGS) -o $(PMM_RELEASE_PATH)/ ./cmd/pmm-managed-starlark/... From 2af5bca264ab177e9cb811eb9fd595d3a3897a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 23 Jul 2024 10:58:48 +0200 Subject: [PATCH 152/215] PMM-13129 Fix connection checker dsn bug. --- managed/services/agents/connection_checker.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/managed/services/agents/connection_checker.go b/managed/services/agents/connection_checker.go index 447dbb9ee8..93e5c92a73 100644 --- a/managed/services/agents/connection_checker.go +++ b/managed/services/agents/connection_checker.go @@ -86,9 +86,9 @@ func (c *ConnectionChecker) CheckConnectionToService(ctx context.Context, q *ref return err } - var sanitizedDSN string + sanitizedDSN := request.Dsn for _, word := range redactWords(agent) { - sanitizedDSN = strings.ReplaceAll(request.Dsn, word, "****") + sanitizedDSN = strings.ReplaceAll(sanitizedDSN, word, "****") } l.Infof("CheckConnectionRequest: type: %s, DSN: %s timeout: %s.", request.Type, sanitizedDSN, request.Timeout) From 0788e74aff27fddbbc1cb3088d6054bce56c578c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 23 Jul 2024 12:10:40 +0200 Subject: [PATCH 153/215] PMM-13129 Another dsn bug. --- managed/services/agents/service_info_broker.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/managed/services/agents/service_info_broker.go b/managed/services/agents/service_info_broker.go index e8e961bbca..6802de66f8 100644 --- a/managed/services/agents/service_info_broker.go +++ b/managed/services/agents/service_info_broker.go @@ -154,9 +154,9 @@ func (c *ServiceInfoBroker) GetInfoFromService(ctx context.Context, q *reform.Qu return err } - var sanitizedDSN string + sanitizedDSN := request.Dsn for _, word := range redactWords(agent) { - sanitizedDSN = strings.ReplaceAll(request.Dsn, word, "****") + sanitizedDSN = strings.ReplaceAll(sanitizedDSN, word, "****") } l.Infof("ServiceInfoRequest: type: %s, DSN: %s timeout: %s.", request.Type, sanitizedDSN, request.Timeout) From fb76317862392a3b63e2c7ab1d1eda598c7a03a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 23 Jul 2024 12:11:25 +0200 Subject: [PATCH 154/215] PMM-13129 Add back decrypt after insert to fix connection checker. --- managed/models/agent_helpers.go | 9 ++++++--- managed/models/database.go | 2 ++ managed/models/encryption_helpers.go | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index 141f6e368f..eb677fc791 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -651,8 +651,9 @@ func CreateNodeExporter(q *reform.Querier, if err := q.Insert(&encryptedAgent); err != nil { return nil, errors.WithStack(err) } + agent := DecryptAgent(encryptedAgent) - return &encryptedAgent, nil + return &agent, nil } // CreateExternalExporterParams params for add external exporter. @@ -737,8 +738,9 @@ func CreateExternalExporter(q *reform.Querier, params *CreateExternalExporterPar if err := q.Insert(&encryptedAgent); err != nil { return nil, errors.WithStack(err) } + agent := DecryptAgent(encryptedAgent) - return &encryptedAgent, nil + return &agent, nil } // CreateAgentParams params for add common exporter. @@ -929,8 +931,9 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara if err := q.Insert(&encryptedAgent); err != nil { return nil, errors.WithStack(err) } + agent := DecryptAgent(encryptedAgent) - return &encryptedAgent, nil + return &agent, nil } // ChangeCommonAgentParams contains parameters that can be changed for all Agents. diff --git a/managed/models/database.go b/managed/models/database.go index 2e0f96d773..c95720d46a 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1146,7 +1146,9 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. } agentColumnsToEncrypt := []encryption.Column{ + {Name: "username"}, {Name: "password"}, + {Name: "aws_access_key"}, {Name: "aws_secret_key"}, {Name: "mongo_db_tls_options", CustomHandler: EncryptMongoDBOptionsHandler}, {Name: "azure_options", CustomHandler: EncryptAzureOptionsHandler}, diff --git a/managed/models/encryption_helpers.go b/managed/models/encryption_helpers.go index 5cf0510d5a..f3ae2e01a8 100644 --- a/managed/models/encryption_helpers.go +++ b/managed/models/encryption_helpers.go @@ -35,6 +35,14 @@ func DecryptAgent(agent Agent) Agent { } func agentEncryption(agent Agent, handler func(string) (string, error)) Agent { + if agent.Username != nil { + username, err := handler(*agent.Username) + if err != nil { + logrus.Warning(err) + } + agent.Username = &username + } + if agent.Password != nil { password, err := handler(*agent.Password) if err != nil { @@ -51,6 +59,14 @@ func agentEncryption(agent Agent, handler func(string) (string, error)) Agent { agent.AgentPassword = &agentPassword } + if agent.AWSAccessKey != nil { + awsAccessKey, err := handler(*agent.AWSAccessKey) + if err != nil { + logrus.Warning(err) + } + agent.AWSAccessKey = &awsAccessKey + } + if agent.AWSSecretKey != nil { awsSecretKey, err := handler(*agent.AWSSecretKey) if err != nil { From d70d472899297c72797f1ce4fc77558bc9fa9e2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 23 Jul 2024 13:59:48 +0200 Subject: [PATCH 155/215] PMM-13129 Update reduct words. --- managed/services/agents/agents.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/managed/services/agents/agents.go b/managed/services/agents/agents.go index 71ffbefadb..451b34f09b 100644 --- a/managed/services/agents/agents.go +++ b/managed/services/agents/agents.go @@ -109,6 +109,25 @@ func redactWords(agent *models.Agent) []string { words = append(words, s) } } + if agent.MySQLOptions != nil { + if s := agent.MySQLOptions.TLSKey; s != "" { + words = append(words, s) + } + } + if agent.PostgreSQLOptions != nil { + if s := agent.PostgreSQLOptions.SSLKey; s != "" { + words = append(words, s) + } + } + if agent.MongoDBOptions != nil { + if s := agent.MongoDBOptions.TLSCertificateKey; s != "" { + words = append(words, s) + } + if s := agent.MongoDBOptions.TLSCertificateKeyFilePassword; s != "" { + words = append(words, s) + } + } + return words } From 59299395a88745a9d22f761f45e94878f4b21637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 23 Jul 2024 14:16:50 +0200 Subject: [PATCH 156/215] PMM-13129 Fix for test after new redact word. --- managed/services/agents/mysql_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/services/agents/mysql_test.go b/managed/services/agents/mysql_test.go index 5a8a3b7b41..6cee95abeb 100644 --- a/managed/services/agents/mysql_test.go +++ b/managed/services/agents/mysql_test.go @@ -199,7 +199,7 @@ func TestMySQLdExporterConfigTablestatsGroupDisabled(t *testing.T) { "DATA_SOURCE_NAME=username:s3cur3 p@$$w0r4.@tcp(1.2.3.4:3306)/?timeout=1s&tls=custom", "HTTP_AUTH=pmm:agent-id", }, - RedactWords: []string{"s3cur3 p@$$w0r4."}, + RedactWords: []string{"s3cur3 p@$$w0r4.", "content-of-tls-key"}, TextFiles: map[string]string{ "tlsCa": "content-of-tls-ca", "tlsCert": "content-of-tls-cert", From 74edcb1cf89cd53e1ae0d3ef7461326378309f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 12 Sep 2024 10:46:07 +0200 Subject: [PATCH 157/215] PMM-13132 Basics. --- encryption-rotation/main.go | 105 +++++++++++++++++++++++++ managed/models/database.go | 75 ++++++++++-------- managed/utils/encryption/encryption.go | 39 +++++++-- 3 files changed, 177 insertions(+), 42 deletions(-) create mode 100644 encryption-rotation/main.go diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go new file mode 100644 index 0000000000..4db969bcfd --- /dev/null +++ b/encryption-rotation/main.go @@ -0,0 +1,105 @@ +// Copyright (C) 2024 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package main + +import ( + "log" + + "github.com/Percona-Lab/kingpin" + "github.com/percona/pmm/managed/models" + "github.com/percona/pmm/managed/utils/encryption" + "gopkg.in/reform.v1" + "gopkg.in/reform.v1/dialects/postgresql" +) + +func main() { + db, dbName := openDB() + err := rotateEncryptionKey(db, dbName) + if err != nil { + log.Panicf("Failed to rotate encryption key: %+v", err) + } +} + +func rotateEncryptionKey(db *reform.DB, params models.SetupDBParams) error { + return db.InTransaction(func(tx *reform.TX) error { + err := models.DecryptDB(tx, params.Name, models.AgentEncryptionColumns) + if err != nil { + return err + } + + err = encryption.RotateKey() + if err != nil { + return err + } + + err = models.EncryptDB(tx, params.Name, models.AgentEncryptionColumns) + if err != nil { + return err + } + + return nil + }) +} + +func openDB() (*reform.DB, models.SetupDBParams) { + postgresAddrF := kingpin.Flag("postgres-addr", "PostgreSQL address"). + Default(models.DefaultPostgreSQLAddr). + Envar("PMM_POSTGRES_ADDR"). + String() + postgresDBNameF := kingpin.Flag("postgres-name", "PostgreSQL database name"). + Default("pmm-managed"). + Envar("PMM_POSTGRES_DBNAME"). + String() + postgresDBUsernameF := kingpin.Flag("postgres-username", "PostgreSQL database username"). + Default("pmm-managed"). + Envar("PMM_POSTGRES_USERNAME"). + String() + postgresSSLModeF := kingpin.Flag("postgres-ssl-mode", "PostgreSQL SSL mode"). + Default(models.DisableSSLMode). + Envar("PMM_POSTGRES_SSL_MODE"). + Enum(models.DisableSSLMode, models.RequireSSLMode, models.VerifyCaSSLMode, models.VerifyFullSSLMode) + postgresSSLCAPathF := kingpin.Flag("postgres-ssl-ca-path", "PostgreSQL SSL CA root certificate path"). + Envar("PMM_POSTGRES_SSL_CA_PATH"). + String() + postgresDBPasswordF := kingpin.Flag("postgres-password", "PostgreSQL database password"). + Default("pmm-managed"). + Envar("PMM_POSTGRES_DBPASSWORD"). + String() + postgresSSLKeyPathF := kingpin.Flag("postgres-ssl-key-path", "PostgreSQL SSL key path"). + Envar("PMM_POSTGRES_SSL_KEY_PATH"). + String() + postgresSSLCertPathF := kingpin.Flag("postgres-ssl-cert-path", "PostgreSQL SSL certificate path"). + Envar("PMM_POSTGRES_SSL_CERT_PATH"). + String() + + setupParams := models.SetupDBParams{ + Address: *postgresAddrF, + Name: *postgresDBNameF, + Username: *postgresDBUsernameF, + Password: *postgresDBPasswordF, + SSLMode: *postgresSSLModeF, + SSLCAPath: *postgresSSLCAPathF, + SSLKeyPath: *postgresSSLKeyPathF, + SSLCertPath: *postgresSSLCertPathF, + } + + sqlDB, err := models.OpenDB(setupParams) + if err != nil { + log.Panicf("Failed to connect to database: %+v", err) + } + + return reform.NewDB(sqlDB, postgresql.Dialect, nil), *postgresDBNameF +} diff --git a/managed/models/database.go b/managed/models/database.go index d2c2066c7f..9359a11e93 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -61,6 +61,24 @@ const ( VerifyFullSSLMode string = "verify-full" ) +var AgentEncryptionColumns = []encryption.Table{ + { + Name: "agents", + Identifiers: []string{"agent_id"}, + Columns: []encryption.Column{ + {Name: "username"}, + {Name: "password"}, + {Name: "aws_access_key"}, + {Name: "aws_secret_key"}, + {Name: "mongo_db_tls_options", CustomHandler: EncryptMongoDBOptionsHandler}, + {Name: "azure_options", CustomHandler: EncryptAzureOptionsHandler}, + {Name: "mysql_options", CustomHandler: EncryptMySQLOptionsHandler}, + {Name: "postgresql_options", CustomHandler: EncryptPostgreSQLOptionsHandler}, + {Name: "agent_password"}, + }, + }, +} + // databaseSchema maps schema version from schema_migrations table (id column) to a slice of DDL queries. var databaseSchema = [][]string{ 1: { @@ -1149,27 +1167,7 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. return nil, errCV } - agentColumnsToEncrypt := []encryption.Column{ - {Name: "username"}, - {Name: "password"}, - {Name: "aws_access_key"}, - {Name: "aws_secret_key"}, - {Name: "mongo_db_tls_options", CustomHandler: EncryptMongoDBOptionsHandler}, - {Name: "azure_options", CustomHandler: EncryptAzureOptionsHandler}, - {Name: "mysql_options", CustomHandler: EncryptMySQLOptionsHandler}, - {Name: "postgresql_options", CustomHandler: EncryptPostgreSQLOptionsHandler}, - {Name: "agent_password"}, - } - - itemsToEncrypt := []encryption.Table{ - { - Name: "agents", - Identifiers: []string{"agent_id"}, - Columns: agentColumnsToEncrypt, - }, - } - - if err := migrateDB(db, params, itemsToEncrypt); err != nil { + if err := migrateDB(db, params, AgentEncryptionColumns); err != nil { return nil, err } @@ -1177,7 +1175,16 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. } // EncryptDB encrypts a set of columns in a specific database and table. -func EncryptDB(tx *reform.TX, params SetupDBParams, itemsToEncrypt []encryption.Table) error { +func EncryptDB(tx *reform.TX, database string, itemsToEncrypt []encryption.Table) error { + return dbEncryption(tx, database, itemsToEncrypt, encryption.EncryptItems) +} + +// DecryptDB decrypts a set of columns in a specific database and table. +func DecryptDB(tx *reform.TX, database string, itemsToEncrypt []encryption.Table) error { + return dbEncryption(tx, database, itemsToEncrypt, encryption.EncryptItems) +} + +func dbEncryption(tx *reform.TX, database string, itemsToEncrypt []encryption.Table, handler func(tx *reform.TX, tables []encryption.Table) error) error { if len(itemsToEncrypt) == 0 { return nil } @@ -1186,42 +1193,42 @@ func EncryptDB(tx *reform.TX, params SetupDBParams, itemsToEncrypt []encryption. if err != nil { return err } - alreadyEncrypted := make(map[string]bool) + currentColumns := make(map[string]bool) for _, v := range settings.EncryptedItems { - alreadyEncrypted[v] = true + currentColumns[v] = true } - notEncrypted := []encryption.Table{} - newlyEncrypted := []string{} + tables := []encryption.Table{} + prepared := []string{} for _, table := range itemsToEncrypt { columns := []encryption.Column{} for _, column := range table.Columns { - dbTableColumn := fmt.Sprintf("%s.%s.%s", params.Name, table.Name, column.Name) - if alreadyEncrypted[dbTableColumn] { + dbTableColumn := fmt.Sprintf("%s.%s.%s", database, table.Name, column.Name) + if currentColumns[dbTableColumn] { continue } columns = append(columns, column) - newlyEncrypted = append(newlyEncrypted, dbTableColumn) + prepared = append(prepared, dbTableColumn) } if len(columns) == 0 { continue } table.Columns = columns - notEncrypted = append(notEncrypted, table) + tables = append(tables, table) } - if len(notEncrypted) == 0 { + if len(tables) == 0 { return nil } - err = encryption.EncryptItems(tx, notEncrypted) + err = handler(tx, tables) if err != nil { return err } _, err = UpdateSettings(tx, &ChangeSettingsParams{ - EncryptedItems: slices.Concat(settings.EncryptedItems, newlyEncrypted), + EncryptedItems: slices.Concat(settings.EncryptedItems, prepared), }) if err != nil { return err @@ -1325,7 +1332,7 @@ func migrateDB(db *reform.DB, params SetupDBParams, itemsToEncrypt []encryption. } } - err := EncryptDB(tx, params, itemsToEncrypt) + err := EncryptDB(tx, params.Name, itemsToEncrypt) if err != nil { return err } diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index a7cba048df..fc9e1ab3bf 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -33,18 +33,13 @@ var ( // ErrEncryptionNotInitialized is error in case of encryption is not initialized. ErrEncryptionNotInitialized = errors.New("encryption is not initialized") // DefaultEncryption is the default implementation of encryption. - DefaultEncryption = New(DefaultEncryptionKeyPath) + DefaultEncryption = New() ) // New creates an encryption; if key on path doesn't exist, it will be generated. -func New(keyPath string) *Encryption { +func New() *Encryption { e := &Encryption{} - customKeyPath := os.Getenv("PMM_ENCRYPTION_KEY_PATH") - if customKeyPath != "" { - e.Path = customKeyPath - } else { - e.Path = keyPath - } + e.Path = getEncryptionKeyPath() bytes, err := os.ReadFile(e.Path) switch { @@ -68,6 +63,34 @@ func New(keyPath string) *Encryption { return e } +func getEncryptionKeyPath() string { + customKeyPath := os.Getenv("PMM_ENCRYPTION_KEY_PATH") + if customKeyPath != "" { + return customKeyPath + } + + return DefaultEncryptionKeyPath +} + +func removeKey() error { + err := os.Remove(getEncryptionKeyPath()) + if err != nil { + return err + } + + return nil +} + +// RotateKey will generate new encryption key. +func RotateKey() error { + err := removeKey() + if err != nil { + return err + } + + return nil +} + // Encrypt is a wrapper around DefaultEncryption.Encrypt. func Encrypt(secret string) (string, error) { return DefaultEncryption.Encrypt(secret) From 3e62767d380d015f0ee6346ffe7f1205278f704b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 12 Sep 2024 11:24:46 +0200 Subject: [PATCH 158/215] PMM-13132 Some changes. --- encryption-rotation/main.go | 12 +++++++---- managed/utils/encryption/encryption.go | 30 +++++++++++++------------- managed/utils/encryption/helpers.go | 18 ++++++++++++++++ 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index 4db969bcfd..d566026408 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -16,6 +16,7 @@ package main import ( + "database/sql" "log" "github.com/Percona-Lab/kingpin" @@ -26,7 +27,10 @@ import ( ) func main() { - db, dbName := openDB() + sqlDB, dbName := openDB() + defer sqlDB.Close() + + db := reform.NewDB(sqlDB, postgresql.Dialect, nil) err := rotateEncryptionKey(db, dbName) if err != nil { log.Panicf("Failed to rotate encryption key: %+v", err) @@ -40,7 +44,7 @@ func rotateEncryptionKey(db *reform.DB, params models.SetupDBParams) error { return err } - err = encryption.RotateKey() + err = encryption.RotateEncryptionKey() if err != nil { return err } @@ -54,7 +58,7 @@ func rotateEncryptionKey(db *reform.DB, params models.SetupDBParams) error { }) } -func openDB() (*reform.DB, models.SetupDBParams) { +func openDB() (*sql.DB, models.SetupDBParams) { postgresAddrF := kingpin.Flag("postgres-addr", "PostgreSQL address"). Default(models.DefaultPostgreSQLAddr). Envar("PMM_POSTGRES_ADDR"). @@ -101,5 +105,5 @@ func openDB() (*reform.DB, models.SetupDBParams) { log.Panicf("Failed to connect to database: %+v", err) } - return reform.NewDB(sqlDB, postgresql.Dialect, nil), *postgresDBNameF + return sqlDB, *postgresDBNameF } diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index fc9e1ab3bf..0f9c67c832 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -20,6 +20,7 @@ import ( "encoding/base64" "os" "slices" + "sync" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -33,13 +34,14 @@ var ( // ErrEncryptionNotInitialized is error in case of encryption is not initialized. ErrEncryptionNotInitialized = errors.New("encryption is not initialized") // DefaultEncryption is the default implementation of encryption. - DefaultEncryption = New() + DefaultEncryption = New() + defaultEncryptionMtx sync.Mutex ) // New creates an encryption; if key on path doesn't exist, it will be generated. func New() *Encryption { e := &Encryption{} - e.Path = getEncryptionKeyPath() + e.Path = encryptionKeyPath() bytes, err := os.ReadFile(e.Path) switch { @@ -63,31 +65,29 @@ func New() *Encryption { return e } -func getEncryptionKeyPath() string { - customKeyPath := os.Getenv("PMM_ENCRYPTION_KEY_PATH") - if customKeyPath != "" { - return customKeyPath - } - - return DefaultEncryptionKeyPath -} - -func removeKey() error { - err := os.Remove(getEncryptionKeyPath()) +// RotateEncryptionKey is a wrapper around DefaultEncryption.RotateEncryptionKey. +func RotateEncryptionKey() error { + err := removeKey() if err != nil { return err } + defaultEncryptionMtx.Lock() + DefaultEncryption = New() + defaultEncryptionMtx.Unlock() + return nil } -// RotateKey will generate new encryption key. -func RotateKey() error { +// RotateEncryptionKey will generate new encryption key. +func (e *Encryption) RotateEncryptionKey() error { err := removeKey() if err != nil { return err } + e = New() + return nil } diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index c8c11ab4e3..26cf72746d 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -31,6 +31,24 @@ import ( "gopkg.in/reform.v1" ) +func encryptionKeyPath() string { + customKeyPath := os.Getenv("PMM_ENCRYPTION_KEY_PATH") + if customKeyPath != "" { + return customKeyPath + } + + return DefaultEncryptionKeyPath +} + +func removeKey() error { + err := os.Remove(encryptionKeyPath()) + if err != nil { + return err + } + + return nil +} + func prepareRowPointers(rows *sql.Rows) ([]any, error) { columnTypes, err := rows.ColumnTypes() if err != nil { From 97bed880229f85dbd4c4be1296671f4d003cf643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 12 Sep 2024 11:32:55 +0200 Subject: [PATCH 159/215] PMM-13132 Make format. --- encryption-rotation/main.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index d566026408..1b5f2bb13a 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -20,10 +20,11 @@ import ( "log" "github.com/Percona-Lab/kingpin" - "github.com/percona/pmm/managed/models" - "github.com/percona/pmm/managed/utils/encryption" "gopkg.in/reform.v1" "gopkg.in/reform.v1/dialects/postgresql" + + "github.com/percona/pmm/managed/models" + "github.com/percona/pmm/managed/utils/encryption" ) func main() { From 50fead62423c35100850b460e1baca5a60453a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 12 Sep 2024 11:46:20 +0200 Subject: [PATCH 160/215] PMM-13132 Mod fix, tidy. --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index 03ec2b5848..0afdbbb749 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/AlekSi/pointer v1.2.0 github.com/ClickHouse/clickhouse-go/v2 v2.23.0 github.com/DATA-DOG/go-sqlmock v1.5.0 + github.com/Percona-Lab/kingpin v2.2.6+incompatible github.com/alecthomas/kong v0.9.0 github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 github.com/aws/aws-sdk-go v1.55.3 diff --git a/go.sum b/go.sum index 3341cfbe23..739219c829 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,8 @@ github.com/Percona-Lab/go-grpc-prometheus v0.0.0-20230116133345-3487748d4592 h1: github.com/Percona-Lab/go-grpc-prometheus v0.0.0-20230116133345-3487748d4592/go.mod h1:xCJfGpj56ERA85Mj1VfBzoeWW4lZ00xXXkvG0LJQjZU= github.com/Percona-Lab/kingpin v2.2.6-percona+incompatible h1:N5oM40aAatvf8bCYjv69YsVdxJLIUhY/MerUG1jRL9Y= github.com/Percona-Lab/kingpin v2.2.6-percona+incompatible/go.mod h1:UC6j/e2eqpHBB/vn+5214ExsoDLiEo6BfUGBhbtf+x0= +github.com/Percona-Lab/kingpin v2.2.6+incompatible h1:i7fo0CKR6IGSxe9ErG2DMFz/shUK6vRigVfyQqOyWvs= +github.com/Percona-Lab/kingpin v2.2.6+incompatible/go.mod h1:UC6j/e2eqpHBB/vn+5214ExsoDLiEo6BfUGBhbtf+x0= github.com/Percona-Lab/spec v0.20.5-percona h1:ViCJVq52QIZxpP8/Nv4/nIed+WnqUirNjPtXvHhset4= github.com/Percona-Lab/spec v0.20.5-percona/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= From fcf215d4c4f42ff6cbb6034f7ccb2489bfc80be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 12 Sep 2024 12:02:33 +0200 Subject: [PATCH 161/215] PMM-13132 Fix. --- encryption-rotation/main.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index 1b5f2bb13a..9f9d090431 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -38,9 +38,9 @@ func main() { } } -func rotateEncryptionKey(db *reform.DB, params models.SetupDBParams) error { +func rotateEncryptionKey(db *reform.DB, dbName string) error { return db.InTransaction(func(tx *reform.TX) error { - err := models.DecryptDB(tx, params.Name, models.AgentEncryptionColumns) + err := models.DecryptDB(tx, dbName, models.AgentEncryptionColumns) if err != nil { return err } @@ -50,7 +50,7 @@ func rotateEncryptionKey(db *reform.DB, params models.SetupDBParams) error { return err } - err = models.EncryptDB(tx, params.Name, models.AgentEncryptionColumns) + err = models.EncryptDB(tx, dbName, models.AgentEncryptionColumns) if err != nil { return err } @@ -59,7 +59,7 @@ func rotateEncryptionKey(db *reform.DB, params models.SetupDBParams) error { }) } -func openDB() (*sql.DB, models.SetupDBParams) { +func openDB() (*sql.DB, string) { postgresAddrF := kingpin.Flag("postgres-addr", "PostgreSQL address"). Default(models.DefaultPostgreSQLAddr). Envar("PMM_POSTGRES_ADDR"). From 57a20248a01613d6e9b5ee822ea2d22ace0f0ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 12 Sep 2024 13:16:08 +0200 Subject: [PATCH 162/215] PMM-13132 Changes. --- encryption-rotation/main.go | 2 +- managed/models/database.go | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index 9f9d090431..97b4503886 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Percona LLC +// Copyright (C) 2023 Percona LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by diff --git a/managed/models/database.go b/managed/models/database.go index 9359a11e93..b49f360a48 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1176,15 +1176,27 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. // EncryptDB encrypts a set of columns in a specific database and table. func EncryptDB(tx *reform.TX, database string, itemsToEncrypt []encryption.Table) error { - return dbEncryption(tx, database, itemsToEncrypt, encryption.EncryptItems) + return dbEncryption(tx, database, itemsToEncrypt, encryption.EncryptItems, encryptionExists) } // DecryptDB decrypts a set of columns in a specific database and table. func DecryptDB(tx *reform.TX, database string, itemsToEncrypt []encryption.Table) error { - return dbEncryption(tx, database, itemsToEncrypt, encryption.EncryptItems) + return dbEncryption(tx, database, itemsToEncrypt, encryption.DecryptItems, encryptionNotExists) } -func dbEncryption(tx *reform.TX, database string, itemsToEncrypt []encryption.Table, handler func(tx *reform.TX, tables []encryption.Table) error) error { +func encryptionExists(m map[string]bool, key string) bool { + if m[key] { + return true + } + + return false +} + +func encryptionNotExists(m map[string]bool, key string) bool { + return !encryptionExists(m, key) +} + +func dbEncryption(tx *reform.TX, database string, itemsToEncrypt []encryption.Table, handler func(tx *reform.TX, tables []encryption.Table) error, check func(m map[string]bool, key string) bool) error { if len(itemsToEncrypt) == 0 { return nil } @@ -1204,7 +1216,7 @@ func dbEncryption(tx *reform.TX, database string, itemsToEncrypt []encryption.Ta columns := []encryption.Column{} for _, column := range table.Columns { dbTableColumn := fmt.Sprintf("%s.%s.%s", database, table.Name, column.Name) - if currentColumns[dbTableColumn] { + if check(currentColumns, dbTableColumn) { continue } From 937bbcda3c807f707902f7a4e498f7261ce38f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 12 Sep 2024 15:00:56 +0200 Subject: [PATCH 163/215] PMM-13132 Changes. --- encryption-rotation/main.go | 2 ++ managed/models/database.go | 8 ++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index 97b4503886..08267bbdba 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -90,6 +90,8 @@ func openDB() (*sql.DB, string) { Envar("PMM_POSTGRES_SSL_CERT_PATH"). String() + kingpin.Parse() + setupParams := models.SetupDBParams{ Address: *postgresAddrF, Name: *postgresDBNameF, diff --git a/managed/models/database.go b/managed/models/database.go index b49f360a48..24c51290d0 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1176,7 +1176,7 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. // EncryptDB encrypts a set of columns in a specific database and table. func EncryptDB(tx *reform.TX, database string, itemsToEncrypt []encryption.Table) error { - return dbEncryption(tx, database, itemsToEncrypt, encryption.EncryptItems, encryptionExists) + return dbEncryption(tx, database, itemsToEncrypt, encryption.EncryptItems, encryptionNotExists) } // DecryptDB decrypts a set of columns in a specific database and table. @@ -1185,11 +1185,7 @@ func DecryptDB(tx *reform.TX, database string, itemsToEncrypt []encryption.Table } func encryptionExists(m map[string]bool, key string) bool { - if m[key] { - return true - } - - return false + return m[key] } func encryptionNotExists(m map[string]bool, key string) bool { From 8961b3fc131eebe7c284ceace0a819b8a3e2477d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 16 Sep 2024 12:54:28 +0200 Subject: [PATCH 164/215] PMM-13132 Rotation. --- encryption-rotation/main.go | 1 + managed/models/database.go | 51 +++++++++++++++++++++++------- managed/models/settings_helpers.go | 5 ++- 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index 08267bbdba..5030533cd0 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -12,6 +12,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +// Package main package main diff --git a/managed/models/database.go b/managed/models/database.go index 24c51290d0..e64cec66b5 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1176,24 +1176,51 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. // EncryptDB encrypts a set of columns in a specific database and table. func EncryptDB(tx *reform.TX, database string, itemsToEncrypt []encryption.Table) error { - return dbEncryption(tx, database, itemsToEncrypt, encryption.EncryptItems, encryptionNotExists) -} - -// DecryptDB decrypts a set of columns in a specific database and table. -func DecryptDB(tx *reform.TX, database string, itemsToEncrypt []encryption.Table) error { - return dbEncryption(tx, database, itemsToEncrypt, encryption.DecryptItems, encryptionNotExists) + return dbEncryption(tx, database, itemsToEncrypt, encryption.EncryptItems, encryptionExists, addToEncryptedItems) } func encryptionExists(m map[string]bool, key string) bool { return m[key] } +func addToEncryptedItems(encryptedItems []string, items []string) []string { + return slices.Concat(encryptedItems, items) +} + +// DecryptDB decrypts a set of columns in a specific database and table. +func DecryptDB(tx *reform.TX, database string, itemsToEncrypt []encryption.Table) error { + return dbEncryption(tx, database, itemsToEncrypt, encryption.DecryptItems, encryptionNotExists, removeFromEncryptedItems) +} + func encryptionNotExists(m map[string]bool, key string) bool { return !encryptionExists(m, key) } -func dbEncryption(tx *reform.TX, database string, itemsToEncrypt []encryption.Table, handler func(tx *reform.TX, tables []encryption.Table) error, check func(m map[string]bool, key string) bool) error { - if len(itemsToEncrypt) == 0 { +func removeFromEncryptedItems(encryptedItems []string, items []string) []string { + res := []string{} + for _, encryptedItem := range encryptedItems { + exists := false + for _, item := range items { + if encryptedItem == item { + exists = true + } + } + + if exists { + continue + } + + res = append(res, encryptedItem) + } + + return res +} + +func dbEncryption(tx *reform.TX, database string, items []encryption.Table, + handler func(tx *reform.TX, tables []encryption.Table) error, + checkHandler func(m map[string]bool, key string) bool, + settingsHandler func(encryptedItems []string, items []string) []string) error { + if len(items) == 0 { return nil } @@ -1208,11 +1235,11 @@ func dbEncryption(tx *reform.TX, database string, itemsToEncrypt []encryption.Ta tables := []encryption.Table{} prepared := []string{} - for _, table := range itemsToEncrypt { + for _, table := range items { columns := []encryption.Column{} for _, column := range table.Columns { dbTableColumn := fmt.Sprintf("%s.%s.%s", database, table.Name, column.Name) - if check(currentColumns, dbTableColumn) { + if checkHandler(currentColumns, dbTableColumn) { continue } @@ -1226,17 +1253,17 @@ func dbEncryption(tx *reform.TX, database string, itemsToEncrypt []encryption.Ta table.Columns = columns tables = append(tables, table) } - if len(tables) == 0 { return nil } err = handler(tx, tables) if err != nil { + return err } _, err = UpdateSettings(tx, &ChangeSettingsParams{ - EncryptedItems: slices.Concat(settings.EncryptedItems, prepared), + EncryptedItems: settingsHandler(settings.EncryptedItems, prepared), }) if err != nil { return err diff --git a/managed/models/settings_helpers.go b/managed/models/settings_helpers.go index fb5125f336..9399d4196b 100644 --- a/managed/models/settings_helpers.go +++ b/managed/models/settings_helpers.go @@ -226,9 +226,8 @@ func UpdateSettings(q reform.DBTX, params *ChangeSettingsParams) (*Settings, err settings.DefaultRoleID = *params.DefaultRoleID } - if len(params.EncryptedItems) != 0 { - settings.EncryptedItems = params.EncryptedItems - } + settings.EncryptedItems = params.EncryptedItems + err = SaveSettings(q, settings) if err != nil { return nil, err From 0176934003aa56426f705b992bb4628d6a483379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 16 Sep 2024 13:06:54 +0200 Subject: [PATCH 165/215] PMM-13132 Format. --- managed/models/database.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index e64cec66b5..943e6e9094 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1219,7 +1219,8 @@ func removeFromEncryptedItems(encryptedItems []string, items []string) []string func dbEncryption(tx *reform.TX, database string, items []encryption.Table, handler func(tx *reform.TX, tables []encryption.Table) error, checkHandler func(m map[string]bool, key string) bool, - settingsHandler func(encryptedItems []string, items []string) []string) error { + settingsHandler func(encryptedItems []string, items []string) []string, +) error { if len(items) == 0 { return nil } @@ -1259,7 +1260,6 @@ func dbEncryption(tx *reform.TX, database string, items []encryption.Table, err = handler(tx, tables) if err != nil { - return err } _, err = UpdateSettings(tx, &ChangeSettingsParams{ From 65d22344736b801cf1d0dc1ca70cddd9342a7ac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 16 Sep 2024 16:12:14 +0200 Subject: [PATCH 166/215] PMM-13132 Changes. --- encryption-rotation/main.go | 68 ++++++++++++++++++++++++++++++++++--- managed/models/database.go | 9 ++--- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index 5030533cd0..f601a8a104 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -12,13 +12,16 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -// Package main - +// Package main is the main package for encryption keys rotation. package main import ( "database/sql" + "errors" + "fmt" "log" + "os/exec" + "strings" "github.com/Percona-Lab/kingpin" "gopkg.in/reform.v1" @@ -30,13 +33,69 @@ import ( func main() { sqlDB, dbName := openDB() - defer sqlDB.Close() + defer sqlDB.Close() //nolint:errcheck db := reform.NewDB(sqlDB, postgresql.Dialect, nil) - err := rotateEncryptionKey(db, dbName) + + err := stopPMMServer() if err != nil { log.Panicf("Failed to rotate encryption key: %+v", err) } + + err = rotateEncryptionKey(db, dbName) + if err != nil { + log.Panicf("Failed to rotate encryption key: %+v", err) + } + + err = startPMMServer() + if err != nil { + log.Panicf("Failed to rotate encryption key: %+v", err) + } +} + +func startPMMServer() error { + if isPMMServerStatus("RUNNING") { + fmt.Println("PMM Server is already running.") + return nil + } + + cmd := exec.Command("supervisorctl", "start pmm-managed") + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%s: %s", err, output) + } + + if !isPMMServerStatus("RUNNING") { + return errors.New("cannot start pmm-managed") + } + + return nil +} + +func stopPMMServer() error { + if isPMMServerStatus("STOPPED") { + fmt.Println("PMM Server is already stopped.") + return nil + } + + cmd := exec.Command("supervisorctl", "stop pmm-managed") + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%s: %s", err, output) + } + + if !isPMMServerStatus("STOPPED") { + return errors.New("cannot stop pmm-managed") + } + + return nil +} + +func isPMMServerStatus(status string) bool { + cmd := exec.Command("supervisorctl", "status pmm-managed") + output, _ := cmd.CombinedOutput() + + return strings.Contains(string(output), strings.ToUpper(status)) } func rotateEncryptionKey(db *reform.DB, dbName string) error { @@ -53,6 +112,7 @@ func rotateEncryptionKey(db *reform.DB, dbName string) error { err = models.EncryptDB(tx, dbName, models.AgentEncryptionColumns) if err != nil { + // rollback old key return err } diff --git a/managed/models/database.go b/managed/models/database.go index 943e6e9094..9e87277916 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -61,7 +61,8 @@ const ( VerifyFullSSLMode string = "verify-full" ) -var AgentEncryptionColumns = []encryption.Table{ +// DefaultAgentEncryptionColumns contains all tables and it's columns to be encrypted in PMM Server DB. +var DefaultAgentEncryptionColumns = []encryption.Table{ { Name: "agents", Identifiers: []string{"agent_id"}, @@ -1167,7 +1168,7 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. return nil, errCV } - if err := migrateDB(db, params, AgentEncryptionColumns); err != nil { + if err := migrateDB(db, params, DefaultAgentEncryptionColumns); err != nil { return nil, err } @@ -1217,7 +1218,7 @@ func removeFromEncryptedItems(encryptedItems []string, items []string) []string } func dbEncryption(tx *reform.TX, database string, items []encryption.Table, - handler func(tx *reform.TX, tables []encryption.Table) error, + encryptionHandler func(tx *reform.TX, tables []encryption.Table) error, checkHandler func(m map[string]bool, key string) bool, settingsHandler func(encryptedItems []string, items []string) []string, ) error { @@ -1258,7 +1259,7 @@ func dbEncryption(tx *reform.TX, database string, items []encryption.Table, return nil } - err = handler(tx, tables) + err = encryptionHandler(tx, tables) if err != nil { return err } From 782e82f0bdc8a5d2ca6470bd07359653e9d083c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 16 Sep 2024 16:14:20 +0200 Subject: [PATCH 167/215] PMM-13132 Fix. --- encryption-rotation/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index f601a8a104..bc22658cb2 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -100,7 +100,7 @@ func isPMMServerStatus(status string) bool { func rotateEncryptionKey(db *reform.DB, dbName string) error { return db.InTransaction(func(tx *reform.TX) error { - err := models.DecryptDB(tx, dbName, models.AgentEncryptionColumns) + err := models.DecryptDB(tx, dbName, models.DefaultAgentEncryptionColumns) if err != nil { return err } @@ -110,7 +110,7 @@ func rotateEncryptionKey(db *reform.DB, dbName string) error { return err } - err = models.EncryptDB(tx, dbName, models.AgentEncryptionColumns) + err = models.EncryptDB(tx, dbName, models.DefaultAgentEncryptionColumns) if err != nil { // rollback old key return err From 2cb54e13f7357457251a2347c2fafe6c39d942b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 16 Sep 2024 16:50:51 +0200 Subject: [PATCH 168/215] PMM-13132 Backup and restore of previous key. --- encryption-rotation/main.go | 22 +++++++++++++--------- managed/utils/encryption/encryption.go | 16 ++++++++++++++-- managed/utils/encryption/helpers.go | 4 ++-- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index bc22658cb2..b465413701 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -17,13 +17,14 @@ package main import ( "database/sql" - "errors" "fmt" "log" + "os" "os/exec" "strings" "github.com/Percona-Lab/kingpin" + "github.com/pkg/errors" "gopkg.in/reform.v1" "gopkg.in/reform.v1/dialects/postgresql" @@ -39,30 +40,32 @@ func main() { err := stopPMMServer() if err != nil { - log.Panicf("Failed to rotate encryption key: %+v", err) + log.Printf("Failed to rotate encryption key: %+v", err) + os.Exit(1) } err = rotateEncryptionKey(db, dbName) if err != nil { - log.Panicf("Failed to rotate encryption key: %+v", err) + log.Printf("Failed to rotate encryption key: %+v", err) + os.Exit(1) } err = startPMMServer() if err != nil { - log.Panicf("Failed to rotate encryption key: %+v", err) + log.Printf("Failed to rotate encryption key: %+v", err) + os.Exit(1) } } func startPMMServer() error { if isPMMServerStatus("RUNNING") { - fmt.Println("PMM Server is already running.") return nil } cmd := exec.Command("supervisorctl", "start pmm-managed") output, err := cmd.CombinedOutput() if err != nil { - return fmt.Errorf("%s: %s", err, output) + return fmt.Errorf("%w: %s", err, output) } if !isPMMServerStatus("RUNNING") { @@ -74,14 +77,13 @@ func startPMMServer() error { func stopPMMServer() error { if isPMMServerStatus("STOPPED") { - fmt.Println("PMM Server is already stopped.") return nil } cmd := exec.Command("supervisorctl", "stop pmm-managed") output, err := cmd.CombinedOutput() if err != nil { - return fmt.Errorf("%s: %s", err, output) + return fmt.Errorf("%w: %s", err, output) } if !isPMMServerStatus("STOPPED") { @@ -112,7 +114,9 @@ func rotateEncryptionKey(db *reform.DB, dbName string) error { err = models.EncryptDB(tx, dbName, models.DefaultAgentEncryptionColumns) if err != nil { - // rollback old key + if e := encryption.RestoreOldEncryptionKey(); e != nil { + return errors.Wrap(e, e.Error()) + } return err } diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 0f9c67c832..ac9b3dc23b 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -18,8 +18,10 @@ package encryption import ( "encoding/base64" + "fmt" "os" "slices" + "strings" "sync" "github.com/pkg/errors" @@ -67,7 +69,7 @@ func New() *Encryption { // RotateEncryptionKey is a wrapper around DefaultEncryption.RotateEncryptionKey. func RotateEncryptionKey() error { - err := removeKey() + err := backupOldEncryptionKey() if err != nil { return err } @@ -81,7 +83,7 @@ func RotateEncryptionKey() error { // RotateEncryptionKey will generate new encryption key. func (e *Encryption) RotateEncryptionKey() error { - err := removeKey() + err := backupOldEncryptionKey() if err != nil { return err } @@ -91,6 +93,16 @@ func (e *Encryption) RotateEncryptionKey() error { return nil } +// RestoreOldEncryptionKey will restore previous backup during rotation. +func RestoreOldEncryptionKey() error { + err := os.Rename(fmt.Sprintf("%s_old.key", strings.TrimSuffix(encryptionKeyPath(), ".key")), encryptionKeyPath()) + if err != nil { + return err + } + + return nil +} + // Encrypt is a wrapper around DefaultEncryption.Encrypt. func Encrypt(secret string) (string, error) { return DefaultEncryption.Encrypt(secret) diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index 26cf72746d..6ad997d58b 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -40,8 +40,8 @@ func encryptionKeyPath() string { return DefaultEncryptionKeyPath } -func removeKey() error { - err := os.Remove(encryptionKeyPath()) +func backupOldEncryptionKey() error { + err := os.Rename(encryptionKeyPath(), fmt.Sprintf("%s_old.key", strings.TrimSuffix(encryptionKeyPath(), ".key"))) if err != nil { return err } From b0ddeab5637582029f080c24cd50d7a7cd4ba521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 16 Sep 2024 17:02:41 +0200 Subject: [PATCH 169/215] PMM-13132 Changes. --- managed/utils/encryption/encryption.go | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index ac9b3dc23b..c65b1ca918 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -67,7 +67,7 @@ func New() *Encryption { return e } -// RotateEncryptionKey is a wrapper around DefaultEncryption.RotateEncryptionKey. +// RotateEncryptionKey is will backup old encryption key and generate new one. func RotateEncryptionKey() error { err := backupOldEncryptionKey() if err != nil { @@ -81,18 +81,6 @@ func RotateEncryptionKey() error { return nil } -// RotateEncryptionKey will generate new encryption key. -func (e *Encryption) RotateEncryptionKey() error { - err := backupOldEncryptionKey() - if err != nil { - return err - } - - e = New() - - return nil -} - // RestoreOldEncryptionKey will restore previous backup during rotation. func RestoreOldEncryptionKey() error { err := os.Rename(fmt.Sprintf("%s_old.key", strings.TrimSuffix(encryptionKeyPath(), ".key")), encryptionKeyPath()) From ad3fec9b11310a854336ea8831aea4b59550e847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 16 Sep 2024 17:07:35 +0200 Subject: [PATCH 170/215] PMM-13132 Lint. --- encryption-rotation/main.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index b465413701..4eef355513 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -33,6 +33,10 @@ import ( ) func main() { + os.Exit(rotate()) +} + +func rotate() int { sqlDB, dbName := openDB() defer sqlDB.Close() //nolint:errcheck @@ -41,20 +45,22 @@ func main() { err := stopPMMServer() if err != nil { log.Printf("Failed to rotate encryption key: %+v", err) - os.Exit(1) + return 1 } err = rotateEncryptionKey(db, dbName) if err != nil { log.Printf("Failed to rotate encryption key: %+v", err) - os.Exit(1) + return 2 } err = startPMMServer() if err != nil { log.Printf("Failed to rotate encryption key: %+v", err) - os.Exit(1) + return 3 } + + return 0 } func startPMMServer() error { From 50f0c837e135d20c7ea9e006feee4e6f1724e46b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 18 Sep 2024 12:45:20 +0200 Subject: [PATCH 171/215] PMM-13132 Correct message. --- encryption-rotation/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index 4eef355513..a690ba4bbd 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -44,7 +44,7 @@ func rotate() int { err := stopPMMServer() if err != nil { - log.Printf("Failed to rotate encryption key: %+v", err) + log.Printf("Failed to stop PMM Server: %+v", err) return 1 } @@ -56,7 +56,7 @@ func rotate() int { err = startPMMServer() if err != nil { - log.Printf("Failed to rotate encryption key: %+v", err) + log.Printf("Failed to start PMM Server: %+v", err) return 3 } From 07edcc77e502b489a8aa9d2e8d666e2de0aef2ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 18 Sep 2024 13:03:56 +0200 Subject: [PATCH 172/215] PMM-13132 Changes related to tests. --- encryption-rotation/main.go | 8 ++-- encryption-rotation/main_test.go | 64 ++++++++++++++++++++++++++ managed/utils/encryption/encryption.go | 5 +- managed/utils/testdb/db.go | 9 ++-- 4 files changed, 75 insertions(+), 11 deletions(-) create mode 100644 encryption-rotation/main_test.go diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index a690ba4bbd..6934046666 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -33,13 +33,13 @@ import ( ) func main() { - os.Exit(rotate()) -} - -func rotate() int { sqlDB, dbName := openDB() defer sqlDB.Close() //nolint:errcheck + os.Exit(rotate(sqlDB, dbName)) +} + +func rotate(sqlDB *sql.DB, dbName string) int { db := reform.NewDB(sqlDB, postgresql.Dialect, nil) err := stopPMMServer() diff --git a/encryption-rotation/main_test.go b/encryption-rotation/main_test.go new file mode 100644 index 0000000000..de4bd5c596 --- /dev/null +++ b/encryption-rotation/main_test.go @@ -0,0 +1,64 @@ +// Copyright (C) 2023 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// Package main is the main package for encryption keys rotation. +package main + +import ( + "os" + "testing" + "time" + + "github.com/AlekSi/pointer" + "github.com/percona/pmm/managed/models" + "github.com/percona/pmm/managed/utils/encryption" + "github.com/percona/pmm/managed/utils/testdb" + "github.com/stretchr/testify/require" +) + +const ( + encryptionKeyTestPath = "/srv/pmm-encryption-rotation-test.key" + originEncryptionKey = `CMatkOIIEmQKWAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5EiIaIKDxOKZxwiJl5Hj6oPZ/unTzmAvfwHWzZ1Wli0vac15YGAEQARjGrZDiCCAB` + originUsernameHash = `AYxEFsZVZMH7UErzcQ8vbm3lVza//yRF6o/yTH7tcRD0PAwsESt6c/d0BzM=` + originPasswordHash = `AYxEFsa5GYg97cnETVOU/A7ZPjrG7A1je3qlu+g5pKI/uH2ndz3lzCaZwkU=` +) + +func TestEncryptionRotation(t *testing.T) { + db := testdb.Open(t, models.SkipFixtures, pointer.ToInt(88)) + defer db.Close() //nolint:errcheck + + encryption.DefaultEncryptionKeyPath = encryptionKeyTestPath + err := os.WriteFile(encryptionKeyTestPath, []byte(originEncryptionKey), 0644) + require.NoError(t, err) + + now := time.Now() + // Insert dummy encrypted agent in DB + _, err = db.Exec( + "INSERT INTO nodes (node_id, node_type, node_name, distro, node_model, az, address, created_at, updated_at) "+ + "VALUES ('1', 'generic', 'name', '', '', '', '', $1, $2)", + now, now) + require.NoError(t, err) + _, err = db.Exec( + "INSERT INTO services (service_id, service_type, service_name, node_id, environment, cluster, replication_set, socket, external_group, created_at, updated_at) "+ + "VALUES ('1', 'mysql', 'name', '1', '', '', '', '/var/run/mysqld/mysqld.sock', '', $1, $2)", + now, now) + require.NoError(t, err) + _, err = db.Exec( + "INSERT INTO agents (agent_id, agent_type, username, password, runs_on_node_id, pmm_agent_id, disabled, status, created_at, updated_at, tls, tls_skip_verify, max_query_length, query_examples_disabled, comments_parsing_disabled, max_query_log_size, table_count_tablestats_group_limit, rds_basic_metrics_disabled, rds_enhanced_metrics_disabled, push_metrics, expose_exporter) "+ + "VALUES ('1', 'pmm-agent', $1, $2, '1', NULL, false, '', $3, $4, false, false, 0, false, true, 0, 0, true, true, false, false)", + originEncryptionKey, originPasswordHash, now, now) + require.NoError(t, err) + + require.Equal(t, 0, rotate(db, testdb.TestDatabase)) +} diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index c65b1ca918..0fa122363e 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -29,10 +29,9 @@ import ( "gopkg.in/reform.v1" ) -// DefaultEncryptionKeyPath contains default PMM encryption key path. -const DefaultEncryptionKeyPath = "/srv/pmm-encryption.key" - var ( + // DefaultEncryptionKeyPath contains default PMM encryption key path. + DefaultEncryptionKeyPath = "/srv/pmm-encryption.key" // ErrEncryptionNotInitialized is error in case of encryption is not initialized. ErrEncryptionNotInitialized = errors.New("encryption is not initialized") // DefaultEncryption is the default implementation of encryption. diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index 2f6ee84cc7..9b496bd075 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -30,7 +30,8 @@ import ( const ( username, password = "postgres", "" - testDatabase = "pmm-managed-dev" + // TestDatabase contains name of pmm-managed DB used for tests. + TestDatabase = "pmm-managed-dev" ) // Open recreates testing PostgreSQL database and returns an open connection to it. @@ -46,15 +47,15 @@ func Open(tb testing.TB, setupFixtures models.SetupFixturesMode, migrationVersio db, err := models.OpenDB(setupParams) require.NoError(tb, err) - _, err = db.Exec(`DROP DATABASE IF EXISTS "` + testDatabase + `"`) + _, err = db.Exec(`DROP DATABASE IF EXISTS "` + TestDatabase + `"`) require.NoError(tb, err) - _, err = db.Exec(`CREATE DATABASE "` + testDatabase + `"`) + _, err = db.Exec(`CREATE DATABASE "` + TestDatabase + `"`) require.NoError(tb, err) err = db.Close() require.NoError(tb, err) - setupParams.Name = testDatabase + setupParams.Name = TestDatabase db, err = models.OpenDB(setupParams) require.NoError(tb, err) SetupDB(tb, db, setupFixtures, migrationVersion) From c8e275ff4760c9e172ea9bde9112948033abfc2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 18 Sep 2024 19:45:06 +0200 Subject: [PATCH 173/215] PMM-13132 Test for whole cycle. --- encryption-rotation/main_test.go | 85 ++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/encryption-rotation/main_test.go b/encryption-rotation/main_test.go index de4bd5c596..3a774bd463 100644 --- a/encryption-rotation/main_test.go +++ b/encryption-rotation/main_test.go @@ -16,15 +16,18 @@ package main import ( + "database/sql" "os" "testing" "time" "github.com/AlekSi/pointer" + "github.com/pkg/errors" + "github.com/stretchr/testify/require" + "github.com/percona/pmm/managed/models" "github.com/percona/pmm/managed/utils/encryption" "github.com/percona/pmm/managed/utils/testdb" - "github.com/stretchr/testify/require" ) const ( @@ -38,27 +41,91 @@ func TestEncryptionRotation(t *testing.T) { db := testdb.Open(t, models.SkipFixtures, pointer.ToInt(88)) defer db.Close() //nolint:errcheck - encryption.DefaultEncryptionKeyPath = encryptionKeyTestPath - err := os.WriteFile(encryptionKeyTestPath, []byte(originEncryptionKey), 0644) + err := createOriginEncryptionKey() + require.NoError(t, err) + + err = insertTestData(db) require.NoError(t, err) + statusCode := rotate(db, testdb.TestDatabase) + require.Equal(t, 0, statusCode) + + newEncryptionKey, err := os.ReadFile(encryptionKeyTestPath) + require.NoError(t, err) + require.NotEqual(t, newEncryptionKey, []byte(originEncryptionKey)) + + err = checkNewlyEncryptedData(db) + require.NoError(t, err) +} + +func createOriginEncryptionKey() error { + encryption.DefaultEncryptionKeyPath = encryptionKeyTestPath + return os.WriteFile(encryptionKeyTestPath, []byte(originEncryptionKey), 0o644) +} + +func insertTestData(db *sql.DB) error { + _, err := models.UpdateSettings(db, &models.ChangeSettingsParams{ + EncryptedItems: []string{"pmm-managed-dev.agents.username", "pmm-managed-dev.agents.password", "pmm-managed-dev.agents.aws_access_key", "pmm-managed-dev.agents.aws_secret_key", "pmm-managed-dev.agents.mongo_db_tls_options", "pmm-managed-dev.agents.azure_options", "pmm-managed-dev.agents.mysql_options", "pmm-managed-dev.agents.postgresql_options", "pmm-managed-dev.agents.agent_password"}, + }) + if err != nil { + return err + } + now := time.Now() - // Insert dummy encrypted agent in DB _, err = db.Exec( "INSERT INTO nodes (node_id, node_type, node_name, distro, node_model, az, address, created_at, updated_at) "+ "VALUES ('1', 'generic', 'name', '', '', '', '', $1, $2)", now, now) - require.NoError(t, err) + if err != nil { + return err + } _, err = db.Exec( "INSERT INTO services (service_id, service_type, service_name, node_id, environment, cluster, replication_set, socket, external_group, created_at, updated_at) "+ "VALUES ('1', 'mysql', 'name', '1', '', '', '', '/var/run/mysqld/mysqld.sock', '', $1, $2)", now, now) - require.NoError(t, err) + if err != nil { + return err + } _, err = db.Exec( "INSERT INTO agents (agent_id, agent_type, username, password, runs_on_node_id, pmm_agent_id, disabled, status, created_at, updated_at, tls, tls_skip_verify, max_query_length, query_examples_disabled, comments_parsing_disabled, max_query_log_size, table_count_tablestats_group_limit, rds_basic_metrics_disabled, rds_enhanced_metrics_disabled, push_metrics, expose_exporter) "+ "VALUES ('1', 'pmm-agent', $1, $2, '1', NULL, false, '', $3, $4, false, false, 0, false, true, 0, 0, true, true, false, false)", - originEncryptionKey, originPasswordHash, now, now) - require.NoError(t, err) + originUsernameHash, originPasswordHash, now, now) + if err != nil { + return err + } + + return nil +} + +func checkNewlyEncryptedData(db *sql.DB) error { + var newlyEncryptedUsername string + var newlyEncryptedPassword string + err := db.QueryRow(`SELECT username, password FROM agents WHERE agent_id = $1`, "1").Scan(&newlyEncryptedUsername, &newlyEncryptedPassword) + if err != nil { + return err + } + if newlyEncryptedUsername == originUsernameHash { + return errors.New("username hash not rotated properly") + } + if newlyEncryptedPassword == originPasswordHash { + return errors.New("password hash not rotated properly") + } + + username, err := encryption.Decrypt(newlyEncryptedUsername) + if err != nil { + return err + } + if username != "pmm-managed" { + return errors.New("username not properly decrypted") + } + + password, err := encryption.Decrypt(newlyEncryptedPassword) + if err != nil { + return err + } + if password != "pmm-managed" { + return errors.New("password not properly decrypted") + } - require.Equal(t, 0, rotate(db, testdb.TestDatabase)) + return nil } From 810743105087a68189e15c37e45172c0ff61df2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 18 Sep 2024 19:52:58 +0200 Subject: [PATCH 174/215] PMM-13132 Handle OS interuptions. --- encryption-rotation/main.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index 6934046666..a2a731abba 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -21,7 +21,9 @@ import ( "log" "os" "os/exec" + "os/signal" "strings" + "syscall" "github.com/Percona-Lab/kingpin" "github.com/pkg/errors" @@ -33,6 +35,8 @@ import ( ) func main() { + signal.Ignore(syscall.SIGINT, syscall.SIGTERM) // to prevent any interuptions during process + sqlDB, dbName := openDB() defer sqlDB.Close() //nolint:errcheck From a60eda2e7df40ae042d085f53b52189f0647340c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 18 Sep 2024 19:59:23 +0200 Subject: [PATCH 175/215] PMM-13132 Lint. --- encryption-rotation/main.go | 5 +++-- encryption-rotation/main_test.go | 11 +++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index a2a731abba..d14f5fe044 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -38,9 +38,10 @@ func main() { signal.Ignore(syscall.SIGINT, syscall.SIGTERM) // to prevent any interuptions during process sqlDB, dbName := openDB() - defer sqlDB.Close() //nolint:errcheck + statusCode := rotate(sqlDB, dbName) + sqlDB.Close() //nolint:errcheck - os.Exit(rotate(sqlDB, dbName)) + os.Exit(statusCode) } func rotate(sqlDB *sql.DB, dbName string) int { diff --git a/encryption-rotation/main_test.go b/encryption-rotation/main_test.go index 3a774bd463..9a2db101d4 100644 --- a/encryption-rotation/main_test.go +++ b/encryption-rotation/main_test.go @@ -12,7 +12,10 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . + // Package main is the main package for encryption keys rotation. +// +//nolint:dupword package main import ( @@ -32,9 +35,9 @@ import ( const ( encryptionKeyTestPath = "/srv/pmm-encryption-rotation-test.key" - originEncryptionKey = `CMatkOIIEmQKWAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5EiIaIKDxOKZxwiJl5Hj6oPZ/unTzmAvfwHWzZ1Wli0vac15YGAEQARjGrZDiCCAB` - originUsernameHash = `AYxEFsZVZMH7UErzcQ8vbm3lVza//yRF6o/yTH7tcRD0PAwsESt6c/d0BzM=` - originPasswordHash = `AYxEFsa5GYg97cnETVOU/A7ZPjrG7A1je3qlu+g5pKI/uH2ndz3lzCaZwkU=` + originEncryptionKey = `CMatkOIIEmQKWAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5EiIaIKDxOKZxwiJl5Hj6oPZ/unTzmAvfwHWzZ1Wli0vac15YGAEQARjGrZDiCCAB` //nolint:gosec + originUsernameHash = `AYxEFsZVZMH7UErzcQ8vbm3lVza//yRF6o/yTH7tcRD0PAwsESt6c/d0BzM=` //nolint:gosec + originPasswordHash = `AYxEFsa5GYg97cnETVOU/A7ZPjrG7A1je3qlu+g5pKI/uH2ndz3lzCaZwkU=` //nolint:gosec ) func TestEncryptionRotation(t *testing.T) { @@ -60,7 +63,7 @@ func TestEncryptionRotation(t *testing.T) { func createOriginEncryptionKey() error { encryption.DefaultEncryptionKeyPath = encryptionKeyTestPath - return os.WriteFile(encryptionKeyTestPath, []byte(originEncryptionKey), 0o644) + return os.WriteFile(encryptionKeyTestPath, []byte(originEncryptionKey), 0o600) } func insertTestData(db *sql.DB) error { From 3474974a4df788991755a379bfc82811b0e1cc2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 18 Sep 2024 20:13:51 +0200 Subject: [PATCH 176/215] PMM-13132 Lint. --- encryption-rotation/main_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/encryption-rotation/main_test.go b/encryption-rotation/main_test.go index 9a2db101d4..13940f51cc 100644 --- a/encryption-rotation/main_test.go +++ b/encryption-rotation/main_test.go @@ -35,9 +35,9 @@ import ( const ( encryptionKeyTestPath = "/srv/pmm-encryption-rotation-test.key" - originEncryptionKey = `CMatkOIIEmQKWAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5EiIaIKDxOKZxwiJl5Hj6oPZ/unTzmAvfwHWzZ1Wli0vac15YGAEQARjGrZDiCCAB` //nolint:gosec - originUsernameHash = `AYxEFsZVZMH7UErzcQ8vbm3lVza//yRF6o/yTH7tcRD0PAwsESt6c/d0BzM=` //nolint:gosec - originPasswordHash = `AYxEFsa5GYg97cnETVOU/A7ZPjrG7A1je3qlu+g5pKI/uH2ndz3lzCaZwkU=` //nolint:gosec + originEncryptionKey = `CMatkOIIEmQKWAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5EiIaIKDxOKZxwiJl5Hj6oPZ/unTzmAvfwHWzZ1Wli0vac15YGAEQARjGrZDiCCAB` + originUsernameHash = `AYxEFsZVZMH7UErzcQ8vbm3lVza//yRF6o/yTH7tcRD0PAwsESt6c/d0BzM=` + originPasswordHash = `AYxEFsa5GYg97cnETVOU/A7ZPjrG7A1je3qlu+g5pKI/uH2ndz3lzCaZwkU=` //nolint:gosec ) func TestEncryptionRotation(t *testing.T) { From 7e58301e343b242c387a25d871478a840f5be642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 18 Sep 2024 20:14:08 +0200 Subject: [PATCH 177/215] PMM-13132 Logger and logs. --- encryption-rotation/main.go | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index d14f5fe044..8d2b23a8f0 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -18,7 +18,6 @@ package main import ( "database/sql" "fmt" - "log" "os" "os/exec" "os/signal" @@ -27,16 +26,20 @@ import ( "github.com/Percona-Lab/kingpin" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "gopkg.in/reform.v1" "gopkg.in/reform.v1/dialects/postgresql" "github.com/percona/pmm/managed/models" "github.com/percona/pmm/managed/utils/encryption" + "github.com/percona/pmm/utils/logger" ) func main() { signal.Ignore(syscall.SIGINT, syscall.SIGTERM) // to prevent any interuptions during process + logger.SetupGlobalLogger() + sqlDB, dbName := openDB() statusCode := rotate(sqlDB, dbName) sqlDB.Close() //nolint:errcheck @@ -49,20 +52,20 @@ func rotate(sqlDB *sql.DB, dbName string) int { err := stopPMMServer() if err != nil { - log.Printf("Failed to stop PMM Server: %+v", err) - return 1 + logrus.Errorf("Failed to stop PMM Server: %+v", err) + return 2 } err = rotateEncryptionKey(db, dbName) if err != nil { - log.Printf("Failed to rotate encryption key: %+v", err) - return 2 + logrus.Errorf("Failed to rotate encryption key: %+v", err) + return 3 } err = startPMMServer() if err != nil { - log.Printf("Failed to start PMM Server: %+v", err) - return 3 + logrus.Errorf("Failed to start PMM Server: %+v", err) + return 4 } return 0 @@ -113,16 +116,21 @@ func isPMMServerStatus(status string) bool { func rotateEncryptionKey(db *reform.DB, dbName string) error { return db.InTransaction(func(tx *reform.TX) error { + logrus.Infof("DB is being decrypted") err := models.DecryptDB(tx, dbName, models.DefaultAgentEncryptionColumns) if err != nil { return err } + logrus.Infof("DB is successfully decrypted") + logrus.Infof("Rotating encryption key") err = encryption.RotateEncryptionKey() if err != nil { return err } + logrus.Infof("New encryption key generated") + logrus.Infof("DB is being encrypted") err = models.EncryptDB(tx, dbName, models.DefaultAgentEncryptionColumns) if err != nil { if e := encryption.RestoreOldEncryptionKey(); e != nil { @@ -130,6 +138,7 @@ func rotateEncryptionKey(db *reform.DB, dbName string) error { } return err } + logrus.Infof("DB is successfully encrypted") return nil }) @@ -181,7 +190,8 @@ func openDB() (*sql.DB, string) { sqlDB, err := models.OpenDB(setupParams) if err != nil { - log.Panicf("Failed to connect to database: %+v", err) + logrus.Errorf("Failed to connect to database: %+v", err) + os.Exit(1) } return sqlDB, *postgresDBNameF From e8f94bf32a85fee0e987b224212f6121aacd24e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 18 Sep 2024 20:32:08 +0200 Subject: [PATCH 178/215] PMM-13132 Test DB. --- encryption-rotation/main_test.go | 2 +- managed/utils/testdb/db.go | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/encryption-rotation/main_test.go b/encryption-rotation/main_test.go index 13940f51cc..89a1b5b0b2 100644 --- a/encryption-rotation/main_test.go +++ b/encryption-rotation/main_test.go @@ -50,7 +50,7 @@ func TestEncryptionRotation(t *testing.T) { err = insertTestData(db) require.NoError(t, err) - statusCode := rotate(db, testdb.TestDatabase) + statusCode := rotate(db, "pmm-managed-dev") require.Equal(t, 0, statusCode) newEncryptionKey, err := os.ReadFile(encryptionKeyTestPath) diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index 9b496bd075..2f6ee84cc7 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -30,8 +30,7 @@ import ( const ( username, password = "postgres", "" - // TestDatabase contains name of pmm-managed DB used for tests. - TestDatabase = "pmm-managed-dev" + testDatabase = "pmm-managed-dev" ) // Open recreates testing PostgreSQL database and returns an open connection to it. @@ -47,15 +46,15 @@ func Open(tb testing.TB, setupFixtures models.SetupFixturesMode, migrationVersio db, err := models.OpenDB(setupParams) require.NoError(tb, err) - _, err = db.Exec(`DROP DATABASE IF EXISTS "` + TestDatabase + `"`) + _, err = db.Exec(`DROP DATABASE IF EXISTS "` + testDatabase + `"`) require.NoError(tb, err) - _, err = db.Exec(`CREATE DATABASE "` + TestDatabase + `"`) + _, err = db.Exec(`CREATE DATABASE "` + testDatabase + `"`) require.NoError(tb, err) err = db.Close() require.NoError(tb, err) - setupParams.Name = TestDatabase + setupParams.Name = testDatabase db, err = models.OpenDB(setupParams) require.NoError(tb, err) SetupDB(tb, db, setupFixtures, migrationVersion) From 5fb3fe64781bb5088b0a947850186882c12822de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 18 Sep 2024 20:43:30 +0200 Subject: [PATCH 179/215] Revert "PMM-13132 Test DB." This reverts commit e8f94bf32a85fee0e987b224212f6121aacd24e5. --- encryption-rotation/main_test.go | 2 +- managed/utils/testdb/db.go | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/encryption-rotation/main_test.go b/encryption-rotation/main_test.go index 89a1b5b0b2..13940f51cc 100644 --- a/encryption-rotation/main_test.go +++ b/encryption-rotation/main_test.go @@ -50,7 +50,7 @@ func TestEncryptionRotation(t *testing.T) { err = insertTestData(db) require.NoError(t, err) - statusCode := rotate(db, "pmm-managed-dev") + statusCode := rotate(db, testdb.TestDatabase) require.Equal(t, 0, statusCode) newEncryptionKey, err := os.ReadFile(encryptionKeyTestPath) diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index 2f6ee84cc7..9b496bd075 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -30,7 +30,8 @@ import ( const ( username, password = "postgres", "" - testDatabase = "pmm-managed-dev" + // TestDatabase contains name of pmm-managed DB used for tests. + TestDatabase = "pmm-managed-dev" ) // Open recreates testing PostgreSQL database and returns an open connection to it. @@ -46,15 +47,15 @@ func Open(tb testing.TB, setupFixtures models.SetupFixturesMode, migrationVersio db, err := models.OpenDB(setupParams) require.NoError(tb, err) - _, err = db.Exec(`DROP DATABASE IF EXISTS "` + testDatabase + `"`) + _, err = db.Exec(`DROP DATABASE IF EXISTS "` + TestDatabase + `"`) require.NoError(tb, err) - _, err = db.Exec(`CREATE DATABASE "` + testDatabase + `"`) + _, err = db.Exec(`CREATE DATABASE "` + TestDatabase + `"`) require.NoError(tb, err) err = db.Close() require.NoError(tb, err) - setupParams.Name = testDatabase + setupParams.Name = TestDatabase db, err = models.OpenDB(setupParams) require.NoError(tb, err) SetupDB(tb, db, setupFixtures, migrationVersion) From 977c64f03bbeebe775f6407b1585b95dfe3619ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Wed, 18 Sep 2024 21:23:13 +0200 Subject: [PATCH 180/215] PMM-13132 Changes, CI. --- .github/workflows/admin.yml | 1 + .github/workflows/agent.yml | 1 + .github/workflows/encryption-rotation.yml | 94 +++++++++++++++++++++++ .github/workflows/managed.yml | 1 + .github/workflows/qan-api2.yml | 1 + .github/workflows/ui.yml | 1 + .github/workflows/update.yml | 1 + .github/workflows/vmproxy.yml | 1 + encryption-rotation/main_test.go | 8 +- 9 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/encryption-rotation.yml diff --git a/.github/workflows/admin.yml b/.github/workflows/admin.yml index 588ce72241..d7e559ca63 100644 --- a/.github/workflows/admin.yml +++ b/.github/workflows/admin.yml @@ -14,6 +14,7 @@ on: - "agent/**" - "api-tests/**" - "docs/**" + - "encryption-rotation/**" - "managed/**" - "qan-api2/**" - "update/**" diff --git a/.github/workflows/agent.yml b/.github/workflows/agent.yml index 957d19720e..82b7cfe5bc 100644 --- a/.github/workflows/agent.yml +++ b/.github/workflows/agent.yml @@ -15,6 +15,7 @@ on: - "api-tests/**" - "cli-tests/**" - "docs/**" + - "encryption-rotation/**" - "managed/**" - "qan-api2/**" - "update/**" diff --git a/.github/workflows/encryption-rotation.yml b/.github/workflows/encryption-rotation.yml new file mode 100644 index 0000000000..b98dee96de --- /dev/null +++ b/.github/workflows/encryption-rotation.yml @@ -0,0 +1,94 @@ +name: 'Encryption Rotation Tool' + +on: + push: + branches: + - main + - v3 + - pmm-* + tags: + - v[0-9]+.[0-9]+.[0-9]+* + + pull_request: + paths-ignore: + - "admin/**" + - "agent/**" + - "api-tests/**" + - "cli-tests/**" + - "docs/**" + - "managed/**" + - "qan-api2/**" + - "update/**" + - "vmproxy/**" + - "ui/**" + +jobs: + test: + name: Tests + runs-on: ubuntu-22.04 + + continue-on-error: true + + env: + + defaults: + run: + working-directory: ${{ github.workspace }}/encryption-rotation + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go release + uses: actions/setup-go@v5 + with: + go-version-file: ${{ github.workspace }}/go.mod + cache: false + + - name: Enable Go build cache + uses: actions/cache@v4 + with: + path: ~/.cache/go-build + key: ${{ runner.os }}-go-build-${{ github.ref }}-${{ hashFiles('**') }} + restore-keys: | + ${{ runner.os }}-go-build-${{ github.ref }}- + ${{ runner.os }}-go-build- + + - name: Enable Go modules cache + uses: actions/cache@v4 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-modules-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-go-modules- + + - name: Download Go modules + run: go mod download -x + + - name: Build and install + run: make install + + - name: Launch containers + env: + ENV_UP_FLAGS: "--detach" + run: make env-up + + - name: Run tests + run: go test ./... + + - name: Upload coverage results + uses: codecov/codecov-action@v4 + with: + file: cover.out + flags: agent + env_vars: MYSQL_IMAGE,MONGO_IMAGE,POSTGRES_IMAGE,PMM_SERVER_IMAGE + fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Run debug commands on failure + if: ${{ failure() }} + run: | + echo "--- Environment variables ---" + env | sort + echo "--- GO Environment ---" + go env | sort + git status diff --git a/.github/workflows/managed.yml b/.github/workflows/managed.yml index 170bf9835c..66476ef61e 100644 --- a/.github/workflows/managed.yml +++ b/.github/workflows/managed.yml @@ -15,6 +15,7 @@ on: - 'api-tests/**' - 'cli-tests/**' - 'docs/**' + - "encryption-rotation/**" - 'qan-api2/**' - 'update/**' - 'vmproxy/**' diff --git a/.github/workflows/qan-api2.yml b/.github/workflows/qan-api2.yml index 2cee867f7f..8f1d00a6ae 100644 --- a/.github/workflows/qan-api2.yml +++ b/.github/workflows/qan-api2.yml @@ -16,6 +16,7 @@ on: - "api-tests/**" - "cli-tests/**" - "docs/**" + - "encryption-rotation/**" - "managed/**" - "update/**" - "vmproxy/**" diff --git a/.github/workflows/ui.yml b/.github/workflows/ui.yml index b243ef265e..2ca2d99678 100644 --- a/.github/workflows/ui.yml +++ b/.github/workflows/ui.yml @@ -15,6 +15,7 @@ on: - "api-tests/**" - "cli-tests/**" - "docs/**" + - "encryption-rotation/**" - "managed/**" - "managed-dev/**" - "qan-api2/**" diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index c0b325c860..f20e8b9cc7 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -15,6 +15,7 @@ on: - "api-tests/**" - "cli-tests/**" - "docs/**" + - "encryption-rotation/**" - "managed/**" - "qan-api2/**" - "vmproxy/**" diff --git a/.github/workflows/vmproxy.yml b/.github/workflows/vmproxy.yml index 94753ca9a6..f55754c6fd 100644 --- a/.github/workflows/vmproxy.yml +++ b/.github/workflows/vmproxy.yml @@ -16,6 +16,7 @@ on: - "api-tests/**" - "cli-tests/**" - "docs/**" + - "encryption-rotation/**" - "managed/**" - "qan-api2/**" - "update/**" diff --git a/encryption-rotation/main_test.go b/encryption-rotation/main_test.go index 13940f51cc..1a8a05c7af 100644 --- a/encryption-rotation/main_test.go +++ b/encryption-rotation/main_test.go @@ -36,8 +36,10 @@ import ( const ( encryptionKeyTestPath = "/srv/pmm-encryption-rotation-test.key" originEncryptionKey = `CMatkOIIEmQKWAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5EiIaIKDxOKZxwiJl5Hj6oPZ/unTzmAvfwHWzZ1Wli0vac15YGAEQARjGrZDiCCAB` - originUsernameHash = `AYxEFsZVZMH7UErzcQ8vbm3lVza//yRF6o/yTH7tcRD0PAwsESt6c/d0BzM=` - originPasswordHash = `AYxEFsa5GYg97cnETVOU/A7ZPjrG7A1je3qlu+g5pKI/uH2ndz3lzCaZwkU=` //nolint:gosec + // pmm-managed-username encrypted with originEncryptionKey + originUsernameHash = `AYxEFsbCFxg31sCqO4KlCsqASFYNeHapjT+vf8seEhsQrN5hWOCuvCSxd/ZERv8RODu3oX4=` + // pmm-managed-password encrypted with originEncryptionKey + originPasswordHash = `AYxEFsajO8X5rrXG4ocOEE4ltWuaNmy7Uz0GyDgZ/Q04O2biFah5IdkenQ9ehXwv+nyiwDw=` //nolint:gosec ) func TestEncryptionRotation(t *testing.T) { @@ -63,7 +65,7 @@ func TestEncryptionRotation(t *testing.T) { func createOriginEncryptionKey() error { encryption.DefaultEncryptionKeyPath = encryptionKeyTestPath - return os.WriteFile(encryptionKeyTestPath, []byte(originEncryptionKey), 0o600) + return os.WriteFile(encryptionKeyTestPath, []byte(originEncryptionKey), 0o644) } func insertTestData(db *sql.DB) error { From 7986a5b94d8d17d7f3f08f36d83cbbaa1d3a288e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 12:22:19 +0200 Subject: [PATCH 181/215] PMM-13132 Fix in test. --- encryption-rotation/main_test.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/encryption-rotation/main_test.go b/encryption-rotation/main_test.go index 1a8a05c7af..3f0d8270c4 100644 --- a/encryption-rotation/main_test.go +++ b/encryption-rotation/main_test.go @@ -37,9 +37,9 @@ const ( encryptionKeyTestPath = "/srv/pmm-encryption-rotation-test.key" originEncryptionKey = `CMatkOIIEmQKWAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5EiIaIKDxOKZxwiJl5Hj6oPZ/unTzmAvfwHWzZ1Wli0vac15YGAEQARjGrZDiCCAB` // pmm-managed-username encrypted with originEncryptionKey - originUsernameHash = `AYxEFsbCFxg31sCqO4KlCsqASFYNeHapjT+vf8seEhsQrN5hWOCuvCSxd/ZERv8RODu3oX4=` + originUsernameHash = `AYxEFsZsg7lp9+eSy6+wPFHlaNNy0ZpTbYN0NuCLPnQOZUYf2S6H9B+XJdF4+DscxC/pJwI=` // pmm-managed-password encrypted with originEncryptionKey - originPasswordHash = `AYxEFsajO8X5rrXG4ocOEE4ltWuaNmy7Uz0GyDgZ/Q04O2biFah5IdkenQ9ehXwv+nyiwDw=` //nolint:gosec + originPasswordHash = `AYxEFsZuL5xZb5IxGGh8NI6GrjDxCzFGxIcHe94UXcg+dnZphu7GQSgmZm633XvZ8CBU2wo=` //nolint:gosec ) func TestEncryptionRotation(t *testing.T) { @@ -65,7 +65,12 @@ func TestEncryptionRotation(t *testing.T) { func createOriginEncryptionKey() error { encryption.DefaultEncryptionKeyPath = encryptionKeyTestPath - return os.WriteFile(encryptionKeyTestPath, []byte(originEncryptionKey), 0o644) + err := os.WriteFile(encryptionKeyTestPath, []byte(originEncryptionKey), 0o600) + if err != nil { + return err + } + encryption.DefaultEncryption = encryption.New() + return nil } func insertTestData(db *sql.DB) error { @@ -120,7 +125,7 @@ func checkNewlyEncryptedData(db *sql.DB) error { if err != nil { return err } - if username != "pmm-managed" { + if username != "pmm-managed-username" { return errors.New("username not properly decrypted") } @@ -128,7 +133,7 @@ func checkNewlyEncryptedData(db *sql.DB) error { if err != nil { return err } - if password != "pmm-managed" { + if password != "pmm-managed-password" { return errors.New("password not properly decrypted") } From 236ade5051ac74594ea27e6097a0a1f396abc3f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 13:44:34 +0200 Subject: [PATCH 182/215] PMM-13132 Changes. --- .github/workflows/encryption-rotation.yml | 21 +++++++++------------ .github/workflows/main.yml | 2 ++ encryption-rotation/main.go | 12 ++++++------ 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/.github/workflows/encryption-rotation.yml b/.github/workflows/encryption-rotation.yml index b98dee96de..1da83e816a 100644 --- a/.github/workflows/encryption-rotation.yml +++ b/.github/workflows/encryption-rotation.yml @@ -1,5 +1,4 @@ name: 'Encryption Rotation Tool' - on: push: branches: @@ -24,16 +23,22 @@ on: jobs: test: - name: Tests + name: Encryption Rotation Test runs-on: ubuntu-22.04 + timeout-minutes: 10 + + defaults: + run: + working-directory: ${{ github.workspace }}/encryption-rotation continue-on-error: true env: + PMM_SERVER_IMAGE: perconalab/pmm-server:3-dev-latest defaults: run: - working-directory: ${{ github.workspace }}/encryption-rotation + working-directory: ${{ github.workspace }}/agent steps: - name: Check out code @@ -75,15 +80,6 @@ jobs: - name: Run tests run: go test ./... - - name: Upload coverage results - uses: codecov/codecov-action@v4 - with: - file: cover.out - flags: agent - env_vars: MYSQL_IMAGE,MONGO_IMAGE,POSTGRES_IMAGE,PMM_SERVER_IMAGE - fail_ci_if_error: false - token: ${{ secrets.CODECOV_TOKEN }} - - name: Run debug commands on failure if: ${{ failure() }} run: | @@ -92,3 +88,4 @@ jobs: echo "--- GO Environment ---" go env | sort git status + diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e7c38a3d46..854c7e00b4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,6 +10,8 @@ on: - v[0-9]+.[0-9]+.[0-9]+* pull_request: + paths-ignore: + - "encryption-rotation/**" jobs: check: diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index 8d2b23a8f0..2d54df4ceb 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -116,29 +116,29 @@ func isPMMServerStatus(status string) bool { func rotateEncryptionKey(db *reform.DB, dbName string) error { return db.InTransaction(func(tx *reform.TX) error { - logrus.Infof("DB is being decrypted") + logrus.Infof("DB %s is being decrypted", dbName) err := models.DecryptDB(tx, dbName, models.DefaultAgentEncryptionColumns) if err != nil { return err } - logrus.Infof("DB is successfully decrypted") + logrus.Infof("DB %s is successfully decrypted", dbName) - logrus.Infof("Rotating encryption key") + logrus.Infoln("Rotating encryption key") err = encryption.RotateEncryptionKey() if err != nil { return err } logrus.Infof("New encryption key generated") - logrus.Infof("DB is being encrypted") + logrus.Infof("DB %s is being encrypted", dbName) err = models.EncryptDB(tx, dbName, models.DefaultAgentEncryptionColumns) if err != nil { if e := encryption.RestoreOldEncryptionKey(); e != nil { - return errors.Wrap(e, e.Error()) + return errors.Wrap(err, e.Error()) } return err } - logrus.Infof("DB is successfully encrypted") + logrus.Infof("DB %s is successfully encrypted", dbName) return nil }) From d5e479d7bdc39eaba26d19265e468c6bc6d7f1a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 13:52:08 +0200 Subject: [PATCH 183/215] PMM-13132 Skip encryption-rotation test in main test. --- .github/workflows/main.yml | 2 -- Makefile.include | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 854c7e00b4..e7c38a3d46 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,8 +10,6 @@ on: - v[0-9]+.[0-9]+.[0-9]+* pull_request: - paths-ignore: - - "encryption-rotation/**" jobs: check: diff --git a/Makefile.include b/Makefile.include index 615f32251b..69835b303a 100644 --- a/Makefile.include +++ b/Makefile.include @@ -45,7 +45,7 @@ gen-mocks: ./bin/mockery --config .mockery.yaml test-common: ## Run tests from API (and other shared) packages only (i.e it ignores directories that are explicitly listed) - go test $(shell go list ./... | grep -v -e admin -e agent -e managed -e api-tests -e qan-api2 -e update) + go test $(shell go list ./... | grep -v -e admin -e agent -e managed -e api-tests -e qan-api2 -e update -e encryption-rotation) api-test: ## Run API tests on dev env. go test -count=1 -race -p 1 -v ./api-tests/... -pmm.server-insecure-tls From 7ff24b6467c3695c8b2f489ad4c297d448eee2e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 14:15:22 +0200 Subject: [PATCH 184/215] PMM-13132 Basic makefile for encryption-rotation. --- encryption-rotation/Makefile | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 encryption-rotation/Makefile diff --git a/encryption-rotation/Makefile b/encryption-rotation/Makefile new file mode 100644 index 0000000000..895b51d25a --- /dev/null +++ b/encryption-rotation/Makefile @@ -0,0 +1,16 @@ +default: run + +PMM_TEST_FLAGS ?= -timeout=180s +PMM_TEST_FILES ?= ./... + +run: + go run main.go + +release: + go build + +test: ## Run tests + go test $(PMM_TEST_FLAGS) -p 1 -race $(PMM_TEST_FILES) + +test-cover: ## Run tests and collect per-package coverage information + go test $(PMM_TEST_FLAGS) -p 1 -race -coverprofile=cover.out -covermode=atomic -coverpkg=$(PMM_TEST_FILES) $(PMM_TEST_FILES) From f59d89ca5cd2723ffbaa3b450703dc8b931054ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 14:32:07 +0200 Subject: [PATCH 185/215] PMM-13132 Remove duplicate defaults. --- .github/workflows/encryption-rotation.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/encryption-rotation.yml b/.github/workflows/encryption-rotation.yml index 1da83e816a..686d248b9d 100644 --- a/.github/workflows/encryption-rotation.yml +++ b/.github/workflows/encryption-rotation.yml @@ -36,10 +36,6 @@ jobs: env: PMM_SERVER_IMAGE: perconalab/pmm-server:3-dev-latest - defaults: - run: - working-directory: ${{ github.workspace }}/agent - steps: - name: Check out code uses: actions/checkout@v4 From 699f31e379547f986d3f3d5c9dd6b29001187e78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 14:50:17 +0200 Subject: [PATCH 186/215] PMM-13132 Changes in workflow. --- .github/workflows/encryption-rotation.yml | 5 +- encryption-rotation/Makefile | 8 ++ encryption-rotation/docker-compose.yml | 97 +++++++++++++++++++++++ 3 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 encryption-rotation/docker-compose.yml diff --git a/.github/workflows/encryption-rotation.yml b/.github/workflows/encryption-rotation.yml index 686d248b9d..1f8682b893 100644 --- a/.github/workflows/encryption-rotation.yml +++ b/.github/workflows/encryption-rotation.yml @@ -65,16 +65,13 @@ jobs: - name: Download Go modules run: go mod download -x - - name: Build and install - run: make install - - name: Launch containers env: ENV_UP_FLAGS: "--detach" run: make env-up - name: Run tests - run: go test ./... + run: make test - name: Run debug commands on failure if: ${{ failure() }} diff --git a/encryption-rotation/Makefile b/encryption-rotation/Makefile index 895b51d25a..fbcc7ff546 100644 --- a/encryption-rotation/Makefile +++ b/encryption-rotation/Makefile @@ -14,3 +14,11 @@ test: ## Run tests test-cover: ## Run tests and collect per-package coverage information go test $(PMM_TEST_FLAGS) -p 1 -race -coverprofile=cover.out -covermode=atomic -coverpkg=$(PMM_TEST_FILES) $(PMM_TEST_FILES) + +ifeq ($(PROFILES),) +PROFILES := 'pmm' +endif + +env-up: ## Start devcontainer + COMPOSE_PROFILES=$(PROFILES) \ + docker compose up -d --wait diff --git a/encryption-rotation/docker-compose.yml b/encryption-rotation/docker-compose.yml new file mode 100644 index 0000000000..b590b81315 --- /dev/null +++ b/encryption-rotation/docker-compose.yml @@ -0,0 +1,97 @@ +# version: '3.7' // Deprecated: see https://docs.docker.com/compose/compose-file/04-version-and-name/ +services: + pmm-server: + profiles: + - pmm + image: ${PMM_CONTAINER:-perconalab/pmm-server-fb:PR-3645-cbac5a6} + platform: linux/amd64 + # build: + # context: . + # args: + # PMM_SERVER_IMAGE: ${PMM_SERVER_IMAGE:-perconalab/pmm-server:3-dev-latest} + # dockerfile: ./.devcontainer/Dockerfile + container_name: pmm-server + hostname: pmm-server + networks: + - ${NETWORK:-default} + environment: + - AWS_ACCESS_KEY=${AWS_ACCESS_KEY} + - AWS_SECRET_KEY=${AWS_SECRET_KEY} + - REVIEWDOG_GITHUB_API_TOKEN=${REVIEWDOG_GITHUB_API_TOKEN} + - PMM_RELEASE_PATH=/root/go/bin + - PMM_ENABLE_ACCESS_CONTROL=${PMM_ENABLE_ACCESS_CONTROL:-0} + - PMM_DEV_VERSION_SERVICE_URL=${PMM_DEV_VERSION_SERVICE_URL} + - PMM_WATCHTOWER_HOST=${PMM_WATCHTOWER_HOST:-http://watchtower:8080} + - PMM_WATCHTOWER_TOKEN=${PMM_WATCHTOWER_TOKEN:-INSECURE_TOKEN} + - PMM_RELEASE_VERSION=3.0.0-alpha + # - PMM_DEV_VERSION_SERVICE_URL=http://localhost:11000 + # - PMM_DEV_PERCONA_PLATFORM_PUBLIC_KEY= + # - PMM_DEV_TELEMETRY_INTERVAL=10s + # - PMM_DEV_TELEMETRY_DISABLE_START_DELAY=1 + # - PMM_DEV_TELEMETRY_RETRY_BACKOFF=10s + # - PMM_CLICKHOUSE_ADDR=127.0.0.1:9000 + # - PMM_CLICKHOUSE_DATABASE=pmm + # - PMM_DEBUG=1 + # - PMM_DEV_ADVISOR_CHECKS_FILE=/srv/checks/local-checks.yml + # - PMM_POSTGRES_ADDR=pg + # - PMM_POSTGRES_DBNAME=pmm-managed + # - PMM_POSTGRES_USERNAME=pmm-managed + # - PMM_POSTGRES_DBPASSWORD=pmm-managed + # - PMM_POSTGRES_SSL_MODE=verify-full + # - PMM_POSTGRES_SSL_CA_PATH=/tmp/certs/root.crt + # - PMM_POSTGRES_SSL_KEY_PATH=/tmp/certs/pmm-managed.key + # - PMM_POSTGRES_SSL_CERT_PATH=/tmp/certs/pmm-managed.crt + # - PMM_DISABLE_BUILTIN_POSTGRES=1 + # - GF_DATABASE_SSL_MODE=verify-full + # - GF_DATABASE_CA_CERT_PATH=/tmp/certs/root.crt + # - GF_DATABASE_CLIENT_KEY_PATH=/tmp/certs/grafana.key + # - GF_DATABASE_CLIENT_CERT_PATH=/tmp/certs/grafana.crt + + extra_hosts: + - host.docker.internal:host-gateway + # - portal.localhost:${PORTAL_HOST:-host-gateway} + # - check.localhost:${PORTAL_CHECK_HOST:-host-gateway} + # - pmm.localhost:${PORTAL_PMM_HOST:-host-gateway} + # - check-dev.percona.com:${PORTAL_PMM_HOST:-host-gateway} + + # for delve + cap_add: + - SYS_PTRACE + security_opt: + - seccomp:unconfined + + # see https://github.com/golang/go/wiki/LinuxKernelSignalVectorBug#what-to-do + ulimits: + memlock: 67108864 + + ports: + - ${PMM_PORT_HTTP:-80}:8080 + - ${PMM_PORT_HTTPS:-443}:8443 + # For headless delve + - ${PMM_PORT_DELVE:-2345}:2345 + # PG + - ${PMM_PORT_PG:-15432}:5432 + # VM + - ${PMM_PORT_VM:-9090}:9090 + # CH + - ${PMM_PORT_CH_TCP:-11000}:9000 + - ${PMM_PORT_CH_HTTP:-11123}:8123 + volumes: + - ./:/root/go/src/github.com/percona/pmm + - ./Makefile.devcontainer:/root/go/src/github.com/percona/pmm/Makefile:ro # substitute Makefile in devcontainer + # caching + - go-modules:/root/go/pkg/mod + - root-cache:/root/.cache + # command: > + # bash -c " + # rm -rf /tmp/certs + # mkdir /tmp/certs + # cp -R /root/go/src/github.com/percona/pmm/managed/testdata/pg/certs/* /tmp/certs + # chown grafana:grafana /tmp/certs/* + # chmod 600 /tmp/certs/* + # /opt/entrypoint.sh + # " + +volumes: + go-modules: + root-cache: \ No newline at end of file From a047d3ee7cc070a5005d7bcbcd07a81d392f4a25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 14:53:41 +0200 Subject: [PATCH 187/215] PMM-13132 Remove devcontainer from makefile. --- encryption-rotation/docker-compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/encryption-rotation/docker-compose.yml b/encryption-rotation/docker-compose.yml index b590b81315..e7a9145af7 100644 --- a/encryption-rotation/docker-compose.yml +++ b/encryption-rotation/docker-compose.yml @@ -78,7 +78,6 @@ services: - ${PMM_PORT_CH_HTTP:-11123}:8123 volumes: - ./:/root/go/src/github.com/percona/pmm - - ./Makefile.devcontainer:/root/go/src/github.com/percona/pmm/Makefile:ro # substitute Makefile in devcontainer # caching - go-modules:/root/go/pkg/mod - root-cache:/root/.cache From 45125dfd0c0704306c99c96ec7e5304ec2ceed5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 14:57:15 +0200 Subject: [PATCH 188/215] PMM-13132 Add ENV variable for rotation key. --- .github/workflows/encryption-rotation.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/encryption-rotation.yml b/.github/workflows/encryption-rotation.yml index 1f8682b893..59a689e16f 100644 --- a/.github/workflows/encryption-rotation.yml +++ b/.github/workflows/encryption-rotation.yml @@ -35,6 +35,7 @@ jobs: env: PMM_SERVER_IMAGE: perconalab/pmm-server:3-dev-latest + PMM_ENCRYPTION_KEY_PATH: pmm-encryption.key steps: - name: Check out code From 6f4a525c91f19b183fa0660980a8423b5a620217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 15:01:43 +0200 Subject: [PATCH 189/215] PMM-13132 Add PG. --- encryption-rotation/docker-compose.yml | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/encryption-rotation/docker-compose.yml b/encryption-rotation/docker-compose.yml index e7a9145af7..313ab4a8be 100644 --- a/encryption-rotation/docker-compose.yml +++ b/encryption-rotation/docker-compose.yml @@ -81,15 +81,23 @@ services: # caching - go-modules:/root/go/pkg/mod - root-cache:/root/.cache - # command: > - # bash -c " - # rm -rf /tmp/certs - # mkdir /tmp/certs - # cp -R /root/go/src/github.com/percona/pmm/managed/testdata/pg/certs/* /tmp/certs - # chown grafana:grafana /tmp/certs/* - # chmod 600 /tmp/certs/* - # /opt/entrypoint.sh - # " + + postgres: + image: ${POSTGRES_IMAGE:-postgres:14} + container_name: pmm-agent_postgres + command: > + -c shared_preload_libraries='${PG_PRELOADED_LIBS:-pg_stat_statements}' + -c track_activity_query_size=2048 + -c pg_stat_statements.max=10000 + -c pg_stat_monitor.pgsm_query_max_len=10000 + -c pg_stat_statements.track=all + -c pg_stat_statements.save=off + -c track_io_timing=on + ports: + - "127.0.0.1:5432:5432" + environment: + - POSTGRES_USER=pmm-agent + - POSTGRES_PASSWORD=pmm-agent-password volumes: go-modules: From 5e6a3d83a07474f143d071e9776aa0f3921c7f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 15:06:16 +0200 Subject: [PATCH 190/215] PMM-13132 Remove user, pass in PG compose. --- encryption-rotation/docker-compose.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/encryption-rotation/docker-compose.yml b/encryption-rotation/docker-compose.yml index 313ab4a8be..532e9dacdd 100644 --- a/encryption-rotation/docker-compose.yml +++ b/encryption-rotation/docker-compose.yml @@ -95,9 +95,6 @@ services: -c track_io_timing=on ports: - "127.0.0.1:5432:5432" - environment: - - POSTGRES_USER=pmm-agent - - POSTGRES_PASSWORD=pmm-agent-password volumes: go-modules: From fbc86a866eab2e209d0919cc37e8d0850940ac5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 15:10:25 +0200 Subject: [PATCH 191/215] PMM-13132 Test of user. --- encryption-rotation/docker-compose.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/encryption-rotation/docker-compose.yml b/encryption-rotation/docker-compose.yml index 532e9dacdd..5c2b1aa5b2 100644 --- a/encryption-rotation/docker-compose.yml +++ b/encryption-rotation/docker-compose.yml @@ -95,6 +95,9 @@ services: -c track_io_timing=on ports: - "127.0.0.1:5432:5432" + environment: + - POSTGRES_USER=postgres + - POSTGRES_HOST_AUTH_METHOD=trust volumes: go-modules: From da0ff7581991ffe97662878034e8939bc7d0173b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 15:23:34 +0200 Subject: [PATCH 192/215] PMM-13132 Change path for test. --- encryption-rotation/main_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encryption-rotation/main_test.go b/encryption-rotation/main_test.go index 3f0d8270c4..ccb8968c69 100644 --- a/encryption-rotation/main_test.go +++ b/encryption-rotation/main_test.go @@ -34,7 +34,7 @@ import ( ) const ( - encryptionKeyTestPath = "/srv/pmm-encryption-rotation-test.key" + encryptionKeyTestPath = "pmm-encryption-rotation-test.key" originEncryptionKey = `CMatkOIIEmQKWAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5EiIaIKDxOKZxwiJl5Hj6oPZ/unTzmAvfwHWzZ1Wli0vac15YGAEQARjGrZDiCCAB` // pmm-managed-username encrypted with originEncryptionKey originUsernameHash = `AYxEFsZsg7lp9+eSy6+wPFHlaNNy0ZpTbYN0NuCLPnQOZUYf2S6H9B+XJdF4+DscxC/pJwI=` From ae7094ff9bec7c60d86b3e4ece114d59a5fc5c2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 15:45:42 +0200 Subject: [PATCH 193/215] PMM-13132 Test of simpler structure. --- .github/workflows/encryption-rotation.yml | 85 --------- encryption-rotation/Makefile | 24 --- encryption-rotation/docker-compose.yml | 104 ----------- encryption-rotation/helpers/helpers.go | 167 ++++++++++++++++++ encryption-rotation/main.go | 4 +- .../ecryption_rotation_test.go | 9 +- 6 files changed, 174 insertions(+), 219 deletions(-) delete mode 100644 .github/workflows/encryption-rotation.yml delete mode 100644 encryption-rotation/Makefile delete mode 100644 encryption-rotation/docker-compose.yml create mode 100644 encryption-rotation/helpers/helpers.go rename encryption-rotation/main_test.go => managed/utils/encryptionrotation/ecryption_rotation_test.go (94%) diff --git a/.github/workflows/encryption-rotation.yml b/.github/workflows/encryption-rotation.yml deleted file mode 100644 index 59a689e16f..0000000000 --- a/.github/workflows/encryption-rotation.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: 'Encryption Rotation Tool' -on: - push: - branches: - - main - - v3 - - pmm-* - tags: - - v[0-9]+.[0-9]+.[0-9]+* - - pull_request: - paths-ignore: - - "admin/**" - - "agent/**" - - "api-tests/**" - - "cli-tests/**" - - "docs/**" - - "managed/**" - - "qan-api2/**" - - "update/**" - - "vmproxy/**" - - "ui/**" - -jobs: - test: - name: Encryption Rotation Test - runs-on: ubuntu-22.04 - timeout-minutes: 10 - - defaults: - run: - working-directory: ${{ github.workspace }}/encryption-rotation - - continue-on-error: true - - env: - PMM_SERVER_IMAGE: perconalab/pmm-server:3-dev-latest - PMM_ENCRYPTION_KEY_PATH: pmm-encryption.key - - steps: - - name: Check out code - uses: actions/checkout@v4 - - - name: Set up Go release - uses: actions/setup-go@v5 - with: - go-version-file: ${{ github.workspace }}/go.mod - cache: false - - - name: Enable Go build cache - uses: actions/cache@v4 - with: - path: ~/.cache/go-build - key: ${{ runner.os }}-go-build-${{ github.ref }}-${{ hashFiles('**') }} - restore-keys: | - ${{ runner.os }}-go-build-${{ github.ref }}- - ${{ runner.os }}-go-build- - - - name: Enable Go modules cache - uses: actions/cache@v4 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-modules-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-go-modules- - - - name: Download Go modules - run: go mod download -x - - - name: Launch containers - env: - ENV_UP_FLAGS: "--detach" - run: make env-up - - - name: Run tests - run: make test - - - name: Run debug commands on failure - if: ${{ failure() }} - run: | - echo "--- Environment variables ---" - env | sort - echo "--- GO Environment ---" - go env | sort - git status - diff --git a/encryption-rotation/Makefile b/encryption-rotation/Makefile deleted file mode 100644 index fbcc7ff546..0000000000 --- a/encryption-rotation/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -default: run - -PMM_TEST_FLAGS ?= -timeout=180s -PMM_TEST_FILES ?= ./... - -run: - go run main.go - -release: - go build - -test: ## Run tests - go test $(PMM_TEST_FLAGS) -p 1 -race $(PMM_TEST_FILES) - -test-cover: ## Run tests and collect per-package coverage information - go test $(PMM_TEST_FLAGS) -p 1 -race -coverprofile=cover.out -covermode=atomic -coverpkg=$(PMM_TEST_FILES) $(PMM_TEST_FILES) - -ifeq ($(PROFILES),) -PROFILES := 'pmm' -endif - -env-up: ## Start devcontainer - COMPOSE_PROFILES=$(PROFILES) \ - docker compose up -d --wait diff --git a/encryption-rotation/docker-compose.yml b/encryption-rotation/docker-compose.yml deleted file mode 100644 index 5c2b1aa5b2..0000000000 --- a/encryption-rotation/docker-compose.yml +++ /dev/null @@ -1,104 +0,0 @@ -# version: '3.7' // Deprecated: see https://docs.docker.com/compose/compose-file/04-version-and-name/ -services: - pmm-server: - profiles: - - pmm - image: ${PMM_CONTAINER:-perconalab/pmm-server-fb:PR-3645-cbac5a6} - platform: linux/amd64 - # build: - # context: . - # args: - # PMM_SERVER_IMAGE: ${PMM_SERVER_IMAGE:-perconalab/pmm-server:3-dev-latest} - # dockerfile: ./.devcontainer/Dockerfile - container_name: pmm-server - hostname: pmm-server - networks: - - ${NETWORK:-default} - environment: - - AWS_ACCESS_KEY=${AWS_ACCESS_KEY} - - AWS_SECRET_KEY=${AWS_SECRET_KEY} - - REVIEWDOG_GITHUB_API_TOKEN=${REVIEWDOG_GITHUB_API_TOKEN} - - PMM_RELEASE_PATH=/root/go/bin - - PMM_ENABLE_ACCESS_CONTROL=${PMM_ENABLE_ACCESS_CONTROL:-0} - - PMM_DEV_VERSION_SERVICE_URL=${PMM_DEV_VERSION_SERVICE_URL} - - PMM_WATCHTOWER_HOST=${PMM_WATCHTOWER_HOST:-http://watchtower:8080} - - PMM_WATCHTOWER_TOKEN=${PMM_WATCHTOWER_TOKEN:-INSECURE_TOKEN} - - PMM_RELEASE_VERSION=3.0.0-alpha - # - PMM_DEV_VERSION_SERVICE_URL=http://localhost:11000 - # - PMM_DEV_PERCONA_PLATFORM_PUBLIC_KEY= - # - PMM_DEV_TELEMETRY_INTERVAL=10s - # - PMM_DEV_TELEMETRY_DISABLE_START_DELAY=1 - # - PMM_DEV_TELEMETRY_RETRY_BACKOFF=10s - # - PMM_CLICKHOUSE_ADDR=127.0.0.1:9000 - # - PMM_CLICKHOUSE_DATABASE=pmm - # - PMM_DEBUG=1 - # - PMM_DEV_ADVISOR_CHECKS_FILE=/srv/checks/local-checks.yml - # - PMM_POSTGRES_ADDR=pg - # - PMM_POSTGRES_DBNAME=pmm-managed - # - PMM_POSTGRES_USERNAME=pmm-managed - # - PMM_POSTGRES_DBPASSWORD=pmm-managed - # - PMM_POSTGRES_SSL_MODE=verify-full - # - PMM_POSTGRES_SSL_CA_PATH=/tmp/certs/root.crt - # - PMM_POSTGRES_SSL_KEY_PATH=/tmp/certs/pmm-managed.key - # - PMM_POSTGRES_SSL_CERT_PATH=/tmp/certs/pmm-managed.crt - # - PMM_DISABLE_BUILTIN_POSTGRES=1 - # - GF_DATABASE_SSL_MODE=verify-full - # - GF_DATABASE_CA_CERT_PATH=/tmp/certs/root.crt - # - GF_DATABASE_CLIENT_KEY_PATH=/tmp/certs/grafana.key - # - GF_DATABASE_CLIENT_CERT_PATH=/tmp/certs/grafana.crt - - extra_hosts: - - host.docker.internal:host-gateway - # - portal.localhost:${PORTAL_HOST:-host-gateway} - # - check.localhost:${PORTAL_CHECK_HOST:-host-gateway} - # - pmm.localhost:${PORTAL_PMM_HOST:-host-gateway} - # - check-dev.percona.com:${PORTAL_PMM_HOST:-host-gateway} - - # for delve - cap_add: - - SYS_PTRACE - security_opt: - - seccomp:unconfined - - # see https://github.com/golang/go/wiki/LinuxKernelSignalVectorBug#what-to-do - ulimits: - memlock: 67108864 - - ports: - - ${PMM_PORT_HTTP:-80}:8080 - - ${PMM_PORT_HTTPS:-443}:8443 - # For headless delve - - ${PMM_PORT_DELVE:-2345}:2345 - # PG - - ${PMM_PORT_PG:-15432}:5432 - # VM - - ${PMM_PORT_VM:-9090}:9090 - # CH - - ${PMM_PORT_CH_TCP:-11000}:9000 - - ${PMM_PORT_CH_HTTP:-11123}:8123 - volumes: - - ./:/root/go/src/github.com/percona/pmm - # caching - - go-modules:/root/go/pkg/mod - - root-cache:/root/.cache - - postgres: - image: ${POSTGRES_IMAGE:-postgres:14} - container_name: pmm-agent_postgres - command: > - -c shared_preload_libraries='${PG_PRELOADED_LIBS:-pg_stat_statements}' - -c track_activity_query_size=2048 - -c pg_stat_statements.max=10000 - -c pg_stat_monitor.pgsm_query_max_len=10000 - -c pg_stat_statements.track=all - -c pg_stat_statements.save=off - -c track_io_timing=on - ports: - - "127.0.0.1:5432:5432" - environment: - - POSTGRES_USER=postgres - - POSTGRES_HOST_AUTH_METHOD=trust - -volumes: - go-modules: - root-cache: \ No newline at end of file diff --git a/encryption-rotation/helpers/helpers.go b/encryption-rotation/helpers/helpers.go new file mode 100644 index 0000000000..f292d7a98b --- /dev/null +++ b/encryption-rotation/helpers/helpers.go @@ -0,0 +1,167 @@ +package helpers + +import ( + "database/sql" + "fmt" + "os" + "os/exec" + "strings" + + "github.com/Percona-Lab/kingpin" + "github.com/percona/pmm/managed/models" + "github.com/percona/pmm/managed/utils/encryption" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "gopkg.in/reform.v1" + "gopkg.in/reform.v1/dialects/postgresql" +) + +func Rotate(sqlDB *sql.DB, dbName string) int { + db := reform.NewDB(sqlDB, postgresql.Dialect, nil) + + err := stopPMMServer() + if err != nil { + logrus.Errorf("Failed to stop PMM Server: %+v", err) + return 2 + } + + err = rotateEncryptionKey(db, dbName) + if err != nil { + logrus.Errorf("Failed to rotate encryption key: %+v", err) + return 3 + } + + err = startPMMServer() + if err != nil { + logrus.Errorf("Failed to start PMM Server: %+v", err) + return 4 + } + + return 0 +} + +func startPMMServer() error { + if isPMMServerStatus("RUNNING") { + return nil + } + + cmd := exec.Command("supervisorctl", "start pmm-managed") + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%w: %s", err, output) + } + + if !isPMMServerStatus("RUNNING") { + return errors.New("cannot start pmm-managed") + } + + return nil +} + +func stopPMMServer() error { + if isPMMServerStatus("STOPPED") { + return nil + } + + cmd := exec.Command("supervisorctl", "stop pmm-managed") + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%w: %s", err, output) + } + + if !isPMMServerStatus("STOPPED") { + return errors.New("cannot stop pmm-managed") + } + + return nil +} + +func isPMMServerStatus(status string) bool { + cmd := exec.Command("supervisorctl", "status pmm-managed") + output, _ := cmd.CombinedOutput() + + return strings.Contains(string(output), strings.ToUpper(status)) +} + +func rotateEncryptionKey(db *reform.DB, dbName string) error { + return db.InTransaction(func(tx *reform.TX) error { + logrus.Infof("DB %s is being decrypted", dbName) + err := models.DecryptDB(tx, dbName, models.DefaultAgentEncryptionColumns) + if err != nil { + return err + } + logrus.Infof("DB %s is successfully decrypted", dbName) + + logrus.Infoln("Rotating encryption key") + err = encryption.RotateEncryptionKey() + if err != nil { + return err + } + logrus.Infof("New encryption key generated") + + logrus.Infof("DB %s is being encrypted", dbName) + err = models.EncryptDB(tx, dbName, models.DefaultAgentEncryptionColumns) + if err != nil { + if e := encryption.RestoreOldEncryptionKey(); e != nil { + return errors.Wrap(err, e.Error()) + } + return err + } + logrus.Infof("DB %s is successfully encrypted", dbName) + + return nil + }) +} + +func openDB() (*sql.DB, string) { + postgresAddrF := kingpin.Flag("postgres-addr", "PostgreSQL address"). + Default(models.DefaultPostgreSQLAddr). + Envar("PMM_POSTGRES_ADDR"). + String() + postgresDBNameF := kingpin.Flag("postgres-name", "PostgreSQL database name"). + Default("pmm-managed"). + Envar("PMM_POSTGRES_DBNAME"). + String() + postgresDBUsernameF := kingpin.Flag("postgres-username", "PostgreSQL database username"). + Default("pmm-managed"). + Envar("PMM_POSTGRES_USERNAME"). + String() + postgresSSLModeF := kingpin.Flag("postgres-ssl-mode", "PostgreSQL SSL mode"). + Default(models.DisableSSLMode). + Envar("PMM_POSTGRES_SSL_MODE"). + Enum(models.DisableSSLMode, models.RequireSSLMode, models.VerifyCaSSLMode, models.VerifyFullSSLMode) + postgresSSLCAPathF := kingpin.Flag("postgres-ssl-ca-path", "PostgreSQL SSL CA root certificate path"). + Envar("PMM_POSTGRES_SSL_CA_PATH"). + String() + postgresDBPasswordF := kingpin.Flag("postgres-password", "PostgreSQL database password"). + Default("pmm-managed"). + Envar("PMM_POSTGRES_DBPASSWORD"). + String() + postgresSSLKeyPathF := kingpin.Flag("postgres-ssl-key-path", "PostgreSQL SSL key path"). + Envar("PMM_POSTGRES_SSL_KEY_PATH"). + String() + postgresSSLCertPathF := kingpin.Flag("postgres-ssl-cert-path", "PostgreSQL SSL certificate path"). + Envar("PMM_POSTGRES_SSL_CERT_PATH"). + String() + + kingpin.Parse() + + setupParams := models.SetupDBParams{ + Address: *postgresAddrF, + Name: *postgresDBNameF, + Username: *postgresDBUsernameF, + Password: *postgresDBPasswordF, + SSLMode: *postgresSSLModeF, + SSLCAPath: *postgresSSLCAPathF, + SSLKeyPath: *postgresSSLKeyPathF, + SSLCertPath: *postgresSSLCertPathF, + } + + sqlDB, err := models.OpenDB(setupParams) + if err != nil { + logrus.Errorf("Failed to connect to database: %+v", err) + os.Exit(1) + } + + return sqlDB, *postgresDBNameF +} diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index 2d54df4ceb..75004cc441 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -41,13 +41,13 @@ func main() { logger.SetupGlobalLogger() sqlDB, dbName := openDB() - statusCode := rotate(sqlDB, dbName) + statusCode := Rotate(sqlDB, dbName) sqlDB.Close() //nolint:errcheck os.Exit(statusCode) } -func rotate(sqlDB *sql.DB, dbName string) int { +func Rotate(sqlDB *sql.DB, dbName string) int { db := reform.NewDB(sqlDB, postgresql.Dialect, nil) err := stopPMMServer() diff --git a/encryption-rotation/main_test.go b/managed/utils/encryptionrotation/ecryption_rotation_test.go similarity index 94% rename from encryption-rotation/main_test.go rename to managed/utils/encryptionrotation/ecryption_rotation_test.go index ccb8968c69..67654b98c2 100644 --- a/encryption-rotation/main_test.go +++ b/managed/utils/encryptionrotation/ecryption_rotation_test.go @@ -13,10 +13,10 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -// Package main is the main package for encryption keys rotation. +// Package encryptionrotation is the package for encryption keys rotation testing. // //nolint:dupword -package main +package encryptionrotation import ( "database/sql" @@ -28,13 +28,14 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/require" + encryptionRotation "github.com/percona/pmm/encryption-rotation/helpers" "github.com/percona/pmm/managed/models" "github.com/percona/pmm/managed/utils/encryption" "github.com/percona/pmm/managed/utils/testdb" ) const ( - encryptionKeyTestPath = "pmm-encryption-rotation-test.key" + encryptionKeyTestPath = "/srv/pmm-encryption-rotation-test.key" originEncryptionKey = `CMatkOIIEmQKWAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5EiIaIKDxOKZxwiJl5Hj6oPZ/unTzmAvfwHWzZ1Wli0vac15YGAEQARjGrZDiCCAB` // pmm-managed-username encrypted with originEncryptionKey originUsernameHash = `AYxEFsZsg7lp9+eSy6+wPFHlaNNy0ZpTbYN0NuCLPnQOZUYf2S6H9B+XJdF4+DscxC/pJwI=` @@ -52,7 +53,7 @@ func TestEncryptionRotation(t *testing.T) { err = insertTestData(db) require.NoError(t, err) - statusCode := rotate(db, testdb.TestDatabase) + statusCode := encryptionRotation.Rotate(db, testdb.TestDatabase) require.Equal(t, 0, statusCode) newEncryptionKey, err := os.ReadFile(encryptionKeyTestPath) From e3d146ae3c3bb9c50ab48c6d7dc90713da053f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 16:04:49 +0200 Subject: [PATCH 194/215] PMM-13132 Another changes in structure. --- encryption-rotation/main.go | 127 ++---------------- .../ecryption_rotation_test.go | 23 +--- .../models/encryption_rotation.go | 64 +-------- 3 files changed, 15 insertions(+), 199 deletions(-) rename managed/{utils/encryptionrotation => models}/ecryption_rotation_test.go (81%) rename encryption-rotation/helpers/helpers.go => managed/models/encryption_rotation.go (50%) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index 75004cc441..93453864cc 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -16,23 +16,14 @@ package main import ( - "database/sql" - "fmt" "os" - "os/exec" "os/signal" - "strings" "syscall" "github.com/Percona-Lab/kingpin" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "gopkg.in/reform.v1" - "gopkg.in/reform.v1/dialects/postgresql" - "github.com/percona/pmm/managed/models" - "github.com/percona/pmm/managed/utils/encryption" "github.com/percona/pmm/utils/logger" + "github.com/sirupsen/logrus" ) func main() { @@ -40,111 +31,19 @@ func main() { logger.SetupGlobalLogger() - sqlDB, dbName := openDB() - statusCode := Rotate(sqlDB, dbName) - sqlDB.Close() //nolint:errcheck - - os.Exit(statusCode) -} - -func Rotate(sqlDB *sql.DB, dbName string) int { - db := reform.NewDB(sqlDB, postgresql.Dialect, nil) - - err := stopPMMServer() - if err != nil { - logrus.Errorf("Failed to stop PMM Server: %+v", err) - return 2 - } - - err = rotateEncryptionKey(db, dbName) - if err != nil { - logrus.Errorf("Failed to rotate encryption key: %+v", err) - return 3 - } - - err = startPMMServer() - if err != nil { - logrus.Errorf("Failed to start PMM Server: %+v", err) - return 4 - } - - return 0 -} - -func startPMMServer() error { - if isPMMServerStatus("RUNNING") { - return nil - } - - cmd := exec.Command("supervisorctl", "start pmm-managed") - output, err := cmd.CombinedOutput() - if err != nil { - return fmt.Errorf("%w: %s", err, output) - } - - if !isPMMServerStatus("RUNNING") { - return errors.New("cannot start pmm-managed") - } - - return nil -} - -func stopPMMServer() error { - if isPMMServerStatus("STOPPED") { - return nil - } - - cmd := exec.Command("supervisorctl", "stop pmm-managed") - output, err := cmd.CombinedOutput() + sqlDB, err := models.OpenDB(setupParams()) if err != nil { - return fmt.Errorf("%w: %s", err, output) - } - - if !isPMMServerStatus("STOPPED") { - return errors.New("cannot stop pmm-managed") + logrus.Error(err) + os.Exit(1) } - return nil -} - -func isPMMServerStatus(status string) bool { - cmd := exec.Command("supervisorctl", "status pmm-managed") - output, _ := cmd.CombinedOutput() - - return strings.Contains(string(output), strings.ToUpper(status)) -} - -func rotateEncryptionKey(db *reform.DB, dbName string) error { - return db.InTransaction(func(tx *reform.TX) error { - logrus.Infof("DB %s is being decrypted", dbName) - err := models.DecryptDB(tx, dbName, models.DefaultAgentEncryptionColumns) - if err != nil { - return err - } - logrus.Infof("DB %s is successfully decrypted", dbName) - - logrus.Infoln("Rotating encryption key") - err = encryption.RotateEncryptionKey() - if err != nil { - return err - } - logrus.Infof("New encryption key generated") - - logrus.Infof("DB %s is being encrypted", dbName) - err = models.EncryptDB(tx, dbName, models.DefaultAgentEncryptionColumns) - if err != nil { - if e := encryption.RestoreOldEncryptionKey(); e != nil { - return errors.Wrap(err, e.Error()) - } - return err - } - logrus.Infof("DB %s is successfully encrypted", dbName) + statusCode := models.RotateEncryptionKey(sqlDB, "pmm-managed") + sqlDB.Close() //nolint:errcheck - return nil - }) + os.Exit(statusCode) } -func openDB() (*sql.DB, string) { +func setupParams() models.SetupDBParams { postgresAddrF := kingpin.Flag("postgres-addr", "PostgreSQL address"). Default(models.DefaultPostgreSQLAddr). Envar("PMM_POSTGRES_ADDR"). @@ -177,7 +76,7 @@ func openDB() (*sql.DB, string) { kingpin.Parse() - setupParams := models.SetupDBParams{ + return models.SetupDBParams{ Address: *postgresAddrF, Name: *postgresDBNameF, Username: *postgresDBUsernameF, @@ -187,12 +86,4 @@ func openDB() (*sql.DB, string) { SSLKeyPath: *postgresSSLKeyPathF, SSLCertPath: *postgresSSLCertPathF, } - - sqlDB, err := models.OpenDB(setupParams) - if err != nil { - logrus.Errorf("Failed to connect to database: %+v", err) - os.Exit(1) - } - - return sqlDB, *postgresDBNameF } diff --git a/managed/utils/encryptionrotation/ecryption_rotation_test.go b/managed/models/ecryption_rotation_test.go similarity index 81% rename from managed/utils/encryptionrotation/ecryption_rotation_test.go rename to managed/models/ecryption_rotation_test.go index 67654b98c2..b66b86277e 100644 --- a/managed/utils/encryptionrotation/ecryption_rotation_test.go +++ b/managed/models/ecryption_rotation_test.go @@ -1,22 +1,4 @@ -// Copyright (C) 2023 Percona LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -// Package encryptionrotation is the package for encryption keys rotation testing. -// -//nolint:dupword -package encryptionrotation +package models_test import ( "database/sql" @@ -28,7 +10,6 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/require" - encryptionRotation "github.com/percona/pmm/encryption-rotation/helpers" "github.com/percona/pmm/managed/models" "github.com/percona/pmm/managed/utils/encryption" "github.com/percona/pmm/managed/utils/testdb" @@ -53,7 +34,7 @@ func TestEncryptionRotation(t *testing.T) { err = insertTestData(db) require.NoError(t, err) - statusCode := encryptionRotation.Rotate(db, testdb.TestDatabase) + statusCode := models.RotateEncryptionKey(db, testdb.TestDatabase) require.Equal(t, 0, statusCode) newEncryptionKey, err := os.ReadFile(encryptionKeyTestPath) diff --git a/encryption-rotation/helpers/helpers.go b/managed/models/encryption_rotation.go similarity index 50% rename from encryption-rotation/helpers/helpers.go rename to managed/models/encryption_rotation.go index f292d7a98b..7c0023d748 100644 --- a/encryption-rotation/helpers/helpers.go +++ b/managed/models/encryption_rotation.go @@ -1,14 +1,11 @@ -package helpers +package models import ( "database/sql" "fmt" - "os" "os/exec" "strings" - "github.com/Percona-Lab/kingpin" - "github.com/percona/pmm/managed/models" "github.com/percona/pmm/managed/utils/encryption" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -16,7 +13,7 @@ import ( "gopkg.in/reform.v1/dialects/postgresql" ) -func Rotate(sqlDB *sql.DB, dbName string) int { +func RotateEncryptionKey(sqlDB *sql.DB, dbName string) int { db := reform.NewDB(sqlDB, postgresql.Dialect, nil) err := stopPMMServer() @@ -86,7 +83,7 @@ func isPMMServerStatus(status string) bool { func rotateEncryptionKey(db *reform.DB, dbName string) error { return db.InTransaction(func(tx *reform.TX) error { logrus.Infof("DB %s is being decrypted", dbName) - err := models.DecryptDB(tx, dbName, models.DefaultAgentEncryptionColumns) + err := DecryptDB(tx, dbName, DefaultAgentEncryptionColumns) if err != nil { return err } @@ -100,7 +97,7 @@ func rotateEncryptionKey(db *reform.DB, dbName string) error { logrus.Infof("New encryption key generated") logrus.Infof("DB %s is being encrypted", dbName) - err = models.EncryptDB(tx, dbName, models.DefaultAgentEncryptionColumns) + err = EncryptDB(tx, dbName, DefaultAgentEncryptionColumns) if err != nil { if e := encryption.RestoreOldEncryptionKey(); e != nil { return errors.Wrap(err, e.Error()) @@ -112,56 +109,3 @@ func rotateEncryptionKey(db *reform.DB, dbName string) error { return nil }) } - -func openDB() (*sql.DB, string) { - postgresAddrF := kingpin.Flag("postgres-addr", "PostgreSQL address"). - Default(models.DefaultPostgreSQLAddr). - Envar("PMM_POSTGRES_ADDR"). - String() - postgresDBNameF := kingpin.Flag("postgres-name", "PostgreSQL database name"). - Default("pmm-managed"). - Envar("PMM_POSTGRES_DBNAME"). - String() - postgresDBUsernameF := kingpin.Flag("postgres-username", "PostgreSQL database username"). - Default("pmm-managed"). - Envar("PMM_POSTGRES_USERNAME"). - String() - postgresSSLModeF := kingpin.Flag("postgres-ssl-mode", "PostgreSQL SSL mode"). - Default(models.DisableSSLMode). - Envar("PMM_POSTGRES_SSL_MODE"). - Enum(models.DisableSSLMode, models.RequireSSLMode, models.VerifyCaSSLMode, models.VerifyFullSSLMode) - postgresSSLCAPathF := kingpin.Flag("postgres-ssl-ca-path", "PostgreSQL SSL CA root certificate path"). - Envar("PMM_POSTGRES_SSL_CA_PATH"). - String() - postgresDBPasswordF := kingpin.Flag("postgres-password", "PostgreSQL database password"). - Default("pmm-managed"). - Envar("PMM_POSTGRES_DBPASSWORD"). - String() - postgresSSLKeyPathF := kingpin.Flag("postgres-ssl-key-path", "PostgreSQL SSL key path"). - Envar("PMM_POSTGRES_SSL_KEY_PATH"). - String() - postgresSSLCertPathF := kingpin.Flag("postgres-ssl-cert-path", "PostgreSQL SSL certificate path"). - Envar("PMM_POSTGRES_SSL_CERT_PATH"). - String() - - kingpin.Parse() - - setupParams := models.SetupDBParams{ - Address: *postgresAddrF, - Name: *postgresDBNameF, - Username: *postgresDBUsernameF, - Password: *postgresDBPasswordF, - SSLMode: *postgresSSLModeF, - SSLCAPath: *postgresSSLCAPathF, - SSLKeyPath: *postgresSSLKeyPathF, - SSLCertPath: *postgresSSLCertPathF, - } - - sqlDB, err := models.OpenDB(setupParams) - if err != nil { - logrus.Errorf("Failed to connect to database: %+v", err) - os.Exit(1) - } - - return sqlDB, *postgresDBNameF -} From d38a8ca280202ca098fb6ff5432f9ef868fd8550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 16:15:31 +0200 Subject: [PATCH 195/215] PMM-13132 Another changes to simplify rotation. --- .github/workflows/admin.yml | 1 - .github/workflows/agent.yml | 1 - .github/workflows/managed.yml | 1 - .github/workflows/qan-api2.yml | 1 - .github/workflows/ui.yml | 1 - .github/workflows/update.yml | 1 - .github/workflows/vmproxy.yml | 1 - Makefile.include | 2 +- .../{ecryption_rotation_test.go => encryption_rotation_test.go} | 0 9 files changed, 1 insertion(+), 8 deletions(-) rename managed/models/{ecryption_rotation_test.go => encryption_rotation_test.go} (100%) diff --git a/.github/workflows/admin.yml b/.github/workflows/admin.yml index d7e559ca63..588ce72241 100644 --- a/.github/workflows/admin.yml +++ b/.github/workflows/admin.yml @@ -14,7 +14,6 @@ on: - "agent/**" - "api-tests/**" - "docs/**" - - "encryption-rotation/**" - "managed/**" - "qan-api2/**" - "update/**" diff --git a/.github/workflows/agent.yml b/.github/workflows/agent.yml index 82b7cfe5bc..957d19720e 100644 --- a/.github/workflows/agent.yml +++ b/.github/workflows/agent.yml @@ -15,7 +15,6 @@ on: - "api-tests/**" - "cli-tests/**" - "docs/**" - - "encryption-rotation/**" - "managed/**" - "qan-api2/**" - "update/**" diff --git a/.github/workflows/managed.yml b/.github/workflows/managed.yml index 66476ef61e..170bf9835c 100644 --- a/.github/workflows/managed.yml +++ b/.github/workflows/managed.yml @@ -15,7 +15,6 @@ on: - 'api-tests/**' - 'cli-tests/**' - 'docs/**' - - "encryption-rotation/**" - 'qan-api2/**' - 'update/**' - 'vmproxy/**' diff --git a/.github/workflows/qan-api2.yml b/.github/workflows/qan-api2.yml index 8f1d00a6ae..2cee867f7f 100644 --- a/.github/workflows/qan-api2.yml +++ b/.github/workflows/qan-api2.yml @@ -16,7 +16,6 @@ on: - "api-tests/**" - "cli-tests/**" - "docs/**" - - "encryption-rotation/**" - "managed/**" - "update/**" - "vmproxy/**" diff --git a/.github/workflows/ui.yml b/.github/workflows/ui.yml index 2ca2d99678..b243ef265e 100644 --- a/.github/workflows/ui.yml +++ b/.github/workflows/ui.yml @@ -15,7 +15,6 @@ on: - "api-tests/**" - "cli-tests/**" - "docs/**" - - "encryption-rotation/**" - "managed/**" - "managed-dev/**" - "qan-api2/**" diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index f20e8b9cc7..c0b325c860 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -15,7 +15,6 @@ on: - "api-tests/**" - "cli-tests/**" - "docs/**" - - "encryption-rotation/**" - "managed/**" - "qan-api2/**" - "vmproxy/**" diff --git a/.github/workflows/vmproxy.yml b/.github/workflows/vmproxy.yml index f55754c6fd..94753ca9a6 100644 --- a/.github/workflows/vmproxy.yml +++ b/.github/workflows/vmproxy.yml @@ -16,7 +16,6 @@ on: - "api-tests/**" - "cli-tests/**" - "docs/**" - - "encryption-rotation/**" - "managed/**" - "qan-api2/**" - "update/**" diff --git a/Makefile.include b/Makefile.include index 69835b303a..615f32251b 100644 --- a/Makefile.include +++ b/Makefile.include @@ -45,7 +45,7 @@ gen-mocks: ./bin/mockery --config .mockery.yaml test-common: ## Run tests from API (and other shared) packages only (i.e it ignores directories that are explicitly listed) - go test $(shell go list ./... | grep -v -e admin -e agent -e managed -e api-tests -e qan-api2 -e update -e encryption-rotation) + go test $(shell go list ./... | grep -v -e admin -e agent -e managed -e api-tests -e qan-api2 -e update) api-test: ## Run API tests on dev env. go test -count=1 -race -p 1 -v ./api-tests/... -pmm.server-insecure-tls diff --git a/managed/models/ecryption_rotation_test.go b/managed/models/encryption_rotation_test.go similarity index 100% rename from managed/models/ecryption_rotation_test.go rename to managed/models/encryption_rotation_test.go From d978cc3d5eccc2b3a9bab78829bebe29378c957d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 16:16:59 +0200 Subject: [PATCH 196/215] PMM-13132 Format. --- encryption-rotation/main.go | 3 ++- managed/models/encryption_rotation.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/encryption-rotation/main.go b/encryption-rotation/main.go index 93453864cc..fe369c6133 100644 --- a/encryption-rotation/main.go +++ b/encryption-rotation/main.go @@ -21,9 +21,10 @@ import ( "syscall" "github.com/Percona-Lab/kingpin" + "github.com/sirupsen/logrus" + "github.com/percona/pmm/managed/models" "github.com/percona/pmm/utils/logger" - "github.com/sirupsen/logrus" ) func main() { diff --git a/managed/models/encryption_rotation.go b/managed/models/encryption_rotation.go index 7c0023d748..fd303e0197 100644 --- a/managed/models/encryption_rotation.go +++ b/managed/models/encryption_rotation.go @@ -6,11 +6,12 @@ import ( "os/exec" "strings" - "github.com/percona/pmm/managed/utils/encryption" "github.com/pkg/errors" "github.com/sirupsen/logrus" "gopkg.in/reform.v1" "gopkg.in/reform.v1/dialects/postgresql" + + "github.com/percona/pmm/managed/utils/encryption" ) func RotateEncryptionKey(sqlDB *sql.DB, dbName string) int { From ec82c9118a452f27d9191d59243ef013d34dd8d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 16:23:02 +0200 Subject: [PATCH 197/215] PMM-13132 Improvements. --- managed/models/encryption_rotation.go | 15 +++++++++++++++ managed/models/encryption_rotation_test.go | 20 +++++++++++++++++++- managed/utils/testdb/db.go | 9 ++++----- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/managed/models/encryption_rotation.go b/managed/models/encryption_rotation.go index fd303e0197..3065bb03db 100644 --- a/managed/models/encryption_rotation.go +++ b/managed/models/encryption_rotation.go @@ -1,3 +1,18 @@ +// Copyright (C) 2023 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + package models import ( diff --git a/managed/models/encryption_rotation_test.go b/managed/models/encryption_rotation_test.go index b66b86277e..9325c61f44 100644 --- a/managed/models/encryption_rotation_test.go +++ b/managed/models/encryption_rotation_test.go @@ -1,3 +1,18 @@ +// Copyright (C) 2023 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + package models_test import ( @@ -34,7 +49,7 @@ func TestEncryptionRotation(t *testing.T) { err = insertTestData(db) require.NoError(t, err) - statusCode := models.RotateEncryptionKey(db, testdb.TestDatabase) + statusCode := models.RotateEncryptionKey(db, "pmm-managed-dev") require.Equal(t, 0, statusCode) newEncryptionKey, err := os.ReadFile(encryptionKeyTestPath) @@ -43,6 +58,9 @@ func TestEncryptionRotation(t *testing.T) { err = checkNewlyEncryptedData(db) require.NoError(t, err) + + err = os.Remove(encryptionKeyTestPath) + require.NoError(t, err) } func createOriginEncryptionKey() error { diff --git a/managed/utils/testdb/db.go b/managed/utils/testdb/db.go index 9b496bd075..2f6ee84cc7 100644 --- a/managed/utils/testdb/db.go +++ b/managed/utils/testdb/db.go @@ -30,8 +30,7 @@ import ( const ( username, password = "postgres", "" - // TestDatabase contains name of pmm-managed DB used for tests. - TestDatabase = "pmm-managed-dev" + testDatabase = "pmm-managed-dev" ) // Open recreates testing PostgreSQL database and returns an open connection to it. @@ -47,15 +46,15 @@ func Open(tb testing.TB, setupFixtures models.SetupFixturesMode, migrationVersio db, err := models.OpenDB(setupParams) require.NoError(tb, err) - _, err = db.Exec(`DROP DATABASE IF EXISTS "` + TestDatabase + `"`) + _, err = db.Exec(`DROP DATABASE IF EXISTS "` + testDatabase + `"`) require.NoError(tb, err) - _, err = db.Exec(`CREATE DATABASE "` + TestDatabase + `"`) + _, err = db.Exec(`CREATE DATABASE "` + testDatabase + `"`) require.NoError(tb, err) err = db.Close() require.NoError(tb, err) - setupParams.Name = TestDatabase + setupParams.Name = testDatabase db, err = models.OpenDB(setupParams) require.NoError(tb, err) SetupDB(tb, db, setupFixtures, migrationVersion) From b13abe807bbeda5cfb06cdba05f0625af420ea5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 16:33:30 +0200 Subject: [PATCH 198/215] PMM-13132 Add command to makefile, lint. --- Makefile | 3 +++ managed/models/encryption_rotation.go | 1 + managed/models/encryption_rotation_test.go | 1 + 3 files changed, 5 insertions(+) diff --git a/Makefile b/Makefile index e0f76fb515..6dbf9abe63 100644 --- a/Makefile +++ b/Makefile @@ -38,3 +38,6 @@ TARGET ?= _bash env: ## Run `make TARGET` in devcontainer (`make env TARGET=help`); TARGET defaults to bash COMPOSE_PROFILES=$(PROFILES) \ docker exec -it --workdir=/root/go/src/github.com/percona/pmm pmm-server make $(TARGET) + +rotate-encryption: ## Rotate encryption key + go run ./encryption-rotation/main.go diff --git a/managed/models/encryption_rotation.go b/managed/models/encryption_rotation.go index 3065bb03db..e89d7fec31 100644 --- a/managed/models/encryption_rotation.go +++ b/managed/models/encryption_rotation.go @@ -29,6 +29,7 @@ import ( "github.com/percona/pmm/managed/utils/encryption" ) +// RotateEncryptionKey will stop PMM server, decrypt data, create new encryption key and encrypt them and start PMM Server again. func RotateEncryptionKey(sqlDB *sql.DB, dbName string) int { db := reform.NewDB(sqlDB, postgresql.Dialect, nil) diff --git a/managed/models/encryption_rotation_test.go b/managed/models/encryption_rotation_test.go index 9325c61f44..267d16e098 100644 --- a/managed/models/encryption_rotation_test.go +++ b/managed/models/encryption_rotation_test.go @@ -13,6 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +//nolint:dupl package models_test import ( From 7aeaec5a296bd7838882ef03f61267404e5751ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 16:39:30 +0200 Subject: [PATCH 199/215] PMM-13132 Lint. --- managed/models/encryption_rotation_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/models/encryption_rotation_test.go b/managed/models/encryption_rotation_test.go index 267d16e098..c2b461c644 100644 --- a/managed/models/encryption_rotation_test.go +++ b/managed/models/encryption_rotation_test.go @@ -13,7 +13,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -//nolint:dupl package models_test import ( @@ -74,6 +73,7 @@ func createOriginEncryptionKey() error { return nil } +//nolint:dupl func insertTestData(db *sql.DB) error { _, err := models.UpdateSettings(db, &models.ChangeSettingsParams{ EncryptedItems: []string{"pmm-managed-dev.agents.username", "pmm-managed-dev.agents.password", "pmm-managed-dev.agents.aws_access_key", "pmm-managed-dev.agents.aws_secret_key", "pmm-managed-dev.agents.mongo_db_tls_options", "pmm-managed-dev.agents.azure_options", "pmm-managed-dev.agents.mysql_options", "pmm-managed-dev.agents.postgresql_options", "pmm-managed-dev.agents.agent_password"}, From 096fc935384be92a0784376d42c83b24644eff25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 16:45:54 +0200 Subject: [PATCH 200/215] PMM-13132 Lint. --- managed/models/encryption_rotation_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/models/encryption_rotation_test.go b/managed/models/encryption_rotation_test.go index c2b461c644..ae46912032 100644 --- a/managed/models/encryption_rotation_test.go +++ b/managed/models/encryption_rotation_test.go @@ -73,7 +73,7 @@ func createOriginEncryptionKey() error { return nil } -//nolint:dupl +//nolint:dupword func insertTestData(db *sql.DB) error { _, err := models.UpdateSettings(db, &models.ChangeSettingsParams{ EncryptedItems: []string{"pmm-managed-dev.agents.username", "pmm-managed-dev.agents.password", "pmm-managed-dev.agents.aws_access_key", "pmm-managed-dev.agents.aws_secret_key", "pmm-managed-dev.agents.mongo_db_tls_options", "pmm-managed-dev.agents.azure_options", "pmm-managed-dev.agents.mysql_options", "pmm-managed-dev.agents.postgresql_options", "pmm-managed-dev.agents.agent_password"}, From 229e08a2f0b85a611850d3f106d79bccac774b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Thu, 19 Sep 2024 17:16:55 +0200 Subject: [PATCH 201/215] PMM-13132 Wrappers around default on newly added methods. --- managed/utils/encryption/encryption.go | 29 ++++++++++++++++++++++++-- managed/utils/encryption/helpers.go | 9 ++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 0fa122363e..5141a7bc05 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -66,7 +66,7 @@ func New() *Encryption { return e } -// RotateEncryptionKey is will backup old encryption key and generate new one. +// RotateEncryptionKey is a wrapper around DefaultEncryption.RotateEncryptionKey. func RotateEncryptionKey() error { err := backupOldEncryptionKey() if err != nil { @@ -80,7 +80,22 @@ func RotateEncryptionKey() error { return nil } -// RestoreOldEncryptionKey will restore previous backup during rotation. +// RotateEncryptionKey is will backup old encryption key and generate new one. +func (e *Encryption) RotateEncryptionKey() error { + err := e.backupOldEncryptionKey() + if err != nil { + return err + } + + enc := New() + e.Key = enc.Key + e.Path = enc.Path + e.Primitive = enc.Primitive + + return nil +} + +// RestoreOldEncryptionKey is a wrapper around DefaultEncryption.RestoreOldEncryptionKey. func RestoreOldEncryptionKey() error { err := os.Rename(fmt.Sprintf("%s_old.key", strings.TrimSuffix(encryptionKeyPath(), ".key")), encryptionKeyPath()) if err != nil { @@ -90,6 +105,16 @@ func RestoreOldEncryptionKey() error { return nil } +// RestoreOldEncryptionKey will restore previous backup during rotation. +func (e *Encryption) RestoreOldEncryptionKey() error { + err := os.Rename(fmt.Sprintf("%s_old.key", strings.TrimSuffix(e.Path, ".key")), e.Path) + if err != nil { + return err + } + + return nil +} + // Encrypt is a wrapper around DefaultEncryption.Encrypt. func Encrypt(secret string) (string, error) { return DefaultEncryption.Encrypt(secret) diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index 6ad997d58b..5a3109cbd7 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -49,6 +49,15 @@ func backupOldEncryptionKey() error { return nil } +func (e *Encryption) backupOldEncryptionKey() error { + err := os.Rename(e.Path, fmt.Sprintf("%s_old.key", strings.TrimSuffix(e.Path, ".key"))) + if err != nil { + return err + } + + return nil +} + func prepareRowPointers(rows *sql.Rows) ([]any, error) { columnTypes, err := rows.ColumnTypes() if err != nil { From 89692aca3d5a3347f65f0b27b3b5200e2e650b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Fri, 20 Sep 2024 07:56:00 +0200 Subject: [PATCH 202/215] PMM-13132 Move into cmd of pmm-managed. --- .../cmd/pmm-encryption-rotation}/main.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {encryption-rotation => managed/cmd/pmm-encryption-rotation}/main.go (100%) diff --git a/encryption-rotation/main.go b/managed/cmd/pmm-encryption-rotation/main.go similarity index 100% rename from encryption-rotation/main.go rename to managed/cmd/pmm-encryption-rotation/main.go From f6035308a25e1495ad751e5e505b1c1c89a380c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Fri, 20 Sep 2024 07:58:52 +0200 Subject: [PATCH 203/215] PMM-13132 Suggested refactor. --- managed/models/database.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index 9e87277916..d3ac372905 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1177,7 +1177,7 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. // EncryptDB encrypts a set of columns in a specific database and table. func EncryptDB(tx *reform.TX, database string, itemsToEncrypt []encryption.Table) error { - return dbEncryption(tx, database, itemsToEncrypt, encryption.EncryptItems, encryptionExists, addToEncryptedItems) + return dbEncryption(tx, database, itemsToEncrypt, encryption.EncryptItems, true, addToEncryptedItems) } func encryptionExists(m map[string]bool, key string) bool { @@ -1190,7 +1190,7 @@ func addToEncryptedItems(encryptedItems []string, items []string) []string { // DecryptDB decrypts a set of columns in a specific database and table. func DecryptDB(tx *reform.TX, database string, itemsToEncrypt []encryption.Table) error { - return dbEncryption(tx, database, itemsToEncrypt, encryption.DecryptItems, encryptionNotExists, removeFromEncryptedItems) + return dbEncryption(tx, database, itemsToEncrypt, encryption.DecryptItems, false, removeFromEncryptedItems) } func encryptionNotExists(m map[string]bool, key string) bool { @@ -1219,7 +1219,7 @@ func removeFromEncryptedItems(encryptedItems []string, items []string) []string func dbEncryption(tx *reform.TX, database string, items []encryption.Table, encryptionHandler func(tx *reform.TX, tables []encryption.Table) error, - checkHandler func(m map[string]bool, key string) bool, + expectedState bool, settingsHandler func(encryptedItems []string, items []string) []string, ) error { if len(items) == 0 { @@ -1241,7 +1241,7 @@ func dbEncryption(tx *reform.TX, database string, items []encryption.Table, columns := []encryption.Column{} for _, column := range table.Columns { dbTableColumn := fmt.Sprintf("%s.%s.%s", database, table.Name, column.Name) - if checkHandler(currentColumns, dbTableColumn) { + if currentColumns[dbTableColumn] == expectedState { continue } From 7fab00f0210aa2ea7a130b8ac35fa98b9642e1e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Fri, 20 Sep 2024 08:14:48 +0200 Subject: [PATCH 204/215] PMM-13132 Another suggested refactor. --- managed/models/database.go | 46 +++++--------------------- managed/models/settings_helpers.go | 4 ++- managed/utils/encryption/encryption.go | 20 ++--------- managed/utils/encryption/helpers.go | 18 ---------- 4 files changed, 14 insertions(+), 74 deletions(-) diff --git a/managed/models/database.go b/managed/models/database.go index d3ac372905..d7a9778721 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -27,7 +27,6 @@ import ( "net" "net/url" "os" - "slices" "strconv" "strings" @@ -1177,50 +1176,17 @@ func SetupDB(ctx context.Context, sqlDB *sql.DB, params SetupDBParams) (*reform. // EncryptDB encrypts a set of columns in a specific database and table. func EncryptDB(tx *reform.TX, database string, itemsToEncrypt []encryption.Table) error { - return dbEncryption(tx, database, itemsToEncrypt, encryption.EncryptItems, true, addToEncryptedItems) -} - -func encryptionExists(m map[string]bool, key string) bool { - return m[key] -} - -func addToEncryptedItems(encryptedItems []string, items []string) []string { - return slices.Concat(encryptedItems, items) + return dbEncryption(tx, database, itemsToEncrypt, encryption.EncryptItems, true) } // DecryptDB decrypts a set of columns in a specific database and table. func DecryptDB(tx *reform.TX, database string, itemsToEncrypt []encryption.Table) error { - return dbEncryption(tx, database, itemsToEncrypt, encryption.DecryptItems, false, removeFromEncryptedItems) -} - -func encryptionNotExists(m map[string]bool, key string) bool { - return !encryptionExists(m, key) -} - -func removeFromEncryptedItems(encryptedItems []string, items []string) []string { - res := []string{} - for _, encryptedItem := range encryptedItems { - exists := false - for _, item := range items { - if encryptedItem == item { - exists = true - } - } - - if exists { - continue - } - - res = append(res, encryptedItem) - } - - return res + return dbEncryption(tx, database, itemsToEncrypt, encryption.DecryptItems, false) } func dbEncryption(tx *reform.TX, database string, items []encryption.Table, encryptionHandler func(tx *reform.TX, tables []encryption.Table) error, expectedState bool, - settingsHandler func(encryptedItems []string, items []string) []string, ) error { if len(items) == 0 { return nil @@ -1263,8 +1229,14 @@ func dbEncryption(tx *reform.TX, database string, items []encryption.Table, if err != nil { return err } + + encryptedItems := []string{} + if expectedState { + encryptedItems = prepared + } + _, err = UpdateSettings(tx, &ChangeSettingsParams{ - EncryptedItems: settingsHandler(settings.EncryptedItems, prepared), + EncryptedItems: encryptedItems, }) if err != nil { return err diff --git a/managed/models/settings_helpers.go b/managed/models/settings_helpers.go index 9399d4196b..d9bd6ddf2e 100644 --- a/managed/models/settings_helpers.go +++ b/managed/models/settings_helpers.go @@ -226,7 +226,9 @@ func UpdateSettings(q reform.DBTX, params *ChangeSettingsParams) (*Settings, err settings.DefaultRoleID = *params.DefaultRoleID } - settings.EncryptedItems = params.EncryptedItems + if settings.EncryptedItems != nil { + settings.EncryptedItems = params.EncryptedItems + } err = SaveSettings(q, settings) if err != nil { diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 5141a7bc05..8ca7b96a6d 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -80,21 +80,6 @@ func RotateEncryptionKey() error { return nil } -// RotateEncryptionKey is will backup old encryption key and generate new one. -func (e *Encryption) RotateEncryptionKey() error { - err := e.backupOldEncryptionKey() - if err != nil { - return err - } - - enc := New() - e.Key = enc.Key - e.Path = enc.Path - e.Primitive = enc.Primitive - - return nil -} - // RestoreOldEncryptionKey is a wrapper around DefaultEncryption.RestoreOldEncryptionKey. func RestoreOldEncryptionKey() error { err := os.Rename(fmt.Sprintf("%s_old.key", strings.TrimSuffix(encryptionKeyPath(), ".key")), encryptionKeyPath()) @@ -105,9 +90,8 @@ func RestoreOldEncryptionKey() error { return nil } -// RestoreOldEncryptionKey will restore previous backup during rotation. -func (e *Encryption) RestoreOldEncryptionKey() error { - err := os.Rename(fmt.Sprintf("%s_old.key", strings.TrimSuffix(e.Path, ".key")), e.Path) +func backupOldEncryptionKey() error { + err := os.Rename(encryptionKeyPath(), fmt.Sprintf("%s_old.key", strings.TrimSuffix(encryptionKeyPath(), ".key"))) if err != nil { return err } diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index 5a3109cbd7..7058e70c5b 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -40,24 +40,6 @@ func encryptionKeyPath() string { return DefaultEncryptionKeyPath } -func backupOldEncryptionKey() error { - err := os.Rename(encryptionKeyPath(), fmt.Sprintf("%s_old.key", strings.TrimSuffix(encryptionKeyPath(), ".key"))) - if err != nil { - return err - } - - return nil -} - -func (e *Encryption) backupOldEncryptionKey() error { - err := os.Rename(e.Path, fmt.Sprintf("%s_old.key", strings.TrimSuffix(e.Path, ".key"))) - if err != nil { - return err - } - - return nil -} - func prepareRowPointers(rows *sql.Rows) ([]any, error) { columnTypes, err := rows.ColumnTypes() if err != nil { From 8a86a5319864cc515018784e8eb7c81afeecad33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Fri, 20 Sep 2024 08:40:56 +0200 Subject: [PATCH 205/215] PMM-13132 Fix. --- managed/models/settings_helpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/models/settings_helpers.go b/managed/models/settings_helpers.go index d9bd6ddf2e..24ab1507a3 100644 --- a/managed/models/settings_helpers.go +++ b/managed/models/settings_helpers.go @@ -226,7 +226,7 @@ func UpdateSettings(q reform.DBTX, params *ChangeSettingsParams) (*Settings, err settings.DefaultRoleID = *params.DefaultRoleID } - if settings.EncryptedItems != nil { + if params.EncryptedItems != nil { settings.EncryptedItems = params.EncryptedItems } From e27d3b09b3164dd4c294c5841ab6595578d4637f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 23 Sep 2024 11:13:29 +0200 Subject: [PATCH 206/215] PMM-13132 Move encryption models into encryption file. --- managed/utils/encryption/encryption.go | 67 ++++++++++++++++++++++++++ managed/utils/encryption/helpers.go | 41 ---------------- managed/utils/encryption/models.go | 45 ----------------- 3 files changed, 67 insertions(+), 86 deletions(-) delete mode 100644 managed/utils/encryption/models.go diff --git a/managed/utils/encryption/encryption.go b/managed/utils/encryption/encryption.go index 8ca7b96a6d..0396e73170 100644 --- a/managed/utils/encryption/encryption.go +++ b/managed/utils/encryption/encryption.go @@ -17,6 +17,7 @@ package encryption import ( + "bytes" "encoding/base64" "fmt" "os" @@ -24,8 +25,12 @@ import ( "strings" "sync" + "github.com/google/tink/go/tink" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "github.com/tink-crypto/tink-go/aead" + "github.com/tink-crypto/tink-go/insecurecleartextkeyset" + "github.com/tink-crypto/tink-go/keyset" "gopkg.in/reform.v1" ) @@ -39,6 +44,33 @@ var ( defaultEncryptionMtx sync.Mutex ) +// Encryption contains fields required for encryption. +type Encryption struct { + Path string + Key string + Primitive tink.AEAD +} + +// Table represents table name, it's identifiers and columns to be encrypted/decrypted. +type Table struct { + Name string + Identifiers []string + Columns []Column +} + +// Column represents column name and column's custom handler (if needed). +type Column struct { + Name string + CustomHandler func(e *Encryption, val any) (any, error) +} + +// QueryValues represents query to update row after encrypt/decrypt. +type QueryValues struct { + Query string + SetValues [][]any + WhereValues [][]any +} + // New creates an encryption; if key on path doesn't exist, it will be generated. func New() *Encryption { e := &Encryption{} @@ -99,6 +131,26 @@ func backupOldEncryptionKey() error { return nil } +func (e *Encryption) generateKey() error { + handle, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) + if err != nil { + return err + } + + buff := &bytes.Buffer{} + err = insecurecleartextkeyset.Write(handle, keyset.NewBinaryWriter(buff)) + if err != nil { + return err + } + e.Key = base64.StdEncoding.EncodeToString(buff.Bytes()) + + return e.saveKeyToFile() +} + +func (e *Encryption) saveKeyToFile() error { + return os.WriteFile(e.Path, []byte(e.Key), 0o644) //nolint:gosec +} + // Encrypt is a wrapper around DefaultEncryption.Encrypt. func Encrypt(secret string) (string, error) { return DefaultEncryption.Encrypt(secret) @@ -234,3 +286,18 @@ func (e *Encryption) DecryptItems(tx *reform.TX, tables []Table) error { return nil } + +func (e *Encryption) getPrimitive() (tink.AEAD, error) { //nolint:ireturn + serializedKeyset, err := base64.StdEncoding.DecodeString(e.Key) + if err != nil { + return nil, err + } + + binaryReader := keyset.NewBinaryReader(bytes.NewBuffer(serializedKeyset)) + parsedHandle, err := insecurecleartextkeyset.Read(binaryReader) + if err != nil { + return nil, err + } + + return aead.New(parsedHandle) +} diff --git a/managed/utils/encryption/helpers.go b/managed/utils/encryption/helpers.go index 7058e70c5b..01a31f2762 100644 --- a/managed/utils/encryption/helpers.go +++ b/managed/utils/encryption/helpers.go @@ -16,18 +16,12 @@ package encryption import ( - "bytes" "database/sql" - "encoding/base64" "fmt" "os" "slices" "strings" - "github.com/google/tink/go/aead" - "github.com/google/tink/go/insecurecleartextkeyset" - "github.com/google/tink/go/keyset" - "github.com/google/tink/go/tink" "gopkg.in/reform.v1" ) @@ -91,41 +85,6 @@ func decryptColumnStringHandler(e *Encryption, val any) (any, error) { return decrypted, nil } -func (e *Encryption) getPrimitive() (tink.AEAD, error) { //nolint:ireturn - serializedKeyset, err := base64.StdEncoding.DecodeString(e.Key) - if err != nil { - return nil, err - } - - binaryReader := keyset.NewBinaryReader(bytes.NewBuffer(serializedKeyset)) - parsedHandle, err := insecurecleartextkeyset.Read(binaryReader) - if err != nil { - return nil, err - } - - return aead.New(parsedHandle) -} - -func (e *Encryption) generateKey() error { - handle, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) - if err != nil { - return err - } - - buff := &bytes.Buffer{} - err = insecurecleartextkeyset.Write(handle, keyset.NewBinaryWriter(buff)) - if err != nil { - return err - } - e.Key = base64.StdEncoding.EncodeToString(buff.Bytes()) - - return e.saveKeyToFile() -} - -func (e *Encryption) saveKeyToFile() error { - return os.WriteFile(e.Path, []byte(e.Key), 0o644) //nolint:gosec -} - func (table Table) columnsList() []string { res := []string{} for _, c := range table.Columns { diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go deleted file mode 100644 index 257b49b1de..0000000000 --- a/managed/utils/encryption/models.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2023 Percona LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package encryption - -import "github.com/google/tink/go/tink" - -// Encryption contains fields required for encryption. -type Encryption struct { - Path string - Key string - Primitive tink.AEAD -} - -// Table represents table name, it's identifiers and columns to be encrypted/decrypted. -type Table struct { - Name string - Identifiers []string - Columns []Column -} - -// Column represents column name and column's custom handler (if needed). -type Column struct { - Name string - CustomHandler func(e *Encryption, val any) (any, error) -} - -// QueryValues represents query to update row after encrypt/decrypt. -type QueryValues struct { - Query string - SetValues [][]any - WhereValues [][]any -} From 07921cf8d67b34e8e57b3711eea2fbcd0a9bb1a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 23 Sep 2024 11:49:04 +0200 Subject: [PATCH 207/215] PMM-13132 Make. --- go.mod | 1 + go.sum | 2 ++ managed/Makefile | 3 +++ 3 files changed, 6 insertions(+) diff --git a/go.mod b/go.mod index 09ecff3b2b..dce8e2c2c8 100644 --- a/go.mod +++ b/go.mod @@ -69,6 +69,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/stretchr/objx v0.5.2 github.com/stretchr/testify v1.9.0 + github.com/tink-crypto/tink-go v0.0.0-20230613075026-d6de17e3f164 go.mongodb.org/mongo-driver v1.16.1 go.starlark.net v0.0.0-20230717150657-8a3343210976 golang.org/x/crypto v0.26.0 diff --git a/go.sum b/go.sum index 20ab67a97a..ff619ebbe1 100644 --- a/go.sum +++ b/go.sum @@ -512,6 +512,8 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tink-crypto/tink-go v0.0.0-20230613075026-d6de17e3f164 h1:yhVO0Yhq84FjdcotvFFvDJRNHJ7mO743G12VdcW4Evc= +github.com/tink-crypto/tink-go v0.0.0-20230613075026-d6de17e3f164/go.mod h1:HhtDVdE/PRZFRia834tkmcwuscnaAzda1RJUW9Pr3Rg= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= diff --git a/managed/Makefile b/managed/Makefile index dcf35ec550..7c1dd10781 100644 --- a/managed/Makefile +++ b/managed/Makefile @@ -38,6 +38,9 @@ clean: ## Remove generated files release: ## Build pmm-managed release binaries env CGO_ENABLED=0 go build -v $(PMM_LD_FLAGS) -o $(PMM_RELEASE_PATH)/ ./cmd/... +release-encryption-rotation: ## Build PMM encryption rotation release binaries + env CGO_ENABLED=0 go build -v $(PMM_LD_FLAGS) -o $(PMM_RELEASE_PATH)/ ./cmd/pmm-encryption-rotation/... + release-starlark: env CGO_ENABLED=0 go build -v $(PMM_LD_FLAGS) -o $(PMM_RELEASE_PATH)/ ./cmd/pmm-managed-starlark/... $(PMM_RELEASE_PATH)/pmm-managed-starlark --version From 8d9d4cc56582ce5214af68362dddade1fbefd959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 23 Sep 2024 12:45:25 +0200 Subject: [PATCH 208/215] PMM-13132 Migration to kong. --- go.mod | 1 - go.sum | 2 - managed/cmd/pmm-encryption-rotation/main.go | 77 ++++++++++----------- 3 files changed, 37 insertions(+), 43 deletions(-) diff --git a/go.mod b/go.mod index dce8e2c2c8..ae66b3b7c1 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,6 @@ require ( github.com/AlekSi/pointer v1.2.0 github.com/ClickHouse/clickhouse-go/v2 v2.23.0 github.com/DATA-DOG/go-sqlmock v1.5.0 - github.com/Percona-Lab/kingpin v2.2.6+incompatible github.com/alecthomas/kong v0.9.0 github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 github.com/aws/aws-sdk-go v1.55.3 diff --git a/go.sum b/go.sum index ff619ebbe1..bdf9c3288e 100644 --- a/go.sum +++ b/go.sum @@ -34,8 +34,6 @@ github.com/Percona-Lab/go-grpc-prometheus v0.0.0-20230116133345-3487748d4592 h1: github.com/Percona-Lab/go-grpc-prometheus v0.0.0-20230116133345-3487748d4592/go.mod h1:xCJfGpj56ERA85Mj1VfBzoeWW4lZ00xXXkvG0LJQjZU= github.com/Percona-Lab/kingpin v2.2.6-percona+incompatible h1:N5oM40aAatvf8bCYjv69YsVdxJLIUhY/MerUG1jRL9Y= github.com/Percona-Lab/kingpin v2.2.6-percona+incompatible/go.mod h1:UC6j/e2eqpHBB/vn+5214ExsoDLiEo6BfUGBhbtf+x0= -github.com/Percona-Lab/kingpin v2.2.6+incompatible h1:i7fo0CKR6IGSxe9ErG2DMFz/shUK6vRigVfyQqOyWvs= -github.com/Percona-Lab/kingpin v2.2.6+incompatible/go.mod h1:UC6j/e2eqpHBB/vn+5214ExsoDLiEo6BfUGBhbtf+x0= github.com/Percona-Lab/spec v0.20.5-percona h1:ViCJVq52QIZxpP8/Nv4/nIed+WnqUirNjPtXvHhset4= github.com/Percona-Lab/spec v0.20.5-percona/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= diff --git a/managed/cmd/pmm-encryption-rotation/main.go b/managed/cmd/pmm-encryption-rotation/main.go index fe369c6133..e5a4f7f410 100644 --- a/managed/cmd/pmm-encryption-rotation/main.go +++ b/managed/cmd/pmm-encryption-rotation/main.go @@ -20,7 +20,7 @@ import ( "os/signal" "syscall" - "github.com/Percona-Lab/kingpin" + "github.com/alecthomas/kong" "github.com/sirupsen/logrus" "github.com/percona/pmm/managed/models" @@ -44,47 +44,44 @@ func main() { os.Exit(statusCode) } -func setupParams() models.SetupDBParams { - postgresAddrF := kingpin.Flag("postgres-addr", "PostgreSQL address"). - Default(models.DefaultPostgreSQLAddr). - Envar("PMM_POSTGRES_ADDR"). - String() - postgresDBNameF := kingpin.Flag("postgres-name", "PostgreSQL database name"). - Default("pmm-managed"). - Envar("PMM_POSTGRES_DBNAME"). - String() - postgresDBUsernameF := kingpin.Flag("postgres-username", "PostgreSQL database username"). - Default("pmm-managed"). - Envar("PMM_POSTGRES_USERNAME"). - String() - postgresSSLModeF := kingpin.Flag("postgres-ssl-mode", "PostgreSQL SSL mode"). - Default(models.DisableSSLMode). - Envar("PMM_POSTGRES_SSL_MODE"). - Enum(models.DisableSSLMode, models.RequireSSLMode, models.VerifyCaSSLMode, models.VerifyFullSSLMode) - postgresSSLCAPathF := kingpin.Flag("postgres-ssl-ca-path", "PostgreSQL SSL CA root certificate path"). - Envar("PMM_POSTGRES_SSL_CA_PATH"). - String() - postgresDBPasswordF := kingpin.Flag("postgres-password", "PostgreSQL database password"). - Default("pmm-managed"). - Envar("PMM_POSTGRES_DBPASSWORD"). - String() - postgresSSLKeyPathF := kingpin.Flag("postgres-ssl-key-path", "PostgreSQL SSL key path"). - Envar("PMM_POSTGRES_SSL_KEY_PATH"). - String() - postgresSSLCertPathF := kingpin.Flag("postgres-ssl-cert-path", "PostgreSQL SSL certificate path"). - Envar("PMM_POSTGRES_SSL_CERT_PATH"). - String() +type flags struct { + Address string `name:"postgres-addr" default:"${address}" help:"PostgreSQL address with port"` + DBName string `name:"postgres-name" default:"pmm-managed" help:"PostgreSQL database name"` + DBUsername string `name:"postgres-username" default:"pmm-managed" help:"PostgreSQL database username name"` + DBPassword string `name:"postgres-password" default:"pmm-managed" help:"PostgreSQL database password"` + SSLMode string `name:"postgres-ssl-mode" default:"${disable_sslmode}" help:"PostgreSQL SSL mode" enum:"${disable_sslmode}, ${require_sslmode},${verify_sslmode}, ${verify_full_sslmode}"` + SSLCAPath string `name:"postgres-ssl-ca-path" help:"PostgreSQL SSL CA root certificate path" type:"path"` + SSLKeyPath string `name:"postgres-ssl-key-path" help:"PostgreSQL SSL key path" type:"path"` + SSLCertPath string `name:"postgres-ssl-cert-path" help:"PostgreSQL SSL certificate path" type:"path"` +} - kingpin.Parse() +func setupParams() models.SetupDBParams { + var opts flags + kong.Parse( + &opts, + kong.Name("encryption-rotation"), + kong.UsageOnError(), + kong.ConfigureHelp(kong.HelpOptions{ + Compact: true, + NoExpandSubcommands: true, + }), + kong.Vars{ + "address": models.DefaultPostgreSQLAddr, + "disable_sslmode": models.DisableSSLMode, + "require_sslmode": models.RequireSSLMode, + "verify_sslmode": models.VerifyCaSSLMode, + "verify_full_sslmode": models.VerifyFullSSLMode, + }, + ) return models.SetupDBParams{ - Address: *postgresAddrF, - Name: *postgresDBNameF, - Username: *postgresDBUsernameF, - Password: *postgresDBPasswordF, - SSLMode: *postgresSSLModeF, - SSLCAPath: *postgresSSLCAPathF, - SSLKeyPath: *postgresSSLKeyPathF, - SSLCertPath: *postgresSSLCertPathF, + Address: opts.Address, + Name: opts.DBName, + Username: opts.DBUsername, + Password: opts.DBPassword, + SSLMode: opts.SSLMode, + SSLCAPath: opts.SSLCAPath, + SSLKeyPath: opts.SSLKeyPath, + SSLCertPath: opts.SSLCertPath, } } From 999c4c8e8cd7c8b371d491396b41e918581b087d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 23 Sep 2024 12:50:19 +0200 Subject: [PATCH 209/215] PMM-13132 Add consts. --- managed/cmd/pmm-encryption-rotation/main.go | 4 +++- managed/models/encryption_rotation.go | 25 ++++++++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/managed/cmd/pmm-encryption-rotation/main.go b/managed/cmd/pmm-encryption-rotation/main.go index e5a4f7f410..76b444b9ff 100644 --- a/managed/cmd/pmm-encryption-rotation/main.go +++ b/managed/cmd/pmm-encryption-rotation/main.go @@ -27,6 +27,8 @@ import ( "github.com/percona/pmm/utils/logger" ) +const codeDBConnectionFailed = 1 + func main() { signal.Ignore(syscall.SIGINT, syscall.SIGTERM) // to prevent any interuptions during process @@ -35,7 +37,7 @@ func main() { sqlDB, err := models.OpenDB(setupParams()) if err != nil { logrus.Error(err) - os.Exit(1) + os.Exit(codeDBConnectionFailed) } statusCode := models.RotateEncryptionKey(sqlDB, "pmm-managed") diff --git a/managed/models/encryption_rotation.go b/managed/models/encryption_rotation.go index e89d7fec31..d6f0ae4936 100644 --- a/managed/models/encryption_rotation.go +++ b/managed/models/encryption_rotation.go @@ -29,6 +29,15 @@ import ( "github.com/percona/pmm/managed/utils/encryption" ) +const ( + statusRunning = "RUNNING" + statusStopped = "STOPPED" + codeOK = 0 + codePMMStopFailed = 2 + codeEncryptionFailed = 3 + codePMMStartFailed = 4 +) + // RotateEncryptionKey will stop PMM server, decrypt data, create new encryption key and encrypt them and start PMM Server again. func RotateEncryptionKey(sqlDB *sql.DB, dbName string) int { db := reform.NewDB(sqlDB, postgresql.Dialect, nil) @@ -36,26 +45,26 @@ func RotateEncryptionKey(sqlDB *sql.DB, dbName string) int { err := stopPMMServer() if err != nil { logrus.Errorf("Failed to stop PMM Server: %+v", err) - return 2 + return codePMMStopFailed } err = rotateEncryptionKey(db, dbName) if err != nil { logrus.Errorf("Failed to rotate encryption key: %+v", err) - return 3 + return codeEncryptionFailed } err = startPMMServer() if err != nil { logrus.Errorf("Failed to start PMM Server: %+v", err) - return 4 + return codePMMStartFailed } - return 0 + return codeOK } func startPMMServer() error { - if isPMMServerStatus("RUNNING") { + if isPMMServerStatus(statusRunning) { return nil } @@ -65,7 +74,7 @@ func startPMMServer() error { return fmt.Errorf("%w: %s", err, output) } - if !isPMMServerStatus("RUNNING") { + if !isPMMServerStatus(statusRunning) { return errors.New("cannot start pmm-managed") } @@ -73,7 +82,7 @@ func startPMMServer() error { } func stopPMMServer() error { - if isPMMServerStatus("STOPPED") { + if isPMMServerStatus(statusStopped) { return nil } @@ -83,7 +92,7 @@ func stopPMMServer() error { return fmt.Errorf("%w: %s", err, output) } - if !isPMMServerStatus("STOPPED") { + if !isPMMServerStatus(statusStopped) { return errors.New("cannot stop pmm-managed") } From dd463b6ec29a2d38d395cde006f2cbfd3acd68c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 23 Sep 2024 12:55:09 +0200 Subject: [PATCH 210/215] PMM-13132 Lint. --- managed/cmd/pmm-encryption-rotation/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/cmd/pmm-encryption-rotation/main.go b/managed/cmd/pmm-encryption-rotation/main.go index 76b444b9ff..3eef7e3199 100644 --- a/managed/cmd/pmm-encryption-rotation/main.go +++ b/managed/cmd/pmm-encryption-rotation/main.go @@ -51,7 +51,7 @@ type flags struct { DBName string `name:"postgres-name" default:"pmm-managed" help:"PostgreSQL database name"` DBUsername string `name:"postgres-username" default:"pmm-managed" help:"PostgreSQL database username name"` DBPassword string `name:"postgres-password" default:"pmm-managed" help:"PostgreSQL database password"` - SSLMode string `name:"postgres-ssl-mode" default:"${disable_sslmode}" help:"PostgreSQL SSL mode" enum:"${disable_sslmode}, ${require_sslmode},${verify_sslmode}, ${verify_full_sslmode}"` + SSLMode string `name:"postgres-ssl-mode" default:"${disable_sslmode}" help:"PostgreSQL SSL mode" enum:"${disable_sslmode}, ${require_sslmode},${verify_sslmode}, ${verify_full_sslmode}"` //nolint:lll SSLCAPath string `name:"postgres-ssl-ca-path" help:"PostgreSQL SSL CA root certificate path" type:"path"` SSLKeyPath string `name:"postgres-ssl-key-path" help:"PostgreSQL SSL key path" type:"path"` SSLCertPath string `name:"postgres-ssl-cert-path" help:"PostgreSQL SSL certificate path" type:"path"` From 6dbc1f98c50ecdff312b2aa325e1fe35670ff825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 23 Sep 2024 13:20:57 +0200 Subject: [PATCH 211/215] PMM-13132 Specs. --- build/packages/rpm/server/SPECS/pmm-managed.spec | 5 +++++ managed/Makefile | 2 +- managed/cmd/pmm-encryption-rotation/main.go | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/build/packages/rpm/server/SPECS/pmm-managed.spec b/build/packages/rpm/server/SPECS/pmm-managed.spec index b1da81ea78..519a4ffc1c 100644 --- a/build/packages/rpm/server/SPECS/pmm-managed.spec +++ b/build/packages/rpm/server/SPECS/pmm-managed.spec @@ -51,6 +51,7 @@ install -d -p %{buildroot}%{_sbindir} install -d -p %{buildroot}%{_datadir}/%{name} install -d -p %{buildroot}%{_datadir}/pmm-ui install -p -m 0755 bin/pmm-managed %{buildroot}%{_sbindir}/pmm-managed +install -p -m 0755 bin/pmm-encryption-rotation %{buildroot}%{_sbindir}/pmm-encryption-rotation install -p -m 0755 bin/pmm-managed-init %{buildroot}%{_sbindir}/pmm-managed-init install -p -m 0755 bin/pmm-managed-starlark %{buildroot}%{_sbindir}/pmm-managed-starlark @@ -62,12 +63,16 @@ cp -pa ./ui/dist/. %{buildroot}%{_datadir}/pmm-ui %license src/%{provider}/LICENSE %doc src/%{provider}/README.md %{_sbindir}/pmm-managed +%{_sbindir}/pmm-encryption-rotation %{_sbindir}/pmm-managed-init %{_sbindir}/pmm-managed-starlark %{_datadir}/%{name} %{_datadir}/pmm-ui %changelog +* Mon Sep 23 2024 Jiri Ctvrtka - 3.0.0-1 +- PMM-13132 add PMM encryption rotation tool + * Fri Mar 22 2024 Matej Kubinec - 3.0.0-1 - PMM-11231 add pmm ui diff --git a/managed/Makefile b/managed/Makefile index 7c1dd10781..ce6c7ae65c 100644 --- a/managed/Makefile +++ b/managed/Makefile @@ -38,7 +38,7 @@ clean: ## Remove generated files release: ## Build pmm-managed release binaries env CGO_ENABLED=0 go build -v $(PMM_LD_FLAGS) -o $(PMM_RELEASE_PATH)/ ./cmd/... -release-encryption-rotation: ## Build PMM encryption rotation release binaries +release-encryption-rotation: ## Build PMM encryption rotation tool env CGO_ENABLED=0 go build -v $(PMM_LD_FLAGS) -o $(PMM_RELEASE_PATH)/ ./cmd/pmm-encryption-rotation/... release-starlark: diff --git a/managed/cmd/pmm-encryption-rotation/main.go b/managed/cmd/pmm-encryption-rotation/main.go index 3eef7e3199..2cd78bf0a4 100644 --- a/managed/cmd/pmm-encryption-rotation/main.go +++ b/managed/cmd/pmm-encryption-rotation/main.go @@ -16,6 +16,7 @@ package main import ( + "fmt" "os" "os/signal" "syscall" @@ -25,6 +26,7 @@ import ( "github.com/percona/pmm/managed/models" "github.com/percona/pmm/utils/logger" + "github.com/percona/pmm/version" ) const codeDBConnectionFailed = 1 @@ -34,6 +36,8 @@ func main() { logger.SetupGlobalLogger() + logrus.Infof("PMM Encryption Rotation Tools version: %s", version.Version) + sqlDB, err := models.OpenDB(setupParams()) if err != nil { logrus.Error(err) @@ -62,6 +66,7 @@ func setupParams() models.SetupDBParams { kong.Parse( &opts, kong.Name("encryption-rotation"), + kong.Description(fmt.Sprintf("Version %s", version.Version)), kong.UsageOnError(), kong.ConfigureHelp(kong.HelpOptions{ Compact: true, From 32a760d3f6c3c631dc7669385f507ac32fc7d699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 23 Sep 2024 16:34:26 +0200 Subject: [PATCH 212/215] PMM-13132 Move encryption rotation to services. --- managed/cmd/pmm-encryption-rotation/main.go | 3 ++- .../{models => services/encryption}/encryption_rotation.go | 7 ++++--- .../encryption}/encryption_rotation_test.go | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) rename managed/{models => services/encryption}/encryption_rotation.go (94%) rename managed/{models => services/encryption}/encryption_rotation_test.go (98%) diff --git a/managed/cmd/pmm-encryption-rotation/main.go b/managed/cmd/pmm-encryption-rotation/main.go index 2cd78bf0a4..1aa01325f0 100644 --- a/managed/cmd/pmm-encryption-rotation/main.go +++ b/managed/cmd/pmm-encryption-rotation/main.go @@ -25,6 +25,7 @@ import ( "github.com/sirupsen/logrus" "github.com/percona/pmm/managed/models" + encryptionService "github.com/percona/pmm/managed/services/encryption" "github.com/percona/pmm/utils/logger" "github.com/percona/pmm/version" ) @@ -44,7 +45,7 @@ func main() { os.Exit(codeDBConnectionFailed) } - statusCode := models.RotateEncryptionKey(sqlDB, "pmm-managed") + statusCode := encryptionService.RotateEncryptionKey(sqlDB, "pmm-managed") sqlDB.Close() //nolint:errcheck os.Exit(statusCode) diff --git a/managed/models/encryption_rotation.go b/managed/services/encryption/encryption_rotation.go similarity index 94% rename from managed/models/encryption_rotation.go rename to managed/services/encryption/encryption_rotation.go index d6f0ae4936..37872e545a 100644 --- a/managed/models/encryption_rotation.go +++ b/managed/services/encryption/encryption_rotation.go @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package models +package encryption import ( "database/sql" @@ -26,6 +26,7 @@ import ( "gopkg.in/reform.v1" "gopkg.in/reform.v1/dialects/postgresql" + "github.com/percona/pmm/managed/models" "github.com/percona/pmm/managed/utils/encryption" ) @@ -109,7 +110,7 @@ func isPMMServerStatus(status string) bool { func rotateEncryptionKey(db *reform.DB, dbName string) error { return db.InTransaction(func(tx *reform.TX) error { logrus.Infof("DB %s is being decrypted", dbName) - err := DecryptDB(tx, dbName, DefaultAgentEncryptionColumns) + err := models.DecryptDB(tx, dbName, models.DefaultAgentEncryptionColumns) if err != nil { return err } @@ -123,7 +124,7 @@ func rotateEncryptionKey(db *reform.DB, dbName string) error { logrus.Infof("New encryption key generated") logrus.Infof("DB %s is being encrypted", dbName) - err = EncryptDB(tx, dbName, DefaultAgentEncryptionColumns) + err = models.EncryptDB(tx, dbName, models.DefaultAgentEncryptionColumns) if err != nil { if e := encryption.RestoreOldEncryptionKey(); e != nil { return errors.Wrap(err, e.Error()) diff --git a/managed/models/encryption_rotation_test.go b/managed/services/encryption/encryption_rotation_test.go similarity index 98% rename from managed/models/encryption_rotation_test.go rename to managed/services/encryption/encryption_rotation_test.go index ae46912032..99a1579916 100644 --- a/managed/models/encryption_rotation_test.go +++ b/managed/services/encryption/encryption_rotation_test.go @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package models_test +package encryption import ( "database/sql" @@ -49,7 +49,7 @@ func TestEncryptionRotation(t *testing.T) { err = insertTestData(db) require.NoError(t, err) - statusCode := models.RotateEncryptionKey(db, "pmm-managed-dev") + statusCode := RotateEncryptionKey(db, "pmm-managed-dev") require.Equal(t, 0, statusCode) newEncryptionKey, err := os.ReadFile(encryptionKeyTestPath) From f4211bff2499255d6eb33f112c2f09647f1c3817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 24 Sep 2024 08:44:55 +0200 Subject: [PATCH 213/215] PMM-13132 Lint. --- managed/services/encryption/encryption_rotation.go | 1 + 1 file changed, 1 insertion(+) diff --git a/managed/services/encryption/encryption_rotation.go b/managed/services/encryption/encryption_rotation.go index 37872e545a..5754358d63 100644 --- a/managed/services/encryption/encryption_rotation.go +++ b/managed/services/encryption/encryption_rotation.go @@ -13,6 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +// Package encryption contains PMM encryption rotation functions. package encryption import ( From 6539cbf0e34115d75ae61ed97ba16c2c9b16d716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Tue, 24 Sep 2024 09:57:52 +0200 Subject: [PATCH 214/215] PMM-13132 Add interval and retries. --- .../encryption/encryption_rotation.go | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/managed/services/encryption/encryption_rotation.go b/managed/services/encryption/encryption_rotation.go index 5754358d63..f73f6d96ed 100644 --- a/managed/services/encryption/encryption_rotation.go +++ b/managed/services/encryption/encryption_rotation.go @@ -21,6 +21,7 @@ import ( "fmt" "os/exec" "strings" + "time" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -32,6 +33,8 @@ import ( ) const ( + retries = 5 + interval = 5 * time.Second statusRunning = "RUNNING" statusStopped = "STOPPED" codeOK = 0 @@ -66,7 +69,8 @@ func RotateEncryptionKey(sqlDB *sql.DB, dbName string) int { } func startPMMServer() error { - if isPMMServerStatus(statusRunning) { + logrus.Infoln("Starting PMM Server") + if pmmServerStatus(statusRunning) { return nil } @@ -76,7 +80,7 @@ func startPMMServer() error { return fmt.Errorf("%w: %s", err, output) } - if !isPMMServerStatus(statusRunning) { + if !pmmServerStatusWithRetries(statusRunning) { return errors.New("cannot start pmm-managed") } @@ -84,7 +88,8 @@ func startPMMServer() error { } func stopPMMServer() error { - if isPMMServerStatus(statusStopped) { + logrus.Infoln("Stopping PMM Server") + if pmmServerStatus(statusStopped) { return nil } @@ -94,20 +99,34 @@ func stopPMMServer() error { return fmt.Errorf("%w: %s", err, output) } - if !isPMMServerStatus(statusStopped) { + if !pmmServerStatusWithRetries(statusStopped) { return errors.New("cannot stop pmm-managed") } return nil } -func isPMMServerStatus(status string) bool { +func pmmServerStatus(status string) bool { cmd := exec.Command("supervisorctl", "status pmm-managed") output, _ := cmd.CombinedOutput() return strings.Contains(string(output), strings.ToUpper(status)) } +func pmmServerStatusWithRetries(status string) bool { + for i := 0; i < retries; i++ { + if !pmmServerStatus(status) { + logrus.Infoln("Retry...") + time.Sleep(interval) + continue + } + + return true + } + + return false +} + func rotateEncryptionKey(db *reform.DB, dbName string) error { return db.InTransaction(func(tx *reform.TX) error { logrus.Infof("DB %s is being decrypted", dbName) From 123f444c2e94bf76aedc746877a5860c52428d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C4=8Ctvrtka?= Date: Mon, 4 Nov 2024 11:03:48 +0100 Subject: [PATCH 215/215] PMM-13132 Fix after conflicts. --- managed/models/database.go | 1 - managed/utils/encryption/models.go | 45 ------------------------------ 2 files changed, 46 deletions(-) delete mode 100644 managed/utils/encryption/models.go diff --git a/managed/models/database.go b/managed/models/database.go index 737910d238..d7a9778721 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -27,7 +27,6 @@ import ( "net" "net/url" "os" - "slices" "strconv" "strings" diff --git a/managed/utils/encryption/models.go b/managed/utils/encryption/models.go deleted file mode 100644 index 257b49b1de..0000000000 --- a/managed/utils/encryption/models.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2023 Percona LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package encryption - -import "github.com/google/tink/go/tink" - -// Encryption contains fields required for encryption. -type Encryption struct { - Path string - Key string - Primitive tink.AEAD -} - -// Table represents table name, it's identifiers and columns to be encrypted/decrypted. -type Table struct { - Name string - Identifiers []string - Columns []Column -} - -// Column represents column name and column's custom handler (if needed). -type Column struct { - Name string - CustomHandler func(e *Encryption, val any) (any, error) -} - -// QueryValues represents query to update row after encrypt/decrypt. -type QueryValues struct { - Query string - SetValues [][]any - WhereValues [][]any -}