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

ssm: service principals are incorrect for all regions since ap-east-1 #16188

Closed
egriffith opened this issue Aug 23, 2021 · 11 comments · Fixed by #17047 or #17984
Closed

ssm: service principals are incorrect for all regions since ap-east-1 #16188

egriffith opened this issue Aug 23, 2021 · 11 comments · Fixed by #17047 or #17984
Labels
@aws-cdk/aws-iam Related to AWS Identity and Access Management @aws-cdk/aws-ssm Related to AWS Systems Manager bug This issue is a bug. effort/small Small work item – less than a day of effort good first issue Related to contributions. See CONTRIBUTING.md p1

Comments

@egriffith
Copy link

🐛 Bug Report

The Issue

iam.ServicePrincipal (https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.aws_iam/ServicePrincipal.html) offers a "region" parameter. Presumably this parameter is so you can say something like ssm.ap-us-east-1.amazonaws.com to be inclusive of regional-only service endpoints.

Such as the ones called out on this page: https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-inventory-datasync.html#systems-manager-inventory-resource-data-sync-AWS-Organizations

"Note

The Asia Pacific Region came online in April 25, 2019. If you create a resource data sync for an AWS Region that came online since the Asia Pacific (Hong Kong) Region (ap-east-1) or later, then you must enter a Region-specific service principal entry in the SSMBucketDelivery section. The following example includes a Region-specific service principal entry for ssm.ap-east-1.amazonaws.com."

However

Here is a snippet of CDK Python that results in some odd behavior:

        kmsKey = kms.Key(
            self,
            "S3-KMSKey",
        )

        kmsKey.add_to_resource_policy(
            statement=iam.PolicyStatement(
                sid="ssm-access-policy",
                conditions=[],
                effect=iam.Effect.ALLOW,
                actions=["kms:GenerateDataKey"],
                principals=[
                    iam.ServicePrincipal(service="ssm.amazonaws.com"),
                    iam.ServicePrincipal(
                        service="ssm.amazonaws.com", region="ap-east-1"
                    ),
                    iam.ServicePrincipal(service="ssm", region="ap-east-1"),
                ],
                resources=[kmsKey.key_arn],
            )
        )

That snippet uses ServicePrincipal three different ways, two of which should result in regional endpoints, and yet none of them do.

That snippet spits out something that looks like...

Resources:
  S3KMSKey26947010:
    Type: AWS::KMS::Key
    Properties:
      KeyPolicy:
        Statement:
          - Action: kms:*
            Effect: Allow
            Principal:
              AWS:
                Fn::Join:
                  - ""
                  - - "arn:"
                    - Ref: AWS::Partition
                    - ":iam::"
                    - Ref: AWS::AccountId
                    - :root
            Resource: "*"
          - Action: kms:GenerateDataKey
            Effect: Allow
            Principal:
              Service:
                - ssm.amazonaws.com
                - ssm.amazonaws.com
                - ssm.amazonaws.com
            Resource:
              Fn::GetAtt:
                - S3KMSKey26947010
                - Arn
            Sid: ssm-access-policy
        Version: "2012-10-17"
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Metadata:
      aws:cdk:path: SsmInventoryAthenaStack/S3-KMSKey/Resource

Notice that none of the Principal -> Service definitions have 'ap-east-1' in their URLs.

This might be related to these two issues:
#2622
#2999

Where CDK was exclusively crafting regional endpoints

