Back to Checks
aws

Bedrock custom model is encrypted with a customer-managed KMS key

bedrock_custom_model_encrypted_with_cmk
Service: bedrockSeverity: critical
OfficialBuilt-inVerified
Categories:gen-aiencryption
Resource Type:Other

Bedrock custom models produced by model customization can be encrypted at rest with a customer-managed KMS key rather than resting under an AWS-owned key the organization cannot audit, rotate, or revoke.

Risk

A fine-tuned model is derived from the training data used to build it and can leak that data through inference.

Under an AWS-owned key, the organization has no key policy to restrict who may decrypt the artifacts, no customer-controlled rotation, no CloudTrail record of key usage, and no way to revoke access to the model by disabling a key.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler aws --checks bedrock_custom_model_encrypted_with_cmk

Recommendation

Choose a customer-managed KMS key when creating each model customization job so the resulting custom model rests under a key with an auditable policy and rotation. The key cannot be changed after the model is created, so re-run customization for existing models.

Remediation

Terraform

resource "aws_bedrock_custom_model" "example_resource" {
  custom_model_name     = "example-custom-model"
  job_name              = "example-customization-job"
  base_model_identifier = "arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-text-express-v1"
  role_arn              = "arn:aws:iam::123456789012:role/example-bedrock-customization-role"
  custom_model_kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/example-key-id" # Critical: without this the model is encrypted with an AWS owned key

  hyperparameters = {
    epochCount = "1"
  }

  output_data_config {
    s3_uri = "s3://example-bucket/output/"
  }

  training_data_config {
    s3_uri = "s3://example-bucket/training/"
  }
}

Other

1Open the AWS Console and go to Amazon Bedrock
2Select **Custom models**, then create a new customization job
3Under encryption, choose a customer-managed KMS key instead of the default
4Grant the customization service role kms:Decrypt, kms:GenerateDataKey and kms:DescribeKey on that key
5Re-run customization, because the key of an existing custom model cannot be changed in place

Check Code

from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.bedrock.bedrock_client import bedrock_client


class bedrock_custom_model_encrypted_with_cmk(Check):
    """Ensure Bedrock custom models are encrypted with a customer-managed KMS key.

    - PASS: GetCustomModel returns a `modelKmsKeyArn`, so the model artifacts
      are encrypted with a key the account controls.
    - FAIL: No `modelKmsKeyArn` is set, so the model is encrypted with an
      AWS-owned key that the organization cannot audit, rotate, or revoke.
    - MANUAL: GetCustomModel failed, so the key could not be retrieved and an
      absent value cannot be read as "no key"; or ListCustomModels failed for a
      region, so that region's custom models are unknown rather than absent.

    Only models this account owns are audited (`ListCustomModels` is called with
    `isOwned=True`): the KMS key of a model shared in through Resource Access
    Manager belongs to the owning account and cannot be changed here.
    """

    def execute(self) -> list[Check_Report_AWS]:
        """Execute the check logic.

        Returns:
            A list of reports containing the result of the check.
        """
        findings = []

        for region, error in sorted(bedrock_client.custom_models_scan_errors.items()):
            report = Check_Report_AWS(
                metadata=self.metadata(), resource={"region": region}
            )
            report.region = region
            report.resource_id = "custom-model/unknown"
            report.resource_arn = f"arn:{bedrock_client.audited_partition}:bedrock:{region}:{bedrock_client.audited_account}:custom-model/unknown"
            report.status = "MANUAL"
            report.status_extended = f"Bedrock custom models could not be listed in region {region} ({error}); verify manually that every custom model uses a customer-managed KMS key."
            findings.append(report)

        for model in bedrock_client.custom_models.values():
            report = Check_Report_AWS(metadata=self.metadata(), resource=model)

            if not model.detail_retrieved:
                report.status = "MANUAL"
                report.status_extended = f"Bedrock custom model {model.name} encryption configuration could not be retrieved in region {model.region}; verify manually that it uses a customer-managed KMS key."
            elif model.kms_key_arn:
                report.status = "PASS"
                report.status_extended = f"Bedrock custom model {model.name} is encrypted with a customer-managed KMS key in region {model.region}."
            else:
                report.status = "FAIL"
                report.status_extended = f"Bedrock custom model {model.name} is not encrypted with a customer-managed KMS key in region {model.region}, so the fine-tuned weights rest under an AWS-owned key the organization cannot audit or revoke."
            findings.append(report)

        return findings