1

I'm learning more about AWS SAM and looking at this template that is the codebase for a featured DZone article describing how to use AWS SAM to wire up Lambdas that encrypt data in DynamoDB tables. In that template the author defines a KMS Key:

KmsKey:
  Type: AWS::KMS::Key
  Properties: 
    Description: CMK for encrypting and decrypting
    KeyPolicy:
      Version: '2012-10-17'
      Id: key-default-1
      Statement:
      - Sid: Enable IAM User Permissions
        Effect: Allow
        Principal:
          AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
        Action: kms:*
        Resource: '*'
      - Sid: Allow administration of the key
        Effect: Allow
        Principal:
          AWS: !Sub arn:aws:iam::${AWS::AccountId}:user/${KeyAdmin}
        Action:
        - kms:Create*
        - kms:Describe*
        - kms:Enable*
        - kms:List*
        - kms:Put*
        - kms:Update*
        - kms:Revoke*
        - kms:Disable*
        - kms:Get*
        - kms:Delete*
        - kms:ScheduleKeyDeletion
        - kms:CancelKeyDeletion
        Resource: '*'
      - Sid: Allow use of the key
        Effect: Allow
        Principal:
          AWS: !Sub arn:aws:iam::${AWS::AccountId}:user/${KeyUser}
        Action:
        - kms:DescribeKey
        - kms:Encrypt
        - kms:Decrypt
        - kms:ReEncrypt*
        - kms:GenerateDataKey
        - kms:GenerateDataKeyWithoutPlaintext
        Resource: '*'

And later on, they define 2 Lambdas that will use that key. One of those Lambdas is defined as:

SignupFunction:
  Type: AWS::Serverless::Function
  Properties:
    Environment:
      Variables:
        userTable: !Ref myDynamoDBTable
        keyid: !Ref KmsKey
    CodeUri: Lambda/
    Handler: signup.lambda_handler
    Runtime: python3.8
    Policies:
     - DynamoDBCrudPolicy:
        TableName: !Ref myDynamoDBTable
     - KMSEncryptPolicy:
        KeyId: !Ref KmsKey 
     - KMSDecryptPolicy:
        KeyId: !Ref KmsKey
    Events:
      getCounter:
        Type: Api
        Properties:
          Path: /signup
          Method: POST
          RestApiId: !Ref ApiGatewaySignupApi

So here the author specifies that a parameter of KeyUser (you provide this as an input parameter to the template at deploy-time) is allowed to use the key:

AWS: !Sub arn:aws:iam::${AWS::AccountId}:user/${KeyUser}

But nowhere do I see the connection between Lambda and the KeyUser. If the Lambda is expected to use the key, I would think that somewhere we need to say "create this Lambda and give it KeyUser permission/role." But I'm not seeing that anywhere.

So I ask: how and where are the Lambdas endowed with KeyUser's privileges, thus giving them permission to use the KMS Key?

hotmeatballsoup
  • 385
  • 6
  • 58
  • 136
  • Haven't verified, but the policy fragment with sid "Enable IAM User Permissions" appears to give kms:* on all resources to IAM principals in this AWS account. – jarmod Dec 08 '22 at 14:30
  • I appreciate the input, but I don't think thats correct (keep me honest though!). A few lines down it restricts that `kms:*` to the account root. In the DZone article, the author explains that the access strategy is: root can do anything, `KeyAdmin` can manage the key but not _use_ it, and `KeyUser` can use the key, but not manage it. – hotmeatballsoup Dec 08 '22 at 14:39
  • 1
    It doesn't restrict it to the account root. It *additionally* allows the account root. An "Allow" statement can never restrict existing permissions. It can only allow additional permissions, even if it has a condition. You may be thinking of "Deny" statements. – jarmod Dec 08 '22 at 17:57
  • **Yes** you are correct on that, its saying that the account root is allowed to do anything with the key. Where are you seeing it allow `kms:*` for all IAM principals in the account though? When I read that policy, I see: (1) let account root do everything (`kms:*`), (2) let the parameter-provided admin user `kms:Create*`, `kms:Describe*`, `kms:Enable*`, `kms:List*`, ..., and `kms:CancelKeyDeletion`. And then (3) let the parameter-provided `KeyUser` perform: `kms:DescribeKey`, `kms:Encrypt`, `kms:Decrypt`, `kms:ReEncrypt*`, `kms:GenerateDataKey` and `kms:GenerateDataKeyWithoutPlaintext`... – hotmeatballsoup Dec 08 '22 at 20:21
  • ...but I'm not seeing where it says "any IAM principal on the account can do `kms:*` – hotmeatballsoup Dec 08 '22 at 20:22
  • By the way, all of this is quite tricky, as you've seen, and I think you're helping a lot of people by asking this question. – jarmod Dec 09 '22 at 16:35

