1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-24 15:59:41 +02:00

feat(database): add encryption support EE-1983 (#6316)

* bootstrap encryption key

* secret key message change in cli and secret key file content trimmed

* Migrate encryption code to latest version

* pull in newer code

* tidying up

* working data encryption layer

* fix tests

* remove stray comment

* fix a few minor issues and improve the comments

* split out databasefilename with param to two methods to be more obvious

* DB encryption integration (#6374)

* json methods moved under DBConnection

* store encryption fixed

* cleaned

* review comments addressed

* newstore value fixed

* backup test updated

* logrus format config updated

* Fix for newStore

Co-authored-by: Matt Hook <hookenz@gmail.com>

* Minor improvements

* Improve the export code.  Add missing webhook for import

* rename HelmUserRepositorys to HelmUserRepositories

* fix logging messages

* when starting portainer with a key (first use) http is disabled by default.  But when starting fresh without a key, http is enabled?

* Fix bug for default settings on new installs

Co-authored-by: Prabhat Khera <prabhat.khera@portainer.io>
Co-authored-by: Prabhat Khera <91852476+prabhat-org@users.noreply.github.com>
This commit is contained in:
Matt Hook 2022-01-17 16:40:02 +13:00 committed by GitHub
parent 59ec22f706
commit 34cc8ea96a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 548 additions and 147 deletions

View file

@ -11,39 +11,78 @@ import (
"github.com/boltdb/bolt"
"github.com/portainer/portainer/api/dataservices/errors"
"github.com/sirupsen/logrus"
)
const (
DatabaseFileName = "portainer.db"
DatabaseFileName = "portainer.db"
EncryptedDatabaseFileName = "portainer.edb"
)
type DbConnection struct {
Path string
Path string
EncryptionKey []byte
isEncrypted bool
*bolt.DB
}
func (connection *DbConnection) GetDatabaseFilename() string {
// GetDatabaseFileName get the database filename
func (connection *DbConnection) GetDatabaseFileName() string {
if connection.IsEncryptedStore() {
return EncryptedDatabaseFileName
}
return DatabaseFileName
}
// GetDataseFilePath get the path + filename for the database file
func (connection *DbConnection) GetDatabaseFilePath() string {
if connection.IsEncryptedStore() {
return path.Join(connection.Path, EncryptedDatabaseFileName)
}
return path.Join(connection.Path, DatabaseFileName)
}
// GetStorePath get the filename and path for the database file
func (connection *DbConnection) GetStorePath() string {
return connection.Path
}
func (connection *DbConnection) SetEncrypted(flag bool) {
connection.isEncrypted = flag
}
// Return true if the database is encrypted
func (connection *DbConnection) IsEncryptedStore() bool {
return connection.getEncryptionKey() != nil
}
// NeedsEncryptionMigration returns true if database encryption is enabled and
// we have an un-encrypted DB that requires migration to an encrypted DB
func (connection *DbConnection) NeedsEncryptionMigration() bool {
if connection.EncryptionKey != nil {
dbFile := path.Join(connection.Path, DatabaseFileName)
if _, err := os.Stat(dbFile); err == nil {
return true
}
// This is an existing encrypted store or a new store.
// A new store will open encrypted from the outset
connection.SetEncrypted(true)
}
return false
}
// Open opens and initializes the BoltDB database.
func (connection *DbConnection) Open() error {
// Disabled for now. Can't use feature flags due to the way that works
// databaseExportPath := path.Join(connection.Path, fmt.Sprintf("raw-%s-%d.json", DatabaseFileName, time.Now().Unix()))
// if err := connection.ExportRaw(databaseExportPath); err != nil {
// log.Printf("raw export to %s error: %s", databaseExportPath, err)
// } else {
// log.Printf("raw export to %s success", databaseExportPath)
// }
databasePath := path.Join(connection.Path, DatabaseFileName)
logrus.Infof("Loading PortainerDB: %s", connection.GetDatabaseFileName())
// Now we open the db
databasePath := connection.GetDatabaseFilePath()
db, err := bolt.Open(databasePath, 0600, &bolt.Options{Timeout: 1 * time.Second})
if err != nil {
return err
@ -71,12 +110,12 @@ func (connection *DbConnection) BackupTo(w io.Writer) error {
}
func (connection *DbConnection) ExportRaw(filename string) error {
databasePath := path.Join(connection.Path, DatabaseFileName)
databasePath := connection.GetDatabaseFilePath()
if _, err := os.Stat(databasePath); err != nil {
return fmt.Errorf("stat on %s failed: %s", databasePath, err)
}
b, err := exportJson(databasePath)
b, err := connection.exportJson(databasePath)
if err != nil {
return err
}
@ -124,7 +163,15 @@ func (connection *DbConnection) GetObject(bucketName string, key []byte, object
return err
}
return UnmarshalObject(data, object)
return connection.UnmarshalObject(data, object)
}
func (connection *DbConnection) getEncryptionKey() []byte {
if !connection.isEncrypted {
return nil
}
return connection.EncryptionKey
}
// UpdateObject is a generic function used to update an object inside a database database.
@ -132,7 +179,7 @@ func (connection *DbConnection) UpdateObject(bucketName string, key []byte, obje
return connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
data, err := MarshalObject(object)
data, err := connection.MarshalObject(object)
if err != nil {
return err
}
@ -163,7 +210,7 @@ func (connection *DbConnection) DeleteAllObjects(bucketName string, matching fun
cursor := bucket.Cursor()
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
var obj interface{}
err := UnmarshalObject(v, &obj)
err := connection.UnmarshalObject(v, &obj)
if err != nil {
return err
}
@ -205,7 +252,7 @@ func (connection *DbConnection) CreateObject(bucketName string, fn func(uint64)
seqId, _ := bucket.NextSequence()
id, obj := fn(seqId)
data, err := MarshalObject(obj)
data, err := connection.MarshalObject(obj)
if err != nil {
return err
}
@ -218,8 +265,7 @@ func (connection *DbConnection) CreateObject(bucketName string, fn func(uint64)
func (connection *DbConnection) CreateObjectWithId(bucketName string, id int, obj interface{}) error {
return connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
data, err := MarshalObject(obj)
data, err := connection.MarshalObject(obj)
if err != nil {
return err
}
@ -240,7 +286,7 @@ func (connection *DbConnection) CreateObjectWithSetSequence(bucketName string, i
return err
}
data, err := MarshalObject(obj)
data, err := connection.MarshalObject(obj)
if err != nil {
return err
}
@ -252,10 +298,9 @@ func (connection *DbConnection) CreateObjectWithSetSequence(bucketName string, i
func (connection *DbConnection) GetAll(bucketName string, obj interface{}, append func(o interface{}) (interface{}, error)) error {
err := connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
cursor := bucket.Cursor()
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
err := UnmarshalObject(v, obj)
err := connection.UnmarshalObject(v, obj)
if err != nil {
return err
}
@ -277,7 +322,7 @@ func (connection *DbConnection) GetAllWithJsoniter(bucketName string, obj interf
cursor := bucket.Cursor()
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
err := UnmarshalObjectWithJsoniter(v, obj)
err := connection.UnmarshalObjectWithJsoniter(v, obj)
if err != nil {
return err
}