From 66025e065211c8b831940903fa6094401de76f42 Mon Sep 17 00:00:00 2001 From: Hakan Memisoglu Date: Mon, 16 Sep 2019 15:26:31 -0700 Subject: [PATCH 1/7] Extract AWS credentials from secret --- pkg/secrets/aws.go | 48 +++++++++++++++++++++++ pkg/secrets/aws_test.go | 87 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 pkg/secrets/aws.go create mode 100644 pkg/secrets/aws_test.go diff --git a/pkg/secrets/aws.go b/pkg/secrets/aws.go new file mode 100644 index 0000000000..fb0747be3e --- /dev/null +++ b/pkg/secrets/aws.go @@ -0,0 +1,48 @@ +package secrets + +import ( + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/pkg/errors" + v1 "k8s.io/api/core/v1" +) + +const ( + // AWSSecretType represent the secret type for AWS credentials. + AWSSecretType string = "secrets.kanister.io/aws" + + // AWSAccessKeyID is the key for AWS access key ID. + AWSAccessKeyID string = "AWS_ACCESS_KEY_ID" + // AWSSecretAccessKey is the key for AWS secret access key. + AWSSecretAccessKey string = "AWS_SECRET_ACCESS_KEY" + // AWSSessionToken is the key for optional AWS session token. + AWSSessionToken string = "AWS_SESSION_TOKEN" +) + +// ExtractAWSCredentials extracts AWS credential values from the given secret. +// +// Extracted values from the secrets are: +// - AWS_ACCESS_KEY_ID (required) +// - AWS_SECRET_ACCESS_KEY (required) +// - AWS_SESSION_TOKEN (optional) +// +// If the type of the secret is not "secret.kanister.io/aws", it returns an error. +// If the required types are not avaialable in the secrets, it returns an errror. +func ExtractAWSCredentials(secret *v1.Secret) (*credentials.Value, error) { + if string(secret.Type) != AWSSecretType { + return nil, errors.New("Secret is not AWS secret") + } + accessKeyID, ok := secret.Data[AWSAccessKeyID] + if !ok { + return nil, errors.New("AWS_ACCESS_KEY_ID is a required field") + } + secretAccessKey, ok := secret.Data[AWSSecretAccessKey] + if !ok { + return nil, errors.New("AWS_SECRET_ACCESS_KEY is a required field") + } + sessionToken := secret.Data[AWSSessionToken] + return &credentials.Value{ + AccessKeyID: string(accessKeyID), + SecretAccessKey: string(secretAccessKey), + SessionToken: string(sessionToken), + }, nil +} diff --git a/pkg/secrets/aws_test.go b/pkg/secrets/aws_test.go new file mode 100644 index 0000000000..7481fa0584 --- /dev/null +++ b/pkg/secrets/aws_test.go @@ -0,0 +1,87 @@ +package secrets + +import ( + "testing" + + "github.com/aws/aws-sdk-go/aws/credentials" + . "gopkg.in/check.v1" + v1 "k8s.io/api/core/v1" +) + +// Hook up gocheck into the "go test" runner. +func Test(t *testing.T) { TestingT(t) } + +type AWSSecretSuite struct{} + +var _ = Suite(&AWSSecretSuite{}) + +func (s *AWSSecretSuite) TestExtractAWSCredentials(c *C) { + tcs := []struct { + secret *v1.Secret + expected *credentials.Value + errChecker Checker + }{ + { + secret: &v1.Secret{ + Type: v1.SecretType(AWSSecretType), + Data: map[string][]byte{ + AWSAccessKeyID: []byte("key_id"), + AWSSecretAccessKey: []byte("secret_key"), + }, + }, + expected: &credentials.Value{ + AccessKeyID: "key_id", + SecretAccessKey: "secret_key", + }, + errChecker: IsNil, + }, + { + secret: &v1.Secret{ + Type: v1.SecretType(AWSSecretType), + Data: map[string][]byte{ + AWSAccessKeyID: []byte("key_id"), + AWSSecretAccessKey: []byte("secret_key"), + AWSSessionToken: []byte("session_token"), + }, + }, + expected: &credentials.Value{ + AccessKeyID: "key_id", + SecretAccessKey: "secret_key", + SessionToken: "session_token", + }, + errChecker: IsNil, + }, + { + secret: &v1.Secret{ + Type: "Opaque", + }, + expected: nil, + errChecker: NotNil, + }, + { + secret: &v1.Secret{ + Type: v1.SecretType(AWSSecretType), + Data: map[string][]byte{ + AWSSecretAccessKey: []byte("secret_key"), + }, + }, + expected: nil, + errChecker: NotNil, + }, + { + secret: &v1.Secret{ + Type: v1.SecretType(AWSSecretType), + Data: map[string][]byte{ + AWSAccessKeyID: []byte("key_id"), + }, + }, + expected: nil, + errChecker: NotNil, + }, + } + for _, tc := range tcs { + creds, err := ExtractAWSCredentials(tc.secret) + c.Check(creds, DeepEquals, tc.expected) + c.Check(err, tc.errChecker) + } +} From 93c9852f18948405651dec4d2ab41a2ec52a18c0 Mon Sep 17 00:00:00 2001 From: Hakan Memisoglu Date: Mon, 16 Sep 2019 16:47:56 -0700 Subject: [PATCH 2/7] Separate validation from extraction --- pkg/secrets/aws.go | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/pkg/secrets/aws.go b/pkg/secrets/aws.go index fb0747be3e..d9496c590e 100644 --- a/pkg/secrets/aws.go +++ b/pkg/secrets/aws.go @@ -18,6 +18,36 @@ const ( AWSSessionToken string = "AWS_SESSION_TOKEN" ) +// ValidateAWSCredentials validates secret has all necessary information +// for AWS credentials. It also checks the secret doesn't have unnnecessary +// information. +// +// Required fields: +// - AWS_ACCESS_KEY_ID +// - AWS_SECRET_ACCESS_KEY +// +// Optional field: +// - AWS_SESSION_TOKEN +func ValidateAWSCredentials(secret *v1.Secret) error { + if string(secret.Type) != AWSSecretType { + return errors.New("Secret is not AWS secret") + } + if _, ok := secret.Data[AWSAccessKeyID]; !ok { + return errors.New("AWS_ACCESS_KEY_ID is a required field") + } + if _, ok := secret.Data[AWSSecretAccessKey]; !ok { + return errors.New("AWS_SECRET_ACCESS_KEY is a required field") + } + count := 2 + if _, ok := secret.Data[AWSSessionToken]; ok { + count++ + } + if len(secret.Data) > count { + return errors.New("Secret has an unknown field") + } + return nil +} + // ExtractAWSCredentials extracts AWS credential values from the given secret. // // Extracted values from the secrets are: @@ -28,17 +58,11 @@ const ( // If the type of the secret is not "secret.kanister.io/aws", it returns an error. // If the required types are not avaialable in the secrets, it returns an errror. func ExtractAWSCredentials(secret *v1.Secret) (*credentials.Value, error) { - if string(secret.Type) != AWSSecretType { - return nil, errors.New("Secret is not AWS secret") - } - accessKeyID, ok := secret.Data[AWSAccessKeyID] - if !ok { - return nil, errors.New("AWS_ACCESS_KEY_ID is a required field") - } - secretAccessKey, ok := secret.Data[AWSSecretAccessKey] - if !ok { - return nil, errors.New("AWS_SECRET_ACCESS_KEY is a required field") + if err := ValidateAWSCredentials(secret); err != nil { + return nil, err } + accessKeyID := secret.Data[AWSAccessKeyID] + secretAccessKey := secret.Data[AWSSecretAccessKey] sessionToken := secret.Data[AWSSessionToken] return &credentials.Value{ AccessKeyID: string(accessKeyID), From fce0a4273db465a2671b7b3cc29a879bbc8296ef Mon Sep 17 00:00:00 2001 From: Hakan Memisoglu Date: Mon, 16 Sep 2019 16:50:01 -0700 Subject: [PATCH 3/7] Add additional test --- pkg/secrets/aws_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/secrets/aws_test.go b/pkg/secrets/aws_test.go index 7481fa0584..0fe4c76dd9 100644 --- a/pkg/secrets/aws_test.go +++ b/pkg/secrets/aws_test.go @@ -78,6 +78,18 @@ func (s *AWSSecretSuite) TestExtractAWSCredentials(c *C) { expected: nil, errChecker: NotNil, }, + { + secret: &v1.Secret{ + Type: v1.SecretType(AWSSecretType), + Data: map[string][]byte{ + AWSAccessKeyID: []byte("key_id"), + AWSSecretAccessKey: []byte("secret_key"), + "ExtraField": []byte("extra_value"), + }, + }, + expected: nil, + errChecker: NotNil, + }, } for _, tc := range tcs { creds, err := ExtractAWSCredentials(tc.secret) From da40ba5c80ea9468551f700f3ebd3393ad9d9421 Mon Sep 17 00:00:00 2001 From: Hakan Memisoglu Date: Mon, 16 Sep 2019 17:04:44 -0700 Subject: [PATCH 4/7] Refactor --- pkg/secrets/aws.go | 8 ++++---- pkg/secrets/aws_test.go | 5 ----- pkg/secrets/secrets_test.go | 9 +++++++++ 3 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 pkg/secrets/secrets_test.go diff --git a/pkg/secrets/aws.go b/pkg/secrets/aws.go index d9496c590e..b7363666ec 100644 --- a/pkg/secrets/aws.go +++ b/pkg/secrets/aws.go @@ -11,16 +11,16 @@ const ( AWSSecretType string = "secrets.kanister.io/aws" // AWSAccessKeyID is the key for AWS access key ID. - AWSAccessKeyID string = "AWS_ACCESS_KEY_ID" + AWSAccessKeyID string = "awsAccessKeyID" // AWSSecretAccessKey is the key for AWS secret access key. - AWSSecretAccessKey string = "AWS_SECRET_ACCESS_KEY" + AWSSecretAccessKey string = "awsSecretAccessKey" // AWSSessionToken is the key for optional AWS session token. - AWSSessionToken string = "AWS_SESSION_TOKEN" + AWSSessionToken string = "awsSessionToken" ) // ValidateAWSCredentials validates secret has all necessary information // for AWS credentials. It also checks the secret doesn't have unnnecessary -// information. +// information. // // Required fields: // - AWS_ACCESS_KEY_ID diff --git a/pkg/secrets/aws_test.go b/pkg/secrets/aws_test.go index 0fe4c76dd9..dd0e760c4e 100644 --- a/pkg/secrets/aws_test.go +++ b/pkg/secrets/aws_test.go @@ -1,16 +1,11 @@ package secrets import ( - "testing" - "github.com/aws/aws-sdk-go/aws/credentials" . "gopkg.in/check.v1" v1 "k8s.io/api/core/v1" ) -// Hook up gocheck into the "go test" runner. -func Test(t *testing.T) { TestingT(t) } - type AWSSecretSuite struct{} var _ = Suite(&AWSSecretSuite{}) diff --git a/pkg/secrets/secrets_test.go b/pkg/secrets/secrets_test.go new file mode 100644 index 0000000000..fc9ec11b1f --- /dev/null +++ b/pkg/secrets/secrets_test.go @@ -0,0 +1,9 @@ +package secrets + +import ( + "testing" + + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { TestingT(t) } \ No newline at end of file From 0910caf4587b89fa0f886181b563dca5f959f75b Mon Sep 17 00:00:00 2001 From: Hakan Memisoglu Date: Mon, 16 Sep 2019 17:07:30 -0700 Subject: [PATCH 5/7] Update doc comments --- pkg/secrets/aws.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/secrets/aws.go b/pkg/secrets/aws.go index b7363666ec..a0012571a0 100644 --- a/pkg/secrets/aws.go +++ b/pkg/secrets/aws.go @@ -23,20 +23,20 @@ const ( // information. // // Required fields: -// - AWS_ACCESS_KEY_ID -// - AWS_SECRET_ACCESS_KEY +// - awsAccessKeyID +// - awsSecretAccessKey // // Optional field: -// - AWS_SESSION_TOKEN +// - awsSessionToken func ValidateAWSCredentials(secret *v1.Secret) error { if string(secret.Type) != AWSSecretType { return errors.New("Secret is not AWS secret") } if _, ok := secret.Data[AWSAccessKeyID]; !ok { - return errors.New("AWS_ACCESS_KEY_ID is a required field") + return errors.New("awsAccessKeyID is a required field") } if _, ok := secret.Data[AWSSecretAccessKey]; !ok { - return errors.New("AWS_SECRET_ACCESS_KEY is a required field") + return errors.New("awsSecretAccessKey is a required field") } count := 2 if _, ok := secret.Data[AWSSessionToken]; ok { @@ -51,9 +51,9 @@ func ValidateAWSCredentials(secret *v1.Secret) error { // ExtractAWSCredentials extracts AWS credential values from the given secret. // // Extracted values from the secrets are: -// - AWS_ACCESS_KEY_ID (required) -// - AWS_SECRET_ACCESS_KEY (required) -// - AWS_SESSION_TOKEN (optional) +// - awsAccessKeyID (required) +// - awsSecretAccessKey (required) +// - awsSessionToken (optional) // // If the type of the secret is not "secret.kanister.io/aws", it returns an error. // If the required types are not avaialable in the secrets, it returns an errror. From f27123451d1200968be39f788a940935827feed7 Mon Sep 17 00:00:00 2001 From: Hakan Memisoglu Date: Mon, 16 Sep 2019 17:48:28 -0700 Subject: [PATCH 6/7] Add newline --- pkg/secrets/secrets_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/secrets/secrets_test.go b/pkg/secrets/secrets_test.go index fc9ec11b1f..f1adce4c0c 100644 --- a/pkg/secrets/secrets_test.go +++ b/pkg/secrets/secrets_test.go @@ -6,4 +6,4 @@ import ( . "gopkg.in/check.v1" ) -func Test(t *testing.T) { TestingT(t) } \ No newline at end of file +func Test(t *testing.T) { TestingT(t) } From 4f6ae77438f537cd948056ea2829e5645684241d Mon Sep 17 00:00:00 2001 From: Hakan Memisoglu Date: Mon, 16 Sep 2019 18:32:35 -0700 Subject: [PATCH 7/7] Update field names --- pkg/secrets/aws.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/secrets/aws.go b/pkg/secrets/aws.go index a0012571a0..ad2823fb6f 100644 --- a/pkg/secrets/aws.go +++ b/pkg/secrets/aws.go @@ -7,15 +7,15 @@ import ( ) const ( - // AWSSecretType represent the secret type for AWS credentials. + // AWSSecretType represents the secret type for AWS credentials. AWSSecretType string = "secrets.kanister.io/aws" // AWSAccessKeyID is the key for AWS access key ID. - AWSAccessKeyID string = "awsAccessKeyID" + AWSAccessKeyID string = "access_key_id" // AWSSecretAccessKey is the key for AWS secret access key. - AWSSecretAccessKey string = "awsSecretAccessKey" + AWSSecretAccessKey string = "secret_access_key" // AWSSessionToken is the key for optional AWS session token. - AWSSessionToken string = "awsSessionToken" + AWSSessionToken string = "session_token" ) // ValidateAWSCredentials validates secret has all necessary information @@ -23,11 +23,11 @@ const ( // information. // // Required fields: -// - awsAccessKeyID -// - awsSecretAccessKey +// - access_key_id +// - secret_access_key // // Optional field: -// - awsSessionToken +// - session_token func ValidateAWSCredentials(secret *v1.Secret) error { if string(secret.Type) != AWSSecretType { return errors.New("Secret is not AWS secret") @@ -51,9 +51,9 @@ func ValidateAWSCredentials(secret *v1.Secret) error { // ExtractAWSCredentials extracts AWS credential values from the given secret. // // Extracted values from the secrets are: -// - awsAccessKeyID (required) -// - awsSecretAccessKey (required) -// - awsSessionToken (optional) +// - access_key_id (required) +// - secret_access_key (required) +// - session_token (optional) // // If the type of the secret is not "secret.kanister.io/aws", it returns an error. // If the required types are not avaialable in the secrets, it returns an errror.