Environment

  • CDK CLI Version: 2.0.0-rc.17 (build fb5dc58)
  • Module Version: 2.0.0rc17 (I think, pulled from python's Pipfile.lock for aws-cdk-libs)
  • Node.js Version: v14.17.1
  • OS: OSX
  • Language: Python
@github-actions github-actions bot added the @aws-cdk/aws-iam Related to AWS Identity and Access Management label Aug 23, 2021
@rix0rrr
Copy link
Contributor

rix0rrr commented Oct 4, 2021

region= is a way to override the default region, but it does not control whether the region is actually rendered. You should almost never need to supply it, because the correct value is taken from the containing Stack by default.

Whether or not the region is rendered is controlled here, in the region-info package:

public static servicePrincipal(service: string, region: string, urlSuffix: string): string {

Looks like we are missing the fact that SSM service principals need to be regionalized for all regions since 2019.

Until this is fixed, you can override the fact database: https://github.com/aws/aws-cdk/tree/v1.125.0/packages/%40aws-cdk/region-info#supplying-new-or-missing-information

@rix0rrr rix0rrr changed the title iam.ServicePrincipal() offers a "region=" parameter, but ignores it at synthesis time. ssm: service principals are incorrect for all regions since ap-east-1 Oct 4, 2021
@github-actions github-actions bot added the @aws-cdk/aws-ssm Related to AWS Systems Manager label Oct 4, 2021
@rix0rrr rix0rrr removed their assignment Oct 4, 2021
@njlynch njlynch added the bug This issue is a bug. label Oct 6, 2021
@njlynch
Copy link
Contributor

njlynch commented Oct 7, 2021

This should be a fairly straight-forward fix (adding ssm) to the list of region-specific principals here:

// Services with a regional principal
case 'states':
return `${service}.${region}.amazonaws.com`;

The "gotcha" will just be testing & verifying that the regional service principals also work for the older regions (e.g., us-east-1, eu-west-1). The linked documentation above isn't explicit about this, so a manual test to verify seems in order.

Contributions welcome!

@njlynch njlynch added effort/small Small work item – less than a day of effort good first issue Related to contributions. See CONTRIBUTING.md labels Oct 7, 2021
@njlynch njlynch removed their assignment Oct 7, 2021
@mergify mergify bot closed this as completed in #17047 Nov 11, 2021
mergify bot pushed a commit that referenced this issue Nov 11, 2021
)

fixes #16188 
----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
@github-actions
Copy link

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

@mickael-caro-sonarsource

@njlynch @rix0rrr this change doesn't work with eu-central-1, CloudFormation complains about a malformed document.

@stephenwiebe
Copy link
Contributor

@njlynch @rix0rrr This appears to have broken our stack in us-east-1, after updating to 1.134.0 getting:

Invalid principal in policy: "SERVICE":"ssm.us-east-1.amazonaws.com" (Service: AmazonIdentityManagement; Status Code: 400; Error Code: MalformedPolicyDocument; Request ID: 86fd6c33-2099-4887-aff4-efff716bc7ea; Proxy: null)

This is the relevant IAM role I'm creating with the SSM service principal:

const ssmServiceRole = new iam.Role(this, 'SSMServiceRole', {
      assumedBy: new iam.ServicePrincipal('ssm.amazonaws.com'),
      managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore')],
      roleName: 'SSMServiceRole'
    });

@adhorn
Copy link

adhorn commented Dec 1, 2021

Also running into the issue.

        // SSMA Role for SSMA Documents faults
        const ssmarole = new iam.Role(this, 'ssma-role', {
            assumedBy: new iam.CompositePrincipal(
                new iam.ServicePrincipal('iam.amazonaws.com'),
                new iam.ServicePrincipal('ssm.amazonaws.com')
            )
        });

        // ssmarole.assumeRolePolicy?.addStatements(
        //     new iam.PolicyStatement({
        //       effect: iam.Effect.ALLOW,
        //       principals: [new iam.ServicePrincipal('ssm.amazonaws.com')],
        //       actions: ['sts:AssumeRole'],
        //     }),
        //   );

Results to:

│ + │ ${FisRole/ssma-role.Arn} │ Allow  │ sts:AssumeRole │ Service:iam.amazonaws.com
│ + │ ${FisRole/ssma-role.Arn} │ Allow  │ sts:AssumeRole │ Service:ssm.${AWS::Region}.amazonaws.com               

Which obviously fails

5:54:10 PM | UPDATE_FAILED        | AWS::IAM::Role     | ssmarole20460B02
Invalid principal in policy: "SERVICE":"ssm.us-east-1.amazonaws.com" (Service: AmazonIdentityManagement; Status Code: 400; Error Code: MalformedPolicyDocument; Request ID: 07c942
b5-8982-4867-974c-239f3bb48dff; Proxy: null)

@rix0rrr rix0rrr reopened this Dec 2, 2021
@rix0rrr rix0rrr added the p1 label Dec 2, 2021
@rix0rrr
Copy link
Contributor

rix0rrr commented Dec 2, 2021

Reopening since this seems unresolved.

It's probably not that ALL ssm ServicePrincipals need to be regionalized, just those for "new" regions.

@rix0rrr
Copy link
Contributor

rix0rrr commented Dec 2, 2021

Also fun: there is apparently no way to write this service principal in an environment-agnostic way.

  • It must be ssm.amazonaws.com for "old" regions
  • It must be ssm.$REGION.amazonaws.com for "new" regions

There does not seem to be a single string that will work in both, and { Fn::FindInMap } cannot do defaults.

@rix0rrr
Copy link
Contributor

rix0rrr commented Dec 2, 2021

On further scouring of the docs, a CfnCondition combined with a Fn.if might do it. We would render something like:

Conditions:
  NoRegionNecessary:
    Fn::Or: 
      - Fn::Equals: [{ Ref: 'AWS::Region }, 'us-east-1']
      - Fn::Equals: [{ Ref: 'AWS::Region }, 'us-east-2']
      - Fn::Equals: [{ Ref: 'AWS::Region }, 'eu-west-1']
      ...

Resources:
  ... 
  Principal:
    Service: { Fn::If: [NoRegionNecessary, 'ssm.amazonaws.com', 'ssm.$REGION.amazonaws.com'] } 

It wouldn't be pretty in the JSON, but who looks at that anyway eh? 😇

@adhorn
Copy link

adhorn commented Dec 2, 2021

Workaround:

const ssmaAsgRole = new iam.Role(this, 'ssma-asg-role', {
        assumedBy: new iam.CompositePrincipal(
        new iam.ServicePrincipal('iam.amazonaws.com'),
        new iam.ServicePrincipal('ssm.amazonaws.com')
       )
});
const ssmaAsgRoleAsCfn = ssmaAsgRole.node.defaultChild as iam.CfnRole;
ssmaAsgRoleAsCfn.addOverride('Properties.AssumeRolePolicyDocument.Statement.0.Principal.Service', ['ssm.amazonaws.com', 'iam.amazonaws.com']);

@mergify mergify bot closed this as completed in #17984 Dec 15, 2021
mergify bot pushed a commit that referenced this issue Dec 15, 2021
…ns (#17984)

The SSM service principal format depends on the region. Older regions have a "global" service principal (`ssm.amazonaws.com`), while newer regions have only regional service principals (`ssm.ap-east-1.amazonaws.com`).

A number of things have been changed to address this:

* Add the notion of a "region order" into the `region-info` library. This allows us to express things like "does this region predate or postdate the change of some convention", and allows us to express that certain regions are *after* SSM introduced this change.
* For region-agnostic stacks, it is no longer possible to supply a single value for the template that will suffice in all regions, as the *format itself* will have changed (neither `"ssm.amazonaws.com"` nor `"ssm.$REGION.amazonaws.com"` will work in all regions). That means we must always introduce a lookup map for region-agnostic stacks. Add `stack.regionalFact()` to generate lookup maps from facts in case it is necessary.
  * Detect if all map values are just an instantiation of a token pattern, and return the simplification if possible (e.g.: if the lookup values are `service.us-east-1.amazonaws.com`, `service.us-east-2.amazonaws.com`, etc, then simplify to `service.$REGION.$URL_SUFFIX` and avoid emitting a lookup).   
  * Simplify existing usage sites of `RegionInfo.regionMap()` in Lambda and CodeBuild to use the new `stack.regionalFact()`.
* Because lookup maps would always include information for all regions, including GovCloud regions, and those are only rarely necessary: add the infrastructure for users to restrict what partitions they want to include information for, by means of a context flag. Defaults to all regions if not specified (so we don't break old templates), but for new projects restricts itself to `['aws', 'aws-cn']`. Set to just `['aws']` for integration tests so we don't break all of our snapshot tests.

Fixes #16188, fixes #17646.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
@github-actions
Copy link

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

mergify bot pushed a commit that referenced this issue Dec 22, 2021
The #17984 (big kudos to @rix0rrr for that) introduced a fix for the SSM service principal format which depends on the region. However, due to a typo in that PR some of regions still don't have correct SSM service principal. 

Currently the SSM service principal for the following regions incorrectly include region, while according to the [issue #16188](#16188) it should be only added to all regions since `ap-east-1`. 

```
cn-north-1
us-iso-east-1
eu-central-1
ap-northeast-2
ap-south-1
us-east-2
ca-central-1
eu-west-2
us-isob-east-1
cn-northwest-1
eu-west-3
ap-northeast-3
us-gov-east-1
eu-north-1
```

It works like that because by accident `RULE_SSM_PRINCIPALS_ARE_REGIONAL` has the same  value as `RULE_S3_WEBSITE_REGIONAL_SUBDOMAIN`. This causes incorrect results returned by the `aws-entities/before` function.

This PR fixes that issue.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
TikiTDO pushed a commit to TikiTDO/aws-cdk that referenced this issue Feb 21, 2022
…#17047)

fixes aws#16188 
----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
TikiTDO pushed a commit to TikiTDO/aws-cdk that referenced this issue Feb 21, 2022
…ns (aws#17984)

The SSM service principal format depends on the region. Older regions have a "global" service principal (`ssm.amazonaws.com`), while newer regions have only regional service principals (`ssm.ap-east-1.amazonaws.com`).

A number of things have been changed to address this:

* Add the notion of a "region order" into the `region-info` library. This allows us to express things like "does this region predate or postdate the change of some convention", and allows us to express that certain regions are *after* SSM introduced this change.
* For region-agnostic stacks, it is no longer possible to supply a single value for the template that will suffice in all regions, as the *format itself* will have changed (neither `"ssm.amazonaws.com"` nor `"ssm.$REGION.amazonaws.com"` will work in all regions). That means we must always introduce a lookup map for region-agnostic stacks. Add `stack.regionalFact()` to generate lookup maps from facts in case it is necessary.
  * Detect if all map values are just an instantiation of a token pattern, and return the simplification if possible (e.g.: if the lookup values are `service.us-east-1.amazonaws.com`, `service.us-east-2.amazonaws.com`, etc, then simplify to `service.$REGION.$URL_SUFFIX` and avoid emitting a lookup).   
  * Simplify existing usage sites of `RegionInfo.regionMap()` in Lambda and CodeBuild to use the new `stack.regionalFact()`.
* Because lookup maps would always include information for all regions, including GovCloud regions, and those are only rarely necessary: add the infrastructure for users to restrict what partitions they want to include information for, by means of a context flag. Defaults to all regions if not specified (so we don't break old templates), but for new projects restricts itself to `['aws', 'aws-cn']`. Set to just `['aws']` for integration tests so we don't break all of our snapshot tests.

Fixes aws#16188, fixes aws#17646.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
TikiTDO pushed a commit to TikiTDO/aws-cdk that referenced this issue Feb 21, 2022
The aws#17984 (big kudos to @rix0rrr for that) introduced a fix for the SSM service principal format which depends on the region. However, due to a typo in that PR some of regions still don't have correct SSM service principal. 

Currently the SSM service principal for the following regions incorrectly include region, while according to the [issue aws#16188](aws#16188) it should be only added to all regions since `ap-east-1`. 

```
cn-north-1
us-iso-east-1
eu-central-1
ap-northeast-2
ap-south-1
us-east-2
ca-central-1
eu-west-2
us-isob-east-1
cn-northwest-1
eu-west-3
ap-northeast-3
us-gov-east-1
eu-north-1
```

It works like that because by accident `RULE_SSM_PRINCIPALS_ARE_REGIONAL` has the same  value as `RULE_S3_WEBSITE_REGIONAL_SUBDOMAIN`. This causes incorrect results returned by the `aws-entities/before` function.

This PR fixes that issue.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-iam Related to AWS Identity and Access Management @aws-cdk/aws-ssm Related to AWS Systems Manager bug This issue is a bug. effort/small Small work item – less than a day of effort good first issue Related to contributions. See CONTRIBUTING.md p1
Projects
None yet
6 participants