diff --git a/go.mod b/go.mod index fd5dc4a..0e7dcd1 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/anchore/harbor-scanner-adapter go 1.12 require ( + github.com/aws/aws-sdk-go v1.25.48 github.com/elazarl/goproxy v0.0.0-20190711103511-473e67f1d7d2 // indirect github.com/elazarl/goproxy/ext v0.0.0-20191011121108-aa519ddbe484 // indirect github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc diff --git a/pkg/adapter/anchore/client/client.go b/pkg/adapter/anchore/client/client.go index 1c3886d..de679af 100644 --- a/pkg/adapter/anchore/client/client.go +++ b/pkg/adapter/anchore/client/client.go @@ -4,9 +4,6 @@ import ( "crypto/tls" "encoding/json" "fmt" - "github.com/anchore/harbor-scanner-adapter/pkg/model/anchore" - "github.com/parnurzeal/gorequest" - log "github.com/sirupsen/logrus" "math" "net/url" "path" @@ -14,6 +11,11 @@ import ( "strconv" "strings" "time" + + "github.com/anchore/harbor-scanner-adapter/pkg/adapter/anchore/credential" + "github.com/anchore/harbor-scanner-adapter/pkg/model/anchore" + "github.com/parnurzeal/gorequest" + log "github.com/sirupsen/logrus" ) const ( @@ -38,6 +40,10 @@ type ClientConfig struct { } func getNewRequest(clientConfiguration *ClientConfig) *gorequest.SuperAgent { + passwordConfig := clientConfiguration.Password + credenitalLoader := credential.CreateCredentialLoader(passwordConfig) + clientConfiguration.Password = credenitalLoader.LoadFromCredentialStore(passwordConfig) + timeout := time.Duration(clientConfiguration.TimeoutSeconds) * time.Second return gorequest.New().TLSClientConfig(&tls.Config{InsecureSkipVerify: clientConfiguration.TLSVerify}).SetBasicAuth(clientConfiguration.Username, clientConfiguration.Password).Timeout(timeout) } diff --git a/pkg/adapter/anchore/credential/awsloader.go b/pkg/adapter/anchore/credential/awsloader.go new file mode 100644 index 0000000..0d8b1a1 --- /dev/null +++ b/pkg/adapter/anchore/credential/awsloader.go @@ -0,0 +1,84 @@ +package credential + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/secretsmanager" + log "github.com/sirupsen/logrus" +) + +type AWSCredenitalLoader struct{} + +func (c *AWSCredenitalLoader) LoadFromCredentialStore(passwordConfig string) string { + if strings.HasPrefix(passwordConfig, "aws:secretmanager") { + log.Debug("Start to load password from AWS Secret Manager") + value := getAWSSecret(passwordConfig) + if value != "" { + return value + } + } + return passwordConfig +} + +func getAWSSecret(configValue string) string { + // The expected format is aws:secretmanager::: + fileds := strings.Split(configValue, ":") + region, name, key := fileds[2], fileds[3], fileds[4] + + log.WithFields(log.Fields{"region": region, "name": name, "key": key}).Debug("pass in secret manager parameters") + + //Create a Secrets Manager client + svc := secretsmanager.New(session.New(), &aws.Config{Region: aws.String(region)}) + input := &secretsmanager.GetSecretValueInput{ + SecretId: aws.String(name), + VersionStage: aws.String("AWSCURRENT"), // VersionStage defaults to AWSCURRENT if unspecified + } + + result, err := svc.GetSecretValue(input) + if err != nil { + if aerr, ok := err.(awserr.Error); ok { + switch aerr.Code() { + case secretsmanager.ErrCodeDecryptionFailure: + // Secrets Manager can't decrypt the protected secret text using the provided KMS key. + fmt.Println(secretsmanager.ErrCodeDecryptionFailure, aerr.Error()) + + case secretsmanager.ErrCodeInternalServiceError: + // An error occurred on the server side. + fmt.Println(secretsmanager.ErrCodeInternalServiceError, aerr.Error()) + + case secretsmanager.ErrCodeInvalidParameterException: + // You provided an invalid value for a parameter. + fmt.Println(secretsmanager.ErrCodeInvalidParameterException, aerr.Error()) + + case secretsmanager.ErrCodeInvalidRequestException: + // You provided a parameter value that is not valid for the current state of the resource. + fmt.Println(secretsmanager.ErrCodeInvalidRequestException, aerr.Error()) + + case secretsmanager.ErrCodeResourceNotFoundException: + // We can't find the resource that you asked for. + fmt.Println(secretsmanager.ErrCodeResourceNotFoundException, aerr.Error()) + } + } else { + // Print the error, cast err to awserr.Error to get the Code and + // Message from an error. + fmt.Println(err.Error()) + } + } else { + // Decrypts secret using the associated KMS CMK. + var secretString string + if result.SecretString != nil { + secretString = *result.SecretString + // a map container to decode the JSON structure into + kmap := make(map[string]string) + json.Unmarshal([]byte(secretString), &kmap) + return kmap[key] + } + } + + return "" +} diff --git a/pkg/adapter/anchore/credential/defaultloader.go b/pkg/adapter/anchore/credential/defaultloader.go new file mode 100644 index 0000000..7632149 --- /dev/null +++ b/pkg/adapter/anchore/credential/defaultloader.go @@ -0,0 +1,7 @@ +package credential + +type DefaultCredenitalLoader struct{} + +func (c *DefaultCredenitalLoader) LoadFromCredentialStore(passwordConfig string) string { + return passwordConfig +} diff --git a/pkg/adapter/anchore/credential/factory.go b/pkg/adapter/anchore/credential/factory.go new file mode 100644 index 0000000..5485b68 --- /dev/null +++ b/pkg/adapter/anchore/credential/factory.go @@ -0,0 +1,16 @@ +package credential + +import ( + "strings" +) + +type CredentialLoader interface { + LoadFromCredentialStore(passwordConfig string) string +} + +func CreateCredentialLoader(passwordConfig string) CredentialLoader { + if strings.HasPrefix(passwordConfig, "aws:secretmanager") { + return &AWSCredenitalLoader{} + } + return &DefaultCredenitalLoader{} +}