Skip to content

Commit

Permalink
Robust testing of AWS using feature-rich mock server
Browse files Browse the repository at this point in the history
  • Loading branch information
doodlesbykumbi committed Apr 4, 2022
1 parent 418b909 commit f9d0322
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 56 deletions.
8 changes: 7 additions & 1 deletion .gitleaks.toml
Expand Up @@ -18,6 +18,9 @@ tags = ["key", "AWS"]
description = "sample AWS key in AWS HTTP connector comments"
regex = '''AKIAJC5FABNOFVBKRWHA'''
[[rules.whitelist]]
description = "sample AWS key in AWS HTTP connector comments"
regex = '''AKIAIOSFODNN7EXAMPLE'''
[[rules.whitelist]]
description = "since-removed sample AWS key"
regex = '''AKIAJADDJE4Q4JVX3HAA'''

Expand Down Expand Up @@ -214,6 +217,7 @@ files = [
"test/ssh/id_(.*)", # since-removed ssh test certs
"test/util/ssl/(.*)", # test ssl certs
"internal/plugin/connectors/tcp/mssql/connection_details_test.go", # fake cert string
"internal/plugin/connectors/http/aws/aws.go", # example AWS authorization header string
]
# As of v4, gitleaks can whitelist paths to accommodate no longer using
# paths in the `files` whitelist.
Expand All @@ -232,10 +236,12 @@ paths = [
"test/connector/tcp/mssql/certs",
"test/ssh",
"test/util/ssl",
"internal/plugin/connectors/tcp/mssql"
"internal/plugin/connectors/tcp/mssql",
"internal/plugin/connectors/http/aws"
]
regexes = [
"AKIAJC5FABNOFVBKRWHA", # sample AWS key in AWS HTTP connector comments
"AKIAIOSFODNN7EXAMPLE", # sample AWS key in AWS HTTP connector comments
"AKIAJADDJE4Q4JVX3HAA", # since-removed sample AWS key
"SuperSecure", # dummy password used in conjur integration test docker-compose
]
Expand Down
8 changes: 7 additions & 1 deletion go.mod
@@ -1,6 +1,8 @@
module github.com/cyberark/secretless-broker

