Published on

Azure OpenAI 401/403 - RBAC·엔드포인트 오류 해결

Authors

서버에서 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는 보통 두 가지 방식으로 인증합니다.

  1. API Key: 헤더 api-key: ...
  2. 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

  1. 배포명 대신 모델명을 넣음
  2. api-version 누락/오타
  3. 엔드포인트에 https:// 누락 또는 슬래시 중복
  4. 키 방식인데 Authorization: Bearer를 같이 보냄
  5. Managed Identity에 RBAC 역할을 안 줌
  6. 네트워크를 “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 인증오류 점검 가이드도 함께 참고하면 트러블슈팅 속도가 더 빨라집니다.