Azure 인증 방식
Azure 인증 방식
azure 인증 방식은 Azure Active Directory 크레덴셜을 사용해 Vault에 대한 인증을 허용해요. Azure를 신뢰할 수 있는 제3자로 취급하고 구성된 테넌트에 대해 Azure Active Directory가 서명한 JSON Web Token (JWT)을 기대해요.
이 방식은 시스템 할당(system-assigned) 및 사용자 할당(user-assigned) 관리 아이덴티티에 대한 인증을 지원해요. 이러한 리소스에 대한 자세한 내용은 Azure 리소스용 관리 아이덴티티를 참고하세요.
이 문서는 Azure 방식이 Vault의 /auth/azure 경로에 마운트되었다고 가정해요. 인증 방식을 어느 위치에서든 활성화할 수 있으므로 API 호출을 그에 따라 업데이트하세요.
출처: 문서
본문
토큰 검증 (Token validation)
퍼스트파티 Azure 리소스를 사용할 때 Vault는 리소스 그룹(resource_group_name), VM 이름(vm_name), VM 스케일 세트 이름(vmss_name) 파라미터를 토큰 클레임에 대해 검증해요. MSI 토큰을 생성하는 머신에 연결된 아이덴티티에 따라 연관된 클레임은 검증을 통과하려면 "xms_mirid" 또는 "xms_az_rid" 중 적어도 하나의 클레임을 포함해야 해요. 이 클레임은 사용자 지정 리소스나 api://로 시작하는 리소스 URI를 사용할 때 토큰에 존재하지 않아 사용할 수 없어요.
| 리소스 유형 | 시스템 할당 관리 아이덴티티 | "xms_mirid" | "xms_az_rid" |
|---|---|---|---|
| 퍼스트파티 Azure 리소스 | 활성화 | /subscriptions/{subscription-id}/resourcegroups/{resource-group-name}/providers/Microsoft.Compute/virtualMachines/{virtual-machine-name} |
없음 |
| 퍼스트파티 Azure 리소스 | 비활성화 | /subscriptions/{subscription-id}/resourcegroups/{resource-group-name}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{user-assigned-managed-identity} |
/subscriptions/{subscription-id}/resourcegroups/{resource-group-name}/providers/Microsoft.Compute/virtualMachines/{virtual-machine-name} |
사용자 지정 리소스 또는 API (api://{id}) |
활성화 | 없음 | 없음 |
사용자 지정 리소스 또는 API (api://{id}) |
비활성화 | 없음 | 없음 |
MSI 토큰을 요청하는 방법에 대한 자세한 내용은 Azure 관리 아이덴티티 REST 엔드포인트 참조와 Azure 리소스용 관리 아이덴티티 FAQ를 참고하세요.
Vault가 토큰 클레임을 평가할 때 검증 파라미터 값을 클레임 파라미터와 대조해 확인해요:
{resource-group-name}은resource_group_name과 일치해야 해요.{virtual-machine-name}은vm_name과 일치해야 하며,vmss_name이 제공되면{vmss_name}_{instance-id}와 일치해야 해요.
확인 중 하나라도 실패하면 로그인도 실패해요.
전제 조건 (Prerequisites)
Azure 인증 방식은 Azure API에 접근하려면 클라이언트 크레덴셜이 필요해요. 인증 방식을 구성하는 데 필요한 것:
- MSI 접근 토큰을 생성하는 리소스로 사용되는 구성된 Azure AD 애플리케이션.
- 특정 Azure Resource Manager 리소스에 대한 읽기 접근이 있는 클라이언트 크레덴셜(공유 시크릿). Azure AD 서비스 간 클라이언트 크레덴셜을 참고하세요.
Vault가 Azure에 호스팅되어 있으면 Vault는 공유 시크릿 대신 MSI를 사용해 Azure에 접근할 수 있어요. 접근 토큰을 획득하는 리소스에 관리 아이덴티티가 활성화되어 있어야 해요.
인증 중 인증 방식이 Azure API에 접근할 수 있으려면 Azure AD 애플리케이션에 다음 Azure 역할 할당이 부여되어야 해요.
역할 할당 (Role assignments)
참고: 역할 할당은 로그인 시 vm_name, vmss_name 또는 resource_id 파라미터가 사용될 때만 필요해요.
| Azure 환경 | 로그인 파라미터 | Azure API 권한 |
|---|---|---|
| Virtual Machine | vm_name |
Microsoft.Compute/virtualMachines/*/read |
| Virtual Machine Scale Set (Uniform Orchestration) | vmss_name |
Microsoft.Compute/virtualMachineScaleSets/*/read |
| Virtual Machine Scale Set (Flexible Orchestration) | vmss_name |
Microsoft.Compute/virtualMachineScaleSets/*/read + Microsoft.ManagedIdentity/userAssignedIdentities/*/read |
| Azure 리소스용 (관리 아이덴티티를 지원하는) 서비스 | resource_id |
JWT를 얻는 데 사용된 리소스에 대한 read |
API 권한 (API permissions)
Vault에 제공된 서비스 프린시펄에 Azure에서 루트 회전을 관리하기 위해 다음 API 권한이 할당되어야 해요:
| 권한 이름 | 유형 |
|---|---|
| Application.ReadWrite.All | Application |
인증 (Authentication)
CLI를 통해
기본 경로는 /auth/azure예요. 이 인증 방식이 다른 경로에 활성화되었다면 auth/my-path/login을 대신 지정하세요.
$ vault write auth/azure/login \
role="dev-role" \
jwt="eyJhbG...VCJ9..." \
subscription_id="12345-..." \
resource_group_name="test-group" \
vm_name="test-vm"
role과 jwt 파라미터는 필수예요. JWT는 모든 역할 바인딩 정보(vm_name, vmss_name, resource_id 제외)를 포함해야 해요. bound_service_principal_ids나 bound_group_ids를 넘어 추가 bound_* 파라미터를 사용할 때는 Azure API 호출이 이루어지며 subscription_id, resource_group_name, vm_name/vmss_name 모두 필요하고 인스턴스 메타데이터를 통해 얻을 수 있어요.
예:
$ vault write auth/azure/login role="dev-role" \
jwt="$(curl -s 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F' -H Metadata:true | jq -r '.access_token')" \
subscription_id=$(curl -s -H Metadata:true "http://169.254.169.254/metadata/instance?api-version=2017-08-01" | jq -r '.compute | .subscriptionId') \
resource_group_name=$(curl -s -H Metadata:true "http://169.254.169.254/metadata/instance?api-version=2017-08-01" | jq -r '.compute | .resourceGroupName') \
vm_name=$(curl -s -H Metadata:true "http://169.254.169.254/metadata/instance?api-version=2017-08-01" | jq -r '.compute | .name')
API를 통해
기본 엔드포인트는 auth/azure/login이에요. 이 인증 방식이 다른 경로에 활성화되었다면 azure 대신 그 값을 사용하세요.
$ curl \
--request POST \
--data '{"role": "dev-role", "jwt": "eyJhbG...VCJ9..."}' \
https://127.0.0.1:8200/v1/auth/azure/login
응답은 auth.client_token에 토큰을 포함해요:
{
"auth": {
"client_token": "f33f8c72-924e-11f8-cb43-ac59d697597c",
"accessor": "0e9e354a-520f-df04-6867-ee81cae3d42d",
"policies": ["default", "dev", "prod"],
"lease_duration": 2764800,
"renewable": true
}
}
구성 (Configuration)
인증 방식은 머신이 인증하기 전에 미리 구성되어야 해요. 이 단계들은 보통 운영자나 설정 관리 도구가 수행해요.
CLI를 통해
1. Vault에서 Azure 인증을 활성화합니다.
$ vault auth enable azure
2. Azure 인증 방식을 구성합니다.
$ vault write auth/azure/config \
tenant_id=7cd1f227-ca67-4fc6-a1a4-9888ea7f388c \
resource=https://management.azure.com/ \
client_id=dd794de4-4c6c-40b3-a930-d84cd32e9699 \
client_secret=IT3B2XfZvWnfB98s1cie8EMe7zWg483Xy8zY004=
전체 구성 옵션 목록은 API 문서를 참고하세요.
어떤 경우에는 Vault 구성에 민감한 계정 크레덴셜을 설정할 수 없어요. 예를 들어 조직이 모든 보안 크레덴셜이 수명이 짧거나 명시적으로 머신 아이덴티티에 연결되도록 요구할 수 있어요.
Vault에 관리 아이덴티티 보안 크레덴셜을 제공하려면 아래와 같이 Vault 플러그인 workload identity federation(WIF)을 사용하는 것을 권장해요.
3. 또는 플러그인 workload identity federation을 위해 오디언스 클레임 값과 Client, Tenant ID를 구성합니다.
$ vault write azure/config \
tenant_id=7cd1f227-ca67-4fc6-a1a4-9888ea7f388c \
client_id=dd794de4-4c6c-40b3-a930-d84cd32e9699 \
identity_token_audience=vault.example/v1/identity/oidc/plugins
Vault 아이덴티티 토큰 프로바이더는 플러그인 아이덴티티 토큰 JWT를 내부적으로 서명해요. Vault와 Azure 사이에 WIF를 통한 신뢰 관계가 존재하면 인증 방식은 Vault 아이덴티티 토큰을 연합 접근 토큰으로 교환할 수 있어요.
Vault와 Azure 사이 신뢰 관계를 구성하려면:
- Vault에 대한 [아이덴티티 토큰 발급자 백엔드]를 구성해야 합니다.
- Azure에는 Vault 플러그인 [아이덴티티 토큰 프로바이더]의 정규화되고 네트워크로 도달 가능한 발급자 URL에 대한 정보로 구성된 [연합 아이덴티티 크레덴셜]이 있어야 합니다.
Vault와 Azure 사이 신뢰 관계를 구축하면 Azure가 JWKS [공용 키]를 가져오고 플러그인 아이덴티티 토큰 서명을 검증할 수 있게 돼요.
4. 역할을 만듭니다.
$ vault write auth/azure/role/dev-role \
policies="prod,dev" \
bound_subscription_ids=6a1d5988-5917-4221-b224-904cd7e24a25 \
bound_resource_groups=vault \
bound_service_principal_ids=3cb88732-1356-4782-b671-4877166be01a
역할은 인증 유형/엔티티 집합과 Vault 정책 집합과 연결돼요. 역할은 인증 유형에 특정한 제약 조건뿐 아니라 생성된 인증 토큰에 대한 전반적인 제약 조건과 구성을 가진 상태로 구성돼요.
참고: 각 역할은 이 역할에 인증할 수 있는 Azure 아이덴티티(서비스 프린시펄 또는 그룹 구성원)를 제한하기 위해
bound_service_principal_ids또는bound_group_ids중 하나를 지정해야 해요.bound_group_ids를 사용하는 대체 예시:
$ vault write auth/azure/role/prod-role \
policies="prod" \
bound_subscription_ids=6a1d5988-5917-4221-b224-904cd7e24a25 \
bound_resource_groups=vault \
bound_group_ids=12345678-1234-1234-1234-123456789012
전체 역할 옵션 목록은 API 문서를 참고하세요.
API를 통해
1. Vault에서 Azure 인증을 활성화합니다.
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data '{"type": "azure"}' \
https://127.0.0.1:8200/v1/sys/auth/azure
2. Azure 인증 방식을 구성합니다.
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data '{"tenant_id": "...", "resource": "..."}' \
https://127.0.0.1:8200/v1/auth/azure/config
3. 역할을 만듭니다.
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data '{"policies": ["dev", "prod"], ...}' \
https://127.0.0.1:8200/v1/auth/azure/role/dev-role
루트 크레덴셜 회전 (Root credential rotation)
마운트는 마운트 내에 직접 구성된 루트 크레덴셜 키를 회전할 수 있어요. Vault 생성 키로 회전하면 키 값을 운영자가 접근할 수 없게 하고 오직 Vault만이 동적 및 정적 크레덴셜을 조작하기 위해 루트 사용자로 작동할 수 있게 보장해요.
vault write -f auth/azure/rotate-root
일정 기반 루트 크레덴셜 회전 (Schedule-based root credential rotation)
적절한 Vault Enterprise 라이선스가 필요해요.
rotation_schedule 필드를 사용해 Azure 인증 엔진의 루트 크레덴셜에 대한 일정 기반 자동 크레덴셜 회전을 구성해요. 예를 들어 다음 명령은 매주 토요일 자정(00:00)에 회전이 발생하도록 설정해요:
$ vault write auth/azure/config \
...
rotation_schedule="0 * * * SAT"
...
예정된 루트 크레덴셜 회전은 예정된 회전이 발생하도록 허용되는 rotation_window를 설정할 수도 있어요. Vault는 창이 만료되면 크레덴셜 회전 시도를 중지해요. 예를 들어 다음 명령은 Vault에게 1시간 범위 내에서 토요일 자정에 크레덴셜을 회전하라고 지시해요. Vault가 실패 등으로 1:00까지 크레덴셜을 회전할 수 없으면 다음 예정된 회전까지 회전 시도를 중지해요.
$ vault write auth/azure/config \
...
rotation_window="1h" \
rotation_schedule="0 * * * SAT"
...
disable_automated_rotation을 true로 설정해 루트 회전을 일시적으로 비활성화할 수 있어요. 이 필드를 설정하면 false로 재설정될 때까지 루트 크레덴셜의 모든 회전을 방지해요. rotation_period를 사용하면 disable_automated_rotation 설정 또한 크레덴셜 TTL을 재설정해요.
Azure 플러그인의 루트 크레덴셜 회전에 대한 자세한 내용은 루트 크레덴셜 회전 API 문서를 참고하세요.
Azure 관리 아이덴티티 (Azure managed identities)
Azure에는 관리 아이덴티티 유형이 두 가지 있어요: 시스템 할당과 사용자 할당. 시스템 할당 아이덴티티는 Azure의 모든 가상 머신에 대해 고유해요. Azure 인증을 사용하는 리소스가 자주 재생성되면 시스템 할당 아이덴티티를 사용하면 많은 Vault 엔티티가 생성될 수 있어요. 높은 일시적 워크로드 환경에서는 사용자 할당 아이덴티티를 권장해요.
제한 사항 (Limitations)
관리 아이덴티티에 대해 Azure AD가 반환하는 접근 토큰의 TTL은 24시간이며 구성할 수 없어요. 자세한 내용은 관리 아이덴티티 사용의 제한 사항을 참고하세요.
Azure 디버그 로그 (Azure debug logs)
Azure 인증 플러그인은 Azure API의 요청과 응답에 대한 추가 정보를 포함하는 디버그 로깅을 지원해요.
Azure 디버그 로그를 활성화하려면 Vault 서버에 다음 환경 변수를 설정하세요:
AZURE_SDK_GO_LOGGING=all
플러그인 Workload Identity Federation (WIF)
이 기능에는 Vault Enterprise(새 탭에서 열림)가 필요해요.
Azure 인증 방식은 플러그인 WIF 워크플로를 지원하고 플러그인 아이덴티티 토큰이라는 신원 소스를 가져요. 플러그인 아이덴티티 토큰은 Vault의 플러그인 아이덴티티 토큰 발급자가 내부적으로 서명한 JWT예요.
Vault와 Azure 사이에 workload identity federation을 통한 신뢰 관계가 구성되어 있으면 인증 방식은 아이덴티티 토큰을 작업을 수행하는 데 필요한 수명이 짧은 접근 토큰으로 교환할 수 있어요.
아이덴티티 토큰을 접근 토큰으로 교환하면 Azure 인증 방식이 민감한 클라이언트 크레덴셜에 대한 명시적 접근을 구성하지 않고 작동할 수 있어요.
인증 방식이 플러그인 WIF를 사용하도록 구성하려면:
- Vault openid-configuration 및 공용 JWKS API가 Azure에서 네트워크로 도달 가능한지 확인하세요. Vault API 노출을 제한해야 한다면 API 프록시나 게이트웨이를 권장해요.
- Vault와 신뢰 관계를 구축하려면 Azure의 전용 애플리케이션 등록에 연합 아이덴티티 크레덴셜을 구성하세요.
- 발급자 URL은
/.well-known/openid-configuration접미사를 제거한 [Vault 플러그인 아이덴티티 토큰 발급자]를 가리켜야 해요. 예:https://host:port/v1/identity/oidc/plugins. - 주체 식별자는 플러그인 아이덴티티 토큰이 발급한 고유
sub클레임과 일치해야 해요. 주체 식별자는plugin-identity:<NAMESPACE>:auth:<AZURE_MOUNT_ACCESSOR>형식이어야 해요. - 오디언스는 600자 미만이어야 해요. Azure의 기본값은
api://AzureADTokenExchange예요.
- 발급자 URL은
- 클라이언트 및 테넌트 ID와 OIDC 오디언스 값으로 Azure 인증 방식을 구성하세요.
$ vault write azure/config \
tenant_id=7cd1f227-ca67-4fc6-a1a4-9888ea7f388c \
client_id=dd794de4-4c6c-40b3-a930-d84cd32e9699 \
identity_token_audience=vault.example/v1/identity/oidc/plugins
이제 인증 방식이 구성 크레덴셜에 플러그인 WIF를 사용할 수 있어요. 기본적으로 WIF 크레덴셜은 1시간의 time-to-live를 가지며 만료 시 자동으로 갱신돼요.
플러그인 WIF와 연관된 필드에 대한 자세한 내용은 API 문서를 참고하세요.
알려진 문제 및 해결 방법 (Known issues and workarounds)
OIDC ID 토큰 오류 (OIDC ID token error)
사용자들은 Azure VM에 배포된 Vault 서버에서 Azure 인증을 사용하는 AKS 내부 워크로드가 다음 오류를 던진다고 보고했어요:
oidc: id token issued by a different provider, expected "https://sts.windows.net/TenantID/" got "https://login.microsoftonline.com/TenantId/v2.0"
오류는 vault-agent-init 컨테이너가 기본적으로 auth-type을 kubernetes로 사용하기 때문에 발생해요(Injector 주석 참조).
오류를 수정하려면:
vault-hashicorp-com-auth-type주석으로 Azure 인증 방식을 명시적으로 정의하세요:
vault.hashicorp.com/auth-type: 'azure'
vault.hashicorp.com/auth-config-resource주석으로 필요한resource필드를 전달하세요:
vault.hashicorp.com/auth-config-resource: "https://management.azure.com/"
예:
...
annotations:
vault.hashicorp.com/auth-type: 'azure'
vault.hashicorp.com/auth-config-resource: "https://management.azure.com/"
...
API
Azure Auth Plugin은 완전한 HTTP API를 제공해요. 자세한 내용은 API 문서를 참고하세요.
Terraform
Vault Terraform 프로바이더로 Azure 인증 리소스를 프로그래밍 방식으로 관리할 수 있어요. 자세한 내용은 Terraform Registry 문서를 참고하세요.
코드 예시 (Code example)
다음 예시는 Azure 인증 방식으로 Vault에 인증하는 것을 보여줘요. (Go/C# 코드)
Go:
package main
import (
"context"
"fmt"
vault "github.com/hashicorp/vault/api"
auth "github.com/hashicorp/vault/api/auth/azure"
)
// Fetches a key-value secret (kv-v2) after authenticating to Vault via Azure authentication.
// This example assumes you have a configured Azure AD Application.
func getSecretWithAzureAuth() (string, error) {
config := vault.DefaultConfig() // modify for more granular configuration
client, err := vault.NewClient(config)
if err != nil {
return "", fmt.Errorf("unable to initialize Vault client: %w", err)
}
azureAuth, err := auth.NewAzureAuth(
"dev-role-azure",
)
if err != nil {
return "", fmt.Errorf("unable to initialize Azure auth method: %w", err)
}
authInfo, err := client.Auth().Login(context.Background(), azureAuth)
if err != nil {
return "", fmt.Errorf("unable to login to Azure auth method: %w", err)
}
if authInfo == nil {
return "", fmt.Errorf("no auth info was returned after login")
}
// get secret from the default mount path for KV v2 in dev mode, "secret"
secret, err := client.KVv2("secret").Get(context.Background(), "creds")
if err != nil {
return "", fmt.Errorf("unable to read secret: %w", err)
}
// data map can contain more than one key-value pair,
// in this case we're just grabbing one of them
value, ok := secret.Data["password"].(string)
if !ok {
return "", fmt.Errorf("value type assertion failed: %T %#v", secret.Data["password"], secret.Data["password"])
}
return value, nil
}
C#:
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using Newtonsoft.Json;
using VaultSharp;
using VaultSharp.V1.AuthMethods;
using VaultSharp.V1.AuthMethods.Azure;
using VaultSharp.V1.Commons;
namespace Examples
{
public class AzureAuthExample
{
public class InstanceMetadata
{
public string name { get; set; }
public string resourceGroupName { get; set; }
public string subscriptionId { get; set; }
}
const string MetadataEndPoint = "http://169.254.169.254/metadata/instance?api-version=2017-08-01";
const string AccessTokenEndPoint = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/";
/// <summary>
/// Fetches a key-value secret (kv-v2) after authenticating to Vault via Azure authentication.
/// This example assumes you have a configured Azure AD Application.
/// </summary>
public string GetSecretWithAzureAuth()
{
string vaultAddr = Environment.GetEnvironmentVariable("VAULT_ADDR");
if(String.IsNullOrEmpty(vaultAddr))
{
throw new System.ArgumentNullException("Vault Address");
}
string roleName = Environment.GetEnvironmentVariable("VAULT_ROLE");
if(String.IsNullOrEmpty(roleName))
{
throw new System.ArgumentNullException("Vault Role Name");
}
string jwt = GetJWT();
InstanceMetadata metadata = GetMetadata();
IAuthMethodInfo authMethod = new AzureAuthMethodInfo(roleName: roleName, jwt: jwt, subscriptionId: metadata.subscriptionId, resourceGroupName: metadata.resourceGroupName, virtualMachineName: metadata.name);
var vaultClientSettings = new VaultClientSettings(vaultAddr, authMethod);
IVaultClient vaultClient = new VaultClient(vaultClientSettings);
// We can retrieve the secret from the VaultClient object
Secret<SecretData> kv2Secret = null;
kv2Secret = vaultClient.V1.Secrets.KeyValue.V2.ReadSecretAsync(path: "/creds").Result;
var password = kv2Secret.Data.Data["password"];
return password.ToString();
}
/// <summary>
/// Query Azure Resource Manage for metadata about the Azure instance
/// </summary>
private InstanceMetadata GetMetadata()
{
HttpWebRequest metadataRequest = (HttpWebRequest)WebRequest.Create(MetadataEndPoint);
metadataRequest.Headers["Metadata"] = "true";
metadataRequest.Method = "GET";
HttpWebResponse metadataResponse = (HttpWebResponse)metadataRequest.GetResponse();
StreamReader streamResponse = new StreamReader(metadataResponse.GetResponseStream());
string stringResponse = streamResponse.ReadToEnd();
var resultsDict = JsonConvert.DeserializeObject<Dictionary<string, InstanceMetadata>>(stringResponse);
return resultsDict["compute"];
}
/// <summary>
/// Query Azure Resource Manager (ARM) for an access token
/// </summary>
private string GetJWT()
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(AccessTokenEndPoint);
request.Headers["Metadata"] = "true";
request.Method = "GET";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
// Pipe response Stream to a StreamReader and extract access token
StreamReader streamResponse = new StreamReader(response.GetResponseStream());
string stringResponse = streamResponse.ReadToEnd();
var resultsDict = JsonConvert.DeserializeObject<Dictionary<string, string>>(stringResponse);
return resultsDict["access_token"];
}
}
}
더 알아보기 (Learn more)
- Azure 인증 방식 전체 API는 Azure 인증 API 문서를 참고하세요.
- 정적 크레덴셜을 위한 Vault 플러그인 WIF 설정은 플러그인 workload identity federation 문서를 참고하세요.
- Kubernetes 인젝터에서 Azure 인증 방식을 사용하려면 Injector 주석 참조를 참고하세요.