Published on

Azure Key Vault 403? Managed Identity 권한 5분 해결

Authors

서버나 배치, 컨테이너에서 Managed Identity로 Azure Key Vault를 읽으려는데 갑자기 403 Forbidden이 뜨면 대부분은 “인증은 됐는데(토큰 발급 성공) 인가가 안 된” 상태입니다. 즉, Key Vault가 요청 주체를 식별했지만 get/list 같은 데이터 작업 권한이 없거나, 네트워크 정책 때문에 차단된 경우가 많습니다.

이 글은 Key Vault 4035분 내에 정리하는 실전 체크리스트입니다. 특히 Key Vault의 권한 모델이 Access policy인지 Azure RBAC인지에 따라 해결 방법이 완전히 달라지는 부분을 빠르게 짚습니다.

1) 403의 정체: 인증(Authentication) vs 인가(Authorization)

Key Vault 접근에서 가장 흔한 오해는 “Managed Identity가 있으니 자동으로 권한이 있을 것”이라는 가정입니다. Managed Identity는 비밀번호 없는 인증 수단일 뿐이고, Key Vault에 대한 권한 부여는 별도입니다.

403이 났을 때 메시지에 아래 단서가 자주 들어 있습니다.

  • ForbiddenByPolicy 또는 AccessDenied
  • Caller is not authorized to perform action
  • This request is not authorized to perform this operation
  • Public network access is disabled (이건 네트워크 차단 계열)

핵심은 두 갈래입니다.

  1. 권한 모델/역할/정책 문제(대부분)
  2. 네트워크/프라이빗 엔드포인트/DNS 문제(조금 까다로움)

네트워크 쪽이 의심된다면, Private Endpoint 구성 시 DNS가 꼬여 NXDOMAIN이나 엉뚱한 IP로 붙는 케이스도 흔합니다. 이 경우는 별도 글인 Azure Private Endpoint DNS 꼬임으로 NXDOMAIN 해결하기도 같이 보세요.

2) 5분 해결 체크리스트(결론부터)

아래 순서대로 보면 빠릅니다.

  1. Key Vault 권한 모델 확인: Vault가 Azure RBAC인지 Vault access policy인지
  2. Managed Identity의 Object ID(Principal) 확인: 시스템 할당/사용자 할당 MI 구분
  3. 권한 부여:
    • RBAC 모델이면 Key Vault Secrets User(또는 더 강한 역할) role assignment
    • Access policy 모델이면 secrets Get/List access policy 추가
  4. 네트워크 설정 확인: Public access 차단 여부, Private Endpoint 사용 여부, 호출 위치가 허용된 네트워크인지
  5. 캐시/전파 지연 고려: RBAC/정책 반영이 수 분 지연될 수 있어 재시도

3) Key Vault 권한 모델부터 확인하기(가장 중요)

Key Vault는 권한 부여 방식이 두 가지입니다.

  • Vault access policy: Key Vault 자체에 정책을 직접 붙임
  • Azure RBAC: Azure 역할 기반 접근 제어로 데이터 작업까지 통제

Portal에서 Key Vault로 들어가서 다음을 확인합니다.

  • Access configuration 또는 Permission model 항목
  • 값이 Azure role-based access control인지 Vault access policy인지

여기서부터 분기합니다.

3-1) Azure RBAC 모델인데 Access policy만 만지고 있었다

RBAC 모델에서는 Access policy를 수정해도 효과가 없거나 제한적입니다. 이 상태에서 403이 나면, 거의 항상 role assignment가 없음이 원인입니다.

3-2) Access policy 모델인데 RBAC role만 줬다

반대로 Access policy 모델에서 역할만 주고 정책을 안 넣으면, 데이터 플레인(secrets/keys/certs) 작업이 막힐 수 있습니다.

4) Managed Identity가 “누구”인지부터 정확히 잡기

권한을 부여하려면 대상 Principal을 정확히 알아야 합니다.

  • System-assigned managed identity: 리소스(예: App Service, VM, Function)에 붙어 있는 MI
  • User-assigned managed identity: 별도 리소스로 존재하며 여러 워크로드가 공유 가능

