mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
Add AWS_SESSION_TOKEN and AWS_SECURITY_TOKEN to aws environemnt credentials
AWS_SESSION_TOKEN or AWS_SECURITY_TOKEN is required when signing AWS requests using ENV credentials from IAM assumed role. Missing token results with S3 403 error when trying to download bundle. Signed-off-by: Kamil Piotrowski <kamil.piotrowski@nordcloud.com>
This commit is contained in:
committed by
Patrick East
parent
00a71ef465
commit
a4412df0fa
@@ -265,6 +265,8 @@ If specifying `environment_credentials`, OPA will expect to find environment var
|
||||
for `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_REGION`, in accordance with the
|
||||
convention used by the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html).
|
||||
|
||||
Please note that if you are using temporary IAM credentials (e.g. assumed IAM role credentials) you have to provide additional `AWS_SESSION_TOKEN` or `AWS_SECURITY_TOKEN` environment variable.
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `services[_].credentials.s3_signing.environment_credentials` | `{}` | Yes | Enables AWS signing using environment variables to source the configuration and credentials |
|
||||
|
||||
+21
-11
@@ -29,17 +29,19 @@ const (
|
||||
ecsRelativePathEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
|
||||
|
||||
// ref. https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html
|
||||
accessKeyEnvVar = "AWS_ACCESS_KEY_ID"
|
||||
secretKeyEnvVar = "AWS_SECRET_ACCESS_KEY"
|
||||
awsRegionEnvVar = "AWS_REGION"
|
||||
accessKeyEnvVar = "AWS_ACCESS_KEY_ID"
|
||||
secretKeyEnvVar = "AWS_SECRET_ACCESS_KEY"
|
||||
securityTokenEnvVar = "AWS_SECURITY_TOKEN"
|
||||
sessionTokenEnvVar = "AWS_SESSION_TOKEN"
|
||||
awsRegionEnvVar = "AWS_REGION"
|
||||
)
|
||||
|
||||
// awsCredentials represents the credentials obtained from an AWS credential provider
|
||||
type awsCredentials struct {
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
RegionName string
|
||||
SecurityToken string
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
RegionName string
|
||||
SessionToken string
|
||||
}
|
||||
|
||||
// awsCredentialService represents the interface for AWS credential providers
|
||||
@@ -64,7 +66,15 @@ func (cs *awsEnvironmentCredentialService) credentials() (awsCredentials, error)
|
||||
if creds.RegionName == "" {
|
||||
return creds, errors.New("no " + awsRegionEnvVar + " set in environment")
|
||||
}
|
||||
creds.SecurityToken = "" // not applicable to this credential provider
|
||||
// SessionToken is required if using temporaty ENV credentials from assumed IAM role
|
||||
// Missing SessionToken results with 403 s3 error.
|
||||
creds.SessionToken = os.Getenv(sessionTokenEnvVar)
|
||||
if creds.SessionToken == "" {
|
||||
// In case of missing SessionToken try to get SecurityToken
|
||||
// AWS switched to use SessionToken, but SecurityToken was left for backward compatibility
|
||||
creds.SessionToken = os.Getenv(securityTokenEnvVar)
|
||||
}
|
||||
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
@@ -162,7 +172,7 @@ func (cs *awsMetadataCredentialService) refreshFromService() error {
|
||||
cs.expiration = payload.Expiration
|
||||
cs.creds.AccessKey = payload.AccessKeyID
|
||||
cs.creds.SecretKey = payload.SecretAccessKey
|
||||
cs.creds.SecurityToken = payload.Token
|
||||
cs.creds.SessionToken = payload.Token
|
||||
cs.creds.RegionName = cs.RegionName
|
||||
|
||||
return nil
|
||||
@@ -227,8 +237,8 @@ func signV4(req *http.Request, credService awsCredentialService, theTime time.Ti
|
||||
|
||||
// the security token header is necessary for ephemeral credentials, e.g. from
|
||||
// the EC2 metadata service
|
||||
if creds.SecurityToken != "" {
|
||||
awsHeaders["x-amz-security-token"] = creds.SecurityToken
|
||||
if creds.SessionToken != "" {
|
||||
awsHeaders["x-amz-security-token"] = creds.SessionToken
|
||||
}
|
||||
|
||||
// ref. https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html
|
||||
|
||||
+36
-18
@@ -42,6 +42,8 @@ func TestEnvironmentCredentialService(t *testing.T) {
|
||||
os.Setenv("AWS_ACCESS_KEY_ID", "")
|
||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "")
|
||||
os.Setenv("AWS_REGION", "")
|
||||
os.Setenv("AWS_SECURITY_TOKEN", "")
|
||||
os.Setenv("AWS_SESSION_TOKEN", "")
|
||||
|
||||
cs := &awsEnvironmentCredentialService{}
|
||||
|
||||
@@ -59,20 +61,36 @@ func TestEnvironmentCredentialService(t *testing.T) {
|
||||
|
||||
os.Setenv("AWS_REGION", "us-east-1")
|
||||
|
||||
// happy path: all required environment is present
|
||||
envCreds, err = cs.credentials()
|
||||
if err != nil {
|
||||
t.Error("unexpected error: " + err.Error())
|
||||
expectedCreds := awsCredentials{
|
||||
AccessKey: "MYAWSACCESSKEYGOESHERE",
|
||||
SecretKey: "MYAWSSECRETACCESSKEYGOESHERE",
|
||||
RegionName: "us-east-1",
|
||||
SessionToken: ""}
|
||||
|
||||
testCases := []struct {
|
||||
tokenEnv string
|
||||
tokenValue string
|
||||
}{
|
||||
// happy path: all required environment is present
|
||||
{"", ""},
|
||||
// happy path: all required environment is present including security token
|
||||
{"AWS_SECURITY_TOKEN", "MYSECURITYTOKENGOESHERE"},
|
||||
// happy path: all required environment is present including session token that is preferred over security token
|
||||
{"AWS_SESSION_TOKEN", "MYSESSIONTOKENGOESHERE"},
|
||||
}
|
||||
|
||||
expectedCreds := awsCredentials{
|
||||
AccessKey: "MYAWSACCESSKEYGOESHERE",
|
||||
SecretKey: "MYAWSSECRETACCESSKEYGOESHERE",
|
||||
RegionName: "us-east-1",
|
||||
SecurityToken: ""}
|
||||
for _, testCase := range testCases {
|
||||
os.Setenv(testCase.tokenEnv, testCase.tokenValue)
|
||||
expectedCreds.SessionToken = testCase.tokenValue
|
||||
|
||||
if envCreds != expectedCreds {
|
||||
t.Error("expected: ", expectedCreds, " but got: ", envCreds)
|
||||
envCreds, err = cs.credentials()
|
||||
if err != nil {
|
||||
t.Error("unexpected error: " + err.Error())
|
||||
}
|
||||
|
||||
if envCreds != expectedCreds {
|
||||
t.Error("expected: ", expectedCreds, " but got: ", envCreds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +161,7 @@ func TestMetadataCredentialService(t *testing.T) {
|
||||
assertEq(creds.AccessKey, ts.payload.AccessKeyID, t)
|
||||
assertEq(creds.SecretKey, ts.payload.SecretAccessKey, t)
|
||||
assertEq(creds.RegionName, cs.RegionName, t)
|
||||
assertEq(creds.SecurityToken, ts.payload.Token, t)
|
||||
assertEq(creds.SessionToken, ts.payload.Token, t)
|
||||
|
||||
// happy path: verify credentials are cached based on expiry
|
||||
ts.payload.AccessKeyID = "ICHANGEDTHISBUTWEWONTSEEIT"
|
||||
@@ -152,7 +170,7 @@ func TestMetadataCredentialService(t *testing.T) {
|
||||
assertEq(creds.AccessKey, "MYAWSACCESSKEYGOESHERE", t) // the original value
|
||||
assertEq(creds.SecretKey, ts.payload.SecretAccessKey, t)
|
||||
assertEq(creds.RegionName, cs.RegionName, t)
|
||||
assertEq(creds.SecurityToken, ts.payload.Token, t)
|
||||
assertEq(creds.SessionToken, ts.payload.Token, t)
|
||||
|
||||
// happy path: with refresh
|
||||
// first time through
|
||||
@@ -172,7 +190,7 @@ func TestMetadataCredentialService(t *testing.T) {
|
||||
assertEq(creds.AccessKey, ts.payload.AccessKeyID, t)
|
||||
assertEq(creds.SecretKey, ts.payload.SecretAccessKey, t)
|
||||
assertEq(creds.RegionName, cs.RegionName, t)
|
||||
assertEq(creds.SecurityToken, ts.payload.Token, t)
|
||||
assertEq(creds.SessionToken, ts.payload.Token, t)
|
||||
|
||||
// second time through, with changes
|
||||
ts.payload.AccessKeyID = "ICHANGEDTHISANDWEWILLSEEIT"
|
||||
@@ -181,16 +199,16 @@ func TestMetadataCredentialService(t *testing.T) {
|
||||
assertEq(creds.AccessKey, ts.payload.AccessKeyID, t) // the new value
|
||||
assertEq(creds.SecretKey, ts.payload.SecretAccessKey, t)
|
||||
assertEq(creds.RegionName, cs.RegionName, t)
|
||||
assertEq(creds.SecurityToken, ts.payload.Token, t)
|
||||
assertEq(creds.SessionToken, ts.payload.Token, t)
|
||||
}
|
||||
|
||||
type testCredentialService struct{}
|
||||
|
||||
func (cs *testCredentialService) credentials() (awsCredentials, error) {
|
||||
return awsCredentials{AccessKey: "MYAWSACCESSKEYGOESHERE",
|
||||
SecretKey: "MYAWSSECRETACCESSKEYGOESHERE",
|
||||
RegionName: "us-east-1",
|
||||
SecurityToken: "MYAWSSECURITYTOKENGOESHERE"}, nil
|
||||
SecretKey: "MYAWSSECRETACCESSKEYGOESHERE",
|
||||
RegionName: "us-east-1",
|
||||
SessionToken: "MYAWSSECURITYTOKENGOESHERE"}, nil
|
||||
}
|
||||
|
||||
func TestV4Signing(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user