AWS Session Policies

  • 17th Nov 2024
  • 9 min read
  • Tags: 
  • aws
  • iam
  • Updated on 21st Jan 2025

The recent appearance of RCPs (resource control policies) which were announced just prior to re:Invent 2024 has both delighted me and given me cause to think more about IAM policies in general.

Building securely on AWS requires a good knowledge of IAM policies and I think it is fair to say that constructing robust IAM polices is key to securing cloud infrastructure but challenging to do well.

Session policies are a type of IAM policy that I think are perhaps underappreciated. However, I think in certain situations they can be very useful. By describing some use cases I hope to demonstrate that.

AWS IAM policies come in different flavours

AWS IAM is a service to securely manage identities and access to AWS services and resources. Access in AWS is managed by defining permissions in a policy which is attached to identities or resources.

Nearly all use of AWS services requires an awareness of IAM, with most AWS users having used an IAM user or role at some point. Building securely on AWS requires a good knowledge of IAM policies and I think it is fair to say that constructing robust IAM polices is key to securing cloud infrastructure but challenging to do well.

At the time of writing, there are 7 policy types, which includes recent additions like RCPs (resource control policies) which were announced just prior to re:Invent 2024. The above list of policy types is ordered from most frequently used to less frequently used and I would suggest that the top 2 policy types which grant permissions are widely known and used while the others that apply additional constraints to set maximum permission are much less well-known (and ACLs we will ignore for now, especially as AWS recommend that S3 ACL are disabled).

Granting permissions

Permissions can only be granted by 3 types of policy: identity-based policies, resource-based policies, and ACLs. Ignoring ACL (as noted above) means we have 2 ways to grant permissions, and it is likely that users of AWS are familiar with these. It is only through allowing permissions in these policy types that permissions may be granted, from an initial default denial (with additional explicit targeted denial also available).

Identity-based polices are policy documents that attach to an IAM user, IAM group, or IAM role and control the actions that can be performed by that identity.

Many AWS users will have used a managed policy to grant admin access to an IAM role, for example when following root user best practices. Many may have defined a more targeted policy through the AWS Management Console using the visual editor or JSON, or using infrastructure as code.

Resource-based policies are policy documents that attach to resources rather than identities. They still control what actions a principal may take, but they are attached to a resource rather than an identity. The role trust policy applied to an IAM role is an example of a resource-based policy that AWS users may be familiar with, defining which principals can assume the role (perhaps with additional conditions).

Restricting permissions

Permissions boundaries come next in the list, the first policy type that cannot grant permissions, only constrain (or limit or bound or restrict or attenuate, or your terminology of choice) by defining the maximum permissions that an identity-based policy may grant to an entity (IAM users and IAM roles). Adding a permissions boundary to an entity enforces the maximum permissions, with the main documented use-case being:

... to delegate permissions management tasks, such as user creation, to IAM users in your account. This permits others to perform tasks on your behalf within a specific boundary of permissions.

Useful, and I would say not uncommon to see, especially in larger organisations but equally I am not surprised when I find unfamiliarity.

When using AWS Organizations there are SCP (service control policies) and RCP (resource control policies) that can be used to define maximum permissions for entities (IAM users and IAM roles) with respect to Organization structure. Service control policies restrict the permissions of entities by defining the maximum permissions that an entity may be granted, with resource control policies being similar but applied to resources. Although not as well-known as the main identity- and resource-based policy types, administrators and potentially users of AWS Organizations are likely familiar with them. Note that these often are used to help enforce a two-person rule as you can have separation between these Organization-level controls and the granting of permissions.

And, finally, we have session policies.

Session policies

As discussed above, permissions boundaries, SCPs, and RCPs all define a maximum set of permissions and are usually attached (to entities or Organization structure) by administrators in order to limit the actions of users. They tend to be slowly changing, centrally managed, and attached to relatively long-lived constructs like entities (IAM users and IAM roles) and Organization structure.

Session policies:

... are advanced policies that you pass as a parameter when you programmatically create a temporary session for a role or federated user. The permissions for a session are the intersection of the identity-based policies for the IAM entity (user or role) used to create the session and the session policies.

There are a few documented examples of session policy use-cases, such as an admin scoping their permissions down to suit the specific task in hand(blog, docs) or reducing the overhead of IAM role and policy management for AWS Transfer Family users.

