Skip to content
Open
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
21 changes: 14 additions & 7 deletions sigv4.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ var sigv4HeaderDenylist = []string{
}

type sigV4RoundTripper struct {
region string
next http.RoundTripper
pool sync.Pool
region string
service string
next http.RoundTripper
pool sync.Pool

signer *signer.Signer
}
Expand Down Expand Up @@ -90,9 +91,10 @@ func NewSigV4RoundTripper(cfg *SigV4Config, next http.RoundTripper) (http.RoundT
}

rt := &sigV4RoundTripper{
region: cfg.Region,
next: next,
signer: signer.NewSigner(signerCreds),
region: cfg.Region,
service: cfg.Service,
next: next,
signer: signer.NewSigner(signerCreds),
}
rt.pool.New = rt.newBuf
return rt, nil
Expand Down Expand Up @@ -136,7 +138,12 @@ func (rt *sigV4RoundTripper) RoundTrip(req *http.Request) (*http.Response, error
signReq.Header.Del(header)
}

headers, err := rt.signer.Sign(signReq, seeker, "aps", rt.region, time.Now().UTC())
service := "aps"
if rt.service != "" {
service = rt.service
}

headers, err := rt.signer.Sign(signReq, seeker, service, rt.region, time.Now().UTC())
if err != nil {
return nil, fmt.Errorf("failed to sign request: %w", err)
}
Expand Down
1 change: 1 addition & 0 deletions sigv4_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type SigV4Config struct {
SecretKey config.Secret `yaml:"secret_key,omitempty"`
Profile string `yaml:"profile,omitempty"`
RoleARN string `yaml:"role_arn,omitempty"`
Service string `yaml:"service,omitempty"`
UseFIPSSTSEndpoint bool `yaml:"use_fips_sts_endpoint,omitempty"`
}

Expand Down
66 changes: 66 additions & 0 deletions sigv4_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,69 @@ func TestSigV4RoundTripper(t *testing.T) {
require.NoError(t, err)
})
}

func TestSigV4RoundTripperWithService(t *testing.T) {
var gotReq *http.Request

rt := &sigV4RoundTripper{
region: "us-east-2",
service: "execute-api",
next: RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
gotReq = req
return &http.Response{StatusCode: http.StatusOK}, nil
}),
signer: signer.NewSigner(credentials.NewStaticCredentials(
"test-id",
"secret",
"token",
)),
}
rt.pool.New = rt.newBuf

cli := &http.Client{Transport: rt}

req, err := http.NewRequest(http.MethodPost, "https://example.com", strings.NewReader("Hello, world!"))
require.NoError(t, err)

_, err = cli.Do(req)
require.NoError(t, err)
require.NotNil(t, gotReq)

origReq := gotReq
require.NotEmpty(t, origReq.Header.Get("Authorization"))
require.NotEmpty(t, origReq.Header.Get("X-Amz-Date"))

// Perform the same request but with a header that shouldn't included in the
// signature; validate that the Authorization signature matches.
t.Run("Ignored Headers", func(t *testing.T) {
req, err := http.NewRequest(http.MethodPost, "https://example.com", strings.NewReader("Hello, world!"))
require.NoError(t, err)

req.Header.Add("Uber-Trace-Id", "some-trace-id")

_, err = cli.Do(req)
require.NoError(t, err)
require.NotNil(t, gotReq)

require.Equal(t, origReq.Header.Get("Authorization"), gotReq.Header.Get("Authorization"))
})

t.Run("Escape URL", func(t *testing.T) {
req, err := http.NewRequest(http.MethodPost, "https://example.com/test//test", strings.NewReader("Hello, world!"))
require.NoError(t, err)
require.Equal(t, "/test//test", req.URL.Path)

_, err = cli.Do(req)
require.NoError(t, err)
require.NotNil(t, gotReq)

require.Equal(t, "/test/test", gotReq.URL.Path)
})

t.Run("No body", func(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, "https://example.com/test/test", nil)
require.NoError(t, err)
_, err = cli.Do(req)
require.NoError(t, err)
})
}
1 change: 1 addition & 0 deletions testdata/sigv4_good.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
region: us-east-2
access_key: AccessKey
secret_key: SecretKey
service: execute-api
profile: profile
role_arn: blah:role/arn
use_fips_sts_endpoint: true