diff --git a/cmd/sops/common/common.go b/cmd/sops/common/common.go index fa54ecc7d..03d6f340a 100644 --- a/cmd/sops/common/common.go +++ b/cmd/sops/common/common.go @@ -10,6 +10,7 @@ import ( "github.com/getsops/sops/v3" "github.com/getsops/sops/v3/cmd/sops/codes" . "github.com/getsops/sops/v3/cmd/sops/formats" + "github.com/getsops/sops/v3/config" "github.com/getsops/sops/v3/keys" "github.com/getsops/sops/v3/keyservice" "github.com/getsops/sops/v3/kms" @@ -29,32 +30,37 @@ type ExampleFileEmitter interface { EmitExample() []byte } +type Configurable interface { + Configure(*config.Config) +} + // Store handles marshaling and unmarshaling from SOPS files type Store interface { sops.Store ExampleFileEmitter + Configurable } -type storeConstructor = func() Store +type storeConstructor = func(*config.StoresConfig) Store -func newBinaryStore() Store { - return &json.BinaryStore{} +func newBinaryStore(c *config.StoresConfig) Store { + return json.NewBinaryStore(&c.JSONBinary) } -func newDotenvStore() Store { - return &dotenv.Store{} +func newDotenvStore(c *config.StoresConfig) Store { + return dotenv.NewStore(&c.Dotenv) } -func newIniStore() Store { - return &ini.Store{} +func newIniStore(c *config.StoresConfig) Store { + return ini.NewStore(&c.INI) } -func newJsonStore() Store { - return &json.Store{} +func newJsonStore(c *config.StoresConfig) Store { + return json.NewStore(&c.JSON) } -func newYamlStore() Store { - return &yaml.Store{} +func newYamlStore(c *config.StoresConfig) Store { + return yaml.NewStore(&c.YAML) } var storeConstructors = map[Format]storeConstructor{ @@ -153,27 +159,27 @@ func NewExitError(i interface{}, exitCode int) *cli.ExitError { // StoreForFormat returns the correct format-specific implementation // of the Store interface given the format. -func StoreForFormat(format Format) Store { +func StoreForFormat(format Format, c *config.StoresConfig) Store { storeConst, found := storeConstructors[format] if !found { storeConst = storeConstructors[Binary] // default } - return storeConst() + return storeConst(c) } // DefaultStoreForPath returns the correct format-specific implementation // of the Store interface given the path to a file -func DefaultStoreForPath(path string) Store { +func DefaultStoreForPath(c *config.StoresConfig, path string) Store { format := FormatForPath(path) - return StoreForFormat(format) + return StoreForFormat(format, c) } // DefaultStoreForPathOrFormat returns the correct format-specific implementation // of the Store interface given the formatString if specified, or the path to a file. // This is to support the cli, where both are provided. -func DefaultStoreForPathOrFormat(path, format string) Store { +func DefaultStoreForPathOrFormat(c *config.StoresConfig, path string, format string) Store { formatFmt := FormatForPathOrString(path, format) - return StoreForFormat(formatFmt) + return StoreForFormat(formatFmt, c) } // KMS_ENC_CTX_BUG_FIXED_VERSION represents the SOPS version in which the diff --git a/cmd/sops/main.go b/cmd/sops/main.go index c78b51478..177f7af48 100644 --- a/cmd/sops/main.go +++ b/cmd/sops/main.go @@ -1066,11 +1066,29 @@ func keyservices(c *cli.Context) (svcs []keyservice.KeyServiceClient) { } func inputStore(context *cli.Context, path string) common.Store { - return common.DefaultStoreForPathOrFormat(path, context.String("input-type")) + var configPath string + if context.String("config") != "" { + configPath = context.String("config") + } else { + // Ignore config not found errors returned from FindConfigFile since the config file is not mandatory + configPath, _ = config.FindConfigFile(".") + } + storesConf, _ := config.LoadStoresConfig(configPath) + + return common.DefaultStoreForPathOrFormat(storesConf, path, context.String("input-type")) } func outputStore(context *cli.Context, path string) common.Store { - return common.DefaultStoreForPathOrFormat(path, context.String("output-type")) + var configPath string + if context.String("config") != "" { + configPath = context.String("config") + } else { + // Ignore config not found errors returned from FindConfigFile since the config file is not mandatory + configPath, _ = config.FindConfigFile(".") + } + storesConf, _ := config.LoadStoresConfig(configPath) + + return common.DefaultStoreForPathOrFormat(storesConf, path, context.String("output-type")) } func parseTreePath(arg string) ([]interface{}, error) { diff --git a/cmd/sops/subcommand/updatekeys/updatekeys.go b/cmd/sops/subcommand/updatekeys/updatekeys.go index 6bb105864..66ea01e22 100644 --- a/cmd/sops/subcommand/updatekeys/updatekeys.go +++ b/cmd/sops/subcommand/updatekeys/updatekeys.go @@ -40,7 +40,11 @@ func UpdateKeys(opts Opts) error { } func updateFile(opts Opts) error { - store := common.DefaultStoreForPathOrFormat(opts.InputPath, opts.InputType) + sc, err := config.LoadStoresConfig(opts.ConfigPath) + if err != nil { + return err + } + store := common.DefaultStoreForPath(sc, opts.InputPath) log.Printf("Syncing keys for file %s", opts.InputPath) tree, err := common.LoadEncryptedFile(store, opts.InputPath) if err != nil { diff --git a/config/config.go b/config/config.go index 67ddea1bb..113a549f1 100644 --- a/config/config.go +++ b/config/config.go @@ -63,9 +63,30 @@ func FindConfigFile(start string) (string, error) { return "", fmt.Errorf("Config file not found") } +type DotenvStoreConfig struct{} + +type INIStoreConfig struct{} + +type JSONStoreConfig struct{} + +type JSONBinaryStoreConfig struct{} + +type YAMLStoreConfig struct { + Indent int `yaml:"indent"` +} + +type StoresConfig struct { + Dotenv DotenvStoreConfig `yaml:"dotenv"` + INI INIStoreConfig `yaml:"ini"` + JSONBinary JSONBinaryStoreConfig `yaml:"json_binary"` + JSON JSONStoreConfig `yaml:"json"` + YAML YAMLStoreConfig `yaml:"yaml"` +} + type configFile struct { CreationRules []creationRule `yaml:"creation_rules"` DestinationRules []destinationRule `yaml:"destination_rules"` + Stores StoresConfig `yaml:"stores"` } type keyGroup struct { @@ -386,3 +407,11 @@ func LoadDestinationRuleForFile(confPath string, filePath string, kmsEncryptionC } return parseDestinationRuleForFile(conf, filePath, kmsEncryptionContext) } + +func LoadStoresConfig(confPath string) (*StoresConfig, error) { + conf, err := loadConfigFile(confPath) + if err != nil { + return nil, err + } + return &conf.Stores, nil +} diff --git a/stores/dotenv/store.go b/stores/dotenv/store.go index fad0f3494..e930d3960 100644 --- a/stores/dotenv/store.go +++ b/stores/dotenv/store.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/getsops/sops/v3" + "github.com/getsops/sops/v3/config" "github.com/getsops/sops/v3/stores" ) @@ -16,6 +17,11 @@ const SopsPrefix = "sops_" // Store handles storage of dotenv data type Store struct { + config config.DotenvStoreConfig +} + +func NewStore(c *config.DotenvStoreConfig) *Store { + return &Store{config: *c} } // LoadEncryptedFile loads an encrypted file's bytes onto a sops.Tree runtime object @@ -196,3 +202,6 @@ func isComplexValue(v interface{}) bool { } return false } + +func (store *Store) Configure(c *config.Config) { +} diff --git a/stores/ini/store.go b/stores/ini/store.go index 6485467f1..b74705e11 100644 --- a/stores/ini/store.go +++ b/stores/ini/store.go @@ -9,12 +9,18 @@ import ( "strings" "github.com/getsops/sops/v3" + "github.com/getsops/sops/v3/config" "github.com/getsops/sops/v3/stores" "gopkg.in/ini.v1" ) // Store handles storage of ini data. type Store struct { + config *config.INIStoreConfig +} + +func NewStore(c *config.INIStoreConfig) *Store { + return &Store{config: c} } func (store Store) encodeTree(branches sops.TreeBranches) ([]byte, error) { @@ -282,3 +288,6 @@ func (store *Store) EmitExample() []byte { } return bytes } + +func (store *Store) Configure(c *config.Config) { +} diff --git a/stores/json/store.go b/stores/json/store.go index 16a4b5d05..bcd8bdba9 100644 --- a/stores/json/store.go +++ b/stores/json/store.go @@ -8,16 +8,27 @@ import ( "io" "github.com/getsops/sops/v3" + "github.com/getsops/sops/v3/config" "github.com/getsops/sops/v3/stores" ) // Store handles storage of JSON data. type Store struct { + config config.JSONStoreConfig +} + +func NewStore(c *config.JSONStoreConfig) *Store { + return &Store{config: *c} } // BinaryStore handles storage of binary data in a JSON envelope. type BinaryStore struct { - store Store + store Store + config config.JSONBinaryStoreConfig +} + +func NewBinaryStore(c *config.JSONBinaryStoreConfig) *BinaryStore { + return &BinaryStore{config: *c} } // LoadEncryptedFile loads an encrypted json file onto a sops.Tree object @@ -337,3 +348,9 @@ func (store *Store) EmitExample() []byte { } return bytes } + +func (store *Store) Configure(c *config.Config) { +} + +func (store *BinaryStore) Configure(c *config.Config) { +} diff --git a/stores/yaml/store.go b/stores/yaml/store.go index 29fe2652a..74138d468 100644 --- a/stores/yaml/store.go +++ b/stores/yaml/store.go @@ -7,12 +7,18 @@ import ( "strings" "github.com/getsops/sops/v3" + "github.com/getsops/sops/v3/config" "github.com/getsops/sops/v3/stores" "gopkg.in/yaml.v3" ) // Store handles storage of YAML data type Store struct { + config config.YAMLStoreConfig +} + +func NewStore(c *config.YAMLStoreConfig) *Store { + return &Store{config: *c} } func (store Store) appendCommentToList(comment string, list []interface{}) []interface{} { @@ -391,3 +397,6 @@ func (store *Store) EmitExample() []byte { } return bytes } + +func (store *Store) Configure(c *config.Config) { +}