amazon-web-servicesamazon-rdsamazon-kms

Minimal KMS permissions to copy a database snapshot


I am trying to setup minimal permissions for doing aws rds copy-db-snapshot with a KMS encryption key:

$ aws rds copy-db-snapshot --source-db-snapshot-identifier rds-backup-share-
mysql --target-db-snapshot-identifier rds-backup-share-mysql-reencrypted --kms-key-id <kms-arn>

(Everything within <> is stripped out by me and contains valid values.)

Unfortunately I get this error:

An error occurred (KMSKeyNotAccessibleFault) when calling the CopyDBSnapshot operation: The target snapshot KMS key [<kms-arn>] does not exist, is not enabled or you do not have permissions to access it.

Currently I allow these actions:

  "Action": [
    "kms:ReEncrypt*",
    "kms:ListKeys",
    "kms:ListAliases",
    "kms:GenerateDataKey*",
    "kms:Encrypt",
    "kms:DescribeKey",
    "kms:Decrypt"
  ],

It works if I replace it with kms:*{code}, so it must be a permission issue.

I tried to figure out the correct permissions with CloudTrail, but it just contains the same unhelpful error message.

So my actual questions:

Edit: This is the is the bottom part of the log output with --debug enabled:

2017-08-22 17:15:37,521 - MainThread - botocore.endpoint - DEBUG - Sending http request: <PreparedRequest [POST]>
2017-08-22 17:15:37,522 - MainThread - botocore.vendored.requests.packages.urllib3.connectionpool - INFO - Starting new HTTPS connection (1): rds.eu-west-1.amazonaws.com
2017-08-22 17:15:37,927 - MainThread - botocore.vendored.requests.packages.urllib3.connectionpool - DEBUG - "POST / HTTP/1.1" 400 437
2017-08-22 17:15:37,934 - MainThread - botocore.parsers - DEBUG - Response headers: {'x-amzn-requestid': 'c097fe4e-874c-11e7-a56a-9d1acedaf516', 'content-type': 'text/xml', 'content-length': '437', 'date': 'Tue, 22 Aug 2017 15:15:37 GMT', 'connection': 'close'}
2017-08-22 17:15:37,936 - MainThread - botocore.parsers - DEBUG - Response body:
b'<ErrorResponse xmlns="http://rds.amazonaws.com/doc/2014-10-31/">\n  <Error>\n    <Type>Sender</Type>\n    <Code>KMSKeyNotAccessibleFault</Code>\n    <Message>The target snapshot KMS key [<kms-arn>] does not exist, is not enabled or you do not have permissions to access it. </Message>\n  </Error>\n  <RequestId>c097fe4e-874c-11e7-a56a-9d1acedaf516</RequestId>\n</ErrorResponse>\n'
2017-08-22 17:15:37,938 - MainThread - botocore.hooks - DEBUG - Event needs-retry.rds.CopyDBSnapshot: calling handler <botocore.retryhandler.RetryHandler object at 0x7f9c7ce84860>
2017-08-22 17:15:37,939 - MainThread - botocore.retryhandler - DEBUG - No retry needed.
2017-08-22 17:15:37,952 - MainThread - awscli.clidriver - DEBUG - Exception caught in main()
Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/awscli/clidriver.py", line 200, in main
    return command_table[parsed_args.command](remaining, parsed_args)
  File "/usr/lib/python3.6/site-packages/awscli/clidriver.py", line 338, in __call__
    return command_table[parsed_args.operation](remaining, parsed_globals)
  File "/usr/lib/python3.6/site-packages/awscli/clidriver.py", line 508, in __call__
    call_parameters, parsed_globals)
  File "/usr/lib/python3.6/site-packages/awscli/clidriver.py", line 627, in invoke
    client, operation_name, parameters, parsed_globals)
  File "/usr/lib/python3.6/site-packages/awscli/clidriver.py", line 639, in _make_client_call
    **parameters)
  File "/usr/lib/python3.6/site-packages/botocore/client.py", line 310, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/usr/lib/python3.6/site-packages/botocore/client.py", line 599, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.errorfactory.KMSKeyNotAccessibleFault: An error occurred (KMSKeyNotAccessibleFault) when calling the CopyDBSnapshot operation: The target snapshot KMS key [<kms-arn>] does not exist, is not enabled or you do not have permissions to access it. 