require (
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Microsoft/go-winio v0.4.16 // indirect
github.com/aws/aws-sdk-go v1.15.79
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/codegangsta/cli v1.20.0
Expand All @@ -17,22 +19,26 @@ require (
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible
github.com/google/btree v1.0.0 // indirect
github.com/googleapis/gnostic v0.3.1 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/hashicorp/vault/api v1.0.2
github.com/heptiolabs/healthcheck v0.0.0-20180807145615-6ff867650f40
github.com/imdario/mergo v0.3.8 // indirect
github.com/joho/godotenv v1.2.0
github.com/json-iterator/go v1.1.8 // indirect
github.com/lib/pq v0.0.0-20180123210206-19c8e9ad0095
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/pkg/errors v0.8.1
github.com/pkg/errors v0.9.1
github.com/pkg/profile v1.2.1
github.com/prometheus/client_golang v1.2.1 // indirect
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.3.0
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529
gopkg.in/yaml.v2 v2.2.2
gotest.tools v2.2.0+incompatible // indirect
k8s.io/api v0.0.0-20180712090710-2d6f90ab1293
k8s.io/apiextensions-apiserver v0.0.0-20180808065829-408db4a50408
k8s.io/apimachinery v0.0.0-20180621070125-103fd098999d
Expand Down
9 changes: 9 additions & 0 deletions go.sum
@@ -1,5 +1,7 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
Expand Down Expand Up @@ -96,6 +98,7 @@ github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1a
github.com/gopherjs/gopherjs v0.0.0-20180202210947-296de816d4fe/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
Expand Down Expand Up @@ -179,12 +182,14 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v0.0.0-20180718012357-94122c33edd3/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
Expand All @@ -195,6 +200,7 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down Expand Up @@ -225,6 +231,7 @@ github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIH
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20170925172151-0b37b35ec743/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
Expand Down Expand Up @@ -281,6 +288,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down Expand Up @@ -322,6 +330,7 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 h1:0efs3hwEZhFKsCoP8l6dDB1AZWMgnEl3yWXWRZTOaEA=
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
k8s.io/api v0.0.0-20180712090710-2d6f90ab1293 h1:hROmpFC7JMobXFXMmD7ZKZLhDKvr1IKfFJoYS/45G/8=
k8s.io/api v0.0.0-20180712090710-2d6f90ab1293/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/apiextensions-apiserver v0.0.0-20180808065829-408db4a50408 h1:GcrrWo5PlDjJ6cSFoxKlIy3xH+IvXa/uYs90NxdbEV4=
Expand Down
111 changes: 66 additions & 45 deletions internal/plugin/connectors/http/aws/aws.go
Expand Up @@ -6,7 +6,6 @@ import (
"io/ioutil"
gohttp "net/http"
"net/url"
"regexp"
"strings"
"time"

Expand All @@ -20,16 +19,7 @@ import (
// From https://github.com/aws/aws-sdk-go/blob/master/aws/signer/v4/v4.go#L77
const timeFormat = "20060102T150405Z"

// reForCredentialComponent matches headers strings like:
//
// Credential=AKIAJC5FABNOFVBKRWHA/20171103/us-east-1/ec2/aws4_request
//
// See https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html
var reForCredentialComponent = regexp.MustCompile(
`^Credential=\w+\/\d+\/([\w-_]+)\/(\w+)\/aws4_request$`,
)

// newAmzDate parses a date string using the AWS signer time format
// newAmzDate parses a Date string using the AWS signer time format
func newAmzDate(amzDateStr string) (time.Time, error) {
if amzDateStr == "" {
return time.Time{}, fmt.Errorf("missing required header: %s", "X-Amz-Date")
Expand All @@ -39,33 +29,64 @@ func newAmzDate(amzDateStr string) (time.Time, error) {
}

// requestMetadataFromAuthz parses an authorization header string and create a
// requestMetadata instance populated with the associated region and service
// RequestMetadata instance populated with the associated Region and service
// name
func requestMetadataFromAuthz(authorizationStr string) (*requestMetadata, error) {
// extract credentials section of authorization header
credentialsComponent, err := extractCredentialsComponent(authorizationStr)
if err != nil {
return nil, err
}
// validate credential component of authorization header, then extract region
// and service name
matches := reForCredentialComponent.FindStringSubmatch(credentialsComponent)
if len(matches) != 3 {
return nil, fmt.Errorf("malformed credential component of Authorization header")
func requestMetadataFromAuthz(authorizationStr string) (*RequestMetadata, error) {
var signedHeaders []string
//var signature string
//var secretKeyId string
//var date string
var region string
var service string

// Authorization Header format:
//
// AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request, SignedHeaders=host;range;x-amz-date, Signature=fe5f80f77d5fa3beca038a248ff027d0445342fe2855ddc963176630326f1024
//
// See https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html
for _, p := range strings.Split(authorizationStr, ", ") {
if strings.HasPrefix(p, "SignedHeaders=") {
signedHeaders = strings.Split(p[len("SignedHeaders="):], ";")
continue
}

//if strings.HasPrefix(p, "Signature=") {
// signature = p[len("Signature="):]
// continue
//}

if strings.HasPrefix(p, "AWS4-HMAC-SHA256 Credential=") {
credentialParts := strings.SplitN(
p[len("AWS4-HMAC-SHA256 Credential="):],
"/",
5,
)
if len(credentialParts) != 5 {
return nil, fmt.Errorf("malformed credential component of Authorization header")
}
// secretKeyId = credentialParts[0]
// date = credentialParts[1]
region = credentialParts[2]
service = credentialParts[3]
continue
}

}

return &requestMetadata{
region: matches[1],
serviceName: matches[2],
return &RequestMetadata{
Region: region,
ServiceName: service,
SignedHeaders: signedHeaders,
}, nil
}

// requestMetadata captures the metadata of a signed AWS request: date, region
// and service name
type requestMetadata struct {
date time.Time
region string
serviceName string
// RequestMetadata captures the metadata of a signed AWS request: Date, Region,
// Service name and Signed headers
type RequestMetadata struct {
Date time.Time
Region string
ServiceName string
SignedHeaders []string
}

// extractCredentialsComponent extracts the credentials component from an
Expand All @@ -87,9 +108,9 @@ func extractCredentialsComponent(authorizationStr string) (string, error) {
return strings.TrimPrefix(tokens[0], "AWS4-HMAC-SHA256 "), nil
}

// newRequestMetadata parses the request headers to extract the metadata
// NewRequestMetadata parses the request headers to extract the metadata
// necessary to sign the request
func newRequestMetadata(r *gohttp.Request) (*requestMetadata, error) {
func NewRequestMetadata(r *gohttp.Request) (*RequestMetadata, error) {
authorizationStr := r.Header.Get("Authorization")
amzDateStr := r.Header.Get("X-Amz-Date")

Expand All @@ -99,22 +120,22 @@ func newRequestMetadata(r *gohttp.Request) (*requestMetadata, error) {
return nil, nil
}

// parse date string
// parse Date string
//
date, err := newAmzDate(amzDateStr)
if err != nil {
return nil, err
}

// create request metadata by extracting service name and region from
// create request metadata by extracting service name and Region from
// Authorization header
reqMeta, err := requestMetadataFromAuthz(authorizationStr)
if err != nil {
return nil, err
}

// populate request metadata with date
reqMeta.date = date
// populate request metadata with Date
reqMeta.Date = date

return reqMeta, nil
}
Expand Down Expand Up @@ -148,7 +169,7 @@ func newAmzStaticCredentials(
// signRequest uses metadata and credentials to sign a request
func signRequest(
r *gohttp.Request,
reqMeta *requestMetadata,
reqMeta *RequestMetadata,
credentialsByID connector.CredentialValuesByID,
) error {
// Create AWS static credentials using provided credentials
Expand All @@ -165,9 +186,9 @@ func signRequest(
_, err = signer.NewSigner(amzCreds).Sign(
r,
bytes.NewReader(bodyBytes),
reqMeta.serviceName,
reqMeta.region,
reqMeta.date,
reqMeta.ServiceName,
reqMeta.Region,
reqMeta.Date,
)
if err != nil {
return err
Expand All @@ -193,7 +214,7 @@ func signRequest(
//
// Note: There is a plan to add a configuration option to instruct Secretless to
// upgrade the connect between Secretless and the target endpoint to TLS.
func setAmzEndpoint(req *gohttp.Request, reqMeta *requestMetadata) error {
func setAmzEndpoint(req *gohttp.Request, reqMeta *RequestMetadata) error {
shouldSetEndpoint := req.URL.Scheme == "http" &&
req.URL.Host == "secretless.empty"

Expand All @@ -202,8 +223,8 @@ func setAmzEndpoint(req *gohttp.Request, reqMeta *requestMetadata) error {
}

endpoint, err := endpoints.DefaultResolver().EndpointFor(
reqMeta.serviceName,
reqMeta.region,
reqMeta.ServiceName,
reqMeta.Region,
)
if err != nil {
return err
Expand Down
10 changes: 5 additions & 5 deletions internal/plugin/connectors/http/aws/connector.go
Expand Up @@ -25,8 +25,8 @@ func (c *Connector) Connect(
) error {
var err error

// Extract metadata of a signed AWS request: date, region and service name
reqMeta, err := newRequestMetadata(req)
// Extract metadata of a signed AWS request: Date, Region and service name
reqMeta, err := NewRequestMetadata(req)
if err != nil {
return err
}
Expand All @@ -47,9 +47,9 @@ func (c *Connector) Connect(

// Use metadata and credentials to sign request
c.logger.Debugf(
"Signing for service=%s region=%s",
reqMeta.serviceName,
reqMeta.region,
"Signing for service=%s Region=%s",
reqMeta.ServiceName,
reqMeta.Region,
)
return signRequest(req, reqMeta, credentialsByID)
}
12 changes: 8 additions & 4 deletions internal/providers/awssecrets/provider.go
Expand Up @@ -19,26 +19,30 @@ type Provider struct {
// ProviderFactory constructs a Provider. The API client is configured from
// in-cluster environment variables and files.
func ProviderFactory(options plugin_v1.ProviderOptions) (plugin_v1.Provider, error) {
return NewProvider(options, aws.Config{})
}

// NewProvider creates a provider with an optional custom AWS config.
func NewProvider(options plugin_v1.ProviderOptions, config aws.Config) (*Provider, error) {
// All clients require a Session. The Session provides the client with
// shared configuration such as region, endpoint, and credentials. A
// Session should be shared where possible to take advantage of
// configuration and credential caching.
sess, err := session.NewSessionWithOptions(session.Options{
Config: config,
SharedConfigState: session.SharedConfigEnable,
})
if err != nil {
return nil, fmt.Errorf("ERROR: Could not create AWS Secrets provider: %s", err)
}

// Create a new instance of the service's client with a Session.
client := secretsmanager.New(sess)

provider := &Provider{
return &Provider{
Name: options.Name,
Client: client,
}

return provider, nil
}, nil
}

// GetName returns the name of the provider
Expand Down

0 comments on commit f9d0322

Please sign in to comment.