Skip to content

Commit

Permalink
feat: only encrypt sensitive parameters (#970)
Browse files Browse the repository at this point in the history
* feat: only encrypt sensitive parameters

* chore: expose the Sensitive property of a StackParameter
  • Loading branch information
tonsV2 authored Jan 13, 2025
1 parent b21f904 commit 422168c
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 51 deletions.
34 changes: 15 additions & 19 deletions pkg/instance/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,28 +107,18 @@ func (r repository) FindDeploymentInstanceById(ctx context.Context, id uint) (*m
return instance, nil
}

func (r repository) FindDecryptedDeploymentInstanceById(ctx context.Context, id uint) (*model.DeploymentInstance, error) {
instance, err := r.FindDeploymentInstanceById(ctx, id)
func (r repository) DecryptDeploymentInstance(deploymentInstance *model.DeploymentInstance, stack *model.Stack) (*model.DeploymentInstance, error) {
err := decryptParameters(r.instanceParameterEncryptionKey, deploymentInstance, stack)
if err != nil {
return nil, err
}

err = decryptParameters(r.instanceParameterEncryptionKey, instance)
if err != nil {
return nil, err
}

return instance, nil
return deploymentInstance, nil
}

func (r repository) FindDecryptedDeploymentById(ctx context.Context, id uint) (*model.Deployment, error) {
deployment, err := r.FindDeploymentById(ctx, id)
if err != nil {
return nil, err
}

func (r repository) DecryptDeployment(deployment *model.Deployment, stacksByName map[string]*model.Stack) (*model.Deployment, error) {
for _, instance := range deployment.Instances {
err := decryptParameters(r.instanceParameterEncryptionKey, instance)
err := decryptParameters(r.instanceParameterEncryptionKey, instance, stacksByName[instance.StackName])
if err != nil {
return nil, err
}
Expand All @@ -137,14 +127,14 @@ func (r repository) FindDecryptedDeploymentById(ctx context.Context, id uint) (*
return deployment, nil
}

func (r repository) SaveInstance(ctx context.Context, instance *model.DeploymentInstance) error {
func (r repository) SaveInstance(ctx context.Context, instance *model.DeploymentInstance, stack *model.Stack) error {
// only use ctx for values (logging) and not cancellation signals on cud operations for now. ctx
// cancellation can lead to rollbacks which we should decide individually.
ctx = context.WithoutCancel(ctx)

key := r.instanceParameterEncryptionKey

err := encryptParameters(key, instance)
err := encryptParameters(key, instance, stack)
if err != nil {
return err
}
Expand Down Expand Up @@ -204,8 +194,11 @@ func (r repository) FindPublicInstances(ctx context.Context) ([]*model.Deploymen
return instances, nil
}

func encryptParameters(key string, instance *model.DeploymentInstance) error {
func encryptParameters(key string, instance *model.DeploymentInstance, stack *model.Stack) error {
for i, parameter := range instance.Parameters {
if !stack.Parameters[parameter.ParameterName].Sensitive {
continue
}
value, err := encryptText(key, parameter.Value)
if err != nil {
return err
Expand All @@ -217,8 +210,11 @@ func encryptParameters(key string, instance *model.DeploymentInstance) error {
return nil
}

func decryptParameters(key string, instance *model.DeploymentInstance) error {
func decryptParameters(key string, instance *model.DeploymentInstance, stack *model.Stack) error {
for i, parameter := range instance.Parameters {
if !stack.Parameters[parameter.ParameterName].Sensitive {
continue
}
value, err := decryptText(key, parameter.Value)
if err != nil {
return err
Expand Down
50 changes: 43 additions & 7 deletions pkg/instance/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,41 @@ func (s Service) FindDeploymentById(ctx context.Context, id uint) (*model.Deploy
}

func (s Service) FindDecryptedDeploymentById(ctx context.Context, id uint) (*model.Deployment, error) {
return s.instanceRepository.FindDecryptedDeploymentById(ctx, id)
deployment, err := s.instanceRepository.FindDeploymentById(ctx, id)
if err != nil {
return nil, err
}

return s.decryptDeployment(deployment)
}

func (s Service) decryptDeployment(deployment *model.Deployment) (*model.Deployment, error) {
var stacksByName = map[string]*model.Stack{}
for _, instance := range deployment.Instances {
stack, err := s.stackService.Find(instance.StackName)
if err != nil {
return nil, err
}
stacksByName[instance.StackName] = stack
}

return s.instanceRepository.DecryptDeployment(deployment, stacksByName)
}

func (s Service) FindDeploymentInstanceById(ctx context.Context, id uint) (*model.DeploymentInstance, error) {
return s.instanceRepository.FindDeploymentInstanceById(ctx, id)
}

func (s Service) FindDecryptedDeploymentInstanceById(ctx context.Context, id uint) (*model.DeploymentInstance, error) {
return s.instanceRepository.FindDecryptedDeploymentInstanceById(ctx, id)
deploymentInstance, err := s.instanceRepository.FindDeploymentInstanceById(ctx, id)
if err != nil {
return nil, err
}
stack, err := s.stackService.Find(deploymentInstance.StackName)
if err != nil {
return nil, err
}
return s.instanceRepository.DecryptDeploymentInstance(deploymentInstance, stack)
}

func (s Service) SaveInstance(ctx context.Context, instance *model.DeploymentInstance) error {
Expand All @@ -83,24 +109,34 @@ func (s Service) SaveInstance(ctx context.Context, instance *model.DeploymentIns
return err
}

deployment, err := s.instanceRepository.FindDecryptedDeploymentById(ctx, instance.DeploymentID)
deployment, err := s.FindDeploymentById(ctx, instance.DeploymentID)
if err != nil {
return err
}

decryptedDeployment, err := s.decryptDeployment(deployment)
if err != nil {
return err
}

deployment.Instances = append(deployment.Instances, instance)
decryptedDeployment.Instances = append(decryptedDeployment.Instances, instance)

_, err = s.validateNoCycles(deployment.Instances)
_, err = s.validateNoCycles(decryptedDeployment.Instances)
if err != nil {
return errdef.NewBadRequest("failed to validate instance: %v", err)
}

err = s.resolveParameters(deployment)
err = s.resolveParameters(decryptedDeployment)
if err != nil {
return errdef.NewBadRequest("failed to resolve parameters: %v", err)
}

return s.instanceRepository.SaveInstance(ctx, instance)
stack, err := s.stackService.Find(instance.StackName)
if err != nil {
return err
}

return s.instanceRepository.SaveInstance(ctx, instance, stack)
}

func (s Service) rejectConsumedParameters(instance *model.DeploymentInstance) error {
Expand Down
3 changes: 2 additions & 1 deletion pkg/model/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ type StackParameter struct {
// Validator ensures that the actual stack parameters are valid according to its rules.
Validator func(value string) error `json:"-"`
// Priority determines the order in which the parameter is shown.
Priority uint `json:"priority"`
Priority uint `json:"priority"`
Sensitive bool `json:"sensitive"`
}

type ParameterProviders map[string]ParameterProvider
Expand Down
2 changes: 2 additions & 0 deletions pkg/stack/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func (h Handler) Find(c *gin.Context) {
DefaultValue: parameter.DefaultValue,
Consumed: parameter.Consumed,
Priority: parameter.Priority,
Sensitive: parameter.Sensitive,
})
}

Expand All @@ -81,6 +82,7 @@ type StackParameter struct {
DefaultValue *string `json:"defaultValue,omitempty"`
Consumed bool `json:"consumed"`
Priority uint `json:"priority"`
Sensitive bool `json:"sensitive"`
}

// swagger:model Stack
Expand Down
48 changes: 24 additions & 24 deletions pkg/stack/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ var DHIS2DB = model.Stack{
"DATABASE_ID": {Priority: 1, DisplayName: "Database"},
"DATABASE_SIZE": {Priority: 2, DisplayName: "Database Size", DefaultValue: &dhis2DBDefaults.dbSize},
"DATABASE_NAME": {Priority: 3, DisplayName: "Database Name", DefaultValue: &dhis2DBDefaults.dbName},
"DATABASE_PASSWORD": {Priority: 4, DisplayName: "Database Password", DefaultValue: &dhis2DBDefaults.dbPassword},
"DATABASE_USERNAME": {Priority: 5, DisplayName: "Database Username", DefaultValue: &dhis2DBDefaults.dbUsername},
"DATABASE_PASSWORD": {Priority: 4, DisplayName: "Database Password", DefaultValue: &dhis2DBDefaults.dbPassword, Sensitive: true},
"DATABASE_USERNAME": {Priority: 5, DisplayName: "Database Username", DefaultValue: &dhis2DBDefaults.dbUsername, Sensitive: true},
"DATABASE_VERSION": {Priority: 6, DisplayName: "Database Version", DefaultValue: &dhis2DBDefaults.dbVersion},
"RESOURCES_REQUESTS_CPU": {Priority: 7, DisplayName: "Resources Requests CPU", DefaultValue: &dhis2DBDefaults.resourcesRequestsCPU},
"RESOURCES_REQUESTS_MEMORY": {Priority: 8, DisplayName: "Resources Requests Memory", DefaultValue: &dhis2DBDefaults.resourcesRequestsMemory},
Expand Down Expand Up @@ -186,9 +186,9 @@ var DHIS2Core = model.Stack{
"IMAGE_PULL_POLICY": {Priority: 3, DisplayName: "Image Pull Policy", DefaultValue: &dhis2CoreDefaults.imagePullPolicy, Validator: imagePullPolicy},
"STORAGE_TYPE": {Priority: 4, DisplayName: "Storage type", DefaultValue: &dhis2CoreDefaults.storageType, Validator: storage},
"S3_BUCKET": {Priority: 5, DisplayName: "S3 bucket", DefaultValue: &dhis2CoreDefaults.s3Bucket},
"S3_REGION": {Priority: 6, DisplayName: "S3 region", DefaultValue: &dhis2CoreDefaults.s3Region},
"S3_IDENTITY": {Priority: 7, DisplayName: "S3 identity", DefaultValue: &dhis2CoreDefaults.s3Identity},
"S3_SECRET": {Priority: 8, DisplayName: "S3 secret", DefaultValue: &dhis2CoreDefaults.s3Secret},
"S3_REGION": {Priority: 6, DisplayName: "S3 region", DefaultValue: &dhis2CoreDefaults.s3Region, Sensitive: true},
"S3_IDENTITY": {Priority: 7, DisplayName: "S3 identity", DefaultValue: &dhis2CoreDefaults.s3Identity, Sensitive: true},
"S3_SECRET": {Priority: 8, DisplayName: "S3 secret", DefaultValue: &dhis2CoreDefaults.s3Secret, Sensitive: true},
"DHIS2_HOME": {Priority: 9, DisplayName: "DHIS2 Home Directory", DefaultValue: &dhis2CoreDefaults.dhis2Home},
"FLYWAY_MIGRATE_OUT_OF_ORDER": {Priority: 10, DisplayName: "Flyway Migrate Out Of Order", DefaultValue: &dhis2CoreDefaults.flywayMigrateOutOfOrder},
"FLYWAY_REPAIR_BEFORE_MIGRATION": {Priority: 11, DisplayName: "Flyway Repair Before Migration", DefaultValue: &dhis2CoreDefaults.flywayRepairBeforeMigration},
Expand All @@ -204,15 +204,15 @@ var DHIS2Core = model.Stack{
"MINIO_CHART_VERSION": {Priority: 21, DisplayName: "MinIO Chart Version", DefaultValue: &dhis2CoreDefaults.minIOChartVersion},
"MINIO_STORAGE_SIZE": {Priority: 22, DisplayName: "MinIO Storage Size", DefaultValue: &dhis2CoreDefaults.minIOStorageSize},
"ENABLE_QUERY_LOGGING": {Priority: 23, DisplayName: "Enable Query Logging", DefaultValue: &dhis2CoreDefaults.enableQueryLogging},
"GOOGLE_AUTH_PROJECT_ID": {Priority: 0, DisplayName: "Google auth project id", DefaultValue: &dhis2CoreDefaults.googleAuthClientId},
"GOOGLE_AUTH_PRIVATE_KEY": {Priority: 0, DisplayName: "Google auth private key", DefaultValue: &dhis2CoreDefaults.googleAuthPrivateKey},
"GOOGLE_AUTH_PRIVATE_KEY_ID": {Priority: 0, DisplayName: "Google auth private key id", DefaultValue: &dhis2CoreDefaults.googleAuthPrivateKeyId},
"GOOGLE_AUTH_CLIENT_EMAIL": {Priority: 0, DisplayName: "Google auth client email", DefaultValue: &dhis2CoreDefaults.googleAuthClientEmail},
"GOOGLE_AUTH_CLIENT_ID": {Priority: 0, DisplayName: "Google auth client id", DefaultValue: &dhis2CoreDefaults.googleAuthClientId},
"GOOGLE_AUTH_PROJECT_ID": {Priority: 0, DisplayName: "Google auth project id", DefaultValue: &dhis2CoreDefaults.googleAuthClientId, Sensitive: true},
"GOOGLE_AUTH_PRIVATE_KEY": {Priority: 0, DisplayName: "Google auth private key", DefaultValue: &dhis2CoreDefaults.googleAuthPrivateKey, Sensitive: true},
"GOOGLE_AUTH_PRIVATE_KEY_ID": {Priority: 0, DisplayName: "Google auth private key id", DefaultValue: &dhis2CoreDefaults.googleAuthPrivateKeyId, Sensitive: true},
"GOOGLE_AUTH_CLIENT_EMAIL": {Priority: 0, DisplayName: "Google auth client email", DefaultValue: &dhis2CoreDefaults.googleAuthClientEmail, Sensitive: true},
"GOOGLE_AUTH_CLIENT_ID": {Priority: 0, DisplayName: "Google auth client id", DefaultValue: &dhis2CoreDefaults.googleAuthClientId, Sensitive: true},
"DATABASE_HOSTNAME": {Priority: 0, DisplayName: "Database Hostname", Consumed: true},
"DATABASE_NAME": {Priority: 0, DisplayName: "Database Name", Consumed: true},
"DATABASE_PASSWORD": {Priority: 0, DisplayName: "Database Password", Consumed: true},
"DATABASE_USERNAME": {Priority: 0, DisplayName: "Database Username", Consumed: true},
"DATABASE_PASSWORD": {Priority: 0, DisplayName: "Database Password", Consumed: true, Sensitive: true},
"DATABASE_USERNAME": {Priority: 0, DisplayName: "Database Username", Consumed: true, Sensitive: true},
},
Requires: []model.Stack{
DHIS2DB,
Expand Down Expand Up @@ -291,9 +291,9 @@ var DHIS2 = model.Stack{
"IMAGE_PULL_POLICY": {Priority: 3, DisplayName: "Image Pull Policy", DefaultValue: &dhis2CoreDefaults.imagePullPolicy, Validator: imagePullPolicy},
"DATABASE_ID": {Priority: 4, DisplayName: "Database"},
"DATABASE_NAME": {Priority: 5, DisplayName: "Database Name", DefaultValue: &dhis2DBDefaults.dbName},
"DATABASE_PASSWORD": {Priority: 6, DisplayName: "Database Password", DefaultValue: &dhis2DBDefaults.dbPassword},
"DATABASE_PASSWORD": {Priority: 6, DisplayName: "Database Password", DefaultValue: &dhis2DBDefaults.dbPassword, Sensitive: true},
"DATABASE_SIZE": {Priority: 7, DisplayName: "Database Size", DefaultValue: &dhis2DBDefaults.dbSize},
"DATABASE_USERNAME": {Priority: 8, DisplayName: "Database Username", DefaultValue: &dhis2DBDefaults.dbUsername},
"DATABASE_USERNAME": {Priority: 8, DisplayName: "Database Username", DefaultValue: &dhis2DBDefaults.dbUsername, Sensitive: true},
"DATABASE_VERSION": {Priority: 9, DisplayName: "Database Version", DefaultValue: &dhis2DBDefaults.dbVersion},
"INSTALL_REDIS": {Priority: 10, DisplayName: "Install Redis", DefaultValue: &dhis2Defaults.installRedis},
"DHIS2_HOME": {Priority: 11, DisplayName: "DHIS2 Home Directory", DefaultValue: &dhis2CoreDefaults.dhis2Home},
Expand All @@ -311,11 +311,11 @@ var DHIS2 = model.Stack{
"CHART_VERSION": {Priority: 23, DisplayName: "Chart Version", DefaultValue: &dhis2CoreDefaults.chartVersion},
"JAVA_OPTS": {Priority: 24, DisplayName: "JAVA Options", DefaultValue: &dhis2CoreDefaults.javaOpts},
"ENABLE_QUERY_LOGGING": {Priority: 25, DisplayName: "Enable Query Logging", DefaultValue: &dhis2CoreDefaults.enableQueryLogging},
"GOOGLE_AUTH_PROJECT_ID": {Priority: 0, DisplayName: "Google auth project id", DefaultValue: &dhis2CoreDefaults.googleAuthClientId},
"GOOGLE_AUTH_PRIVATE_KEY": {Priority: 0, DisplayName: "Google auth private key", DefaultValue: &dhis2CoreDefaults.googleAuthPrivateKey},
"GOOGLE_AUTH_PRIVATE_KEY_ID": {Priority: 0, DisplayName: "Google auth private key id", DefaultValue: &dhis2CoreDefaults.googleAuthPrivateKeyId},
"GOOGLE_AUTH_CLIENT_EMAIL": {Priority: 0, DisplayName: "Google auth client email", DefaultValue: &dhis2CoreDefaults.googleAuthClientEmail},
"GOOGLE_AUTH_CLIENT_ID": {Priority: 0, DisplayName: "Google auth client id", DefaultValue: &dhis2CoreDefaults.googleAuthClientId},
"GOOGLE_AUTH_PROJECT_ID": {Priority: 0, DisplayName: "Google auth project id", DefaultValue: &dhis2CoreDefaults.googleAuthClientId, Sensitive: true},
"GOOGLE_AUTH_PRIVATE_KEY": {Priority: 0, DisplayName: "Google auth private key", DefaultValue: &dhis2CoreDefaults.googleAuthPrivateKey, Sensitive: true},
"GOOGLE_AUTH_PRIVATE_KEY_ID": {Priority: 0, DisplayName: "Google auth private key id", DefaultValue: &dhis2CoreDefaults.googleAuthPrivateKeyId, Sensitive: true},
"GOOGLE_AUTH_CLIENT_EMAIL": {Priority: 0, DisplayName: "Google auth client email", DefaultValue: &dhis2CoreDefaults.googleAuthClientEmail, Sensitive: true},
"GOOGLE_AUTH_CLIENT_ID": {Priority: 0, DisplayName: "Google auth client id", DefaultValue: &dhis2CoreDefaults.googleAuthClientId, Sensitive: true},
},
ParameterProviders: model.ParameterProviders{
"DATABASE_HOSTNAME": postgresHostnameProvider,
Expand All @@ -332,12 +332,12 @@ var dhis2Defaults = struct {
var PgAdmin = model.Stack{
Name: "pgadmin",
Parameters: model.StackParameters{
"PGADMIN_USERNAME": {Priority: 1, DisplayName: "pgAdmin Username"},
"PGADMIN_PASSWORD": {Priority: 2, DisplayName: "pgAdmin Password"},
"PGADMIN_USERNAME": {Priority: 1, DisplayName: "pgAdmin Username", Sensitive: true},
"PGADMIN_PASSWORD": {Priority: 2, DisplayName: "pgAdmin Password", Sensitive: true},
"CHART_VERSION": {Priority: 3, DisplayName: "Chart Version", DefaultValue: &pgAdminDefaults.chartVersion},
"DATABASE_HOSTNAME": {Priority: 0, DisplayName: "Database Hostname", Consumed: true},
"DATABASE_NAME": {Priority: 0, DisplayName: "Database Name", Consumed: true},
"DATABASE_USERNAME": {Priority: 0, DisplayName: "Database Username", Consumed: true},
"DATABASE_USERNAME": {Priority: 0, DisplayName: "Database Username", Consumed: true, Sensitive: true},
},
Requires: []model.Stack{
DHIS2DB,
Expand Down Expand Up @@ -386,9 +386,9 @@ var IMJobRunner = model.Stack{
"PAYLOAD": {Priority: 0, DisplayName: "Payload", DefaultValue: &imJobRunnerDefaults.payload},
"DHIS2_DATABASE_DATABASE": {Priority: 0, DisplayName: "DHIS2 Database Name", DefaultValue: &dhis2DBDefaults.dbName},
"DHIS2_DATABASE_HOSTNAME": {Priority: 0, DisplayName: "DHIS2 Database Hostname", DefaultValue: &imJobRunnerDefaults.dbHostname},
"DHIS2_DATABASE_PASSWORD": {Priority: 0, DisplayName: "DHIS2 Database Password", DefaultValue: &dhis2DBDefaults.dbPassword},
"DHIS2_DATABASE_PASSWORD": {Priority: 0, DisplayName: "DHIS2 Database Password", DefaultValue: &dhis2DBDefaults.dbPassword, Sensitive: true},
"DHIS2_DATABASE_PORT": {Priority: 0, DisplayName: "DHIS2 Database Port", DefaultValue: &imJobRunnerDefaults.dbPort},
"DHIS2_DATABASE_USERNAME": {Priority: 0, DisplayName: "DHIS2 Database Username", DefaultValue: &dhis2DBDefaults.dbUsername},
"DHIS2_DATABASE_USERNAME": {Priority: 0, DisplayName: "DHIS2 Database Username", DefaultValue: &dhis2DBDefaults.dbUsername, Sensitive: true},
"DHIS2_HOSTNAME": {Priority: 0, DisplayName: "DHIS2 Hostname", DefaultValue: &imJobRunnerDefaults.dhis2Hostname},
"CHART_VERSION": {Priority: 0, DisplayName: "Chart Version", DefaultValue: &imJobRunnerDefaults.chartVersion},
},
Expand Down
3 changes: 3 additions & 0 deletions swagger/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,9 @@ definitions:
format: uint64
type: integer
x-go-name: Priority
sensitive:
type: boolean
x-go-name: Sensitive
type: object
x-go-package: github.com/dhis2-sre/im-manager/pkg/stack
UpdateDatabaseRequest:
Expand Down

0 comments on commit 422168c

Please sign in to comment.