Note that the control rests with the initiator of the session, which will be the user unless a vending solution is used (which it may be to give a two-person rule).

How session policies take effect

It's as per the docs but some elaboration may help in setting the background of some example policies and patterns.

The common diagram - for example in the AWS docs - that I'll recreate here shows the intersection of a session policy with an identity-based policy: Diagram showing the effective permissions when identity-based

The intersection is a useful visualisation but, more precisely, the IAM policy evaluation logic, shows that the identity-based policy is evaluated first, with the session policy evaluated later. Both must contain appropriate allow statements for permission to be granted, that is the statements and conditions need not be identical, rather the whole policy must evaulate such as to grant permission.

If we had a session policy that were identical to an identity-based policy then the combination would be the same as the identity-based policy and the session policy would add no restrictions. The same is true if the session policy is more permissive than the identity-based policy:

Diagram showing the effective permissions

If we then consider using the deny action in the session policy, we can effectively revoke permissions:

Diagram showing the effective permissions

Overall, we have two patterns here that we can use to add restrictions to identity-based policies using session policies:

  1. Granting all the desired permissions from the identity-based policies but creating gaps so the restricted action is not explicitly allowed
  • This may look like omiting whole allow statements, using additional conditions, or producing finer-grained allow statements to fine-tune actions or principals.
  1. Granting all permissions and explicitly denying to create restrictions

This is a familiar policy consideration: do we build up from a baseline default deny, or restrict down from a baseline allow. We may be able to choose our approach freely, or the particulars of a policy or situation may force our hand.

Selected examples of uses for session policies

### Testing the effects of service control policies

Service control policies (SCP)

Limiting a session to read-only

A good example is when you have control of IAM policies delegated to individual teams and CI/CD centralised. To help reduce risk - for example credential exfiltration - the management of AWS resources through infrastructure as code and CI/CD can be limited to read-only for planning and prevent deletion where appropriate.

### Limiting to a single tenant

https://engineering.clever.com/2019/07/24/using-iam-roles-with-session-policies-for-least-privilege/

Writing session policies

I think that it is common to feel that good IAM policies are hard to write - I certainly do - because of various factors, such as:

  • Stacking up negation (and other complexities like existence checks). For example, reasoning through having a Deny effect with a NotAction policy element with a StringNotEqualsIgnoreCaseIfExists condition operator
  • Availablity of condition operators
  • Understanding various edge cases

Because of this, I like to try and document IAM policies, in order to explain:

  • What the intent is
  • What I believe is happening
  • Limitations or caveats
    • In being able to express the desired policy given the expressiveness of the IAM policy language
    • In using the policy

The hope is that it helps people (including my future self) understand why the policy is written as it is, and help in evaluating whether the policy is indeed effective.

To keep documentation close to code, I often use YAML to write the policy, as this allows for inline comments. For example:

Version: "2012-10-17"
Statement:

  # NOTE: this is intended for use as a session policy. Things that are usually
  # *bad* in identity-based policies may be OK here, so don't use it other than
  # as intended as a session policy.

  # This session policy is intended to add a restriction that all EC2 instances
  # must have the `Name` tag set on creation.

  # Start with effectively `FullAdmin` in order that we intersect fully with other policies
  # in effect and do not restrict them, other than by the subsequent deny statements.
  # It may help for tooling that may inspect policy if we instead use the FullAdmin
  # managed role on the session policy and remove this from here.
  - Effect: Allow
    Action: '*'
    Resource: '*'

  # Note not defining a SID despite good practice, as we need to reduce characters in the policy
  - Effect: Deny
    Action:
      - ec2:RunInstances
    Resource: arn:aws:ec2:*:*:instance/*
    Condition:
      # Now, need really to not use IfExists, because that becomse True, not ignored. Null?
      # Note can't use the null check because it does not support IfExists and we are wildcard-y on resources/actions
      Null:
        # Deny if the context does not contain the `Name` tag
        aws:RequestTag/Name: true

Above per https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps_examples_tagging.html#example-require-tag-on-create

Another example is a tweak like preventing tags other than on creation.

If done in a standardised TF apply pipeline component/action then can test impact of SCP.

  • Trial SCP
  • Lower privileges for safety (e.g. TF plan).
    • Noting that we may need S3 access for state
  • Adding a principal restriction to stop credential leakage

Note that some of these things should be in the identity-based policies but either belt-and-braces or when you may not have full control like shared CI/CD.