Google Cloud KMS로 CMEK 구성하기
Google Cloud KMS로 CMEK 구성하기
이 가이드는 Google Cloud KMS 키를 Anthropic 조직의 고객 관리 암호화 키(CMEK)로 구성하는 방법을 안내해 드릴게요.
CMEK 활성화는 영구적이에요. KMS 키가 삭제되거나 비활성화되면 Anthropic은 그 아래에서 암호화된 데이터를 복구할 수 없어요. 시작하기 전에 경고와 제한 사항을 검토하세요.
출처: 문서
본문
전제 조건
- 청구가 활성화된 Google Cloud 프로젝트.
- Cloud KMS API 활성화(
cloudkms.googleapis.com). - KMS 키 링과 키 생성, 그리고 그에 대한 IAM 정책 설정 권한(
roles/cloudkms.admin또는 동등). - 조직의 Anthropic Admin API 키.
- 설치·인증된
gcloudCLI. - 프로젝트에 Cloud KMS Data Access 감사 로그 활성화(IAM & Admin > Audit Logs > Cloud Key Management Service,
DATA_READ와DATA_WRITE포함). 기본적으로 꺼져 있으며, 없으면 Anthropic의 암호화·복호화 작업이 Cloud Logging에 항목을 만들지 않아요.
Anthropic 서비스 계정 이메일
Anthropic이 암호화 키를 사용하게 하려면 Anthropic의 서비스 계정에 데이터 암호화에 사용할 키를 부여해야 해요. Anthropic CMEK의 서비스 계정 이메일은:
anthropic-cmek-client-us@gcp-anthropic-cmek-clients.iam.gserviceaccount.com
게시된 이 서비스 계정 이메일만 사용하세요. 이메일, 채팅, 온보딩 채널로 제공된 식별자는 절대 신뢰하지 마세요.
도메인 제한 공유: 프로젝트가
constraints/iam.allowedPolicyMemberDomains를 적용하는 Google Cloud 조직 아래에 있다면, Anthropic 서비스 계정이 조직 밖에 있으므로 다음 IAM 바인딩이 거부돼요. 해당 제약에 프로젝트 수준 제외가 있거나, 허용 목록에 Anthropic의 Cloud Identity 고객 ID(C0xxxxxxxx형식)를 추가해야 해요. 필요하면 Anthropic에 고객 ID를 문의하세요.
암호화 키 설정
키 링 만들기 또는 선택
재사용할 키 링이 이미 있다면 이 단계를 건너뛰세요. 키 링은 리전별이에요. 구성 중인 Anthropic 지역과 일치하는 us-east5 같은 단일 리전 US 위치를 선택하세요. us나 global 같은 멀티 리전 위치는 지원되지 않아요.
gcloud kms keyrings create <your-keyring-name> \
--project=<your-project-id> \
--location=<region>
크립토 키 만들기
ENCRYPT_DECRYPT 목적의 대칭 키를 만드세요. Anthropic은 HSM 보호를 강력히 권장해요. Cloud KMS HSM 키는 FIPS 140-2 Level 3 검증을 받았고 소프트웨어 키와의 비용 차이가 작아요.
--labels 옵션은 조직 레이블 anthropic-org-<ORGANIZATION_UUID>(값 true)를 추가해요. 여기서 <ORGANIZATION_UUID>는 소문자 Anthropic 조직 ID예요. 레이블은 Anthropic이 키를 검증하는 데 필요해요.
조직 ID 찾기: Claude Console의 Settings > Organization 아래 Organization ID 필드를 복사하거나, claude.ai의 Organization settings > Organization 아래에서 복사하거나, Organization Info 엔드포인트에서
id필드를 읽으세요.org_접두사가 없는 순수 UUID를 사용하세요.
gcloud kms keys create <KEY_NAME> \
--project=<PROJECT_ID> \
--location=<REGION> \
--keyring=<KEYRING_NAME> \
--purpose=encryption \
--protection-level=hsm \
--labels=anthropic-org-<ORGANIZATION_UUID>=true
소프트웨어 보호를 원하면 --protection-level=hsm을 생략하세요. 이 가이드의 다른 부분은 변하지 않아요.
Google Cloud Console에서도 키를 만들 수 있어요. 키 링을 열고 Create key를 클릭한 뒤 Generated key를 선택하고 목적과 알고리즘을 대칭 암호화·복호화로 설정하고 보호 수준 아래 HSM을 선택하세요.
여러 Anthropic 조직 간에 하나의 키를 공유하려면 조직마다 레이블을 하나씩 추가하세요. 키는 자신의 것을 포함해 최대 64개 레이블을 담을 수 있어요.
레이블이 없는 키에 레이블을 추가하려면
gcloud kms keys update <KEY_NAME> --project=<PROJECT_ID> --location=<REGION> --keyring=<KEYRING_NAME> --update-labels=anthropic-org-<ORGANIZATION_UUID>=true를 실행하세요. 키가 이미 가진 레이블과 병합돼요.
Anthropic 서비스 계정에 키 접근 권한 부여
두 개의 키 수준 IAM 바인딩이 필요해요. 둘 다 프로젝트나 키 링 전체가 아닌 단일 크립토 키로 범위가 지정돼요.
Anthropic이 워크스페이스 데이터를 보호하는 데이터 키를 암호화·복호화(봉투 암호화)하는 데 사용하는 암호화·복호화:
gcloud kms keys add-iam-policy-binding <your-key-name> \
--project=<your-project-id> \
--location=<region> \
--keyring=<your-keyring-name> \
--member="serviceAccount:anthropic-cmek-client-us@gcp-anthropic-cmek-clients.iam.gserviceaccount.com" \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter
Anthropic이 시작 시 키의 목적과 알고리즘을 검증하기 위해 수행하는 메타데이터 읽기(cryptoKeys.get)용 viewer:
gcloud kms keys add-iam-policy-binding <your-key-name> \
--project=<your-project-id> \
--location=<region> \
--keyring=<your-keyring-name> \
--member="serviceAccount:anthropic-cmek-client-us@gcp-anthropic-cmek-clients.iam.gserviceaccount.com" \
--role=roles/cloudkms.viewer
Console에서 키를 선택하고 Permissions 패널을 열어 Grant access를 클릭하고, Cloud KMS CryptoKey Encrypter/Decrypter와 Cloud KMS Viewer 역할 모두로 서비스 계정을 추가하세요. 키 링이나 프로젝트가 아니라 키의 권한 페이지에 있는지 확인해 부여가 이 키에만 범위가 지정되게 하세요.
전체 키 리소스 이름 기록
키를 등록할 때 이것을 Anthropic에 전달해요. 형식은:
projects/<your-project-id>/locations/<region>/keyRings/<your-keyring-name>/cryptoKeys/<your-key-name>
이렇게 조회할 수 있어요:
gcloud kms keys describe <your-key-name> \
--project=<your-project-id> \
--location=<region> \
--keyring=<your-keyring-name> \
--format="value(name)"
Console에서 키의 세부 정보 페이지를 열고 Copy resource name을 클릭하세요.
Anthropic에 키 등록
키 등록 방법은 사용하는 제품에 따라 달라져요.
Claude Platform
키는 Claude Console이나 Admin API로 설정할 수 있으며 결과는 같아요.
Claude Console:
- Anthropic에 키 등록 — Claude Console에서 Settings > Encryption keys를 열고 Add key를 클릭하세요. 표시 이름을 입력하고 Google Cloud KMS를 선택한 뒤 Continue를 클릭하세요. Key resource name에 전체 키 리소스 이름을 붙여넣고 Add를 클릭하세요. 키 세부 사항 단계에 조직 레이블이 표시돼요. 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": "gcp", "key_name": "projects/ /locations/ /keyRings/ /cryptoKeys/ " } }' ``` ant beta:organization:external-keys create <<'YAML' display_name: "<friendly-name>" geo: us provider_config: type: gcp key_name: "projects/<your-project-id>/locations/<region>/keyRings/<your-keyring-name>/cryptoKeys/<your-key-name>" YAMLclient = anthropic.Anthropic() external_key = client.beta.organization.external_keys.create( display_name="<friendly-name>", geo="us", provider_config={ "type": "gcp", "key_name": "projects/<your-project-id>/locations/<region>/keyRings/<your-keyring-name>/cryptoKeys/<your-key-name>", }, ) 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: "gcp", key_name: "projects/<your-project-id>/locations/<region>/keyRings/<your-keyring-name>/cryptoKeys/<your-key-name>" } }); 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 BetaGcpExternalKeyConfig { KeyName = "projects/<your-project-id>/locations/<region>/keyRings/<your-keyring-name>/cryptoKeys/<your-key-name>" } }); 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{ OfGCP: &anthropic.BetaGCPExternalKeyConfigParam{ KeyName: "projects/<your-project-id>/locations/<region>/keyRings/<your-keyring-name>/cryptoKeys/<your-key-name>", }, }, }) 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.ExternalKeyCreateParams; void main() { AnthropicClient client = AnthropicOkHttpClient.fromEnv(); var params = ExternalKeyCreateParams.builder() .displayName("<friendly-name>") .geo(ExternalKeyCreateParams.Geo.US) .gcpProviderConfig("projects/<your-project-id>/locations/<region>/keyRings/<your-keyring-name>/cryptoKeys/<your-key-name>") .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' => 'gcp', 'keyName' => 'projects/<your-project-id>/locations/<region>/keyRings/<your-keyring-name>/cryptoKeys/<your-key-name>', ], ); 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: :gcp, key_name: "projects/<your-project-id>/locations/<region>/keyRings/<your-keyring-name>/cryptoKeys/<your-key-name>" } ) 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 }검증이 실패하면 일반적인 원인은:
- VPC Service Controls: 서비스 경계(perimeter)가 프로젝트의 Cloud KMS를 보호한다면 경계의 접근 수준에 Anthropic을 추가하거나(또는 키의 프로젝트 제외) Anthropic이 키에 도달할 수 있게 하세요.
- 도메인 제한 공유:
constraints/iam.allowedPolicyMemberDomains조직 정책이 Anthropic 서비스 계정 바인딩을 제거할 수 있어요(앞선 주석 참고).gcloud kms keys get-iam-policy <your-key-name> --project=<your-project-id> --location=<region> --keyring=<your-keyring-name>으로 바인딩이 있는지 확인하세요. - 비활성화 또는 파괴된 키 버전: 키의 기본 버전이 활성화되어 있고, 비활성화·파괴 예약·파괴 상태가 아닌지 확인하세요.
-
키를 워크스페이스에 연결 — 키가 검증되면 그 워크스페이스로 요청을 보내기 전에 새 워크스페이스에 연결하세요. 이미 요청을 받는 워크스페이스의 경우 키가 적용되는 데 최대 하루 걸릴 수 있어요.
```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를 클릭하세요. Google Cloud를 선택하고 이전 단계의 전체 키 리소스 이름을 붙여넣은 뒤 Continue를 클릭하세요. Anthropic이 암호화·복호화 왕복으로 키를 검증해요. 검증된 것으로 표시되면 그 시점부터 조직이 CMEK로 보호돼요.
Claude Enterprise에서 CMEK는 전체 조직에 적용되므로 별도의 워크스페이스 연결 단계가 없고, 조직은 키를 하나만 가질 수 있어요.
Terraform
IaC(infrastructure-as-code) 배포의 경우 동일한 단계가 google 제공자의 google_kms_key_ring, google_kms_crypto_key, google_kms_crypto_key_iam_member 리소스로 매핑돼요.