From ed42eca94607b707211ceaae661af0399fcf9ef0 Mon Sep 17 00:00:00 2001 From: Jan-Philipp Benecke Date: Mon, 12 Apr 2021 22:20:32 +0200 Subject: [PATCH] Make creds file executable (#1119) * Make creds file executable * Implementing code review * Abstract reading creds file into function * Implement code review --- providers/config/providerConfig.go | 60 ++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/providers/config/providerConfig.go b/providers/config/providerConfig.go index 4fdb7028a..3c2951385 100644 --- a/providers/config/providerConfig.go +++ b/providers/config/providerConfig.go @@ -8,24 +8,36 @@ import ( "encoding/json" "fmt" "os" + "os/exec" + "path/filepath" "strings" "github.com/DisposaBoy/JsonConfigReader" "github.com/TomOnTime/utfutil" ) -// LoadProviderConfigs will open the specified file name, and parse its contents. It will replace environment variables it finds if any value matches $[A-Za-z_-0-9]+ +// LoadProviderConfigs will open or execute the specified file name, and parse its contents. It will replace environment variables it finds if any value matches $[A-Za-z_-0-9]+ func LoadProviderConfigs(fname string) (map[string]map[string]string, error) { var results = map[string]map[string]string{} - dat, err := utfutil.ReadFile(fname, utfutil.POSIX) - if err != nil { - // no creds file is ok. Bind requires nothing for example. Individual providers will error if things not found. - if os.IsNotExist(err) { - fmt.Printf("INFO: Config file %q does not exist. Skipping.\n", fname) - return results, nil + + var dat []byte + var err error + + if strings.HasPrefix(fname, "!") { + dat, err = executeCredsFile(strings.TrimPrefix(fname, "!")) + if err != nil { + return nil, err } - return nil, fmt.Errorf("failed reading provider credentials file %v: %v", fname, err) + } else if isExecutable(fname) { + dat, err = executeCredsFile(fname) + if err != nil { + return nil, err + } + } else { + // no executable bit found nor marked as executable so read it in + dat, err = readCredsFile(fname) } + s := string(dat) r := JsonConfigReader.New(strings.NewReader(s)) err = json.NewDecoder(r).Decode(&results) @@ -38,6 +50,38 @@ func LoadProviderConfigs(fname string) (map[string]map[string]string, error) { return results, nil } +func isExecutable(filename string) bool { + if stat, statErr := os.Stat(filename); statErr == nil { + if mode := stat.Mode(); mode&0111 == 0111 { + return true + } + } + return false +} + +func readCredsFile(filename string) ([]byte, error) { + dat, err := utfutil.ReadFile(filename, utfutil.POSIX) + if err != nil { + // no creds file is ok. Bind requires nothing for example. Individual providers will error if things not found. + if os.IsNotExist(err) { + fmt.Printf("INFO: Config file %q does not exist. Skipping.\n", filename) + return []byte{}, nil + } + return nil, fmt.Errorf("failed reading provider credentials file %v: %v", filename, err) + } + return dat, nil +} + +func executeCredsFile(filename string) ([]byte, error) { + cmd := filename + if !strings.HasPrefix(filename, "/") { + // if the path doesn't start with `/` make sure we aren't relying on $PATH. + cmd = strings.Join([]string{".", filename}, string(filepath.Separator)) + } + out, err := exec.Command(cmd).Output() + return out, err +} + func replaceEnvVars(m map[string]map[string]string) error { for _, keys := range m { for k, v := range keys {