Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

describe_key bug when passing alias ARN as KeyId #4259

Closed
arastoob opened this issue Sep 5, 2024 · 5 comments
Closed

describe_key bug when passing alias ARN as KeyId #4259

arastoob opened this issue Sep 5, 2024 · 5 comments
Assignees
Labels
bug This issue is a confirmed bug. kms p2 This is a standard priority issue

Comments

@arastoob
Copy link

arastoob commented Sep 5, 2024

Describe the bug

As stated in describe_key's documentation, you can pass the alias ARN as KeyId when the key is in another account. However, there is a bug when we have this scenario.

Expected Behavior

The call to describe_key should gives us the key attributes, when calling it with alias or alias ARN

Current Behavior

  File ".venv/lib/python3.12/site-packages/moto/kms/responses.py", line 189, in describe_key
    self._validate_key_policy(key_id, "kms:DescribeKey")
  File ".venv/lib/python3.12/site-packages/moto/kms/responses.py", line 114, in _validate_key_policy
    key = self.kms_backend.describe_key(self.kms_backend.get_key_id(key_id))
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.12/site-packages/moto/kms/models.py", line 355, in describe_key
    return self.keys[self.get_key_id(key_id)]
                     ^^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.12/site-packages/moto/kms/models.py", line 364, in get_key_id
    if key_id.startswith("arn:") and ":key/" in key_id:
       ^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'startswith'

Reproduction Steps

  boto3.setup_default_session(
      aws_access_key_id="testing",
      aws_secret_access_key="testing",
      aws_session_token="testing"
  )
  kms_client = boto3.client("kms", region_name="us-east-1")
  alias = "alias/example_com"
  key = kms_client.create_key(Description="KMS for tests")
  key_id = key["KeyMetadata"]["KeyId"]
  kms_client.create_alias(AliasName=alias, TargetKeyId=key_id)
  alias_arn = kms_client.list_aliases()['Aliases'][0]['AliasArn']
  response = kms_client.describe_key(KeyId=alias_arn)
  ret_key_id = response['KeyMetadata']['KeyId']
  assert key_id == ret_key_id

Possible Solution

The key_id becomes None in get_key_id_from_alias function calling in describe_key

Additional Information/Context

Versions:
python = "3.12.1"
boto3 = "^1.26.109"

SDK version used

^1.26.109

Environment details (OS name and version, etc.)

MacOS, python 3.12.1

@arastoob arastoob added bug This issue is a confirmed bug. needs-triage This issue or PR still needs to be triaged. labels Sep 5, 2024
@tim-finnigan tim-finnigan self-assigned this Sep 6, 2024
@tim-finnigan
Copy link
Contributor

tim-finnigan commented Sep 6, 2024

Thanks for reaching out. When I call describe_key, I get the expected response when specifying KeyId as either an ARN or alias. Can you share your debug logs (with any sensitive info redacted) to help with investigating the issue? You can get those by adding boto3.set_stream_logger('') to your script.

I think one issue with your code snippet is the line alias_arn = kms_client.list_aliases()['Aliases'][0]['AliasArn'], as it assumes that the first result from list_aliases will return the alias/ARN you're looking for, which may not be the case.

I should also note that version 1.26.109 is very old — the latest version is 1.35.14 per the CHANGELOG. We recommend updating to a more recent version if you can.

@tim-finnigan tim-finnigan added response-requested Waiting on additional information or feedback. kms p2 This is a standard priority issue and removed needs-triage This issue or PR still needs to be triaged. labels Sep 6, 2024
@arastoob
Copy link
Author

arastoob commented Sep 7, 2024

Thanks @tim-finnigan for your reply. You are right about the line that I assume the first one is always the one I want. Here is a complete code snippet that you can run:

import boto3
from moto import mock_aws


@mock_aws
def test_kms_bug():
    boto3.set_stream_logger('')

    boto3.setup_default_session(
        aws_access_key_id="testing",
        aws_secret_access_key="testing",
        aws_session_token="testing"
    )
    kms_client = boto3.client("kms", region_name="us-east-1")
    alias = "alias/example_com"
    key = kms_client.create_key(Description="KMS for tests")
    key_id = key["KeyMetadata"]["KeyId"]
    kms_client.create_alias(AliasName=alias, TargetKeyId=key_id)
    all_aliases = kms_client.list_aliases()
    alias_arn = None
    for al in all_aliases['Aliases']:
        if alias == al['AliasName']:
            alias_arn = al['AliasArn']
            break
    assert alias_arn

    response = kms_client.describe_key(KeyId=alias_arn)
    ret_key_id = response['KeyMetadata']['KeyId']
    assert key_id == ret_key_id

and here are the logs:


boto_bug.py:4 (test_kms_bug)
@mock_aws
    def test_kms_bug():
        boto3.set_stream_logger('')
    
        boto3.setup_default_session(
            aws_access_key_id="testing",
            aws_secret_access_key="testing",
            aws_session_token="testing"
        )
        kms_client = boto3.client("kms", region_name="us-east-1")
        alias = "alias/example_com"
        key = kms_client.create_key(Description="KMS for tests")
        key_id = key["KeyMetadata"]["KeyId"]
        kms_client.create_alias(AliasName=alias, TargetKeyId=key_id)
        all_aliases = kms_client.list_aliases()
        alias_arn = None
        for al in all_aliases['Aliases']:
            if alias == al['AliasName']:
                alias_arn = al['AliasArn']
                break
        assert alias_arn
    
