AWS and other interesting stuff

Identity Delegation And Federation

Roles

Roles allow you to assign short-lived AWS credentials to users and other AWS services. Those users and services then use those temporary credentials to view and make changes to your account resources.

Using short-lived credentials is important from a security point-of-view, and in my Long-lived versus Short-lived credentials article I outline why that is.

Roles used in EC2 Instance Profiles are automatically rotated multiple times per day. The role information is available to the instance via the meta data service:

curl http://129.254.169.254/latest/meta-data/iam/security-credentials/<role-name>

The SDKs will automatically retrieve these credentials.

Outside of the AWS environment we need:

  • AccessKey
  • SecretAccessKey
  • SessionToken

We get these from the STS service.

Role Permissions and Conditions

The permissions (Access Policy) are the what the role has permission to do.

The trust policy conditions place additional restrictions on when a role can be assumed.

Permissions Trust Relationships
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "DateGreaterThan": {
          "aws:CurrentTime": "2013-12-15T12:00:00Z"
        },
        "IpAddress": {
          "aws:SourceIp": [
            "192.0.2.0/24",
            "203.0.113.0/24"
          ]
        }
      }
    }
  ]
}
Condition logic
Values for the same condition key are evaluated as OR. Keys within a condition are ANDed and conditions are ANDed. i.e. above, IPs are ORed then DateGreaterThan is ANDed with IpAddress

Role Types

There are various types of roles in IAM. The only difference between the role types is the restriction on which user or service can assume them. This restriction is specified via the role Trust Policy’s Principal setting and optionally its Condition settings.

  • AWS Service Roles (roles made for AWS services to assume)
  • Role for Cross-Account access
    • Provide access between AWS accounts you own
    • Provide access between your AWS account and a 3rd party AWS account
  • Role for Identity Provider access
    • Web identity providers
    • Web Single Sign-On (WebSSO) for SAML providers
    • API access to SAML providers

I’ll outline what the differences and similarities are between the role types below.

Delegation And Federation

  • Delegation (AKA trust): You allow users in other AWS accounts access to your resources directly.
  • Federation: delegate/trust users from an external Identity Provider.

    • Corporate / Enterprise Identity Federation - e.g. Active Directory, LDAP, Custom Federation Proxy (Token Vending Machine), SAML or AWS Directory Service
    • Web / Social Identity Federation - Amazon, Facebook, Google, Twitter or any OpenID Connect providers
    • AWS Cognito can handle this integration, aggregating multiple provider identities into one identity
  • Role: Object that allows an entity to be granted a set of permissions to your accounts

    • Trust Policy: who can assume the role
    • Access Policy: the permissions the role has
  • Session: a set of temporary credentials. An access and secret key with an expiry.

    • Obtained via Security Token Service (STS)
    • STS:AssumeRole, STS:AssumeRoleWithSAML, STS:AssumeRoleWithWebIdentity

Role Type Trust Policy Comparison

Trust policies differ in their action and principal settings:

AWS Service Roles

The service that is allowed to assume the role is specified in the principal e.g.

{
  "Action": "sts:AssumeRole",
  "Principal": {
    "Service": "ec2.amazonaws.com"
  }
}

Service Role principals include:

  • ec2.amazonaws.com
  • opsworks.amazonaws.com
  • rds.amazonaws.com
  • etc…

Roles for cross-account access

Provide access between AWS accounts you own:

{
  "Action": "sts:AssumeRole",
  "Principal": {
    "AWS": "arn:aws:iam::012345678910:user/steve"
  }
}

Provide access between your AWS account and a 3rd party AWS account:

{
  "Action": "sts:AssumeRole",
  "Principal": {
    "AWS": "arn:aws:iam::012345678910:user/steve"
  },
  "Condition": {
    "StringEquals": {
      "sts:ExternalId": "abc"
    },
    "Bool": {
      "aws:MultiFactorAuthPresent": "true"
    }
  }
}

Note: sts:ExternalId is used to prevent the confused deputy problem i.e. the person assuming the role specifies who they intend to perform the action for; if you’re not that entity, don’t let them assume the role. The ExternalId is the unique ID the third-party refers to you as e.g. CompanyXYZ.

Roles for identity providers

Web identity providers
  • Allows trusted third-parties to authenticate users
  • Avoids having to create and manage many users in IAM
  • Simplifies access control via roles
  • It improves security by using short-lived credentials
  • Using Web Identity Federation and Cognito also provides user state syncing

Facebook

{
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Principal": {
    "Federated": "graph.facebook.com"
  },
  "Condition": {
    "StringEquals": {
      "graph.facebook.com:app_id": "23873249387492374"
    }
  }
}

Cognito

{
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Principal": {
    "Federated": "cognito-identity.amazonaws.com"
  },
  "Condition": {
    "StringEquals": {
      "cognito-identity.amazonaws.com:aud": "ap-southeast-2:a2ad0e91-5d8a-4e51-8d80-007ef84fb169"
    }
  }
}

