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

Support signing sigv4 requests #23

Merged
merged 8 commits into from
Aug 26, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ go 1.20

require (
github.com/alecthomas/assert/v2 v2.3.0
github.com/aws/aws-sdk-go-v2 v1.30.3
github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027
github.com/sirupsen/logrus v1.9.3
github.com/superfly/macaroon v0.2.14-0.20240718172852-139f90b76537
github.com/superfly/macaroon v0.2.14-0.20240819201738-61a02aa53648
golang.org/x/crypto v0.12.0
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
)

require (
github.com/alecthomas/repr v0.2.0 // indirect
github.com/aws/smithy-go v1.20.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ github.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVd
github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand All @@ -26,6 +30,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/superfly/macaroon v0.2.14-0.20240718172852-139f90b76537 h1:xL2tIkau3Dr3dd4WOLbGz14kRcF49x15bVIMdOkLTyI=
github.com/superfly/macaroon v0.2.14-0.20240718172852-139f90b76537/go.mod h1:Kt6/EdSYfFjR4GIe+erMwcJgU8iMu1noYVceQ5dNdKo=
github.com/superfly/macaroon v0.2.14-0.20240819201738-61a02aa53648 h1:YQG1v1QcTFQxJureNBcbtxosZ98u78ceUNCDQgI/vgM=
github.com/superfly/macaroon v0.2.14-0.20240819201738-61a02aa53648/go.mod h1:Kt6/EdSYfFjR4GIe+erMwcJgU8iMu1noYVceQ5dNdKo=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
Expand Down
97 changes: 97 additions & 0 deletions processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import (
"net/http"
"net/textproto"
"strings"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"golang.org/x/exp/slices"
)

Expand Down Expand Up @@ -145,6 +148,100 @@ func (c *OAuthProcessorConfig) Processor(params map[string]string) (RequestProce
}, nil
}

type Sigv4ProcessorConfig struct {
AccessKey string `json:"access_key"`
SecretKey string `json:"secret_key"`
}

var _ ProcessorConfig = (*Sigv4ProcessorConfig)(nil)

func (c *Sigv4ProcessorConfig) Processor(params map[string]string) (RequestProcessor, error) {

if len(c.AccessKey) == 0 {
return nil, errors.New("missing access key")
}
if len(c.SecretKey) == 0 {
return nil, errors.New("missing secret key")
}

return func(r *http.Request) error {
// NOTE: Sigv4 has defenses against request forgery and reuse.
// This does *not* make those guarantees, and likely can not.

var (
service string
region string
date time.Time
err error
)

// Parse the auth header to get the service, region, and date
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
return errors.New("expected request to contain an Authorization header")
}

for _, section := range strings.Split(authHeader, " ") {
section = strings.TrimRight(section, ",")
keyValuePair := strings.SplitN(section, "=", 2)
if len(keyValuePair) != 2 {
continue
}

if keyValuePair[0] == "Credential" {
credParts := strings.Split(keyValuePair[1], "/")
if len(credParts) != 5 {
return errors.New("invalid credential in auth header")
}

dateStr := credParts[1]
date, err = time.Parse("20060102", dateStr)
if err != nil {
return err
}

service = credParts[2]
region = credParts[3]
break
}
}
if timestamp := r.Header.Get("X-Amz-Date"); timestamp != "" {
date, err = time.Parse("20060102T150405Z", timestamp)
if err != nil {
return err
}
}
if service == "" || region == "" || date.IsZero() {
return errors.New("expected valid sigv4 authentication header in request to tokenizer")
}

alichay marked this conversation as resolved.
Show resolved Hide resolved
// Strip the Authorization header from the request
r.Header.Del("Authorization")

credentials := aws.Credentials{
AccessKeyID: c.AccessKey,
SecretAccessKey: c.SecretKey,
}

// HACK: We have to strip the filtered headers *before* the request gets signed,
// since sigv4 expects a signature of all the request's headers.
for _, h := range FilteredHeaders {
r.Header.Del(h)
}
// Remove headers that goproxy will strip out later anyway. Otherwise, the header signature check will fail.
// https://github.com/elazarl/goproxy/blob/8b0c205063807802a7ac1d75351a90172a9c83fb/proxy.go#L87-L92
r.Header.Del("Accept-Encoding")
r.Header.Del("Proxy-Connection")
r.Header.Del("Proxy-Authenticate")
r.Header.Del("Proxy-Authorization")

signer := v4.NewSigner()
err = signer.SignHTTP(r.Context(), credentials, r, r.Header.Get("X-Amz-Content-Sha256"), service, region, date)

return err
}, nil
}

// A helper type to be embedded in RequestProcessors wanting to use the `fmt` config/param.
type FmtProcessor struct {
Fmt string `json:"fmt,omitempty"`
Expand Down
7 changes: 7 additions & 0 deletions secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type wireSecret struct {
*InjectProcessorConfig `json:"inject_processor,omitempty"`
*InjectHMACProcessorConfig `json:"inject_hmac_processor,omitempty"`
*OAuthProcessorConfig `json:"oauth2_processor,omitempty"`
*Sigv4ProcessorConfig `json:"sigv4_processor,omitempty"`
*BearerAuthConfig `json:"bearer_auth,omitempty"`
*MacaroonAuthConfig `json:"macaroon_auth,omitempty"`
*FlyioMacaroonAuthConfig `json:"flyio_macaroon_auth,omitempty"`
Expand Down Expand Up @@ -81,6 +82,8 @@ func (s *Secret) MarshalJSON() ([]byte, error) {
ws.InjectHMACProcessorConfig = p
case *OAuthProcessorConfig:
ws.OAuthProcessorConfig = p
case *Sigv4ProcessorConfig:
ws.Sigv4ProcessorConfig = p
default:
return nil, errors.New("bad processor config")
}
Expand Down Expand Up @@ -121,6 +124,10 @@ func (s *Secret) UnmarshalJSON(b []byte) error {
np += 1
s.ProcessorConfig = ws.OAuthProcessorConfig
}
if ws.Sigv4ProcessorConfig != nil {
np += 1
s.ProcessorConfig = ws.Sigv4ProcessorConfig
}
if np != 1 {
return errors.New("bad processor config")
}
Expand Down
Loading