2017-08-22 17:15:37,955 - MainThread - awscli.clidriver - DEBUG - Exiting with rc 255

An error occurred (KMSKeyNotAccessibleFault) when calling the CopyDBSnapshot operation: The target snapshot KMS key [<kms-arn>] does not exist, is not enabled or you do not have permissions to access it.

FTR: I did a cross-post to the AWS forum: https://forums.aws.amazon.com/thread.jspa?messageID=801745


Solution

  • Now, I figured it out by trial and error. Since I don't like to do the same task more than once, I automated it (see script below).

    This are the required permissions for copying a RDS snapshot:

    ["kms:CreateGrant","kms:DescribeKey"]
    

    This is the script I used. Maybe it is useful for other people which have a similar problem. It is hacked together, so don't expect it to work out of the box.

    #!/bin/bash
    
    set -euo pipefail
    
    unknown=(
        kms:CancelKeyDeletion
        kms:CreateAlias
        kms:CreateAlias
        kms:CreateGrant
        kms:CreateKey
        kms:Decrypt
        kms:DeleteAlias
        kms:DeleteAlias
        kms:DescribeKey
        kms:DisableKey
        kms:DisableKeyRotation
        kms:EnableKey
        kms:EnableKeyRotation
        kms:Encrypt
        kms:GenerateRandom
        kms:GenerateDataKey
        kms:GenerateDataKeyWithoutPlaintext
        kms:GetKeyPolicy
        kms:GetKeyRotationStatus
        kms:ListAliases
        kms:ListGrants
        kms:ListKeyPolicies
        kms:ListKeys
        kms:ListRetirableGrants
        kms:PutKeyPolicy
        kms:ReEncryptFrom
        kms:ReEncryptTo
        kms:RetireGrant
        kms:RevokeGrant
        kms:ScheduleKeyDeletion
        kms:UpdateAlias
        kms:UpdateAlias
        kms:UpdateKeyDescription
    )
    required=()
    
    KEY_ID=86a6300d-38f9-4892-b7a1-d8f821e8438c
    export AWS_DEFAULT_OUTPUT=json
    
    function check_copy {
        permissions=$( echo -n "${required[*]} ${unknown[*]}" | jq -R -s 'split(" ")' )
    
        policy=$( aws kms \
            get-key-policy \
            --key-id ${KEY_ID} \
            --policy-name default \
          | jq ".Policy" -r \
          | jq ".Statement[1].Action |= ${permissions}"
        )
    
        aws kms \
            put-key-policy \
            --key-id ${KEY_ID} \
            --policy-name default \
            --policy "${policy}"
    
        aws rds \
            delete-db-snapshot \
            --db-snapshot-identifier rds-backup-share-mysql-reencrypted \
            || true
    
        (
            set -x
            AWS_ACCESS_KEY_ID=XXX \
            AWS_SECRET_ACCESS_KEY=XXX \
            aws rds \
                copy-db-snapshot \
                --source-db-snapshot-identifier rds-backup-share-mysql \
                --target-db-snapshot-identifier rds-backup-share-mysql-reencrypted \
                --kms-key-id alias/rds-snapshot-share \
                || return 1
    
        aws rds \
            wait db-snapshot-completed \
            --db-snapshot-identifier rds-backup-share-mysql-reencrypted
        ) || return 1
    
        return 0
    }
    
    check_copy
    while [ ${#unknown[@]} -gt 0 ]
    do
        removed=${unknown[0]}
        unknown=(${unknown[@]:1})
    
        if ! check_copy
        then
            required+=($removed)
        fi
    
        echo "Required permissions so far: ${required[*]}"
        echo "Unknown permissions so far: ${unknown[*]}"
    done
    
    echo -n "Minimal permissions: "
    echo -n "${required[*]}" | jq -R -s -c 'split(" ")'