Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 33 additions & 8 deletions cmd/sops/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ func main() {
if c.GlobalString("config") != "" {
configPath = c.GlobalString("config")
} else {
configPath, err = findConfigFile()
configPath, err = findConfigFile(c, c.Args().First())
if err != nil {
return common.NewExitError(err, codes.ErrorGeneric)
}
Expand Down Expand Up @@ -740,8 +740,8 @@ func main() {
var configPath string
if c.GlobalString("config") != "" {
configPath = c.GlobalString("config")
} else {
configPath, err = findConfigFile()
} else if !c.GlobalBool("config-from-file-dir") {
configPath, err = findConfigFile(c, "")
if err != nil {
return common.NewExitError(err, codes.ErrorGeneric)
}
Expand All @@ -751,12 +751,23 @@ func main() {
}
failedCounter := 0
for _, path := range c.Args() {
fileConfigPath := configPath
if fileConfigPath == "" {
// --config-from-file-dir: every file may have its own config
var err error
fileConfigPath, err = findConfigFile(c, path)
if err != nil {
failedCounter++
log.Error(err)
continue
}
}
err := updatekeys.UpdateKeys(updatekeys.Opts{
InputPath: path,
ShamirThreshold: c.Int("shamir-secret-sharing-threshold"),
KeyServices: keyservices(c),
Interactive: !c.Bool("yes"),
ConfigPath: configPath,
ConfigPath: fileConfigPath,
InputType: c.String("input-type"),
})

Expand Down Expand Up @@ -1855,6 +1866,11 @@ func main() {
Usage: "path to sops' config file. If set, sops will not search for the config file recursively.",
EnvVar: "SOPS_CONFIG",
},
cli.BoolFlag{
Name: "config-from-file-dir",
Usage: "search for the config file starting from the target file's directory instead of the current directory. Ignored when --config is set.",
EnvVar: "SOPS_CONFIG_FROM_FILE_DIR",
},
cli.StringFlag{
Name: "encryption-context",
Usage: "comma separated list of KMS encryption context key:value pairs",
Expand Down Expand Up @@ -2361,8 +2377,17 @@ func keyservices(c *cli.Context) (svcs []keyservice.KeyServiceClient) {
}

// Wrapper of config.LookupConfigFile that takes care of handling the returned warning.
func findConfigFile() (string, error) {
result, err := config.LookupConfigFile(".")
// The search starts at the directory of file if --config-from-file-dir is set, and at the current directory otherwise.
func findConfigFile(c *cli.Context, file string) (string, error) {
start := "."
if c.GlobalBool("config-from-file-dir") {
start = file
if info, err := os.Stat(file); err == nil && info.IsDir() {
// LookupConfigFile starts at the directory part, so make the directory itself that part
start = file + string(filepath.Separator)
}
}
result, err := config.LookupConfigFile(start)
if len(result.Warning) > 0 && !showedConfigFileWarning {
showedConfigFileWarning = true
log.Warn(result.Warning)
Expand All @@ -2374,7 +2399,7 @@ func loadStoresConfig(context *cli.Context, path string) (*config.StoresConfig,
configPath := context.GlobalString("config")
if configPath == "" {
// Ignore config not found errors returned from findConfigFile since the config file is not mandatory
foundPath, err := findConfigFile()
foundPath, err := findConfigFile(context, path)
if err != nil {
return config.NewStoresConfig(), nil
}
Expand Down Expand Up @@ -2531,7 +2556,7 @@ func loadConfig(c *cli.Context, file string, kmsEncryptionContext map[string]*st
configPath := c.GlobalString("config")
if configPath == "" {
// Ignore config not found errors returned from findConfigFile since the config file is not mandatory
configPath, err = findConfigFile()
configPath, err = findConfigFile(c, file)
if err != nil {
// If we can't find a config file, but we were not explicitly requested to, assume it does not exist
return nil, nil
Expand Down
9 changes: 4 additions & 5 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package config //import "github.com/getsops/sops/v3/config"
import (
"fmt"
"os"
"path"
"path/filepath"
"regexp"
"strings"
Expand Down Expand Up @@ -53,11 +52,11 @@ type ConfigFileResult struct {
// and on parent directories, up to the maxDepth limit.
// It returns a result containing the file path and any warnings.
func LookupConfigFile(start string) (ConfigFileResult, error) {
filepath := path.Dir(start)
dir := filepath.Dir(start)
var foundAlternatePath string

for i := 0; i < maxDepth; i++ {
configPath := path.Join(filepath, configFileName)
configPath := filepath.Join(dir, configFileName)
_, err := fs.Stat(configPath)
if err == nil {
result := ConfigFileResult{Path: configPath}
Expand All @@ -72,14 +71,14 @@ func LookupConfigFile(start string) (ConfigFileResult, error) {

// Check for alternate filename if we haven't found one yet
if foundAlternatePath == "" {
alternatePath := path.Join(filepath, alternateConfigName)
alternatePath := filepath.Join(dir, alternateConfigName)
_, altErr := fs.Stat(alternatePath)
if altErr == nil {
foundAlternatePath = alternatePath
}
}

filepath = path.Join(filepath, "..")
dir = filepath.Join(dir, "..")
}

// No config file found
Expand Down
20 changes: 20 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path"
"path/filepath"
"testing"

"github.com/getsops/sops/v3/keys"
Expand Down Expand Up @@ -44,6 +45,25 @@ func TestFindConfigFileCurrentDir(t *testing.T) {
assert.Equal(t, expectedPath, filepath)
}

func TestFindConfigFileFromFilePath(t *testing.T) {
orig := fs
t.Cleanup(func() { fs = orig })
expectedPath := filepath.Join("a", ".sops.yaml")
fs = mockFS{stat: func(name string) (os.FileInfo, error) {
if name == expectedPath {
return nil, nil
}
return nil, &os.PathError{}
}}
found, err := FindConfigFile(filepath.Join("a", "b", "c", "secret.yaml"))
assert.Nil(t, err)
assert.Equal(t, expectedPath, found)

// the search starts at the file's directory, so a config next to the cwd is not found
_, err = FindConfigFile(filepath.Join("x", "secret.yaml"))
assert.NotNil(t, err)
}

var sampleConfig = []byte(`
creation_rules:
- path_regex: foobar*
Expand Down