1 Answers1

1

Your KMS key's key policy contains the following:

Statement:
- Sid: Enable IAM User Permissions
  Effect: Allow
  Principal:
    AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
  Action: kms:*
  Resource: '*'

What this policy statement means is that all IAM principals in the ${AWS::AccountId} AWS account can have permission to use this KMS key. Note: it's not saying that they do have permission, but that they can have permission.

The fact that the principal includes root doesn't represent the AWS account root user. It represents the entire AWS account (specifically all IAM principals in the account).

So, the policy statement doesn't actually give those permissions to any IAM principal. It just states that if a given IAM principal in this account has IAM policies permitting access to this KMS key, then they are approved for anything up to kms:* on this key.

Remember that KMS key permissions are bilateral. For a given IAM principal (an IAM user or IAM role) to have permission to use a KMS key, two things have to happen:

  1. the IAM principal must have permission for the key, and
  2. the key's policy must give permission to the IAM principal

This is different to other typical IAM scenarios where IAM permissions on an IAM principal are sufficient.

Next, your Lambda function has the following policies:

Policies:
- DynamoDBCrudPolicy:
  TableName: !Ref myDynamoDBTable
- KMSEncryptPolicy:
  KeyId: !Ref KmsKey 
- KMSDecryptPolicy:
  KeyId: !Ref KmsKey

The latter KMSEncryptPolicy and KMSDecryptPolicy policies on KmsKey give the Lambda function permission to invoke KMS actions on the key.

So, the key policy allows all IAM principals in this account to use the key and the Lambda function's policies allow use of the KMS key. In combination, that results in an approval decision.

Related articles:

  • How can I prevent IAM policies allowing a user or role to access a KMS key? (link)
  • Understanding KMS policy? (link)

On the topic of attempting to control what the AWS account root user can do:

  1. the root user has full access to all resources in the account
  2. you can't use IAM policies to explicitly deny the root user access to any resource
  3. [AWS Organizations only] you can use an AWS Organizations service control policy (SCP) to limit the permissions of the root user of a member account
jarmod
  • 71,565
  • 16
  • 115
  • 122
  • Thanks @jarmod (+1) this helps clear a lot up for me. A few followup questions if you don't mind: **(1)** is it safe to say that the defined `KMSEncryptPolicy`/`KMSDescryptPolicy` form that bi-lateral permission you mentioned? Meaning up above we say all IAM principals _can_ use this key (the first criterion) and here, these policies complete the 2nd criterion and form bi-lateral-ness? – hotmeatballsoup Dec 09 '22 at 14:28
  • And **(2)** Ideally I want 3 levels of usage: the true AWS account root user can do **anything** with the key. AWS admins/superusers (`KeyAdmin` above) can create/delete (but **not** view/use) the key. And I want the Lambda (who I want to specify as `KeyUser` in the template) to be able to use to use the key but not manage it. What would I have to change to accomplish this? Thanks again so much for your help here! – hotmeatballsoup Dec 09 '22 at 14:28
  • The hope here is: only the Lambda can _ever_ encrypt/decrypt data with the key. AWS admin can manage it but never see the key and use it to decrypt (very sensitive) data. The true AWS account root user is able to do everything + anything because they are omnipotent. – hotmeatballsoup Dec 09 '22 at 14:30
  • Nothing in your current key policy prevents key usage being given to an IAM principal. Read the [link](https://aws.amazon.com/premiumsupport/knowledge-center/kms-prevent-access/) I sent earlier. You would need to remove the statement described as "Enable IAM User Permissions" and then selectively enable permissions for the specific Lambda function. And also delete the statement described as "Allow use of the key" because that allows `...user/${KeyUser}` to be given permission and, afaik, you don't want any IAM user to be able to use the key. – jarmod Dec 09 '22 at 16:06
  • BTW the policy you have there originally is the default KMS key IAM policy described [here](https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-default.html). You need to remove the things you don't want (the 1st and 3rd statements), and add your Lambda function as a valid user of the key. – jarmod Dec 09 '22 at 16:08
  • It should go without saying, please test all of this thoroughly in a non-production environment where a bad outcome (e.g. loss of access to a KMS key) is inconsequential. – jarmod Dec 09 '22 at 16:40