실수 포인트:

  • App Service의 MI를 줘야 하는데, 다른 리소스의 MI에 권한을 준 경우
  • User-assigned MI를 쓴다고 해놓고 실제 앱은 system-assigned로 토큰을 받는 경우(또는 반대)

Azure CLI로 확인하는 예시입니다.

# App Service의 system-assigned MI principalId 확인
az webapp identity show \
  --resource-group my-rg \
  --name my-app \
  --query principalId -o tsv

# User-assigned MI의 principalId 확인
az identity show \
  --resource-group my-rg \
  --name my-uami \
  --query principalId -o tsv

principalId(Object ID)가 Key Vault 권한 부여의 대상입니다.

5) 해결 1: Azure RBAC 모델에서 403 해결(가장 흔함)

RBAC 모델이면 Key Vault에 대해 데이터 플레인 역할을 부여해야 합니다.

대표적으로 secrets 읽기만 필요하면 아래 역할이 많이 쓰입니다.

  • Key Vault Secrets User: secret get 권한 중심
  • Key Vault Secrets Officer: 관리/쓰기까지
  • Key Vault Administrator: 거의 전체(권장 X)

CLI로 role assignment를 붙입니다.

KV_ID=$(az keyvault show -g my-rg -n my-kv --query id -o tsv)
PRINCIPAL_ID=$(az webapp identity show -g my-rg -n my-app --query principalId -o tsv)

az role assignment create \
  --assignee-object-id "$PRINCIPAL_ID" \
  --assignee-principal-type ServicePrincipal \
  --role "Key Vault Secrets User" \
  --scope "$KV_ID"

자주 하는 실수:

  • scope를 리소스 그룹이나 구독에 걸어놓고 “왜 안 되지?” 하는 경우
    • 가능은 하지만, 의도치 않게 범위가 커지거나 반대로 데이터 플레인에 필요한 범위가 안 맞을 수 있어 Key Vault 리소스 자체 scope로 주는 걸 추천합니다.
  • --assignee에 애매한 값을 넣어 다른 Principal에 할당되는 경우
    • 가능하면 --assignee-object-id를 쓰는 게 안전합니다.

전파 지연(Propagation delay)

RBAC는 보통 빠르지만 수 분 지연이 날 수 있습니다. 배포 직후 403이 나면 아래처럼 재시도 로직을 넣는 것도 실무적으로 유효합니다(특히 IaC 직후 첫 부팅 시).

6) 해결 2: Access policy 모델에서 403 해결

Access policy 모델이면 Key Vault의 Access policy에 Managed Identity를 추가하고, 필요한 권한을 체크해야 합니다.

secrets 읽기라면 최소한 다음이 필요합니다.

  • Get
  • (환경변수로 secret 이름을 열거하거나 탐색이 필요하면) List

CLI 예시:

PRINCIPAL_ID=$(az webapp identity show -g my-rg -n my-app --query principalId -o tsv)

az keyvault set-policy \
  --name my-kv \
  --object-id "$PRINCIPAL_ID" \
  --secret-permissions get list

주의할 점:

  • object-id는 애플리케이션(앱 등록) ID가 아니라 서비스 프린시펄/MI의 Object ID입니다.
  • list 권한은 보안상 불필요하면 주지 않는 게 좋습니다. 애플리케이션이 특정 secret 이름을 알고 get만 하는 구조가 더 안전합니다.

7) 네트워크 때문에 나는 403/차단 케이스 빠른 분류

권한을 다 줬는데도 안 되면, 다음을 확인합니다.

7-1) Key Vault에서 Public network access가 꺼져 있음

Key Vault 네트워크 설정에서 Public network access: Disabled인데,

  • 호출 주체(앱/VM/AKS)가 Private Endpoint 경로로 붙지 못하면 실패합니다.

이 경우 증상은 403 또는 네트워크 오류처럼 보일 수 있고, 로그에 Public network access is disabled 같은 문구가 보이기도 합니다.

