Skip to content

Commit

Permalink
Merge branch 'v3' into PMM-13485-fix-ovf-pipeline
Browse files Browse the repository at this point in the history
  • Loading branch information
talhabinrizwan authored Nov 7, 2024
2 parents 5e0fc5d + 4266c88 commit 9e99f7f
Show file tree
Hide file tree
Showing 13 changed files with 582 additions and 136 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 5 additions & 0 deletions build/packages/rpm/server/SPECS/pmm-managed.spec
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 <[email protected]> - 3.0.0-1
- PMM-13132 add PMM encryption rotation tool

* Fri Mar 22 2024 Matej Kubinec <[email protected]> - 3.0.0-1
- PMM-11231 add pmm ui

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,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.17.1
go.starlark.net v0.0.0-20230717150657-8a3343210976
golang.org/x/crypto v0.28.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,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=
Expand Down
3 changes: 3 additions & 0 deletions managed/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 tool
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
Expand Down
95 changes: 95 additions & 0 deletions managed/cmd/pmm-encryption-rotation/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// 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 <https://www.gnu.org/licenses/>.
// Package main is the main package for encryption keys rotation.
package main

import (
"fmt"
"os"
"os/signal"
"syscall"

"github.com/alecthomas/kong"
"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"
)

const codeDBConnectionFailed = 1

func main() {
signal.Ignore(syscall.SIGINT, syscall.SIGTERM) // to prevent any interuptions during process

logger.SetupGlobalLogger()

logrus.Infof("PMM Encryption Rotation Tools version: %s", version.Version)

sqlDB, err := models.OpenDB(setupParams())
if err != nil {
logrus.Error(err)
os.Exit(codeDBConnectionFailed)
}

statusCode := encryptionService.RotateEncryptionKey(sqlDB, "pmm-managed")
sqlDB.Close() //nolint:errcheck

os.Exit(statusCode)
}

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}"` //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"`
}

func setupParams() models.SetupDBParams {
var opts flags
kong.Parse(
&opts,
kong.Name("encryption-rotation"),
kong.Description(fmt.Sprintf("Version %s", version.Version)),
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: opts.Address,
Name: opts.DBName,
Username: opts.DBUsername,
Password: opts.DBPassword,
SSLMode: opts.SSLMode,
SSLCAPath: opts.SSLCAPath,
SSLKeyPath: opts.SSLKeyPath,
SSLCertPath: opts.SSLCertPath,
}
}
91 changes: 53 additions & 38 deletions managed/models/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
"net"
"net/url"
"os"
"slices"
"strconv"
"strings"

Expand Down Expand Up @@ -61,6 +60,25 @@ const (
VerifyFullSSLMode string = "verify-full"
)

// 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"},
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: {
Expand Down Expand Up @@ -1149,79 +1167,76 @@ 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, DefaultAgentEncryptionColumns); err != nil {
return nil, err
}

return db, nil
}

// 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 {
func EncryptDB(tx *reform.TX, database string, itemsToEncrypt []encryption.Table) error {
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)
}

func dbEncryption(tx *reform.TX, database string, items []encryption.Table,
encryptionHandler func(tx *reform.TX, tables []encryption.Table) error,
expectedState bool,
) error {
if len(items) == 0 {
return nil
}

settings, err := GetSettings(tx)
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{}
for _, table := range itemsToEncrypt {
tables := []encryption.Table{}
prepared := []string{}
for _, table := range items {
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] == expectedState {
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 = encryptionHandler(tx, tables)
if err != nil {
return err
}

encryptedItems := []string{}
if expectedState {
encryptedItems = prepared
}

_, err = UpdateSettings(tx, &ChangeSettingsParams{
EncryptedItems: slices.Concat(settings.EncryptedItems, newlyEncrypted),
EncryptedItems: encryptedItems,
})
if err != nil {
return err
Expand Down Expand Up @@ -1325,7 +1340,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
}
Expand Down
3 changes: 2 additions & 1 deletion managed/models/settings_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,10 @@ func UpdateSettings(q reform.DBTX, params *ChangeSettingsParams) (*Settings, err
settings.DefaultRoleID = *params.DefaultRoleID
}

if len(params.EncryptedItems) != 0 {
if params.EncryptedItems != nil {
settings.EncryptedItems = params.EncryptedItems
}

err = SaveSettings(q, settings)
if err != nil {
return nil, err
Expand Down
Loading

0 comments on commit 9e99f7f

Please sign in to comment.