diff --git a/cerberus-core/src/main/java/com/nike/cerberus/error/DefaultApiError.java b/cerberus-core/src/main/java/com/nike/cerberus/error/DefaultApiError.java index c264a1b92..36970d08c 100644 --- a/cerberus-core/src/main/java/com/nike/cerberus/error/DefaultApiError.java +++ b/cerberus-core/src/main/java/com/nike/cerberus/error/DefaultApiError.java @@ -233,6 +233,18 @@ public enum DefaultApiError implements ApiError { "Failed to validate factor. Please try again or try a different factor.", SC_UNAUTHORIZED), + /** AWS China ARNs are not allowed. */ + AWS_CHINA_NOT_ALLOWED( + 99244, + "The AWS China partition is disabled by the admin. If you're creating or updating an SDB, please remove IAM principal ARNs that start with \"arn:aws-cn:\"", + SC_UNAUTHORIZED), + + /** AWS Global ARNs are not allowed. */ + AWS_GLOBAL_NOT_ALLOWED( + 99245, + "The AWS Global partition is disabled by the admin. If you're creating or updating an SDB, please remove IAM principal ARNs that start with \"arn:aws:\"", + SC_UNAUTHORIZED), + /** Generic not found error. */ ENTITY_NOT_FOUND(99996, "Not found", SC_NOT_FOUND), diff --git a/cerberus-domain/src/main/java/com/nike/cerberus/domain/DomainConstants.java b/cerberus-domain/src/main/java/com/nike/cerberus/domain/DomainConstants.java index a397e52c5..2fe1c7f28 100644 --- a/cerberus-domain/src/main/java/com/nike/cerberus/domain/DomainConstants.java +++ b/cerberus-domain/src/main/java/com/nike/cerberus/domain/DomainConstants.java @@ -20,7 +20,11 @@ public class DomainConstants { - public static final String AWS_IAM_ROLE_ARN_TEMPLATE = "arn:aws:iam::%s:role/%s"; + public static final String AWS_GLOBAL_PARTITION_NAME = "aws"; + + public static final String AWS_CHINA_PARTITION_NAME = "aws-cn"; + + public static final String AWS_IAM_ROLE_ARN_TEMPLATE = "arn:%s:iam::%s:role/%s"; /** * Pattern used to determine if an ARN should be allowed in DB. @@ -28,7 +32,7 @@ public class DomainConstants { *

This is also the list of ARN types that are allowed in KMS key policies. */ public static final String AWS_IAM_PRINCIPAL_ARN_REGEX_ALLOWED = - "^arn:aws:(iam|sts)::(?\\d+?):(role|user|federated-user|assumed-role).*/.+(?aws|aws-cn):(iam|sts)::(?\\d+?):(role|user|federated-user|assumed-role).*/.+(?\\d+?):(?!group).+?/(?.+)$"; + "^arn:(?aws|aws-cn):(iam|sts)::(?\\d+?):(?!group).+?/(?.+)$"; /** * Pattern used for generating a role from another ARN. @@ -58,20 +62,21 @@ public class DomainConstants { public static final Pattern IAM_PRINCIPAL_ARN_PATTERN_ROLE_GENERATION = Pattern.compile(AWS_IAM_PRINCIPAL_ARN_REGEX_ROLE_GENERATION); - public static final String AWS_ACCOUNT_ROOT_ARN_REGEX = "^arn:aws:iam::(?\\d+?):root$"; + public static final String AWS_ACCOUNT_ROOT_ARN_REGEX = + "^arn:(?aws|aws-cn):iam::(?\\d+?):root$"; public static final Pattern AWS_ACCOUNT_ROOT_ARN_PATTERN = Pattern.compile(AWS_ACCOUNT_ROOT_ARN_REGEX); private static final String AWS_IAM_ROLE_ARN_REGEX = - "^arn:aws:iam::(?\\d+?):role/(?.+)$"; + "^arn:(?aws|aws-cn):iam::(?\\d+?):role/(?.+)$"; public static final Pattern IAM_ROLE_ARN_PATTERN = Pattern.compile(AWS_IAM_ROLE_ARN_REGEX); private static final String AWS_IAM_ASSUMED_ROLE_ARN_REGEX = - "^arn:aws:sts::(?\\d+?):assumed-role/(?.+)/.+$"; + "^arn:(?aws|aws-cn):sts::(?\\d+?):assumed-role/(?.+)/.+$"; public static final Pattern IAM_ASSUMED_ROLE_ARN_PATTERN = Pattern.compile(AWS_IAM_ASSUMED_ROLE_ARN_REGEX); private static final String GENERIC_ASSUMED_ROLE_REGEX = - "^arn:aws:sts::(?\\d+?):assumed-role/.+$"; + "^arn:(?aws|aws-cn):sts::(?\\d+?):assumed-role/.+$"; public static final Pattern GENERIC_ASSUMED_ROLE_PATTERN = Pattern.compile(GENERIC_ASSUMED_ROLE_REGEX); private static final Pattern AWS_IAM_ARN_ACCOUNT_ID_PATTERN = - Pattern.compile("arn:aws:(iam|sts)::(?\\d+?):.+"); + Pattern.compile("^arn:(?aws|aws-cn):(iam|sts)::(?\\d+?):.+"); } diff --git a/cerberus-web/src/main/java/com/nike/cerberus/aws/sts/AwsStsHttpClient.java b/cerberus-web/src/main/java/com/nike/cerberus/aws/sts/AwsStsHttpClient.java index 15aa71dc1..6149e8cf6 100644 --- a/cerberus-web/src/main/java/com/nike/cerberus/aws/sts/AwsStsHttpClient.java +++ b/cerberus-web/src/main/java/com/nike/cerberus/aws/sts/AwsStsHttpClient.java @@ -38,6 +38,7 @@ public class AwsStsHttpClient { MediaType.parse("application/x-www-form-urlencoded"); private static final MediaType DEFAULT_ACCEPTED_MEDIA_TYPE = MediaType.parse("application/json"); private static final String AWS_STS_ENDPOINT_TEMPLATE = "https://sts.%s.amazonaws.com"; + private static final String AWS_CN_STS_ENDPOINT_TEMPLATE = "https://sts.%s.amazonaws.com.cn"; private static final String DEFAULT_GET_CALLER_IDENTITY_ACTION = "Action=GetCallerIdentity&Version=2011-06-15"; private static final String DEFAULT_METHOD = "POST"; @@ -115,9 +116,15 @@ public M execute( /** Build the request */ protected Request buildRequest(String region, Map headers) { + String stsEndpointUrl; + if (region.startsWith("cn-")) { + stsEndpointUrl = String.format(AWS_CN_STS_ENDPOINT_TEMPLATE, region); + } else { + stsEndpointUrl = String.format(AWS_STS_ENDPOINT_TEMPLATE, region); + } Request.Builder requestBuilder = new Request.Builder() - .url(String.format(AWS_STS_ENDPOINT_TEMPLATE, region)) + .url(stsEndpointUrl) .addHeader("Accept", DEFAULT_ACCEPTED_MEDIA_TYPE.toString()); if (headers != null) { diff --git a/cerberus-web/src/main/java/com/nike/cerberus/service/AuthenticationService.java b/cerberus-web/src/main/java/com/nike/cerberus/service/AuthenticationService.java index 1a1844025..88dcefe9c 100644 --- a/cerberus-web/src/main/java/com/nike/cerberus/service/AuthenticationService.java +++ b/cerberus-web/src/main/java/com/nike/cerberus/service/AuthenticationService.java @@ -16,6 +16,7 @@ package com.nike.cerberus.service; +import static com.nike.cerberus.domain.DomainConstants.AWS_GLOBAL_PARTITION_NAME; import static com.nike.cerberus.domain.DomainConstants.AWS_IAM_ROLE_ARN_TEMPLATE; import static com.nike.cerberus.security.CerberusPrincipal.*; @@ -221,7 +222,10 @@ public EncryptedAuthDataWrapper authenticate(IamRoleCredentials credentials) { final String iamPrincipalArn = String.format( - AWS_IAM_ROLE_ARN_TEMPLATE, credentials.getAccountId(), credentials.getRoleName()); + AWS_IAM_ROLE_ARN_TEMPLATE, + AWS_GLOBAL_PARTITION_NAME, // hardcoding this to AWS Global for backwards compatibility + credentials.getAccountId(), + credentials.getRoleName()); final String region = credentials.getRegion(); final AwsIamKmsAuthRequest awsIamKmsAuthRequest = new AwsIamKmsAuthRequest(); @@ -243,6 +247,7 @@ public EncryptedAuthDataWrapper authenticate(IamRoleCredentials credentials) { public EncryptedAuthDataWrapper authenticate(AwsIamKmsAuthRequest awsIamKmsAuthRequest) { final String iamPrincipalArn = awsIamKmsAuthRequest.getIamPrincipalArn(); + awsIamRoleArnParser.iamPrincipalPartitionCheck(iamPrincipalArn); final Map authPrincipalMetadata = generateCommonIamPrincipalAuthMetadata(iamPrincipalArn, awsIamKmsAuthRequest.getRegion()); authPrincipalMetadata.put( @@ -258,6 +263,7 @@ public EncryptedAuthDataWrapper authenticate(AwsIamKmsAuthRequest awsIamKmsAuthR * @return Unencrypted auth response */ public AuthTokenResponse stsAuthenticate(final String iamPrincipalArn) { + awsIamRoleArnParser.iamPrincipalPartitionCheck(iamPrincipalArn); final Map authPrincipalMetadata = generateCommonIamPrincipalAuthMetadata(iamPrincipalArn); authPrincipalMetadata.put( diff --git a/cerberus-web/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java b/cerberus-web/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java index bdfd98520..275baed88 100644 --- a/cerberus-web/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java +++ b/cerberus-web/src/main/java/com/nike/cerberus/service/SafeDepositBoxService.java @@ -248,6 +248,7 @@ public SafeDepositBoxV2 createSafeDepositBoxV2( final Set iamRolePermissionSet = safeDepositBox.getIamPrincipalPermissions(); + partitionCheck(iamRolePermissionSet); final boolean isSlugUnique = safeDepositBoxDao.isSlugUnique(boxRecordToStore.getSdbNameSlug()); @@ -309,6 +310,8 @@ public SafeDepositBoxV2 updateSafeDepositBoxV2( final Set iamRolePermissionSet = safeDepositBox.getIamPrincipalPermissions(); + partitionCheck(iamRolePermissionSet); + if (!StringUtils.equals(currentBox.getDescription(), boxToUpdate.getDescription())) { safeDepositBoxDao.updateSafeDepositBox(boxToUpdate); } @@ -636,6 +639,9 @@ protected SafeDepositBoxV2 convertSafeDepositBoxV1ToV2(SafeDepositBoxV1 safeDepo .withIamPrincipalArn( String.format( DomainConstants.AWS_IAM_ROLE_ARN_TEMPLATE, + DomainConstants + .AWS_GLOBAL_PARTITION_NAME, // hardcoding this to AWS Global for + // backwards compatibility iamRolePermission.getAccountId(), iamRolePermission.getIamRoleName())) .withRoleId(iamRolePermission.getRoleId())) @@ -793,4 +799,12 @@ public SafeDepositBoxV2 getSafeDepositBoxDangerouslyWithoutPermissionValidation( () -> ApiException.newBuilder().withApiErrors(DefaultApiError.ENTITY_NOT_FOUND).build())); } + + private void partitionCheck(Set iamRolePermissionSet) { + iamRolePermissionSet.stream() + .forEach( + iamRolePermission -> + awsIamRoleArnParser.iamPrincipalPartitionCheck( + iamRolePermission.getIamPrincipalArn())); + } } diff --git a/cerberus-web/src/main/java/com/nike/cerberus/util/AwsIamRoleArnParser.java b/cerberus-web/src/main/java/com/nike/cerberus/util/AwsIamRoleArnParser.java index 7c8a4f39c..06dbfddcd 100644 --- a/cerberus-web/src/main/java/com/nike/cerberus/util/AwsIamRoleArnParser.java +++ b/cerberus-web/src/main/java/com/nike/cerberus/util/AwsIamRoleArnParser.java @@ -18,14 +18,25 @@ import com.nike.backstopper.exception.ApiException; import com.nike.cerberus.domain.DomainConstants; +import com.nike.cerberus.error.DefaultApiError; import com.nike.cerberus.error.InvalidIamRoleArnApiError; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** Utility class for concatenating and parsing AWS IAM role ARNs. */ @Component public class AwsIamRoleArnParser { + private final boolean awsChinaEnabled; + private final boolean awsGlobalEnabled; + + public AwsIamRoleArnParser( + @Value("${cerberus.partitions.awsGlobal.enabled}") boolean awsGlobalEnabled, + @Value("${cerberus.partitions.awsChina.enabled}") boolean awsChinaEnabled) { + this.awsGlobalEnabled = awsGlobalEnabled; + this.awsChinaEnabled = awsChinaEnabled; + } /** * Gets account ID from a 'role' ARN @@ -119,8 +130,10 @@ public String convertPrincipalArnToRoleArn(final String principalArn) { final String accountId = getNamedGroupFromRegexPattern(patternToMatch, "accountId", principalArn); final String roleName = getNamedGroupFromRegexPattern(patternToMatch, "roleName", principalArn); + final String partition = + getNamedGroupFromRegexPattern(patternToMatch, "partition", principalArn); - return String.format(DomainConstants.AWS_IAM_ROLE_ARN_TEMPLATE, accountId, roleName); + return String.format(DomainConstants.AWS_IAM_ROLE_ARN_TEMPLATE, partition, accountId, roleName); } public String convertPrincipalArnToRootArn(final String principalArn) { @@ -133,7 +146,11 @@ public String convertPrincipalArnToRootArn(final String principalArn) { getNamedGroupFromRegexPattern( DomainConstants.IAM_PRINCIPAL_ARN_PATTERN_ALLOWED, "accountId", principalArn); - return String.format("arn:aws:iam::%s:root", accountId); + final String partition = + getNamedGroupFromRegexPattern( + DomainConstants.IAM_PRINCIPAL_ARN_PATTERN_ALLOWED, "partition", principalArn); + + return String.format("arn:%s:iam::%s:root", partition, accountId); } /** @@ -150,6 +167,17 @@ public String stripOutDescription(final String principalArn) { } } + /** + * Checks if the partition of an IAM principal ARN is enabled + * + * @param iamPrincipalArn The IAM principal ARN to be checked + * @throws ApiException Throws an exception if the partition of the IAM principal isn't enabled + */ + public void iamPrincipalPartitionCheck(String iamPrincipalArn) { + getNamedGroupFromRegexPattern( + DomainConstants.IAM_PRINCIPAL_ARN_PATTERN_ALLOWED, "partition", iamPrincipalArn); + } + private String getNamedGroupFromRegexPattern( final Pattern pattern, final String groupName, final String input) { final Matcher iamRoleArnMatcher = pattern.matcher(input); @@ -160,7 +188,17 @@ private String getNamedGroupFromRegexPattern( .withExceptionMessage("ARN does not match pattern: " + pattern.toString()) .build(); } + partitionCheck(iamRoleArnMatcher.group("partition")); return iamRoleArnMatcher.group(groupName); } + + private void partitionCheck(String partition) { + if (DomainConstants.AWS_GLOBAL_PARTITION_NAME.equals(partition) && !awsGlobalEnabled) { + throw ApiException.newBuilder().withApiErrors(DefaultApiError.AWS_GLOBAL_NOT_ALLOWED).build(); + } + if (DomainConstants.AWS_CHINA_PARTITION_NAME.equals(partition) && !awsChinaEnabled) { + throw ApiException.newBuilder().withApiErrors(DefaultApiError.AWS_CHINA_NOT_ALLOWED).build(); + } + } } diff --git a/cerberus-web/src/main/resources/cerberus.yaml b/cerberus-web/src/main/resources/cerberus.yaml index d1271d62f..5ef6d1928 100644 --- a/cerberus-web/src/main/resources/cerberus.yaml +++ b/cerberus-web/src/main/resources/cerberus.yaml @@ -44,6 +44,11 @@ c3p0: preferredTestQuery: SELECT 1 cerberus: + partitions: + awsGlobal: + enabled: true + awsChina: + enabled: false environmentName: TODO admin: # These are aws principal that you want to allow to use the admin API diff --git a/cerberus-web/src/test/java/com/nike/cerberus/aws/sts/AwsStsHttpHeaderTest.java b/cerberus-web/src/test/java/com/nike/cerberus/aws/sts/AwsStsHttpHeaderTest.java index 0087f51bd..e0ba0c855 100644 --- a/cerberus-web/src/test/java/com/nike/cerberus/aws/sts/AwsStsHttpHeaderTest.java +++ b/cerberus-web/src/test/java/com/nike/cerberus/aws/sts/AwsStsHttpHeaderTest.java @@ -39,5 +39,13 @@ public void test_getRegion_returns_region_as_expected() { "AWS4-HMAC-SHA256 Credential=ASIA5S2FQS2GYQLK5FFF/20180904/us-east-1/sts/aws4_request, SignedHeaders=host;x-amz-date, Signature=ddb9417d2b9bfe6f8b03e31a8f5d8ab98e0f4alkj12312098asdf"); assertEquals("us-east-1", header.getRegion()); + + header = + new AwsStsHttpHeader( + "20180904T205115Z", + "FQoGZXIvYXdzEFYaDEYceadsfLKJLKlkj908098oB/rJIdxdo57fx3Ef2wW8WhFbSpLGg3hwNqhuepdkf/c0F7OXJutqM2yjgnZCiO7SPAdnMSJhoEgH7SJlkPaPfiRzZAf0yxxD6e4z0VJU74uQfbgfZpn5RL+JyDpgoYkUrjuyL8zRB1knGSOCi32Q75+asdfasd+7bWxMyJIKEb/HF2Le8xM/9F4WRqa5P0+asdfasdfasdf+MGlDlNG0KTzg1JT6QXf95ozWR5bBFSz5DbrFhXhMegMQ7+7Kvx+asdfasdl.jlkj++5NpRRlE54cct7+aG3HQskow9y73AU=", + "AWS4-HMAC-SHA256 Credential=ASIA5S2FQS2GYQLK5FFF/20180904/cn-northwest-1/sts/aws4_request, SignedHeaders=host;x-amz-date, Signature=ddb9417d2b9bfe6f8b03e31a8f5d8ab98e0f4alkj12312098asdf"); + + assertEquals("cn-northwest-1", header.getRegion()); } } diff --git a/cerberus-web/src/test/java/com/nike/cerberus/service/AuthenticationServiceTest.java b/cerberus-web/src/test/java/com/nike/cerberus/service/AuthenticationServiceTest.java index d7defb783..085da66dd 100644 --- a/cerberus-web/src/test/java/com/nike/cerberus/service/AuthenticationServiceTest.java +++ b/cerberus-web/src/test/java/com/nike/cerberus/service/AuthenticationServiceTest.java @@ -16,7 +16,7 @@ package com.nike.cerberus.service; -import static com.nike.cerberus.domain.DomainConstants.AWS_IAM_ROLE_ARN_TEMPLATE; +import static com.nike.cerberus.domain.DomainConstants.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -266,7 +266,8 @@ public void test_that_getKeyId_only_validates_kms_policy_one_time_within_interva String accountId = "0000000000"; String roleName = "role/path"; String principalArn = String.format("arn:aws:iam::%s:instance-profile/%s", accountId, roleName); - String roleArn = String.format(AWS_IAM_ROLE_ARN_TEMPLATE, accountId, roleName); + String roleArn = + String.format(AWS_IAM_ROLE_ARN_TEMPLATE, AWS_GLOBAL_PARTITION_NAME, accountId, roleName); AwsIamRoleRecord awsIamRoleRecord = mock(AwsIamRoleRecord.class); when(awsIamRoleDao.getIamRole(principalArn)).thenReturn(Optional.empty()); @@ -311,7 +312,8 @@ public void test_that_findIamRoleAssociatedWithSdb_returns_empty_optional_when_r String accountId = "0000000000"; String roleName = "role/path"; String principalArn = String.format("arn:aws:iam::%s:instance-profile/%s", accountId, roleName); - String roleArn = String.format(AWS_IAM_ROLE_ARN_TEMPLATE, accountId, roleName); + String roleArn = + String.format(AWS_IAM_ROLE_ARN_TEMPLATE, AWS_GLOBAL_PARTITION_NAME, accountId, roleName); String rootArn = String.format("arn:aws:iam::%s:root", accountId); AwsIamRoleRecord rootRecord = mock(AwsIamRoleRecord.class); @@ -332,6 +334,36 @@ public void test_that_findIamRoleAssociatedWithSdb_returns_empty_optional_when_r assertEquals(roleRecord, result.get()); } + @Test + public void + test_that_findIamRoleAssociatedWithSdb_returns_generic_role_when_iam_principal_not_found_and_root_found_for_aws_china() { + + String accountId = "0000000000"; + String roleName = "role/path"; + String principalArn = + String.format("arn:aws-cn:iam::%s:instance-profile/%s", accountId, roleName); + String roleArn = + String.format(AWS_IAM_ROLE_ARN_TEMPLATE, AWS_CHINA_PARTITION_NAME, accountId, roleName); + String rootArn = String.format("arn:aws-cn:iam::%s:root", accountId); + + AwsIamRoleRecord rootRecord = mock(AwsIamRoleRecord.class); + AwsIamRoleRecord roleRecord = mock(AwsIamRoleRecord.class); + when(awsIamRoleDao.getIamRole(principalArn)).thenReturn(Optional.empty()); + when(awsIamRoleDao.getIamRole(roleArn)).thenReturn(Optional.empty()); + when(awsIamRoleDao.getIamRole(rootArn)).thenReturn(Optional.of(rootRecord)); + + when(awsIamRoleArnParser.isRoleArn(principalArn)).thenReturn(false); + when(awsIamRoleArnParser.convertPrincipalArnToRoleArn(principalArn)).thenReturn(roleArn); + when(awsIamRoleArnParser.convertPrincipalArnToRootArn(roleArn)).thenReturn(rootArn); + + when(awsIamRoleService.createIamRole(roleArn)).thenReturn(roleRecord); + + Optional result = + authenticationService.findIamRoleAssociatedWithSdb(principalArn); + + assertEquals(roleRecord, result.get()); + } + @Test public void tests_that_validateAuthPayloadSizeAndTruncateIfLargerThanMaxKmsSupportedSize_returns_the_original_payload_if_the_size_can_be_encrypted_by_kms() diff --git a/cerberus-web/src/test/java/com/nike/cerberus/service/KmsPolicyServiceTest.java b/cerberus-web/src/test/java/com/nike/cerberus/service/KmsPolicyServiceTest.java index d0f185ed1..c357af943 100644 --- a/cerberus-web/src/test/java/com/nike/cerberus/service/KmsPolicyServiceTest.java +++ b/cerberus-web/src/test/java/com/nike/cerberus/service/KmsPolicyServiceTest.java @@ -51,29 +51,29 @@ public void before() { String cmsRoleArn = "arn:aws:iam::1111111111:role/cms-iam-role"; kmsPolicyService = new KmsPolicyService( - true, rootUserArn, adminRoleArn, cmsRoleArn, new AwsIamRoleArnParser()); + true, rootUserArn, adminRoleArn, cmsRoleArn, new AwsIamRoleArnParser(true, false)); objectMapper = new ObjectMapper(); } @Test(expected = NullPointerException.class) public void test_that_KmsPolicyService_throws_error_when_required_field_null_rootUserArn() { - new KmsPolicyService(true, null, "foo", "bar", new AwsIamRoleArnParser()); + new KmsPolicyService(true, null, "foo", "bar", new AwsIamRoleArnParser(true, false)); } @Test(expected = NullPointerException.class) public void test_that_KmsPolicyService_throws_error_when_required_field_null_adminRoleArn() { - new KmsPolicyService(true, "foo", null, "bar", new AwsIamRoleArnParser()); + new KmsPolicyService(true, "foo", null, "bar", new AwsIamRoleArnParser(true, false)); } @Test(expected = NullPointerException.class) public void test_that_KmsPolicyService_throws_error_when_required_field_null_cmsRoleArn() { - new KmsPolicyService(true, "foo", "bar", null, new AwsIamRoleArnParser()); + new KmsPolicyService(true, "foo", "bar", null, new AwsIamRoleArnParser(true, false)); } @Test() public void test_that_KmsPolicyService_throws_no_error_when_required_fields_are_null_but_kms_auth_disabled() { - new KmsPolicyService(false, null, null, null, new AwsIamRoleArnParser()); + new KmsPolicyService(false, null, null, null, new AwsIamRoleArnParser(true, false)); } @Test diff --git a/cerberus-web/src/test/java/com/nike/cerberus/service/KmsServiceTest.java b/cerberus-web/src/test/java/com/nike/cerberus/service/KmsServiceTest.java index 69d5e2184..3c85619e3 100644 --- a/cerberus-web/src/test/java/com/nike/cerberus/service/KmsServiceTest.java +++ b/cerberus-web/src/test/java/com/nike/cerberus/service/KmsServiceTest.java @@ -98,7 +98,7 @@ public void setup() { kmsClientFactory, kmsPolicyService, dateTimeSupplier, - new AwsIamRoleArnParser(), + new AwsIamRoleArnParser(true, false), 3000, ENV, slugger, diff --git a/cerberus-web/src/test/java/com/nike/cerberus/util/AwsIamRoleArnParserTest.java b/cerberus-web/src/test/java/com/nike/cerberus/util/AwsIamRoleArnParserTest.java index 95f891fac..f289c0991 100644 --- a/cerberus-web/src/test/java/com/nike/cerberus/util/AwsIamRoleArnParserTest.java +++ b/cerberus-web/src/test/java/com/nike/cerberus/util/AwsIamRoleArnParserTest.java @@ -27,39 +27,46 @@ /** Tests the AwsIamRoleArnParser class */ public class AwsIamRoleArnParserTest { - private AwsIamRoleArnParser awsIamRoleArnParser; + private AwsIamRoleArnParser awsGlobalIamRoleArnParser; + private AwsIamRoleArnParser awsChinaIamRoleArnParser; @Before public void setup() { - - awsIamRoleArnParser = new AwsIamRoleArnParser(); + awsGlobalIamRoleArnParser = new AwsIamRoleArnParser(true, false); + awsChinaIamRoleArnParser = new AwsIamRoleArnParser(false, true); } @Test public void getAccountId_returns_an_account_id_given_a_valid_arn() { - assertEquals( "1111111111", - awsIamRoleArnParser.getAccountId("arn:aws:iam::1111111111:role/lamb_dev_health")); + awsGlobalIamRoleArnParser.getAccountId("arn:aws:iam::1111111111:role/lamb_dev_health")); + assertEquals( + "1111111111", + awsChinaIamRoleArnParser.getAccountId("arn:aws-cn:iam::1111111111:role/lamb_dev_health")); } @Test(expected = RuntimeException.class) public void getAccountId_fails_on_invalid_arn() { - awsIamRoleArnParser.getAccountId("hullabaloo"); + awsGlobalIamRoleArnParser.getAccountId("hullabaloo"); } @Test public void getRoleNameHappy_returns_the_role_name_given_a_valid_arn() { assertEquals( - "my_roleName", awsIamRoleArnParser.getRoleName("arn:aws:iam::222222:role/my_roleName")); + "my_roleName", + awsGlobalIamRoleArnParser.getRoleName("arn:aws:iam::222222:role/my_roleName")); + assertEquals( + "my_roleName", + awsChinaIamRoleArnParser.getRoleName("arn:aws-cn:iam::222222:role/my_roleName")); } @Test(expected = RuntimeException.class) public void getRoleName_fails_on_invalid_arn() { - awsIamRoleArnParser.getRoleName("brouhaha"); + awsGlobalIamRoleArnParser.getRoleName("brouhaha"); } @Test @@ -67,51 +74,87 @@ public void convertPrincipalArnToRoleArn_properly_converts_principals_to_role_ar assertEquals( "arn:aws:iam::1111111111:role/lamb_dev_health", - awsIamRoleArnParser.convertPrincipalArnToRoleArn( + awsGlobalIamRoleArnParser.convertPrincipalArnToRoleArn( "arn:aws:sts::1111111111:federated-user/lamb_dev_health")); assertEquals( "arn:aws:iam::2222222222:role/prince_role", - awsIamRoleArnParser.convertPrincipalArnToRoleArn( + awsGlobalIamRoleArnParser.convertPrincipalArnToRoleArn( "arn:aws:sts::2222222222:assumed-role/prince_role/session-name")); assertEquals( "arn:aws:iam::2222222222:role/sir/alfred/role", - awsIamRoleArnParser.convertPrincipalArnToRoleArn( + awsGlobalIamRoleArnParser.convertPrincipalArnToRoleArn( "arn:aws:sts::2222222222:assumed-role/sir/alfred/role/session-name")); assertEquals( "arn:aws:iam::3333333333:role/path/to/foo", - awsIamRoleArnParser.convertPrincipalArnToRoleArn( + awsGlobalIamRoleArnParser.convertPrincipalArnToRoleArn( "arn:aws:iam::3333333333:role/path/to/foo")); assertEquals( "arn:aws:iam::4444444444:role/name", - awsIamRoleArnParser.convertPrincipalArnToRoleArn("arn:aws:iam::4444444444:role/name")); + awsGlobalIamRoleArnParser.convertPrincipalArnToRoleArn( + "arn:aws:iam::4444444444:role/name")); + + assertEquals( + "arn:aws-cn:iam::1111111111:role/lamb_dev_health", + awsChinaIamRoleArnParser.convertPrincipalArnToRoleArn( + "arn:aws-cn:sts::1111111111:federated-user/lamb_dev_health")); + assertEquals( + "arn:aws-cn:iam::2222222222:role/prince_role", + awsChinaIamRoleArnParser.convertPrincipalArnToRoleArn( + "arn:aws-cn:sts::2222222222:assumed-role/prince_role/session-name")); + assertEquals( + "arn:aws-cn:iam::2222222222:role/sir/alfred/role", + awsChinaIamRoleArnParser.convertPrincipalArnToRoleArn( + "arn:aws-cn:sts::2222222222:assumed-role/sir/alfred/role/session-name")); + assertEquals( + "arn:aws-cn:iam::3333333333:role/path/to/foo", + awsChinaIamRoleArnParser.convertPrincipalArnToRoleArn( + "arn:aws-cn:iam::3333333333:role/path/to/foo")); + assertEquals( + "arn:aws-cn:iam::4444444444:role/name", + awsChinaIamRoleArnParser.convertPrincipalArnToRoleArn( + "arn:aws-cn:iam::4444444444:role/name")); } @Test(expected = RuntimeException.class) public void convertPrincipalArnToRoleArn_fails_on_invalid_arn() { - awsIamRoleArnParser.convertPrincipalArnToRoleArn("foobar"); + awsGlobalIamRoleArnParser.convertPrincipalArnToRoleArn("foobar"); } @Test(expected = RuntimeException.class) public void convertPrincipalArnToRoleArn_fails_on_group_arn() { - awsIamRoleArnParser.convertPrincipalArnToRoleArn("arn:aws:iam::1111111111:group/path/to/group"); + awsGlobalIamRoleArnParser.convertPrincipalArnToRoleArn( + "arn:aws:iam::1111111111:group/path/to/group"); } @Test(expected = RuntimeException.class) public void convertPrincipalArnToRoleArn_fails_on_invalid_assumed_role_arn() { - awsIamRoleArnParser.convertPrincipalArnToRoleArn("arn:aws:sts::1111111111:assumed-role/blah"); + awsGlobalIamRoleArnParser.convertPrincipalArnToRoleArn( + "arn:aws:sts::1111111111:assumed-role/blah"); } @Test public void isRoleArn_returns_true_when_is_role_arn() { - assertTrue(awsIamRoleArnParser.isRoleArn("arn:aws:iam::2222222222:role/fancy/role/path")); - assertTrue(awsIamRoleArnParser.isRoleArn("arn:aws:iam::1111111111:role/name")); - assertFalse(awsIamRoleArnParser.isRoleArn("arn:aws:iam::3333333333:assumed-role/happy/path")); - assertFalse(awsIamRoleArnParser.isRoleArn("arn:aws:sts::1111111111:federated-user/my_user")); - assertFalse(awsIamRoleArnParser.isRoleArn("arn:aws:iam::1111111111:group/path/to/group")); + assertTrue(awsGlobalIamRoleArnParser.isRoleArn("arn:aws:iam::2222222222:role/fancy/role/path")); + assertTrue(awsGlobalIamRoleArnParser.isRoleArn("arn:aws:iam::1111111111:role/name")); + assertFalse( + awsGlobalIamRoleArnParser.isRoleArn("arn:aws:iam::3333333333:assumed-role/happy/path")); + assertFalse( + awsGlobalIamRoleArnParser.isRoleArn("arn:aws:sts::1111111111:federated-user/my_user")); + assertFalse(awsGlobalIamRoleArnParser.isRoleArn("arn:aws:iam::1111111111:group/path/to/group")); + + assertTrue( + awsChinaIamRoleArnParser.isRoleArn("arn:aws-cn:iam::2222222222:role/fancy/role/path")); + assertTrue(awsChinaIamRoleArnParser.isRoleArn("arn:aws-cn:iam::1111111111:role/name")); + assertFalse( + awsChinaIamRoleArnParser.isRoleArn("arn:aws-cn:iam::3333333333:assumed-role/happy/path")); + assertFalse( + awsChinaIamRoleArnParser.isRoleArn("arn:aws-cn:sts::1111111111:federated-user/my_user")); + assertFalse( + awsChinaIamRoleArnParser.isRoleArn("arn:aws-cn:iam::1111111111:group/path/to/group")); } @Test @@ -191,33 +234,33 @@ public void test_IAM_PRINCIPAL_ARN_PATTERN_invalid_ARNs() { @Test public void test_isArnThatCanGoInKeyPolicy() { assertTrue( - awsIamRoleArnParser.isArnThatCanGoInKeyPolicy( + awsGlobalIamRoleArnParser.isArnThatCanGoInKeyPolicy( "arn:aws:iam::12345678901234:role/some-role")); assertTrue( - awsIamRoleArnParser.isArnThatCanGoInKeyPolicy( + awsGlobalIamRoleArnParser.isArnThatCanGoInKeyPolicy( "arn:aws:iam::12345678901234:role/some/path/some-role")); assertTrue( - awsIamRoleArnParser.isArnThatCanGoInKeyPolicy( + awsGlobalIamRoleArnParser.isArnThatCanGoInKeyPolicy( "arn:aws:iam::12345678901234:user/some-user")); assertTrue( - awsIamRoleArnParser.isArnThatCanGoInKeyPolicy( + awsGlobalIamRoleArnParser.isArnThatCanGoInKeyPolicy( "arn:aws:sts::12345678901234:assumed-role/some-path/some-role")); assertTrue( - awsIamRoleArnParser.isArnThatCanGoInKeyPolicy( + awsGlobalIamRoleArnParser.isArnThatCanGoInKeyPolicy( "arn:aws:sts::12345678901234:assumed-role/some-role")); assertTrue( - awsIamRoleArnParser.isArnThatCanGoInKeyPolicy( + awsGlobalIamRoleArnParser.isArnThatCanGoInKeyPolicy( "arn:aws:sts::12345678901234:federated-user/my_user")); // invalid - KMS doesn't allow 'group' or 'instance-profile' assertFalse( - awsIamRoleArnParser.isArnThatCanGoInKeyPolicy( + awsGlobalIamRoleArnParser.isArnThatCanGoInKeyPolicy( "arn:aws:iam::12345678901234:group/some-group")); assertFalse( - awsIamRoleArnParser.isArnThatCanGoInKeyPolicy( + awsGlobalIamRoleArnParser.isArnThatCanGoInKeyPolicy( "arn:aws:iam::12345678901234:instance-profile/some-profile")); assertFalse( - awsIamRoleArnParser.isArnThatCanGoInKeyPolicy( + awsGlobalIamRoleArnParser.isArnThatCanGoInKeyPolicy( "arn:aws:iam::12345678901234:other/some-value")); } @@ -225,38 +268,42 @@ public void test_isArnThatCanGoInKeyPolicy() { public void test_stripOutDescription() { assertEquals( "12345678901234/some-role", - awsIamRoleArnParser.stripOutDescription("arn:aws:iam::12345678901234:role/some-role")); + awsGlobalIamRoleArnParser.stripOutDescription( + "arn:aws:iam::12345678901234:role/some-role")); assertEquals( "12345678901234/some/path/some-role", - awsIamRoleArnParser.stripOutDescription( + awsGlobalIamRoleArnParser.stripOutDescription( "arn:aws:iam::12345678901234:role/some/path/some-role")); assertEquals( "12345678901234/some-user", - awsIamRoleArnParser.stripOutDescription("arn:aws:iam::12345678901234:user/some-user")); + awsGlobalIamRoleArnParser.stripOutDescription( + "arn:aws:iam::12345678901234:user/some-user")); assertEquals( "12345678901234/some-path/some-role", - awsIamRoleArnParser.stripOutDescription( + awsGlobalIamRoleArnParser.stripOutDescription( "arn:aws:sts::12345678901234:assumed-role/some-path/some-role")); assertEquals( "12345678901234/some-role", - awsIamRoleArnParser.stripOutDescription( + awsGlobalIamRoleArnParser.stripOutDescription( "arn:aws:sts::12345678901234:assumed-role/some-role")); assertEquals( "12345678901234/my_user", - awsIamRoleArnParser.stripOutDescription( + awsGlobalIamRoleArnParser.stripOutDescription( "arn:aws:sts::12345678901234:federated-user/my_user")); // invalid - KMS doesn't allow 'group' or 'instance-profile' though some parsing is still // possible (this behavior isn't important) assertEquals( "", - awsIamRoleArnParser.stripOutDescription("arn:aws:iam::12345678901234:group/some-group")); + awsGlobalIamRoleArnParser.stripOutDescription( + "arn:aws:iam::12345678901234:group/some-group")); assertEquals( "12345678901234/some-value", - awsIamRoleArnParser.stripOutDescription("arn:aws:iam::12345678901234:other/some-value")); + awsGlobalIamRoleArnParser.stripOutDescription( + "arn:aws:iam::12345678901234:other/some-value")); assertEquals( "12345678901234/some-profile", - awsIamRoleArnParser.stripOutDescription( + awsGlobalIamRoleArnParser.stripOutDescription( "arn:aws:iam::12345678901234:instance-profile/some-profile")); } @@ -274,13 +321,27 @@ public void test_that_an_iam_role_can_not_end_with_whitespace() { @Test public void test_isAccountRootArn() { - assertTrue(awsIamRoleArnParser.isAccountRootArn("arn:aws:iam::0000000000:root")); + assertTrue(awsGlobalIamRoleArnParser.isAccountRootArn("arn:aws:iam::0000000000:root")); - assertFalse(awsIamRoleArnParser.isAccountRootArn("arn:aws:iam::0000000000:role/foo")); - assertFalse(awsIamRoleArnParser.isAccountRootArn("arn:aws:iam::0000000000:user/bar")); - assertFalse(awsIamRoleArnParser.isAccountRootArn("arn:aws:sts::0000000000:assumed-role/baz")); - assertFalse(awsIamRoleArnParser.isAccountRootArn("arn:aws:iam::0000000000:group/foobar")); + assertFalse(awsGlobalIamRoleArnParser.isAccountRootArn("arn:aws:iam::0000000000:role/foo")); + assertFalse(awsGlobalIamRoleArnParser.isAccountRootArn("arn:aws:iam::0000000000:user/bar")); assertFalse( - awsIamRoleArnParser.isAccountRootArn("arn:aws:sts::0000000000:federated-user/foobaz")); + awsGlobalIamRoleArnParser.isAccountRootArn("arn:aws:sts::0000000000:assumed-role/baz")); + assertFalse(awsGlobalIamRoleArnParser.isAccountRootArn("arn:aws:iam::0000000000:group/foobar")); + assertFalse( + awsGlobalIamRoleArnParser.isAccountRootArn( + "arn:aws:sts::0000000000:federated-user/foobaz")); + } + + @Test(expected = RuntimeException.class) + public void iamPrincipalPartitionCheck_fails_on_disabled_aws_china_partition() { + awsGlobalIamRoleArnParser.iamPrincipalPartitionCheck( + "arn:aws-cn:iam::1111111111:role/lamb_dev_health"); + } + + @Test(expected = RuntimeException.class) + public void iamPrincipalPartitionCheck_fails_on_disabled_aws_global_partition() { + awsChinaIamRoleArnParser.iamPrincipalPartitionCheck( + "arn:aws:iam::1111111111:role/lamb_dev_health"); } } diff --git a/gradle.properties b/gradle.properties index 56bdf4eed..5c1d957dd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,6 +14,6 @@ # limitations under the License. # -version=4.6.2 +version=4.7.0 group=com.nike.cerberus springBootVersion=2.3.2.RELEASE