7-2) Private Endpoint인데 DNS가 공인 엔드포인트로 해석됨

Private Endpoint를 쓰면 my-kv.vault.azure.net이 사설 IP로 해석돼야 합니다. DNS가 잘못되면 공인으로 붙으려다 막혀서 실패합니다.

  • VNet DNS 설정
  • Private DNS Zone 링크
  • 레코드 존재 여부

이 축은 디버깅이 길어질 수 있으니, 앞서 언급한 글(Azure Private Endpoint DNS 꼬임으로 NXDOMAIN 해결하기)의 체크리스트가 도움이 됩니다.

8) 애플리케이션 코드: Managed Identity로 Key Vault 호출 예시

아래는 .NET에서 Managed Identity를 사용해 Key Vault secret을 읽는 전형적인 코드입니다. (App Service/VM/AKS 등에서 동작)

using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

var kvUri = new Uri("https://my-kv.vault.azure.net/");
var client = new SecretClient(kvUri, new DefaultAzureCredential());

KeyVaultSecret secret = await client.GetSecretAsync("my-secret-name");
Console.WriteLine(secret.Value);

여기서 DefaultAzureCredential은 환경에 따라 Managed Identity를 우선 사용합니다. 하지만 로컬 개발 환경에서는 개발자 로그인(예: Azure CLI 로그인)으로도 토큰을 얻을 수 있어, 로컬에서는 되는데 배포하면 403이 나는 전형적인 함정이 생깁니다.

  • 로컬: 내 사용자 계정에 Key Vault 권한이 있어서 성공
  • 배포: Managed Identity에는 권한이 없어서 403

따라서 “배포 환경의 Principal이 누구인지”를 먼저 고정하고 권한을 부여해야 합니다.

9) 운영에서 자주 겪는 함정 7가지

  1. 권한 모델 혼동: RBAC인데 access policy만 수정
  2. Principal 착각: system-assigned와 user-assigned 혼용
  3. 역할 선택 오류: Reader 같은 관리 플레인 역할만 주고 데이터 플레인은 미부여
  4. scope 실수: 다른 Key Vault나 다른 구독/리소스 그룹에 role assignment
  5. list 권한 누락: 앱이 내부적으로 secret 나열을 시도하는데 list가 없어 403
  6. 네트워크 차단: public access off + private endpoint 미구성
  7. 전파 지연: IaC 직후 즉시 호출하여 403, 재시도 없어서 장애로 확대

10) 빠른 트러블슈팅 루틴(현장용)

문제 재현 시 아래 3개만 빠르게 수집해도 원인 파악이 빨라집니다.

  • Key Vault의 Permission model
  • 호출 워크로드의 Managed Identity principalId
  • Key Vault의 네트워크 설정(공개 차단 여부, private endpoint 여부)

그리고 CI/CD에서 권한 설정을 자동화할 때는, 실패 시 원인을 끝까지 끌고 가는 셸 옵션/트랩이 도움이 됩니다. 파이프라인에서 에러가 묻히는 경우가 많다면 Bash set -e가 무시될 때 - pipefail·trap도 같이 참고하면 좋습니다.

11) 마무리: 403은 “대부분 권한 모델”부터

Azure Key Vault의 403은 대개 인증 문제가 아니라 인가 문제입니다. 특히 권한 모델이 RBAC로 바뀌었는데 예전 습관대로 Access policy만 만지는 순간, 아무리 설정해도 계속 403이 납니다.

정리하면:

  • 먼저 Key Vault 권한 모델을 확인하고(RBAC vs Access policy)
  • 실제로 토큰을 받는 Managed Identity principal을 특정한 뒤
  • RBAC이면 Key Vault Secrets User 같은 역할을 Key Vault scope로 할당
  • Access policy면 get/list를 object id에 직접 부여
  • 그래도 안 되면 네트워크(Private Endpoint, DNS)를 의심

이 흐름대로 보면 “5분 해결”이 과장이 아닌 케이스가 대부분입니다.