diff --git a/cmd/sops/main.go b/cmd/sops/main.go index 81c412e3a..c676480eb 100644 --- a/cmd/sops/main.go +++ b/cmd/sops/main.go @@ -218,6 +218,10 @@ func main() { Name: "yes, y", Usage: `pre-approve all changes and run non-interactively`, }, + cli.BoolFlag{ + Name: "omit-extensions", + Usage: "Omit file extensions in destination path when publishing sops file to configured destinations", + }, cli.BoolFlag{ Name: "recurse", Usage: "If source path is directory, publish all its content recursively", @@ -240,12 +244,13 @@ func main() { } fileName := c.Args()[0] err = publishcmd.Run(publishcmd.Opts{ - ConfigPath: configPath, - InputPath: fileName, - Cipher: aes.NewCipher(), - KeyServices: keyservices(c), - Interactive: !c.Bool("yes"), - Recurse: c.Bool("recurse"), + ConfigPath: configPath, + InputPath: fileName, + Cipher: aes.NewCipher(), + KeyServices: keyservices(c), + Interactive: !c.Bool("yes"), + OmitExtensions: c.Bool("omit-extensions"), + Recurse: c.Bool("recurse"), }) if cliErr, ok := err.(*cli.ExitError); ok && cliErr != nil { return cliErr diff --git a/cmd/sops/subcommand/publish/publish.go b/cmd/sops/subcommand/publish/publish.go index 855c5f2b0..6c22988ca 100644 --- a/cmd/sops/subcommand/publish/publish.go +++ b/cmd/sops/subcommand/publish/publish.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "go.mozilla.org/sops/v3" "go.mozilla.org/sops/v3/cmd/sops/codes" @@ -27,13 +28,14 @@ func init() { // Opts represents publish options and config type Opts struct { - Interactive bool - Cipher sops.Cipher - ConfigPath string - InputPath string - KeyServices []keyservice.KeyServiceClient - InputStore sops.Store - Recurse bool + Interactive bool + Cipher sops.Cipher + ConfigPath string + InputPath string + KeyServices []keyservice.KeyServiceClient + InputStore sops.Store + OmitExtensions bool + Recurse bool } // Run publish operation @@ -65,7 +67,8 @@ func Run(opts Opts) error { } return nil } - opts.InputStore = common.DefaultStoreForPathOrFormat(path, filepath.Ext(path)) + fileSuffix := filepath.Ext(path) + opts.InputStore = common.DefaultStoreForPathOrFormat(path, fileSuffix) destinationPath := opts.InputPath conf, err := config.LoadDestinationRuleForFile(opts.ConfigPath, opts.InputPath, make(map[string]*string)) @@ -75,6 +78,9 @@ func Run(opts Opts) error { if conf.Destination == nil { return errors.New("no destination configured for this file") } + if opts.OmitExtensions || conf.OmitExtensions { + destinationPath = strings.TrimSuffix(destinationPath, fileSuffix) + } // Check that this is a sops-encrypted file tree, err := common.LoadEncryptedFile(opts.InputStore, opts.InputPath) diff --git a/config/config.go b/config/config.go index 8e42c3a6b..62f48913d 100644 --- a/config/config.go +++ b/config/config.go @@ -100,6 +100,7 @@ type destinationRule struct { VaultKVMountName string `yaml:"vault_kv_mount_name"` VaultKVVersion int `yaml:"vault_kv_version"` RecreationRule creationRule `yaml:"recreation_rule,omitempty"` + OmitExtensions bool `yaml:"omit_extensions"` } type creationRule struct { @@ -133,6 +134,7 @@ type Config struct { EncryptedSuffix string EncryptedRegex string Destination publish.Destination + OmitExtensions bool } func getKeyGroupsFromCreationRule(cRule *creationRule, kmsEncryptionContext map[string]*string) ([]sops.KeyGroup, error) { @@ -266,6 +268,7 @@ func parseDestinationRuleForFile(conf *configFile, filePath string, kmsEncryptio return nil, err } config.Destination = dest + config.OmitExtensions = dRule.OmitExtensions return config, nil }