AWS KMS로 CMEK 구성하기
AWS KMS로 CMEK 구성하기
이 가이드는 AWS KMS 키를 Anthropic 조직의 고객 관리 암호화 키(CMEK)로 구성하는 방법을 안내해 드릴게요.
CMEK 활성화는 영구적이에요. KMS 키가 삭제되거나 비활성화되면 Anthropic은 그 아래에서 암호화된 데이터를 복구할 수 없어요. 시작하기 전에 경고와 제한 사항을 검토하세요.
AWS의 Claude Platform: Claude Platform on AWS에서는 키 정책이 Anthropic의 IAM 역할이 아니라 AWS 서비스 principal에 접근 권한을 부여하고, 별도의 검증 단계가 없으며, 키를 Claude Console에서 등록·연결해요. 이 페이지의 AWS의 Claude Platform에서 CMEK 설정을 다음 섹션들의 단계 대신 따르세요.
출처: 문서
본문
전제 조건
- KMS 키 생성과 키 정책 설정 권한(
kms:CreateKey,kms:PutKeyPolicy)이 있는 AWS 계정. - 조직의 Anthropic Admin API 키.
- 설치·인증된 AWS CLI.
Anthropic용 Amazon 리소스 이름(ARN)
Anthropic이 암호화 키를 사용하게 하려면 Anthropic의 IAM 역할에 데이터 암호화에 사용할 KMS 키를 부여해야 해요. Anthropic CMEK의 ARN은:
arn:aws:iam::915198916910:role/anthropic-cmek-client-us
게시된 이 ARN만 사용하세요. 이메일, 채팅, 온보딩 채널로 제공된 식별자는 절대 신뢰하지 마세요.
암호화 키 설정
크로스 계정 키 정책으로 KMS 키 만들기
AWS의 Claude Platform: 이 단계는 건너뛰세요. 키 정책이 AWS 서비스 principal에 접근 권한을 부여하고 조직 조건이 없어요. AWS의 Claude Platform에서 CMEK 설정이 그 정책을 제공해요.
키 정책은 Anthropic의 IAM 역할에 크로스 계정 접근을 부여해요. 세 개의 문(statement)이 필요해요:
- 계정 루트 admin: 표준 KMS 패턴. 계정이 전체 관리 제어권을 유지해요.
- Anthropic 암호화·복호화:
kms:Encrypt와kms:Decrypt작업. Anthropic이 워크스페이스 데이터를 보호하는 데이터 키를 암호화·복호화(봉투 암호화, envelope encryption)하는 데 사용해요. - Anthropic describe: 시작 시 Anthropic이 수행하는 메타데이터 읽기.
DescribeKey에는EncryptionContext매개변수가 없어서 이 작업에EncryptionContext조건을 걸면 항상 거부되므로 별도로 부여돼요.
내 AWS 계정 ID를 찾으려면 aws sts get-caller-identity --query Account --output text를 실행하세요.
정책에서 <AWS_ACCOUNT_ID>를 AWS 계정 ID로, <ORGANIZATION_UUID>를 조직 ID로 바꾸세요. kms:EncryptionContext:anthropic:org_uuid의 StringEquals 조건은 키를 Anthropic 조직에 바인딩하며, 검증은 조건이 없는 키를 거부해요. 여러 Anthropic 조직 간에 하나의 키를 공유하려면 조건 값에 각 조직 ID를 나열하세요.
조직 ID 찾기: Claude Console의 Settings > Organization 아래 Organization ID 필드를 복사하거나, claude.ai의 Organization settings > Organization 아래에서 복사하거나, Organization Info 엔드포인트에서
id필드를 읽으세요.org_접두사가 없는 순수 UUID를 사용하세요.
정책을 key-policy.json으로 저장하세요. 대신 AWS Console에서 키를 만들려면 이 단계 뒷부분에 설명한 대로 거기에 정책을 붙여넣으세요.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AccountRootAdmin",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<AWS_ACCOUNT_ID>:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "AllowAnthropicCMEKCrypto",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::915198916910:role/anthropic-cmek-client-us"
},
"Action": ["kms:Encrypt", "kms:Decrypt"],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:EncryptionContext:anthropic:org_uuid": ["<ORGANIZATION_UUID>"]
}
}
},
{
"Sid": "AllowAnthropicCMEKDescribe",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::915198916910:role/anthropic-cmek-client-us"
},
"Action": "kms:DescribeKey",
"Resource": "*"
}
]
}
선택 사항: 키를 일부 워크스페이스로 제한하려면 앞선 정책 JSON 대신 이
AllowAnthropicCMEKCrypto문을 워크스페이스마다 하나의 컴파트먼트(compartment) ID로 사용하세요. 키를 연결하기 전에 워크스페이스의 컴파트먼트 ID를 추가하세요. 새 워크스페이스는 키 없이 만든 뒤 컴파트먼트 ID를 추가하고 키를 연결하세요.{ "Sid": "AllowAnthropicCMEKCrypto", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::915198916910:role/anthropic-cmek-client-us" }, "Action": ["kms:Encrypt", "kms:Decrypt"], "Resource": "*", "Condition": { "StringEquals": { "kms:EncryptionContext:anthropic:org_uuid": ["<ORGANIZATION_UUID>"] }, "StringEqualsIfExists": { "kms:EncryptionContext:anthropic:compartment_uuid": ["<COMPARTMENT_UUID>"] } } }
aws kms create-key \
--region <REGION> \
--description "Anthropic CMEK" \
--key-usage ENCRYPT_DECRYPT \
--policy file://key-policy.json
출력에서 KeyMetadata.Arn을 기록하세요. 다음 단계에서 키를 등록할 때 필요해요.
키가 이미 CMEK용으로 구성되어 기존 데이터를 보호한다면, 앞선 정책의 세 문 외에 Anthropic이 그 데이터를 복호화할 수 있게 하는 문을 추가해야 해요. 그 조건에는 키가 현재 또는 과거에 연결된 모든 워크스페이스의 컴파트먼트 ID를 나열하세요.
{ "Sid": "AllowAnthropicCMEKDecryptExistingData", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::915198916910:role/anthropic-cmek-client-us" }, "Action": "kms:Decrypt", "Resource": "*", "Condition": { "StringEquals": { "kms:EncryptionContext:anthropic:compartment_uuid": ["<COMPARTMENT_UUID>"] } } }
Anthropic은 키를 검증하거나 워크스페이스에 연결할 때 키를 검증해요. 각 검증은 CloudTrail에 네 개의 접근 거부 오류를 추가해요. 이는 예상된 동작이에요. 걸러내야 한다면 다음 세 값 모두로 필터링하세요. 누구나 설정할 수 있으므로 첫 번째 값만으로는 충분하지 않아요:
requestParameters.encryptionContext.associatedData:Y21lay12YWxpZGF0aW9uuserIdentity.accountId:915198916910resources.ARN:arn:aws:kms:<REGION>:<AWS_ACCOUNT_ID>:key/<KEY_ID>
컴파트먼트 ID 찾기: Anthropic에 키 등록의 Claude Platform 탭을 참고하세요.
AWS Console에서도 키를 만들 수 있어요. 암호화·복호화 키 사용, 단일 리전 키, KMS 키 자료 원본을 가진 대칭 키를 선택하세요. Create-key 마법사는 Review 단계에서 키 정책을 확정해요. 거기서 키 사용 권한에 Anthropic의 계정 ID 915198916910을 추가하면 생성된 정책이 EncryptionContext 조건 없이 Anthropic 전체 계정에 더 넓은 작업(예: kms:ReEncrypt*, kms:GenerateDataKey*)을 부여하고 검증이 이를 거부해요. 과도하게 허용된 키를 남기지 않으려면 관리 권한만으로 마법사를 끝낸 뒤 키의 Key policy 탭을 열고 JSON을 이 단계 앞부분에 보여준 key-policy.json 정책으로 교체하세요.
Anthropic에 키 등록
키 등록 방법은 사용하는 제품에 따라 달라져요.
Claude Platform
AWS의 Claude Platform: principal, 키 정책, 등록 흐름이 다르고 별도 검증 단계가 없어요. 이 탭 대신 AWS의 Claude Platform에서 CMEK 설정을 따르세요.
컴파트먼트 ID 찾기: 각 워크스페이스에는 CMEK 데이터의 범위를 지정하는 컴파트먼트 ID가 있어요. Claude Console에서 찾으려면 Manage > Security로 이동해 사이드바 상단의 워크스페이스 선택기에서 워크스페이스를 선택하세요. ID는 Encryption key 아래 Compartment ID 필드에 있어요. Get Workspace 엔드포인트가 반환하는
compartment_id필드에서도 읽을 수 있어요.
키는 Claude Console이나 Admin API로 설정할 수 있으며 결과는 같아요.
Claude Console:
- Anthropic에 키 등록 — Claude Console에서 Settings > Encryption keys를 열고 Add key를 클릭하세요. 표시 이름을 입력하고 AWS KMS를 선택한 뒤 Continue를 클릭하세요. KMS key ARN에 키 ARN을 붙여넣고 Add를 클릭하세요. 키 세부 사항 단계에 조직 ID가 표시돼요. Add를 클릭하기 전에 이를 키 정책에 추가하세요.
- 키 검증 — Encryption keys 페이지에서 키 옆 Verify를 클릭하세요. 확인이 통과하면 Connected가 표시돼요. 실패하면 메시지가 이유를 알려줘요.
- 키를 워크스페이스에 연결 — Claude Console에서 Manage > Security로 이동해 사이드바 상단의 워크스페이스 선택기에서 워크스페이스를 선택하세요. Encryption key 아래에서 키를 선택하고 Save를 클릭한 뒤 확인하세요. 키 연결은 되돌릴 수 없어요. 이미 요청을 받는 워크스페이스의 경우 키가 적용되는 데 최대 하루 걸릴 수 있어요.
API:
-
Anthropic에 키 등록 — Admin API를 통해 외부 키 구성을 만드세요.
```bash cURL curl -sS "https://api.anthropic.com/v1/organizations/external_keys" \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "display_name": " ", "geo": "us", "provider_config": { "type": "aws", "kms_arn": " " } }' ``` ant beta:organization:external-keys create <<'YAML' display_name: "<friendly-name>" geo: us provider_config: type: aws kms_arn: "<key-arn-from-create-key-step>" YAMLclient = anthropic.Anthropic() external_key = client.beta.organization.external_keys.create( display_name="<friendly-name>", geo="us", provider_config={"type": "aws", "kms_arn": "<key-arn-from-create-key-step>"}, ) print(f"id: {external_key.id}") print(f"display_name: {external_key.display_name}")const client = new Anthropic(); const externalKey = await client.beta.organization.externalKeys.create({ display_name: "<friendly-name>", geo: "us", provider_config: { type: "aws", kms_arn: "<key-arn-from-create-key-step>" } }); console.log(`id: ${externalKey.id}`); console.log(`display_name: ${externalKey.display_name}`);using Anthropic.Models.Beta.Organization.ExternalKeys; AnthropicClient client = new(); var externalKey = await client.Beta.Organization.ExternalKeys.Create(new() { DisplayName = "<friendly-name>", Geo = Geo.Us, ProviderConfig = new BetaAwsExternalKeyConfig { KmsArn = "<key-arn-from-create-key-step>" } }); Console.WriteLine($"id: {externalKey.ID}"); Console.WriteLine($"display_name: {externalKey.DisplayName}");client := anthropic.NewClient() externalKey, err := client.Beta.Organization.ExternalKeys.New(context.Background(), anthropic.BetaOrganizationExternalKeyNewParams{ DisplayName: anthropic.String("<friendly-name>"), Geo: anthropic.BetaOrganizationExternalKeyNewParamsGeoUs, ProviderConfig: anthropic.BetaOrganizationExternalKeyNewParamsProviderConfigUnion{ OfAWS: &anthropic.BetaAWSExternalKeyConfigParam{ KMSARN: "<key-arn-from-create-key-step>", }, }, }) if err != nil { log.Fatal(err) } fmt.Printf("id: %s\n", externalKey.ID) fmt.Printf("display_name: %s\n", externalKey.DisplayName)import com.anthropic.models.beta.organization.externalkeys.BetaAwsExternalKeyConfig; import com.anthropic.models.beta.organization.externalkeys.ExternalKeyCreateParams; void main() { AnthropicClient client = AnthropicOkHttpClient.fromEnv(); var params = ExternalKeyCreateParams.builder() .displayName("<friendly-name>") .geo(ExternalKeyCreateParams.Geo.US) .providerConfig(BetaAwsExternalKeyConfig.builder() .kmsArn("<key-arn-from-create-key-step>") .build()) .build(); var externalKey = client.beta().organization().externalKeys().create(params); IO.println("id: " + externalKey.id()); IO.println("display_name: " + externalKey.displayName().orElseThrow()); }use Anthropic\Beta\Organization\ExternalKeys\ExternalKeyCreateParams\Geo; // ... $client = new Client(); $externalKey = $client->beta->organization->externalKeys->create( displayName: '<friendly-name>', geo: Geo::US, providerConfig: [ 'type' => 'aws', 'kmsARN' => '<key-arn-from-create-key-step>', ], ); echo "id: {$externalKey->id}\n"; echo "display_name: {$externalKey->displayName}\n";client = Anthropic::Client.new external_key = client.beta.organization.external_keys.create( display_name: "<friendly-name>", geo: :us, provider_config: { type: :aws, kms_arn: "<key-arn-from-create-key-step>" } ) puts "id: #{external_key.id}" puts "display_name: #{external_key.display_name}"응답에 외부 키 ID가 포함돼요:
{ "type": "external_key", "id": "ekey_<id>", "display_name": "<friendly-name>" } -
키 검증 — 키에 대해 암호화·복호화 왕복을 트리거하세요.
```bash cURL curl -sS -X POST "https://api.anthropic.com/v1/organizations/external_keys/ekey_ /validate" \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" ``` ant beta:organization:external-keys validate --external-key-id "ekey_<id>"client = anthropic.Anthropic() validation = client.beta.organization.external_keys.validate("ekey_<id>") print(f"status: {validation.status}") print(f"error: {validation.error}")const client = new Anthropic(); const validation = await client.beta.organization.externalKeys.validate("ekey_<id>"); console.log(`status: ${validation.status}`); console.log(`error: ${validation.error}`);AnthropicClient client = new(); var validation = await client.Beta.Organization.ExternalKeys.Validate("ekey_<id>"); Console.WriteLine($"status: {validation.Status.Raw()}"); Console.WriteLine($"error: {validation.Error}");client := anthropic.NewClient() validation, err := client.Beta.Organization.ExternalKeys.Validate(context.Background(), "ekey_<id>") if err != nil { log.Fatal(err) } fmt.Printf("status: %s\n", validation.Status) fmt.Printf("error: %s\n", validation.Error)AnthropicClient client = AnthropicOkHttpClient.fromEnv(); var validation = client.beta().organization().externalKeys().validate("ekey_<id>"); IO.println("status: " + validation.status().asString()); IO.println("error: " + validation.error().orElse(""));$client = new Client(); $validation = $client->beta->organization->externalKeys->validate( externalKeyID: 'ekey_<id>', ); echo "status: {$validation->status}\n"; echo "error: {$validation->error}\n";client = Anthropic::Client.new external_key_id = "ekey_<id>" validation = client.beta.organization.external_keys.validate(external_key_id) puts "status: #{validation.status}" puts "error: #{validation.error}"성공 응답은 이렇게 생겼어요:
{ "type": "external_key_validation", "status": "success", "error": null }검증이 실패하면 일반적인 원인은:
- 암호화 컨텍스트 불일치: 정책에
kms:EncryptionContext:anthropic:compartment_uuid조건이 있다면 키가 연결된 각 워크스페이스의 컴파트먼트 ID를 나열했는지 확인하세요. 검증은 검사하는 워크스페이스의 컴파트먼트 ID를 보내요. 컴파트먼트 문이 여전히kms:Encrypt를 허용하는 이전 키는 연결되지 않은 동안 모두 0 값(00000000-0000-0000-0000-000000000000)으로 검증되므로 그 값을 목록에 유지하세요. - 리소스 제어 정책(RCP): AWS 조직에
aws:PrincipalOrgID가 조직과 일치하지 않을 때 KMS 작업을 거부하는 RCP가 있다면 Anthropic의 크로스 계정 역할을 차단해요. RCP에는 이 키 또는 Anthropic 역할 ARN에 대한 제외가 필요해요. 서비스 제어 정책은 리소스 기반 정책을 통해 호출하는 외부 principal을 평가하지 않으므로 여기에는 적용되지 않아요. - 키 정책이 아닌 IAM을 통해 부여된 접근: 크로스 계정 KMS 접근은 계정의 IAM 정책이 아니라 키 정책 자체에서 부여해야 해요.
aws kms get-key-policy --key-id <id> --policy-name default로 확인하세요. - 리전 불일치: 키의 리전이 구성한 geo 티어에 대해 Anthropic이 운영하는 리전 중 하나인지 확인하세요.
- 암호화 컨텍스트 불일치: 정책에
-
키를 워크스페이스에 연결 — 키가 검증되면 그 워크스페이스로 요청을 보내기 전에 새 워크스페이스에 연결하세요. 이미 요청을 받는 워크스페이스의 경우 키가 적용되는 데 최대 하루 걸릴 수 있어요.
```bash cURL curl -sS -X POST "https://api.anthropic.com/v1/organizations/workspaces/ " \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "external_key_id": "ekey_ " }' ``` ant beta:organization:workspaces update \ --workspace-id "<workspace-id>" \ --external-key-id "ekey_<id>"client = anthropic.Anthropic() workspace = client.beta.organization.workspaces.update( "<workspace-id>", external_key_id="ekey_<id>" ) print(f"id: {workspace.id}") print(f"external_key_id: {workspace.external_key_id}")const client = new Anthropic(); const workspace = await client.beta.organization.workspaces.update("<workspace-id>", { external_key_id: "ekey_<id>" }); console.log(`id: ${workspace.id}`); console.log(`external_key_id: ${workspace.external_key_id}`);AnthropicClient client = new(); var workspace = await client.Beta.Organization.Workspaces.Update("<workspace-id>", new() { ExternalKeyID = "ekey_<id>" }); Console.WriteLine($"id: {workspace.ID}"); Console.WriteLine($"external_key_id: {workspace.ExternalKeyID}");client := anthropic.NewClient() workspace, err := client.Beta.Organization.Workspaces.Update( context.Background(), "<workspace-id>", anthropic.BetaOrganizationWorkspaceUpdateParams{ ExternalKeyID: anthropic.String("ekey_<id>"), }, ) if err != nil { log.Fatal(err) } fmt.Printf("id: %s\n", workspace.ID) fmt.Printf("external_key_id: %s\n", workspace.ExternalKeyID)import com.anthropic.models.beta.organization.workspaces.WorkspaceUpdateParams; void main() { AnthropicClient client = AnthropicOkHttpClient.fromEnv(); var params = WorkspaceUpdateParams.builder() .externalKeyId("ekey_<id>") .build(); var workspace = client.beta().organization().workspaces().update("<workspace-id>", params); IO.println("id: " + workspace.id()); IO.println("external_key_id: " + workspace.externalKeyId().orElseThrow()); }$client = new Client(); $workspace = $client->beta->organization->workspaces->update( workspaceID: '<workspace-id>', externalKeyID: 'ekey_<id>', ); echo "id: {$workspace->id}\n"; echo "external_key_id: {$workspace->externalKeyID}\n";client = Anthropic::Client.new workspace_id = "<workspace-id>" workspace = client.beta.organization.workspaces.update( workspace_id, external_key_id: "ekey_<id>" ) puts "id: #{workspace.id}" puts "external_key_id: #{workspace.external_key_id}"
Claude Enterprise
claude.ai > Organization settings > Data and privacy에서 Encryption keys를 연 뒤 Add key를 클릭하세요. AWS를 선택하고 Continue를 클릭한 다음 이전 단계의 Key ARN을 붙여넣고 Add를 클릭하세요. Anthropic이 암호화·복호화 왕복으로 키를 검증해요. 검증된 것으로 표시되면 그 시점부터 조직이 CMEK로 보호돼요.
이 흐름의 키 세부 사항 단계에는 복사 버튼이 있는 Organization ID for the key policy가 표시돼요. 키 정책에서 그 값을 <ORGANIZATION_UUID> 대신 사용하세요. 키를 만들기 전에 흐름을 열어 ID를 복사할 수 있어요.
Claude Enterprise에서 CMEK는 전체 조직에 적용되므로 별도의 워크스페이스 연결 단계가 없고, 조직은 키를 하나만 가질 수 있어요.
AWS의 Claude Platform에서 CMEK 설정
Claude Platform on AWS에서 CMEK는 AWS KMS 키만 사용하며, 설정은 다음 방식으로 앞선 섹션들과 달라요:
- Principal: 키 정책이 AWS 서비스 principal
aws-external-anthropic.amazonaws.com에 접근 권한을 부여해요. Anthropic의 IAM 역할과 계정 ID는 사용되지 않으므로 Anthropic용 ARN은 적용되지 않아요. - 키 요구 사항: 키는 암호화·복호화 사용, 단일 리전, 그리고 연결하는 워크스페이스와 동일한 AWS 계정·리전의 대칭 KMS 키여야 해요. 크로스 계정 키는 지원되지 않아요. 키는 조직을 호스팅하는 AWS 계정에 있어야 해요. 멀티 리전 키(
mrk-로 시작하는 키 ID)와 별칭 ARN은 등록 시 거부되므로 키 ARN을 사용하세요. - 별도 검증 단계 없음: 등록 시 키 ARN에 대한 이러한 검사 외에 키는 워크스페이스에 연결할 때 검증돼요. 연결 호출이 워크스페이스의 컴파트먼트 ID를 암호화 컨텍스트로 키에 대해 암호화·복호화 왕복을 수행하므로, 키 정책 문제는 등록이 아니라 연결 시점에 드러나요. 따라서
EncryptionContext조건에는 모두 0 항목이 필요 없어요. - 키 관리 위치: Admin 역할로 AWS를 통해 로그인한 Claude Console에서 키를 등록·연결하세요. 외부 키 엔드포인트도 AWS의 Claude Platform에서 IAM 작업으로 인가되어 사용할 수 있어요. 그곳에서 키는
ekey_ID가 아니라 KMS 키 ARN으로 식별돼요.
게시된 이 서비스 principal 이름만 사용하세요. 이메일, 채팅, 온보딩 채널로 제공된 식별자는 절대 신뢰하지 마세요.
전제 조건
- Claude Platform on AWS 조직을 호스팅하는 AWS 계정으로, KMS 키 생성과 키 정책 설정 권한(
kms:CreateKey,kms:PutKeyPolicy)이 있어야 해요. - Claude Platform on AWS용 Claude Console의 Admin 역할. Claude Console 사용하기 참고.
- Claude Console에 로그인하는 IAM principal은
aws-external-anthropic:AssumeConsole외에도 그곳에서 수행하는 작업에 대한 IAM 작업이 필요해요. Encryption keys 페이지와 키 연결이 AWS 게이트웨이를 거치기 때문이에요. 키 등록은RegisterKey(등록 보기에는ListKeys와GetKey), 연결은UpdateWorkspace또는CreateWorkspace예요. 외부 키 작업(및CreateWorkspace)은 계정 범위이므로Resource: "*"로 부여하세요. 워크스페이스 ARN으로 제한된 정책은 이를 포함하지 않아요. - 키를 워크스페이스에 연결하는 IAM principal(Claude Console에 로그인한 신원): 키에 대한
kms:DescribeKey,kms:Encrypt,kms:Decrypt. 연결 시 서비스 principal 외에도 principal의 키 접근이 확인돼요. - 선택 사항, Claude Console의 키 선택기용: 로그인하는 principal의
kms:ListKeys와kms:DescribeKey. 없으면 대신 키 ARN을 붙여넣으세요.
KMS 키 만들기
키 정책은 세 문이에요: 계정 루트 admin 문; Claude Platform on AWS 서비스 principal이 데이터 키를 암호화·복호화·생성할 수 있게 하는 문; kms:DescribeKey용 별도 문. 두 서비스 principal 문 모두 권장 aws:SourceArn 조건을 담아요. 서비스가 특정 워크스페이스를 대신해 키를 호출하고 그 워크스페이스의 ARN을 출처 ARN으로 전달하므로, 보여진 패턴은 부여를 계정의 워크스페이스로 제한해요. DescribeKey는 EncryptionContext 매개변수가 없어서 그 작업에 EncryptionContext 조건을 걸면 항상 거부되므로 별도로 부여돼요.
여기에 보여진 선택적 EncryptionContext 조건을 사용하려면 먼저 워크스페이스를(키 없이) 만들고 그 컴파트먼트 ID를 복사해 <compartment-uuid>에 넣으세요. ID를 Claude Console에서 찾으려면 Manage > Security로 이동해 사이드바 상단의 워크스페이스 선택기에서 워크스페이스를 선택하세요. ID는 Encryption key 아래 Compartment ID 필드에 있어요. Get Workspace 엔드포인트가 반환하는 compartment_id 필드에서도 읽을 수 있어요. 조건을 사용하지 않으려면 그 문의 Condition 블록에서 StringEquals 항목을 삭제하고 ArnLike 항목은 유지하세요.
export YOUR_ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
aws kms create-key \
--region <workspace-region> \
--description "Anthropic CMEK (Claude Platform on AWS)" \
--key-usage ENCRYPT_DECRYPT \
--policy "{
\"Version\": \"2012-10-17\",
\"Statement\": [
{
\"Sid\": \"AccountRootAdmin\",
\"Effect\": \"Allow\",
\"Principal\": {\"AWS\": \"arn:aws:iam::${YOUR_ACCOUNT}:root\"},
\"Action\": \"kms:*\",
\"Resource\": \"*\"
},
{
\"Sid\": \"AllowClaudePlatformOnAWSCrypto\",
\"Effect\": \"Allow\",
\"Principal\": {\"Service\": \"aws-external-anthropic.amazonaws.com\"},
\"Action\": [\"kms:Encrypt\", \"kms:Decrypt\", \"kms:GenerateDataKey\"],
\"Resource\": \"*\",
\"Condition\": {
\"ArnLike\": {
\"aws:SourceArn\": \"arn:aws:aws-external-anthropic:*:${YOUR_ACCOUNT}:workspace/*\"
},
\"StringEquals\": {
\"kms:EncryptionContext:anthropic:compartment_uuid\": [
\"<compartment-uuid>\"
]
}
}
},
{
\"Sid\": \"AllowClaudePlatformOnAWSDescribe\",
\"Effect\": \"Allow\",
\"Principal\": {\"Service\": \"aws-external-anthropic.amazonaws.com\"},
\"Action\": \"kms:DescribeKey\",
\"Resource\": \"*\",
\"Condition\": {
\"ArnLike\": {
\"aws:SourceArn\": \"arn:aws:aws-external-anthropic:*:${YOUR_ACCOUNT}:workspace/*\"
}
}
}
]
}"
출력에서 KeyMetadata.Arn을 기록하세요. 키를 등록할 때 필요해요.
두 조건 모두 선택적 강화이며 서로 결합해요. aws:SourceArn 조건은 워크스페이스가 존재하기 전에 작성할 수 있어요. 전체 계정 대신 특정 워크스페이스에 키를 고정하려면 와일드카드 패턴 대신 전체 워크스페이스 ARN을 나열하고, 없이 시작하려면 두 서비스 principal 문에서 ArnLike 항목을 삭제하세요(그러면 비게 되는 Condition 블록도 제거). EncryptionContext 조건도 선택적이에요. 워크스페이스에 대해 이루어지는 모든 암호화·복호화·데이터 키 호출(연결 시점 검사 포함)은 해당 워크스페이스의 컴파트먼트 ID를 anthropic:compartment_uuid로 담아요. 따라서 조건은 키를 연결하는 각 워크스페이스의 컴파트먼트 ID를 나열하며 모두 0 항목이 필요 없어요. 추가하면 IAM 계층에서도 키를 나열한 워크스페이스에 바인딩해요. 컴파트먼트 ID는 워크스페이스가 존재해야만 존재하므로 순서는: 워크스페이스를 만들고, 조건에 컴파트먼트 ID를 넣고(키 생성 시 또는 나중에 kms:PutKeyPolicy로), 키를 연결하는 거예요. 각 추가 워크스페이스에 키를 연결하기 전에 같은 방식으로 그 워크스페이스의 컴파트먼트 ID를 추가하세요. 없이 시작하려면 AllowClaudePlatformOnAWSCrypto 문의 Condition 블록에서 StringEquals 항목을 삭제하세요. 나중에 추가한다면 키가 이미 연결된 모든 워크스페이스의 컴파트먼트 ID를 포함하세요.
AWS Console에서도 키를 만들 수 있어요: 워크스페이스의 리전에서 대칭 키, 암호화·복호화 키 사용, 단일 리전 키, KMS 키 자료 원본을 선택하세요. Create-key 마법사에서 키 사용 권한은 비워 두고, 키의 Key policy 탭을 열어 JSON을 여기 보여준 정책으로 교체하세요.
키 등록 및 연결
-
키 등록 — Claude Console에서 Settings > Encryption keys를 열고 Add key를 클릭하세요. 표시 이름을 입력한 뒤 키 선택기에서 키를 선택하거나 Enter ARN manually를 선택하고 키 ARN을 붙여넣고 Add를 클릭하세요. 키는 조직을 호스팅하는 AWS 계정에 있어야 해요. 크로스 계정 키는 지원되지 않아요. 선택기는 조직의 리전 중 하나에서 계정의 활성화된, 고객 관리, 대칭, 단일 리전 키를 나열해요. 선택기에 없는 키는 ARN을 입력하세요. 로그인한 principal이
kms:ListKeys와kms:DescribeKey를 호출할 수 있는 경우에만 키를 나열해요. -
키를 워크스페이스에 연결 — 그 워크스페이스로 요청을 보내기 전에 키를 새 워크스페이스에 연결하세요. 이미 요청을 받는 워크스페이스의 경우 키가 적용되는 데 최대 하루 걸릴 수 있어요. Claude Console에서 Manage > Security로 이동해 사이드바 상단의 워크스페이스 선택기에서 워크스페이스를 선택하세요. Encryption key 아래에서 키를 선택하고 Save를 클릭한 뒤 확인하세요. 키 정책이 아직 특정 워크스페이스를 지명하지 않은 경우에만(즉
EncryptionContext조건이 없고 개별 워크스페이스 ARN이 아닌 계정 전체aws:SourceArn패턴) Claude Console에서 워크스페이스를 만들 때 키를 선택할 수도 있어요. 워크스페이스의 ID와 컴파트먼트 ID가 생성 시 할당되기 때문이에요. 일단 연결되면 워크스페이스의 키는 변경할 수 없어요.이때 키가 검증돼요. 연결 호출이 principal의 키 접근을 확인하고 워크스페이스의 컴파트먼트 ID를 암호화 컨텍스트로 키에 대해 암호화·복호화 왕복을 수행하므로, 키 정책이나 principal 권한의 문제는 해당 호출에서 오류로 드러나요. KMS 접근 오류로 연결이 실패하면 다음을 확인하세요:
- 키 정책이
aws-external-anthropic.amazonaws.com서비스 principal을 지명하고kms:Encrypt,kms:Decrypt,kms:GenerateDataKey를 부여하며,EncryptionContext조건이 없는 별도 문에kms:DescribeKey도 부여해요. aws:SourceArn조건이 이 워크스페이스의 ARN(계정 ID, 특정 ARN을 나열했다면 워크스페이스)과 일치하고, 어떤EncryptionContext조건이든 이 워크스페이스의 컴파트먼트 ID를 포함해요.- 키가 활성화·단일 리전이며 워크스페이스와 동일한 AWS 계정·리전에 있어요.
- 로그인한 principal이 키에 대한
kms:DescribeKey,kms:Encrypt,kms:Decrypt권한을 가져요. - AWS 조직의 어떤 서비스 제어 정책이나 리소스 제어 정책도 서비스 principal 또는 principal이 키를 사용하는 것을 막지 않아요.
- 정책이 맞아 보여도 연결이 여전히 실패하면 키의 계정에서 CloudTrail에서 거부된
kms:이벤트를 찾아보고(호출 principal과, 암호화 호출의 경우 암호화 컨텍스트를 보여줌)aws:SourceArn조건을 임시로 제거한 채 다시 시도해 출처 ARN 불일치와 암호화 컨텍스트 불일치를 구분하세요. 키가 연결되면(그 재시도에서든 암호화 컨텍스트를 고친 후든)kms:PutKeyPolicy로 두 서비스 principal 문에ArnLike항목을 복원하되, 계정 전체aws:SourceArn패턴이나 키가 연결된 모든 워크스페이스의 ARN을 사용하세요.
- 키 정책이
Terraform
IaC(infrastructure-as-code) 배포의 경우 동일한 단계가 aws 제공자의 aws_kms_key와 aws_kms_alias 리소스로 매핑돼요.