Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for credential_process #1716

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions pkg/credentials/credentials.json
@@ -0,0 +1,7 @@
{
"Version": 1,
"SessionToken": "token",
"AccessKeyId": "accessKey",
"SecretAccessKey": "secret",
"Expiration": "9999-04-27T16:02:25.000Z"
}
3 changes: 3 additions & 0 deletions pkg/credentials/credentials.sample
Expand Up @@ -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
48 changes: 43 additions & 5 deletions pkg/credentials/file_aws_credentials.go
Expand Up @@ -18,17 +18,33 @@
package credentials

import (
"encoding/json"
"errors"
"os"
"os/exec"
"path/filepath"
"strings"
"time"

ini "gopkg.in/ini.v1"
)

// A externalProcessCredentials stores the output of a credential_process
type externalProcessCredentials struct {
Version int
SessionToken string
AccessKeyID string `json:"AccessKeyId"`
SecretAccessKey string
Expiration time.Time
}

// A FileAWSCredentials retrieves credentials from the current user's home
// directory, and keeps track if those credentials are expired.
//
// Profile ini file example: $HOME/.aws/credentials
type FileAWSCredentials struct {
Expiry

// Path to the shared credentials file.
//
// If empty will look for "AWS_SHARED_CREDENTIALS_FILE" env variable. If the
Expand Down Expand Up @@ -89,6 +105,33 @@ 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
credentialProcess := strings.TrimSpace(iniProfile.Key("credential_process").String())
if credentialProcess != "" {
args := strings.Fields(credentialProcess)
if len(args) <= 1 {
return Value{}, errors.New("invalid credential process args")
}
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
p.SetExpiration(externalProcessCredentials.Expiration, DefaultExpiryWindow)
return Value{
AccessKeyID: externalProcessCredentials.AccessKeyID,
SecretAccessKey: externalProcessCredentials.SecretAccessKey,
SessionToken: externalProcessCredentials.SessionToken,
SignerType: SignatureV4,
}, nil
}
p.retrieved = true
return Value{
AccessKeyID: id.String(),
Expand All @@ -98,11 +141,6 @@ func (p *FileAWSCredentials) Retrieve() (Value, error) {
}, nil
}

// IsExpired returns if the shared credentials have expired.
func (p *FileAWSCredentials) IsExpired() bool {
return !p.retrieved
}

// loadProfiles loads from the file pointed to by shared credentials filename for profile.
// The credentials retrieved from the profile will be returned or error. Error will be
// returned if it fails to read from the file, or the data is invalid.
Expand Down
21 changes: 21 additions & 0 deletions pkg/credentials/file_test.go
Expand Up @@ -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) {
Expand Down