How to limit access to AWS Resources based on SAML Attributes using CloudFormation

Cloud Migration Scenarios

Four scenarios to migrate to AWS – from infrastructure to ML

Restricting access to IAM resources based on SAML Subject

Many larger organizations manage their own Active Directory servers. For AWS access, they typically create an identity provider to provide a single sign on (SSO) experience for logging onto AWS. The user is then often granted access to a particular role that grants particular rights. This approach, however, lacks practical fine-grained control. Managing these rights effectively means creating and maintaining many IAM roles and AD groups.

An alternative approach, outlined in this blog post, is to utilize the SAML assertions in your IAM policies. If you enable ABAC (which requires the IAM::TagSession privilege on the role) you will be able to differentiate based on custom SAML attributes. Without ABAC you can still use a couple of attributes such as the SAML subject. In this post we will first focus on the latter: How to use the SAML subject to restrict access to IAM Roles.

The CloudFormation template below creates an IAM Role. Its only permissions are listing buckets, listing a particular S3 bucket and full S3 access on a particular subfolder. Finally, we limit the access to the subfolder based on the list of SAML subjects provided as a parameter to this stack.

In the AssumeRolePolicyDocument (Trust policy) we define the principal as our SAML provider. That means that anyone that has federated access to AWS can assume this role:

        Statement:
          - Action:
              - sts:AssumeRoleWithSAML
              - sts:TagSession
            Condition:
              StringEquals:
                SAML:aud: https://signin.aws.amazon.com/saml
            Effect: Allow
            Principal:
              Federated: !Ref 'SamlProviderArn'

To limit access to a supplied comma-separated list of SAML Subjects we use the following Condition in the S3 IAM Policy:

              Condition:
                ForAllValues:StringLike:
                  saml:sub: !Split
                    - ','
                    - !Ref 'SamlSubjects'

This condition basically says: Only allow this if your SAML subject is one of those in the list provided. Note that you could also limit access to the role by moving the condition section to the Trust Policy.

The template below takes 3 parameters: The bucket name you wish to secure, the ARN of the SAML provider, and finally the comma-separated list of SAML subjects.

Complete template:

Description: Bucket Restriction Stack
Parameters:
  BucketName:
    Description: Bucket name to grant access to
    Type: String
  SamlProviderArn:
    Description: ARN of SAML provider
    Type: String
  SamlSubjects:
    Description: List of Strings
    Type: String
Resources:
  SamlBucketRole:
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRoleWithSAML
              - sts:TagSession
            Condition:
              StringEquals:
                SAML:aud: https://signin.aws.amazon.com/saml
            Effect: Allow
            Principal:
              Federated: !Ref 'SamlProviderArn'
        Version: '2012-10-17'
      Path: /
      Policies:
        - Statement:
            - Action:
                - s3:ListAllMyBuckets
                - s3:GetBucketLocation
              Effect: Allow
              Resource: '*'
            - Action:
                - s3:ListBucket
              Effect: Allow
              Resource:
                - !Sub 'arn:aws:s3:::${BucketName}'
            - Action:
                - s3:*
              Condition:
                ForAllValues:StringLike:
                  saml:sub: !Split
                    - ','
                    - !Ref 'SamlSubjects'
              Effect: Allow
              Resource:
                - !Sub 'arn:aws:s3:::${BucketName}'
          Version: '2012-10-17'
    Type: AWS::IAM::Role

Now you may not want to maintain a list of SAML subjects. Imagine, for example, you have an S3 Bucket you want to secure and it has a list of subfolders for each user, with the folder name being identical to the SAML subject. A policy for this access policy could look something like this:

        {
            "Action": [
                "s3:ListBucket"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::some-s3-bucket"
            ],
            "Condition": {
                "StringLike": {
                    "s3:prefix": [
                        "",
                        "${saml:sub}/*"
                    ]
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::some-s3-bucket/${saml:sub}/*"
            ]
        }

These parameterized way of creating IAM policies for SAML subjects scales really well, but you’ll lose some flexibility in terms of what you can provide access too.

Lastly, you may want to be able to restrict access based on custom SAML attributes. AWS’ default SAML attributes that can be used is very limited. By enabling Session Tags you can utilize custom SAML attributes and differentiate on them in your IAM policies.

Here is an example:

        {
            "Action": [
                "s3:ListBucket"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::my-s3-bucket"
            ],
            "Condition": {
                "StringLike": {
                    "s3:prefix": [
                        "",
                        "${aws:PrincipalTag/my-custom-attribute}/*"
                    ]
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::my-s3-bucket/${aws:PrincipalTag/my-custom-attribute}/*"
            ]
        }

Similarly to the first example, we can also maintain a list of explicit conditions using the custom SAML attribute if we use ABAC.

Using Infrastructure as Code to maintain and deploy these roles and policies reduces operational overhead and prevents human errors, however: there are hard limits on the size of policies and the amount of conditions one can use. It does not scale very well out of the box.

Conclusion

Federation requires you to think hard about your data access patterns. Using SAML attributes in your IAM policies is a powerful method of automating access. In order for this to scale for large organizations you must take great care of your deployment strategy of these policies. One approach could be to use several (nested) stacks and individual roles to work around the policy size limitations.

You may also like How to get AWS credentials and access keys using the Auth0 SAML identity provider and How to configure a SAML identity provider to enable SSO in AWS CloudFormation.

Share this article: Tweet this post / Post on LinkedIn