From 871910d84782ae3b00ca66a725692bc9288f27e6 Mon Sep 17 00:00:00 2001 From: Alberto Murillo Date: Tue, 4 May 2021 13:19:51 -0700 Subject: [PATCH] Add support for credential_process Fixes #1485 Signed-off-by: Alberto Murillo --- pkg/credentials/credentials.json | 7 ++++ pkg/credentials/credentials.sample | 3 ++ pkg/credentials/file_aws_credentials.go | 52 ++++++++++++++++++++++++- pkg/credentials/file_test.go | 21 ++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 pkg/credentials/credentials.json diff --git a/pkg/credentials/credentials.json b/pkg/credentials/credentials.json new file mode 100644 index 0000000000..afbfad559e --- /dev/null +++ b/pkg/credentials/credentials.json @@ -0,0 +1,7 @@ +{ + "Version": 1, + "SessionToken": "token", + "AccessKeyId": "accessKey", + "SecretAccessKey": "secret", + "Expiration": "9999-04-27T16:02:25.000Z" +} diff --git a/pkg/credentials/credentials.sample b/pkg/credentials/credentials.sample index 7fc91d9d20..e2dc1bfecb 100644 --- a/pkg/credentials/credentials.sample +++ b/pkg/credentials/credentials.sample @@ -10,3 +10,6 @@ aws_secret_access_key = secret [with_colon] aws_access_key_id: accessKey aws_secret_access_key: secret + +[with_process] +credential_process = /bin/cat credentials.json diff --git a/pkg/credentials/file_aws_credentials.go b/pkg/credentials/file_aws_credentials.go index ccc8251f4b..7b67be446f 100644 --- a/pkg/credentials/file_aws_credentials.go +++ b/pkg/credentials/file_aws_credentials.go @@ -18,13 +18,26 @@ package credentials import ( + "encoding/json" "os" + "os/exec" "path/filepath" + "strings" + "time" homedir "github.com/mitchellh/go-homedir" ini "gopkg.in/ini.v1" ) +// A externalProcessCredentials stores the output of a credential_process +type externalProcessCredentials struct { + Version int + SessionToken string + AccessKeyId string + SecretAccessKey string + Expiration string +} + // A FileAWSCredentials retrieves credentials from the current user's home // directory, and keeps track if those credentials are expired. // @@ -45,6 +58,9 @@ type FileAWSCredentials struct { // retrieved states if the credentials have been successfully retrieved. retrieved bool + + // expiration states when the credentials expires + expiration time.Time } // NewFileAWSCredentials returns a pointer to a new Credentials object @@ -90,6 +106,35 @@ func (p *FileAWSCredentials) Retrieve() (Value, error) { // Default to empty string if not found. token := iniProfile.Key("aws_session_token") + // If credential_process is defined, obtain credentials by executing + // the external process + credential_process := iniProfile.Key("credential_process").String() + if credential_process != "" { + args := strings.Fields(credential_process) + cmd := exec.Command(args[0], args[1:]...) + out, err := cmd.Output() + if err != nil { + return Value{}, err + } + var externalProcessCredentials externalProcessCredentials + err = json.Unmarshal([]byte(out), &externalProcessCredentials) + if err != nil { + return Value{}, err + } + p.retrieved = true + t, err := time.Parse(time.RFC3339, externalProcessCredentials.Expiration) + if err != nil { + return Value{}, err + } + p.expiration = t + return Value{ + AccessKeyID: externalProcessCredentials.AccessKeyId, + SecretAccessKey: externalProcessCredentials.SecretAccessKey, + SessionToken: externalProcessCredentials.SessionToken, + SignerType: SignatureV4, + }, nil + } + p.retrieved = true return Value{ AccessKeyID: id.String(), @@ -101,7 +146,12 @@ func (p *FileAWSCredentials) Retrieve() (Value, error) { // IsExpired returns if the shared credentials have expired. func (p *FileAWSCredentials) IsExpired() bool { - return !p.retrieved + if p.expiration.IsZero() { + return !p.retrieved + } + + now := time.Now() + return now.After(p.expiration) } // loadProfiles loads from the file pointed to by shared credentials filename for profile. diff --git a/pkg/credentials/file_test.go b/pkg/credentials/file_test.go index 598c3f52ec..d42fd3a51d 100644 --- a/pkg/credentials/file_test.go +++ b/pkg/credentials/file_test.go @@ -120,6 +120,27 @@ func TestFileAWS(t *testing.T) { if !creds.IsExpired() { t.Error("Should be expired if not loaded") } + + os.Clearenv() + + creds = NewFileAWSCredentials("credentials.sample", "with_process") + credValues, err = creds.Get() + if err != nil { + t.Fatal(err) + } + + if credValues.AccessKeyID != "accessKey" { + t.Errorf("Expected 'accessKey', got %s'", credValues.AccessKeyID) + } + if credValues.SecretAccessKey != "secret" { + t.Errorf("Expected 'secret', got %s'", credValues.SecretAccessKey) + } + if credValues.SessionToken != "token" { + t.Errorf("Expected 'token', got %s'", credValues.SessionToken) + } + if creds.IsExpired() { + t.Error("Should not be expired") + } } func TestFileMinioClient(t *testing.T) {