>       response = kms_client.describe_key(KeyId=alias_arn)

boto_bug.py:27: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
venv/lib/python3.12/site-packages/botocore/client.py:569: in _api_call
    return self._make_api_call(operation_name, kwargs)
venv/lib/python3.12/site-packages/botocore/client.py:1005: in _make_api_call
    http, parsed_response = self._make_request(
venv/lib/python3.12/site-packages/botocore/client.py:1029: in _make_request
    return self._endpoint.make_request(operation_model, request_dict)
venv/lib/python3.12/site-packages/botocore/endpoint.py:119: in make_request
    return self._send_request(request_dict, operation_model)
venv/lib/python3.12/site-packages/botocore/endpoint.py:200: in _send_request
    while self._needs_retry(
venv/lib/python3.12/site-packages/botocore/endpoint.py:352: in _needs_retry
    responses = self._event_emitter.emit(
venv/lib/python3.12/site-packages/botocore/hooks.py:412: in emit
    return self._emitter.emit(aliased_event_name, **kwargs)
venv/lib/python3.12/site-packages/botocore/hooks.py:256: in emit
    return self._emit(event_name, kwargs)
venv/lib/python3.12/site-packages/botocore/hooks.py:239: in _emit
    response = handler(**kwargs)
venv/lib/python3.12/site-packages/botocore/retryhandler.py:207: in __call__
    if self._checker(**checker_kwargs):
venv/lib/python3.12/site-packages/botocore/retryhandler.py:284: in __call__
    should_retry = self._should_retry(
venv/lib/python3.12/site-packages/botocore/retryhandler.py:307: in _should_retry
    return self._checker(
venv/lib/python3.12/site-packages/botocore/retryhandler.py:363: in __call__
    checker_response = checker(
venv/lib/python3.12/site-packages/botocore/retryhandler.py:247: in __call__
    return self._check_caught_exception(
venv/lib/python3.12/site-packages/botocore/retryhandler.py:416: in _check_caught_exception
    raise caught_exception
venv/lib/python3.12/site-packages/botocore/endpoint.py:276: in _do_get_response
    responses = self._event_emitter.emit(event_name, request=request)
venv/lib/python3.12/site-packages/botocore/hooks.py:412: in emit
    return self._emitter.emit(aliased_event_name, **kwargs)
venv/lib/python3.12/site-packages/botocore/hooks.py:256: in emit
    return self._emit(event_name, kwargs)
venv/lib/python3.12/site-packages/botocore/hooks.py:239: in _emit
    response = handler(**kwargs)
venv/lib/python3.12/site-packages/moto/core/botocore_stubber.py:38: in __call__
    response = self.process_request(request)
venv/lib/python3.12/site-packages/moto/core/botocore_stubber.py:88: in process_request
    status, headers, body = method_to_execute(
venv/lib/python3.12/site-packages/moto/core/responses.py:290: in dispatch
    return cls()._dispatch(*args, **kwargs)
venv/lib/python3.12/site-packages/moto/core/responses.py:501: in _dispatch
    return self.call_action()
venv/lib/python3.12/site-packages/moto/core/responses.py:587: in call_action
    response = method()
venv/lib/python3.12/site-packages/moto/kms/responses.py:188: in describe_key
    self._validate_key_policy(key_id, "kms:DescribeKey")
venv/lib/python3.12/site-packages/moto/kms/responses.py:113: in _validate_key_policy
    key = self.kms_backend.describe_key(self.kms_backend.get_key_id(key_id))
venv/lib/python3.12/site-packages/moto/kms/models.py:381: in describe_key
    key = self.keys[self.get_key_id(key_id)]
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

key_id = None

    @staticmethod
    def get_key_id(key_id: str) -> str:
        # Allow use of ARN as well as pure KeyId
>       if key_id.startswith("arn:") and ":key/" in key_id:
E       AttributeError: 'NoneType' object has no attribute 'startswith'

venv/lib/python3.12/site-packages/moto/kms/models.py:398: AttributeError

The describe_key gives me the expected response when specifying KeyId as either an ARN or alias, but not with an alias ARN. In my case:

  • the alias ARN is arn:aws:kms:us-east-1:123456789012:alias/example_com
  • alias is alias/example_com
  • the ARN is arn:aws:kms:us-east-1:123456789012:key/{the-key-id}

The bug appear when we pass alias ARN, even with the latest boto3 version:

> pip show boto3
Name: boto3
Version: 1.35.14
Summary: The AWS SDK for Python
Home-page: https://github.com/boto/boto3
Author: Amazon Web Services
Author-email: 
License: Apache License 2.0
Location: .venv/lib/python3.12/site-packages
Requires: botocore, jmespath, s3transfer
Required-by: moto

@github-actions github-actions bot removed the response-requested Waiting on additional information or feedback. label Sep 7, 2024
@arastoob
Copy link
Author

arastoob commented Sep 9, 2024

@tim-finnigan It seams like the bug is from the moto library (getmoto/moto#8090), not from boto. I updated my moto version to 5.0.14 and the test is passing successfully.
I think we can close this issue now?

@tim-finnigan
Copy link
Contributor

Ok thanks for letting us know, I'll go ahead and close this.

Copy link

github-actions bot commented Sep 9, 2024

This issue is now closed. Comments on closed issues are hard for our team to see.
If you need more assistance, please open a new issue that references this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue is a confirmed bug. kms p2 This is a standard priority issue
Projects
None yet
Development

No branches or pull requests

2 participants