OpenID Connect (You need to configure the OpenID Connect Provider in IAM first)

{
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Principal": {
    "Federated": "arn:aws:iam::<REDACTED>:oidc-provider/accounts.google.com/"
  },
  "Condition": {
    "StringEquals": {
      "accounts.google.com/:aud": "Fy9IS4qjAjdjxIja6vcjlcK7"
    }
  }
}

I’m using Google above, but it could be any OpenID Connect provider.

Web Single Sign-On (WebSSO) for SAML providers

You need to configure the SAML Provider in IAM first. As part of this process you upload the provider supplied .xml meta data to configure it.

WebSSO always uses the SAML:aud field (the audience: the sign-in endpoint)

{
  "Action": "sts:AssumeRoleWithSAML",
  "Principal": {
    "Federated": "arn:aws:iam::<REDACTED>:saml-provider/Auth0"
  },
  "Condition": {
    "StringEquals": {
      "SAML:aud": "https://signin.aws.amazon.com/saml"
    }
  }
}
API access to SAML providers

You need to configure the SAML Provider in IAM first. As part of this process you upload the provider supplied .xml meta data to configure it.

In this case, the SAML provider will access IAM via the API. The IAM console requires that you set a condition on any one of the SAML:* fields below:

  • SAML:aud - the audience: the sign-in endpoint
  • SAML:sub - the subject of the claim: unique id of user within an organisation
  • SAML:iss - the issuer: unique id of issuing provider e.g. urn:.au.auth0.com
  • SAML:sub_type
  • SAML:eduPersonOrnDN

In this example, it looks as the issuer.

{
  "Action": "sts:AssumeRoleWithSAML",
  "Principal": {
    "Federated": "arn:aws:iam::<REDACTED>:saml-provider/Auth0"
  },
  "Condition": {
    "StringEquals": {
      "SAML:iss": "urn:<yourapp>.au.auth0.com"
    }
  }
}

Reference: https://auth0.com/docs/aws-api-setup

Web Identity Federation

Amazon Cognito

Cognito allows you to aggregate a number of social identity providers under one service.

Terminology

  • User Pools - custom hosted database of users
  • Federated Identities - identities from third-parties
    • Identity Pools - for example, one pool per app “My App Name”
    • Unauthenticated identities - optionally allow unauthenticated users
    • Authentication providers
    • Cognito - A User Pool (see above)
    • Amazon, Facebook, Google+, Twitter
    • OpenID - must be configured as an identity provider in IAM
    • SAML - must be configured as an identity provider in IAM
    • Custom - “Developer provider”: register and authenticate users via your own existing authentication process

Examples of Cognito in-action are used in the Web Identity provider code below.

Authentication Flow

In the enhanced (simplified) workflows, AWS will talk to STS on your application’s behalf to the the credentials.

Reference: http://docs.aws.amazon.com/cognito/latest/developerguide/authentication-flow.html

Corporate Identity Federation

Allows you to use an existing identity store for AWS access. Having 1 definitive source minimises admin overhead and reduces the number of identities your staff need to remember (reduced security attack surface).

  • AWS Directory Services, SAML (e.g. Active Directory) or a custom federation proxy (Token Vending Machine)
  • The proxy needs permissions to list and assume roles
  • Access is obtained using STS:AssumeRole or GetFederationToken operations.
    • STS:AssumeRole
      • Session time: 15 min to 1 hour, default of 1 hour
    • GetFederationToken
      • Session time: 15 min to 36 hours, default of 12 hours
      • The federation proxy needs to have all the permissions that all apps with require in union; it reduces the set of permissions a session has to the bare essentials, but that reduced set must be a subset of the permissions the proxy has i.e. the proxy needs to be secure!
      • GetFederationToken does not support MFA

Custom Proxy - Console - AssumeRole

Custom Proxy - API - GenerateFederationToken

IAM Identity Provider:

An IAM object which holds configuration information about the external identity providers. e.g. AD endpoint, SAML provider You generally map Groups in your identity provider with Roles inside AWS.

Role Type Examples and Code

Cross-Account Access Example

In the console, you use the menu to switch roles:

You need to specify the account and the role within that account that you’d like to assume:

You can then see the role you’re using in the menu.

You can use the menu to go back to your original account login.

Note: You can not specify an ExternalId when using the console http://docs.aws.amazon.com/IAM/latest/UserGuide/tutorial_cross-account-with-roles.html

Web Identity provider Examples

Social

Minimal Example

Example: https://github.com/SteveHoggNZ/aws-web-identity-usage-samples/blob/master/social-auth-minimal.html

All the example does it use the Facebook SDK to prompt a user to login and then output the result.

The result looks like this:

