- Published on
Azure OpenAI 401/403 - RBAC·엔드포인트 오류 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버에서 Azure OpenAI를 붙였는데 갑자기 401 Unauthorized 또는 403 Forbidden이 터지면, 대부분은 (1) 인증 헤더/토큰 스코프 문제, (2) RBAC 역할 미부여 또는 전파 지연, (3) 엔드포인트·배포명·API 버전 불일치, (4) 네트워크/방화벽 제한 중 하나입니다. 문제는 에러 메시지가 비슷하게 보여도 원인이 전혀 다르다는 점입니다.
이 글은 “무엇부터 확인해야 가장 빨리 원인에 도달하는가”에 초점을 맞춰, Azure OpenAI 401/403을 증상 → 원인 후보 → 확인 명령/코드 → 해결 순으로 정리합니다.
> 참고: OpenAI 계열 API에서 401/403을 넓게 점검하는 체크리스트는 OpenAI Responses API 401 403 인증오류 점검 가이드도 함께 보면 좋습니다. (Azure OpenAI는 RBAC·리소스 엔드포인트 개념이 더 강합니다.)
1) 먼저 분류: 401 vs 403, 어디서 막혔나
401 Unauthorized에서 흔한 패턴
api-key가 없거나 틀림- AAD 토큰을 쓰는데 scope/audience가 틀림
- 엔드포인트 호스트가 Azure OpenAI가 아닌 다른 리소스를 가리킴(예: Cognitive Services 일반 엔드포인트)
403 Forbidden에서 흔한 패턴
- RBAC 역할이 없음(특히 AAD 토큰 사용 시)
- 리소스에 Public network access 차단 + 사설망/허용 IP 미설정
- 구독/리소스 정책(Policy) 또는 리전 제한
- (드물게) 배포/모델 접근 권한 문제
실무적으로는 “키 기반 호출이면 401이 많고, AAD 기반 호출이면 403(RBAC)이 많다”가 경험칙입니다. 하지만 네트워크 차단은 키 기반이어도 403/401처럼 보일 수 있으니 아래 체크를 따라가면 안전합니다.
2) Azure OpenAI 호출의 정석: URL 구조와 필수 파라미터
Azure OpenAI는 리소스(=엔드포인트) + 배포명(deployment) + api-version이 3종 세트입니다.
- 엔드포인트:
https://{resource-name}.openai.azure.com - 예시(Responses API 스타일):
POST /openai/responses?api-version=2024-xx-xx - 예시(Chat Completions):
POST /openai/deployments/{deployment}/chat/completions?api-version=2024-xx-xx
여기서 실수가 가장 잦은 지점은:
{resource-name}오타/다른 리소스명{deployment}에 모델명을 넣음(예:gpt-4o-mini) → 실제는 포털에서 만든 배포 이름(예:prod-gpt4o-mini)api-version누락 또는 미지원 버전
3) 원인 1: 엔드포인트/배포명/버전 불일치로 인한 401/403
3-1. 배포명과 모델명을 혼동했는지 확인
Azure OpenAI는 “모델”을 직접 호출하는 게 아니라 배포(Deployment) 를 호출합니다.
- 포털 → Azure OpenAI 리소스 → Deployments에서 확인
- 배포명은 팀 컨벤션에 따라
dev-,stg-,prod-등으로 다르게 쓰는 경우가 많습니다.
Python 예시 (Chat Completions)
import os
import requests
endpoint = os.environ["AZURE_OPENAI_ENDPOINT"] # https://xxx.openai.azure.com
api_key = os.environ["AZURE_OPENAI_API_KEY"]
deployment = os.environ["AZURE_OPENAI_DEPLOYMENT"] # 배포명
api_version = "2024-02-15-preview" # 예시, 실제 사용 버전은 환경에 맞게
url = f"{endpoint}/openai/deployments/{deployment}/chat/completions?api-version={api_version}"
resp = requests.post(
url,
headers={
"Content-Type": "application/json",
"api-key": api_key,
},
json={
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "ping"},
]
},
timeout=30,
)
print(resp.status_code)
print(resp.text)
체크 포인트
deployment값이 포털의 배포명과 정확히 일치하는가?- 엔드포인트가
openai.azure.com도메인이 맞는가? api-version이 현재 리소스/SDK와 호환되는가?
3-2. 엔드포인트가 “다른 리전/다른 리소스”를 가리키는지 확인
멀티 환경에서 .env나 Key Vault가 섞이면, dev 키로 prod 엔드포인트를 치는 식의 교차가 발생합니다. 이 경우 401/403이 애매하게 나타납니다.
- 엔드포인트와 키가 같은 Azure OpenAI 리소스에서 발급된 조합인지 확인
- 특히 IaC로 리소스를 여러 개 만들면 리소스명도 비슷해집니다.
4) 원인 2: 인증 방식 혼용(키 vs AAD 토큰)으로 인한 401
Azure OpenAI는 보통 두 가지 방식으로 인증합니다.
- API Key: 헤더
api-key: ... - Entra ID(AAD) 토큰: 헤더
Authorization: Bearer ...
여기서 자주 하는 실수:
- AAD 토큰을 쓰면서도
api-key헤더를 보내거나(또는 반대) - AAD 토큰의 scope/audience가 Azure OpenAI가 아닌 다른 리소스로 발급됨
AAD 토큰 스코프의 핵심
일반적으로 Cognitive Services 계열은 https://cognitiveservices.azure.com/.default 스코프를 사용합니다. 토큰이 다른 audience로 발급되면 401이 납니다.
Azure Identity + OpenAI SDK (Python) 예시
from azure.identity import DefaultAzureCredential
from openai import AzureOpenAI
credential = DefaultAzureCredential()
client = AzureOpenAI(
azure_endpoint="https://<resource-name>.openai.azure.com",
api_version="2024-02-15-preview",
azure_ad_token_provider=lambda: credential.get_token(
"https://cognitiveservices.azure.com/.default"
).token,
)
resp = client.chat.completions.create(
model="<deployment-name>",
messages=[{"role": "user", "content": "hello"}],
)
print(resp.choices[0].message.content)
체크 포인트
DefaultAzureCredential이 어떤 자격 증명으로 로그인했는지(로컬은 Azure CLI, 서버는 Managed Identity 등)- 토큰 스코프가
cognitiveservices.azure.com/.default인지 - 키 방식과 토큰 방식을 한 요청에서 섞지 않았는지
5) 원인 3: RBAC 미설정/전파 지연으로 인한 403
AAD 토큰(Entra ID) 기반으로 호출할 때 403이 뜨면, 거의 항상 RBAC 역할이 없거나 범위(scope)가 틀린 경우입니다.
5-1. 필요한 역할: Cognitive Services OpenAI User
일반적으로 호출자(사용자/서비스 프린시플/Managed Identity)에게 아래 역할이 필요합니다.
Cognitive Services OpenAI User(데이터 플레인 호출 권한)
부여 범위는 보통 다음 중 하나입니다.
- Azure OpenAI 리소스 단위(권장)
- 리소스 그룹 단위
- 구독 단위(관리상 필요할 때만)
5-2. Managed Identity를 쓰는 서버에서 특히 많이 터지는 케이스
App Service, Functions, VM, AKS 등에서 Managed Identity로 토큰을 받아 호출하는데 403이 나는 경우:
- 해당 Managed Identity에 역할을 부여하지 않음
- 역할을 부여했지만 전파(Propagation) 지연으로 수 분~수십 분 동안 실패
Azure CLI로 역할 부여(예시)
# 리소스 ID 확인
az resource show \
--name <openai-resource-name> \
--resource-group <rg> \
--resource-type "Microsoft.CognitiveServices/accounts" \
--query id -o tsv
# (예) 시스템 할당 Managed Identity의 principalId는 리소스별로 다릅니다.
# App Service라면:
az webapp identity show \
--name <app-name> \
--resource-group <rg> \
--query principalId -o tsv
# 역할 부여
az role assignment create \
--assignee <principalId-or-objectId> \
--role "Cognitive Services OpenAI User" \
--scope <openai-resource-id>
역할 할당 확인
az role assignment list \
--assignee <principalId-or-objectId> \
--scope <openai-resource-id> \
-o table
실전 팁
- 403이 났을 때 “내 계정”이 아니라 “실제로 토큰을 발급받는 주체”가 누구인지부터 확정하세요.
- 로컬: Azure CLI 로그인 계정
- CI: Service Principal
- 런타임: Managed Identity
6) 원인 4: 네트워크/방화벽 설정(Public access, Private Endpoint)으로 인한 403
Azure OpenAI 리소스는 네트워크 설정에 따라 공용 인터넷에서의 접근을 차단할 수 있습니다.
6-1. 대표 증상
- 로컬에서는 되는데 회사 서버/CI에서는 403
- 또는 그 반대로, 특정 VNet/사설망에서만 되고 외부는 실패
6-2. 확인할 설정
포털에서 Azure OpenAI 리소스 → Networking에서 확인합니다.
- Public network access: Enabled/Disabled
- Selected networks(허용 IP/서브넷)
- Private endpoint 구성 여부
6-3. 빠른 판별: 같은 자격 증명으로 다른 네트워크에서 호출
- 같은 API Key/AAD 토큰으로
- 집/모바일 핫스팟에서는 성공
- 사내 NAT/특정 서버에서는 실패
이면 네트워크 차단 가능성이 큽니다.
7) “진짜 원인”을 빨리 잡는 로그/진단 체크리스트
7-1. 요청 단위로 반드시 남길 것
- 최종 URL(단, 키/토큰은 마스킹)
- 사용한 인증 방식(키 vs AAD)
- 배포명
- api-version
- 응답 헤더의
x-ms-request-id또는 코릴레이션 가능한 값
7-2. 흔한 실수 Top 6
- 배포명 대신 모델명을 넣음
api-version누락/오타- 엔드포인트에
https://누락 또는 슬래시 중복 - 키 방식인데
Authorization: Bearer를 같이 보냄 - Managed Identity에 RBAC 역할을 안 줌
- 네트워크를 “Selected networks”로 잠가놓고 CI IP를 허용 안 함
8) 재현 가능한 테스트: curl로 최소 요청 만들어보기
애플리케이션 코드(프록시, 미들웨어, 리트라이, 헤더 변조)가 개입되면 원인 파악이 늦어집니다. 먼저 curl로 최소 요청을 재현하세요.
API Key 기반 (Chat Completions 예시)
export ENDPOINT="https://<resource-name>.openai.azure.com"
export DEPLOYMENT="<deployment-name>"
export API_VERSION="2024-02-15-preview"
export API_KEY="<your-key>"
curl -i \
-H "Content-Type: application/json" \
-H "api-key: ${API_KEY}" \
-X POST "${ENDPOINT}/openai/deployments/${DEPLOYMENT}/chat/completions?api-version=${API_VERSION}" \
-d '{"messages":[{"role":"user","content":"ping"}]}'
- 여기서 401이면 키/엔드포인트 조합을 의심
- 403이면 네트워크 제한 또는 정책/RBAC(키 기반이어도 리소스 정책 가능)를 의심
9) 운영 환경에서의 권장 구성(실패율 줄이기)
9-1. 인증 전략을 환경별로 분리
- 로컬 개발: API Key(간단) 또는 개발자 AAD
- 운영: Managed Identity + RBAC(키 유출 위험 감소)
9-2. 배포명 컨벤션과 구성 검증
- 배포명은
env-purpose-model형태로 고정 (prod-chat-gpt4o-mini) - 앱 시작 시 “배포 존재 여부”를 1회 헬스체크로 검증(실패 시 즉시 알람)
9-3. 변경 작업 후 전파 시간 고려
- RBAC 역할 부여/변경 후 즉시 테스트하면 403이 계속 날 수 있습니다.
- 파이프라인에서 “권한 설정 → 즉시 호출 테스트”를 붙이면 간헐 실패가 생깁니다.
- 수 분 대기 또는 재시도(backoff) 전략을 두세요.
10) 결론: 401/403을 ‘권한’으로만 보지 말고, 4축으로 쪼개라
Azure OpenAI의 401/403은 단순히 “키가 틀렸나?” 수준이 아니라,
- 엔드포인트/배포명/api-version
- 인증 방식(키 vs AAD)과 토큰 스코프
- RBAC 역할과 범위
- 네트워크 제한(공용 차단/사설망)
이 4축에서 10분 안에 결론을 내릴 수 있습니다. 특히 AAD 기반 운영을 한다면 Cognitive Services OpenAI User 역할과 실제 호출 주체(Managed Identity/Service Principal)를 먼저 확정하는 것만으로도 403의 절반은 바로 해결됩니다.
추가로, 인증/권한 오류를 더 넓은 관점에서 점검하는 체크리스트가 필요하면 OpenAI Responses API 401 403 인증오류 점검 가이드도 함께 참고하면 트러블슈팅 속도가 더 빨라집니다.