Skip to content

Commit

Permalink
Update AWS auth method certificates (#15719)
Browse files Browse the repository at this point in the history
Update AWS auth method certificates

Add tests that the `rsa2048` document can also be verified using the
`pkcs7` field for AWS auth.

Due to the use of SHA-1-based signatures for the `identity` and `pkcs7`
methods, we want to encourage moving toward using the RSA 2048 workflow,
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/verify-rsa2048.html

This doesn't require code changes for Vault necessarily, but adding in
the (many) certificates will help end users.

Also adds `rsa2048` option to API to fetch the RSA 2048 signature.

I will make a PR to update to the AWS auth docs to document the RSA 2048
flow soon after this.
  • Loading branch information
swenson authored Jun 1, 2022
1 parent a94f577 commit df79e2c
Show file tree
Hide file tree
Showing 5 changed files with 941 additions and 70 deletions.
15 changes: 13 additions & 2 deletions api/auth/aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type AWSAuth struct {
mountPath string
// Can be "iam" or "ec2". Defaults to "iam".
authType string
// Can be "pkcs7" or "identity". Defaults to "pkcs7".
// Can be "pkcs7", "identity", or "rsa2048". Defaults to "pkcs7".
signatureType string
region string
iamServerIDHeaderValue string
Expand All @@ -42,6 +42,7 @@ const (
ec2Type = "ec2"
pkcs7Type = "pkcs7"
identityType = "identity"
rsa2048Type = "rsa2048"
defaultMountPath = "aws"
defaultAuthType = iamType
defaultRegion = "us-east-1"
Expand Down Expand Up @@ -108,7 +109,7 @@ func (a *AWSAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, e
}
pkcs7 := strings.TrimSpace(resp)
loginData["pkcs7"] = pkcs7
} else {
} else if a.signatureType == identityType {
// fetch signature from identity document
doc, err := metadataSvc.GetDynamicData("/instance-identity/document")
if err != nil {
Expand All @@ -121,6 +122,16 @@ func (a *AWSAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, e
return nil, fmt.Errorf("error requesting signature: %w", err)
}
loginData["signature"] = signature
} else if a.signatureType == rsa2048Type {
// fetch RSA 2048 signature, which is also a PKCS#7 signature
resp, err := metadataSvc.GetDynamicData("/instance-identity/rsa2048")
if err != nil {
return nil, fmt.Errorf("unable to get PKCS 7 data from metadata service: %w", err)
}
pkcs7 := strings.TrimSpace(resp)
loginData["pkcs7"] = pkcs7
} else {
return nil, fmt.Errorf("unknown signature type: %s", a.signatureType)
}

// Add the reauthentication value, if we have one
Expand Down
39 changes: 38 additions & 1 deletion builtin/credential/aws/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,7 @@ func TestBackend_PathBlacklistRoleTag(t *testing.T) {

/* This is an acceptance test.
Requires the following env vars:
TEST_AWS_EC2_RSA2048
TEST_AWS_EC2_PKCS7
TEST_AWS_EC2_IDENTITY_DOCUMENT
TEST_AWS_EC2_IDENTITY_DOCUMENT_SIG
Expand All @@ -1032,6 +1033,7 @@ func TestBackend_PathBlacklistRoleTag(t *testing.T) {
If this is being run on an EC2 instance, you can set the environment vars using this bash snippet:
export TEST_AWS_EC2_RSA2048=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/rsa2048)
export TEST_AWS_EC2_PKCS7=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/pkcs7)
export TEST_AWS_EC2_IDENTITY_DOCUMENT=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | base64 -w 0)
export TEST_AWS_EC2_IDENTITY_DOCUMENT_SIG=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/signature | tr -d '\n')
Expand All @@ -1054,6 +1056,11 @@ func TestBackendAcc_LoginWithInstanceIdentityDocAndAccessListIdentity(t *testing
return
}

rsa2048 := os.Getenv("TEST_AWS_EC2_RSA2048")
if rsa2048 == "" {
t.Skipf("env var TEST_AWS_EC2_RSA2048 not set, skipping test")
}

pkcs7 := os.Getenv("TEST_AWS_EC2_PKCS7")
if pkcs7 == "" {
t.Skipf("env var TEST_AWS_EC2_PKCS7 not set, skipping test")
Expand Down Expand Up @@ -1290,7 +1297,7 @@ func TestBackendAcc_LoginWithInstanceIdentityDocAndAccessListIdentity(t *testing
t.Fatalf("login attempt should have failed due to client nonce mismatch")
}

// Check if a access list identity entry is created after the login.
// Check if an access list identity entry is created after the login.
wlRequest := &logical.Request{
Operation: logical.ReadOperation,
Path: path + instanceID,
Expand Down Expand Up @@ -1329,6 +1336,36 @@ func TestBackendAcc_LoginWithInstanceIdentityDocAndAccessListIdentity(t *testing
if !ok {
t.Fatalf("expected nonce to be returned")
}

// Attempt to re-login with the rsa2048 signature as a pkcs7 signature
wlRequest.Operation = logical.DeleteOperation
resp, err = b.HandleRequest(context.Background(), wlRequest)
if err != nil {
t.Fatal(err)
}
if resp.IsError() {
t.Fatalf("failed to delete access list identity")
}
delete(loginInput, "identity")
delete(loginInput, "signature")
loginInput["pkcs7"] = rsa2048

resp, err = b.HandleRequest(context.Background(), loginRequest)
if err != nil {
t.Fatal(err)
}
if resp == nil || resp.Auth == nil || resp.IsError() {
t.Fatalf("bad: failed to login: resp:%#v\nerr:%v", resp, err)
}

// verify the presence of instance_id in the response object.
instanceID = resp.Auth.Metadata["instance_id"]
if instanceID == "" {
t.Fatalf("instance ID not present in the response object")
}
if instanceID != parsedIdentityDoc.InstanceID {
t.Fatalf("instance ID in response (%q) did not match instance ID from identity document (%q)", instanceID, parsedIdentityDoc.InstanceID)
}
}
}

Expand Down
Loading

0 comments on commit df79e2c

Please sign in to comment.