{
 "authResponse": {
  "accessToken": "EAADJZBsDf77EBAPJKdg2yT5OBN0TS43paxj8kutRkZAydQ...",
  "userID": "10154359158912695",
  "expiresIn": 6513,
  "signedRequest": "gGW6zhsMvZtfS554pIOmPCJiS89xlSwbIKVBbXo5-5g.e..."
 },
 "status": "connected"
}

In the next example I’ll use the accessToken to assume an IAM role as per https://aws.amazon.com/developers/getting-started/browser/

Upload To S3 Example - Social

Setup

Create a new app with Facebook:

The S3 bucket needs CORS setup so that the browser app can make direct requests to it:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01">
   <CORSRule>
      <AllowedOrigin>*</AllowedOrigin>
      <AllowedMethod>GET</AllowedMethod>
      <AllowedMethod>PUT</AllowedMethod>
      <AllowedMethod>POST</AllowedMethod>
      <AllowedMethod>DELETE</AllowedMethod>
      <AllowedHeader>*</AllowedHeader>
   </CORSRule>
</CORSConfiguration>

Create a IAM managed policy that allows read/write for a specific key prefix for the Facebook user id:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": [
                "arn:aws:s3:::h4-tmp/facebook-${graph.facebook.com:id}/*"
            ],
            "Effect": "Allow"
        },
        {
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::h4-tmp"
            ],
            "Effect": "Allow",
            "Condition": {
                "StringEquals": {
                    "s3:prefix": "facebook-${graph.facebook.com:id}"
                }
            }
        }
    ]
}

As no bucket policy is setup, access to the bucket is determined solely by the IAM policy above. FYI:

Use IAM policies if:

  • You need to control access to AWS services other than S3. IAM policies will be easier to manage since you can centrally manage all of your permissions in IAM, instead of spreading them between IAM and S3.
  • You have numerous S3 buckets each with different permissions requirements. IAM policies will be easier to manage since you don’t have to define a large number of S3 bucket policies and can instead rely on fewer, more detailed IAM policies.
  • You prefer to keep access control policies in the IAM environment.

Use S3 bucket policies if:

  • You want a simple way to grant cross-account access to your S3 environment, without using IAM roles.
  • Your IAM policies bump up against the size limit (up to 2 kb for users, 5 kb for groups, and 10 kb for roles). S3 supports bucket policies of up 20 kb.
  • You prefer to keep access control policies in the S3 environment.

Assign the managed policy to a role (s3TestRole), and allow that role to be assumed by the Facebook app (222078814908337 is the ID assigned by Facebook when the app is created):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "graph.facebook.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "graph.facebook.com:app_id": "222078814908337"
        }
      }
    }
  ]
}
Code

Example: https://github.com/SteveHoggNZ/aws-web-identity-usage-samples/blob/master/social-auth-example.html

The example extends the minimal one by using the Facebook token to set the bucket.config.credentials:

bucket.config.credentials = new AWS.WebIdentityCredentials({
    ProviderId: 'graph.facebook.com',
    RoleArn: roleArn,
    WebIdentityToken: response.authResponse.accessToken
});

fbUserId = response.authResponse.userID;

The fbUserId is used to set the S3 key to use:

var objKey = 'facebook-' + fbUserId + '/' + file.name;

This is the result after uploading an image:

Upload To S3 Example - Cognito Unauthenticated

Attach the S3 managed policy to the Cognito Unauthenticated role that was configured when you setup the identity pool.

Example: https://github.com/SteveHoggNZ/aws-web-identity-usage-samples/blob/master/cognito-unauth-example.html

Difference between social example and the unauth Cognito one:

Remove …

  var appId = '222078814908337';
  var roleArn = 'arn:aws:iam::798269391015:role/s3TestRole';

… and the bucket.config.credentials setting in the Facebook login handling code.

Add:

  AWS.config.credentials = new AWS.CognitoIdentityCredentials({
    IdentityPoolId: 'ap-southeast-2:a2ad0e91-5d8a-4e51-8d80-007ef84fb169'
  });
  AWS.config.credentials.get(function(err) {
    if (err) alert(err);
    console.log(AWS.config.credentials);
  });

Upload To S3 Example - Cognito Authenticated

Attach the S3 managed policy to the Cognito Authenticated role that was configured when you setup the identity pool.

https://github.com/SteveHoggNZ/aws-web-identity-usage-samples/blob/master/cognito-auth-example.html

Difference between social example and the auth Cognito one:

Remove:

  var roleArn = 'arn:aws:iam::798269391015:role/s3TestRole';

… and the bucket.config.credentials setting in the Facebook login handling code and replace it with:

  bucket.config.credentials = new AWS.CognitoIdentityCredentials({
      IdentityPoolId: 'ap-southeast-2:a2ad0e91-5d8a-4e51-8d80-007ef84fb169',
      Logins: {
        'graph.facebook.com': response.authResponse.accessToken
      }
  }