diff --git a/cli/config.go b/cli/config.go index 73b6c0c7..637751b9 100644 --- a/cli/config.go +++ b/cli/config.go @@ -22,11 +22,12 @@ const configFlag = "config" // Config contains the application configuration. type Config struct { - Log logger.LogConfig `mapstructure:"log"` - Syncer SyncerConf `mapstructure:"syncer"` - Service ServeConfig `mapstructure:"service"` - PGConnStr string `mapstructure:"pg_conn_str" default:"postgres://postgres@localhost:5432/entropy?sslmode=disable"` - Telemetry telemetry.Config `mapstructure:"telemetry"` + Log logger.LogConfig `mapstructure:"log"` + Syncer SyncerConf `mapstructure:"syncer"` + Service ServeConfig `mapstructure:"service"` + PGConnStr string `mapstructure:"pg_conn_str" default:"postgres://postgres@localhost:5432/entropy?sslmode=disable"` + Telemetry telemetry.Config `mapstructure:"telemetry"` + SecretMask string `mapstructure:"secret_mask"` } type SyncerConf struct { diff --git a/cli/serve.go b/cli/serve.go index 095f33bd..85a243a5 100644 --- a/cli/serve.go +++ b/cli/serve.go @@ -63,7 +63,7 @@ func StartServer(ctx context.Context, cfg Config, migrate, spawnWorker bool) err ) store := setupStorage(cfg.PGConnStr, cfg.Syncer, cfg.Service) - moduleService := module.NewService(setupRegistry(), store) + moduleService := module.NewService(setupRegistry(), store, cfg.SecretMask) resourceService := core.New(store, moduleService, time.Now, cfg.Syncer.SyncBackoffInterval, cfg.Syncer.MaxRetries) if migrate { diff --git a/cli/worker.go b/cli/worker.go index 7c79114c..0600e64e 100644 --- a/cli/worker.go +++ b/cli/worker.go @@ -57,7 +57,7 @@ func cmdWorker() *cobra.Command { func StartWorkers(ctx context.Context, cfg Config) error { store := setupStorage(cfg.PGConnStr, cfg.Syncer, cfg.Service) - moduleService := module.NewService(setupRegistry(), store) + moduleService := module.NewService(setupRegistry(), store, cfg.SecretMask) resourceService := core.New(store, moduleService, time.Now, cfg.Syncer.SyncBackoffInterval, cfg.Syncer.MaxRetries) eg := &errgroup.Group{} diff --git a/core/core.go b/core/core.go index e8a97d9d..84a1fbf0 100644 --- a/core/core.go +++ b/core/core.go @@ -18,6 +18,7 @@ type Service struct { moduleSvc ModuleService syncBackoff time.Duration maxSyncRetries int + secretMask string } type ModuleService interface { @@ -25,6 +26,7 @@ type ModuleService interface { SyncState(ctx context.Context, res module.ExpandedResource) (*resource.State, error) StreamLogs(ctx context.Context, res module.ExpandedResource, filter map[string]string) (<-chan module.LogChunk, error) GetOutput(ctx context.Context, res module.ExpandedResource) (json.RawMessage, error) + MaskSecrets(ctx context.Context, res resource.Resource) (resource.Resource, error) } func New(repo resource.Store, moduleSvc ModuleService, clockFn func() time.Time, syncBackoffInterval time.Duration, maxRetries int) *Service { diff --git a/core/module/module.go b/core/module/module.go index a5611be9..6b579655 100644 --- a/core/module/module.go +++ b/core/module/module.go @@ -29,6 +29,7 @@ type Descriptor struct { Actions []ActionDesc `json:"actions"` Dependencies map[string]string `json:"dependencies"` DriverFactory func(conf json.RawMessage) (Driver, error) `json:"-"` + Secrets []string `json:"secrets"` } // Registry is responsible for installing and managing module-drivers as per diff --git a/core/module/service.go b/core/module/service.go index d2c5ddef..2c41a392 100644 --- a/core/module/service.go +++ b/core/module/service.go @@ -7,17 +7,20 @@ import ( "github.com/goto/entropy/core/resource" "github.com/goto/entropy/pkg/errors" + jsonpkg "github.com/goto/entropy/pkg/json" ) type Service struct { - store Store - registry Registry + store Store + registry Registry + secretMask string } -func NewService(registry Registry, store Store) *Service { +func NewService(registry Registry, store Store, secretMask string) *Service { return &Service{ - store: store, - registry: registry, + store: store, + registry: registry, + secretMask: secretMask, } } @@ -177,3 +180,30 @@ func (mr *Service) initDriver(ctx context.Context, mod Module) (Driver, Descript func generateURN(name, project string) string { return fmt.Sprintf("orn:entropy:module:%s:%s", project, name) } + +func (mr *Service) MaskSecrets(ctx context.Context, res resource.Resource) (resource.Resource, error) { + mod, err := mr.discoverModule(ctx, res.Kind, res.Project) + if err != nil { + return resource.Resource{}, err + } + + _, desc, err := mr.initDriver(ctx, *mod) + if err != nil { + return resource.Resource{}, err + } + + for _, secret := range desc.Secrets { + maskedResSpecConfigs, err := jsonpkg.SetJSONField(res.Spec.Configs, secret, mr.secretMask) + if err != nil { + return resource.Resource{}, errors.ErrInternal.WithCausef(err.Error()) + } + res.Spec.Configs = maskedResSpecConfigs + + maskedResStateOutput, err := jsonpkg.SetJSONField(res.State.Output, secret, mr.secretMask) + if err != nil { + return resource.Resource{}, errors.ErrInternal.WithCausef(err.Error()) + } + res.State.Output = maskedResStateOutput + } + return res, nil +} diff --git a/core/read.go b/core/read.go index 36f67060..85d6c6a8 100644 --- a/core/read.go +++ b/core/read.go @@ -36,6 +36,11 @@ func (svc *Service) GetResource(ctx context.Context, urn string) (*resource.Reso } } + *res, err = svc.moduleSvc.MaskSecrets(ctx, *res) + if err != nil { + return nil, err + } + return res, nil } diff --git a/core/read_test.go b/core/read_test.go index 4df4262b..f1fee1fa 100644 --- a/core/read_test.go +++ b/core/read_test.go @@ -17,6 +17,7 @@ import ( const ( defaultMaxRetries = 5 defaultSyncBackoff = 5 * time.Second + secretMask = "[MASKED]" ) func TestService_GetResource(t *testing.T) { diff --git a/modules/registry.go b/modules/registry.go index 6853399c..a6505df5 100644 --- a/modules/registry.go +++ b/modules/registry.go @@ -2,7 +2,10 @@ package modules import ( "context" + "fmt" + "os" "reflect" + "strings" "sync" "github.com/goto/entropy/core/module" @@ -53,6 +56,10 @@ func (mr *Registry) Register(desc module.Descriptor) error { } desc.Actions[i] = action } + + secretKey := fmt.Sprintf("%s_SECRET", strings.ToUpper(desc.Kind)) + secrets := os.Getenv(secretKey) + desc.Secrets = strings.Split(secrets, ",") mr.modules[desc.Kind] = desc return nil } diff --git a/pkg/json/json.go b/pkg/json/json.go new file mode 100644 index 00000000..896cac58 --- /dev/null +++ b/pkg/json/json.go @@ -0,0 +1,17 @@ +package json + +import ( + "encoding/json" + + "github.com/tidwall/sjson" +) + +func SetJSONField(json json.RawMessage, key string, value interface{}) (json.RawMessage, error) { + sjson, err := sjson.SetBytes(json, key, value) + + if err != nil { + return nil, err + } + + return sjson, nil +}