Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PMM-13132 Encryption rotation. #3199

Open
wants to merge 62 commits into
base: PMM-13129-encryption
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
74edcb1
PMM-13132 Basics.
JiriCtvrtka Sep 12, 2024
3e62767
PMM-13132 Some changes.
JiriCtvrtka Sep 12, 2024
97bed88
PMM-13132 Make format.
JiriCtvrtka Sep 12, 2024
50fead6
PMM-13132 Mod fix, tidy.
JiriCtvrtka Sep 12, 2024
fcf215d
PMM-13132 Fix.
JiriCtvrtka Sep 12, 2024
57a2024
PMM-13132 Changes.
JiriCtvrtka Sep 12, 2024
937bbcd
PMM-13132 Changes.
JiriCtvrtka Sep 12, 2024
8961b3f
PMM-13132 Rotation.
JiriCtvrtka Sep 16, 2024
0176934
PMM-13132 Format.
JiriCtvrtka Sep 16, 2024
65d2234
PMM-13132 Changes.
JiriCtvrtka Sep 16, 2024
782e82f
PMM-13132 Fix.
JiriCtvrtka Sep 16, 2024
2cb54e1
PMM-13132 Backup and restore of previous key.
JiriCtvrtka Sep 16, 2024
b0ddeab
PMM-13132 Changes.
JiriCtvrtka Sep 16, 2024
ad3fec9
PMM-13132 Lint.
JiriCtvrtka Sep 16, 2024
cb5b6c5
Merge branch 'PMM-13129-encryption' into PMM-13132-encryption-rotation
JiriCtvrtka Sep 18, 2024
50f0c83
PMM-13132 Correct message.
JiriCtvrtka Sep 18, 2024
07edcc7
PMM-13132 Changes related to tests.
JiriCtvrtka Sep 18, 2024
4a0588b
Merge branch 'PMM-13129-encryption' into PMM-13132-encryption-rotation
JiriCtvrtka Sep 18, 2024
c8e275f
PMM-13132 Test for whole cycle.
JiriCtvrtka Sep 18, 2024
8107431
PMM-13132 Handle OS interuptions.
JiriCtvrtka Sep 18, 2024
a60eda2
PMM-13132 Lint.
JiriCtvrtka Sep 18, 2024
3474974
PMM-13132 Lint.
JiriCtvrtka Sep 18, 2024
7e58301
PMM-13132 Logger and logs.
JiriCtvrtka Sep 18, 2024
e8f94bf
PMM-13132 Test DB.
JiriCtvrtka Sep 18, 2024
5fb3fe6
Revert "PMM-13132 Test DB."
JiriCtvrtka Sep 18, 2024
977c64f
PMM-13132 Changes, CI.
JiriCtvrtka Sep 18, 2024
be0c4b7
Merge branch 'PMM-13129-encryption' into PMM-13132-encryption-rotation
JiriCtvrtka Sep 19, 2024
7986a5b
PMM-13132 Fix in test.
JiriCtvrtka Sep 19, 2024
236ade5
PMM-13132 Changes.
JiriCtvrtka Sep 19, 2024
d5e479d
PMM-13132 Skip encryption-rotation test in main test.
JiriCtvrtka Sep 19, 2024
7ff24b6
PMM-13132 Basic makefile for encryption-rotation.
JiriCtvrtka Sep 19, 2024
f59d89c
PMM-13132 Remove duplicate defaults.
JiriCtvrtka Sep 19, 2024
699f31e
PMM-13132 Changes in workflow.
JiriCtvrtka Sep 19, 2024
a047d3e
PMM-13132 Remove devcontainer from makefile.
JiriCtvrtka Sep 19, 2024
45125df
PMM-13132 Add ENV variable for rotation key.
JiriCtvrtka Sep 19, 2024
6f4a525
PMM-13132 Add PG.
JiriCtvrtka Sep 19, 2024
5e6a3d8
PMM-13132 Remove user, pass in PG compose.
JiriCtvrtka Sep 19, 2024
fbc86a8
PMM-13132 Test of user.
JiriCtvrtka Sep 19, 2024
da0ff75
PMM-13132 Change path for test.
JiriCtvrtka Sep 19, 2024
ae7094f
PMM-13132 Test of simpler structure.
JiriCtvrtka Sep 19, 2024
e3d146a
PMM-13132 Another changes in structure.
JiriCtvrtka Sep 19, 2024
d38a8ca
PMM-13132 Another changes to simplify rotation.
JiriCtvrtka Sep 19, 2024
d978cc3
PMM-13132 Format.
JiriCtvrtka Sep 19, 2024
ec82c91
PMM-13132 Improvements.
JiriCtvrtka Sep 19, 2024
b13abe8
PMM-13132 Add command to makefile, lint.
JiriCtvrtka Sep 19, 2024
7aeaec5
PMM-13132 Lint.
JiriCtvrtka Sep 19, 2024
096fc93
PMM-13132 Lint.
JiriCtvrtka Sep 19, 2024
229e08a
PMM-13132 Wrappers around default on newly added methods.
JiriCtvrtka Sep 19, 2024
89692ac
PMM-13132 Move into cmd of pmm-managed.
JiriCtvrtka Sep 20, 2024
f603530
PMM-13132 Suggested refactor.
JiriCtvrtka Sep 20, 2024
7fab00f
PMM-13132 Another suggested refactor.
JiriCtvrtka Sep 20, 2024
8a86a53
PMM-13132 Fix.
JiriCtvrtka Sep 20, 2024
e27d3b0
PMM-13132 Move encryption models into encryption file.
JiriCtvrtka Sep 23, 2024
07921cf
PMM-13132 Make.
JiriCtvrtka Sep 23, 2024
8d9d4cc
PMM-13132 Migration to kong.
JiriCtvrtka Sep 23, 2024
999c4c8
PMM-13132 Add consts.
JiriCtvrtka Sep 23, 2024
dd463b6
PMM-13132 Lint.
JiriCtvrtka Sep 23, 2024
6dbc1f9
PMM-13132 Specs.
JiriCtvrtka Sep 23, 2024
3f00936
Merge branch 'PMM-13129-encryption' into PMM-13132-encryption-rotation
JiriCtvrtka Sep 23, 2024
32a760d
PMM-13132 Move encryption rotation to services.
JiriCtvrtka Sep 23, 2024
f4211bf
PMM-13132 Lint.
JiriCtvrtka Sep 24, 2024
6539cbf
PMM-13132 Add interval and retries.
JiriCtvrtka Sep 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -66,6 +66,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
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,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
JiriCtvrtka marked this conversation as resolved.
Show resolved Hide resolved
}

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
Loading