AppRole 인증 사용하기
AppRole 인증 사용하기
approle 인증 방식은 머신 또는 앱이 Vault로 정의된 *역할(roles)*로 인증할 수 있게 해요. AppRole의 개방형 설계는 많은 앱을 처리할 수 있는 다양한 워크플로와 구성을 가능하게 해요. 이 인증 방식은 자동화된 워크플로(머신과 서비스)를 대상으로 하며, 사람 운영자에게는 덜 유용해요. AppRole 인증 방식에는 배치(batch) 토큰을 사용할 것을 권장해요.
"AppRole"은 그 정책을 가진 토큰을 받기 위해 충족되어야 하는 Vault 정책 집합과 로그인 제약을 나타내요. 범위는 원하는 대로 좁거나 넓게 잡을 수 있어요. AppRole은 특정 머신 또는 그 머신의 특정 사용자, 또는 머신에 걸쳐 퍼진 서비스를 위해 만들 수 있어요. 성공적인 로그인에 필요한 크레덴셜은 크레덴셜과 연결된 AppRole에 설정된 제약에 따라 달라져요.
출처: 문서
본문
인증 (Authentication)
CLI를 통해
기본 경로는 /approle이에요. 이 인증 방식이 다른 경로에 활성화되었다면 auth/my-path/login을 지정하세요.
$ vault write auth/approle/login \
role_id=db02de05-fa39-4855-059b-67221c5c2f63 \
secret_id=6a174c20-f6de-a53c-74d2-6018fcceff64
Key Value
--- -----
token 65b74ffd-842c-fd43-1386-f7d7006e520a
token_accessor 3c29bc22-5c72-11a6-f778-2bc8f48cea0e
token_duration 20m0s
token_renewable true
token_policies [default]
API를 통해
기본 엔드포인트는 auth/approle/login이에요. 이 인증 방식이 다른 경로에 활성화되었다면 approle 대신 그 값을 사용하세요.
$ curl \
--request POST \
--data '{"role_id":"988a9df-...","secret_id":"37b74931..."}' \
http://127.0.0.1:8200/v1/auth/approle/login
응답은 auth.client_token에 토큰을 포함해요.
{
"auth": {
"renewable": true,
"lease_duration": 2764800,
"metadata": {},
"policies": ["default", "dev-policy", "test-policy"],
"accessor": "5d7fb475-07cb-4060-c2de-1ca3fcbf0c56",
"client_token": "98a4c7ab-b1fe-361b-ba0b-e307aacfd587"
}
}
애플리케이션 통합: AppRole 인증 방식으로 Vault에 인증하는 방법을 보여 주는 코드 조각은 코드 예시 섹션을 참고하세요.
구성 (Configuration)
인증 방식은 사용자나 머신이 인증하기 전에 미리 구성되어야 해요. 이 단계들은 보통 운영자나 설정 관리 도구가 수행해요.
CLI를 통해
1. AppRole 인증 방식을 활성화합니다.
$ vault auth enable approle
2. 이름 있는 역할을 만듭니다.
$ vault write auth/approle/role/my-role \
token_type=batch \
secret_id_ttl=10m \
token_ttl=20m \
token_max_ttl=30m \
secret_id_num_uses=40
참고: approle이 발급한 토큰이 자식 토큰을 만들 수 있는 능력이 필요하면
token_num_uses를 0으로 설정해야 해요.
구성 옵션의 전체 목록은 API 문서를 참고하세요.
3. AppRole의 RoleID를 가져옵니다.
$ vault read auth/approle/role/my-role/role-id
role_id db02de05-fa39-4855-059b-67221c5c2f63
4. AppRole에 대해 발급된 SecretID를 얻습니다.
$ vault write -f auth/approle/role/my-role/secret-id
secret_id 6a174c20-f6de-a53c-74d2-6018fcceff64
secret_id_accessor c454f7e5-996e-7230-6074-6ef26b7bcf86
secret_id_ttl 10m
secret_id_num_uses 40
API를 통해
1. AppRole 인증 방식을 활성화합니다.
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data '{"type": "approle"}' \
http://127.0.0.1:8200/v1/sys/auth/approle
2. 원하는 정책 집합으로 AppRole을 만듭니다.
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data '{"policies": "dev-policy,test-policy", "token_type": "batch"}' \
http://127.0.0.1:8200/v1/auth/approle/role/my-role
3. 역할의 식별자를 가져옵니다.
$ curl \
--header "X-Vault-Token: ..." \
http://127.0.0.1:8200/v1/auth/approle/role/my-role/role-id
응답은 다음과 같아요.
{
"data": {
"role_id": "988a9dfd-ea69-4a53-6cb6-9d6b86474bba"
}
}
4. 역할 아래에 새 시크릿 식별자를 만듭니다.
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
http://127.0.0.1:8200/v1/auth/approle/role/my-role/secret-id
응답은 다음과 같아요.
{
"data": {
"secret_id_accessor": "45946873-1d96-a9d4-678c-9229f74386a5",
"secret_id": "37b74931-c4cd-d49a-9246-ccc62d682a25",
"secret_id_ttl": 600,
"secret_id_num_uses": 40
}
}
크레덴셜/제약 (Credentials/Constraints)
RoleID
RoleID는 다른 크레덴셜이 평가되는 AppRole을 선택하는 식별자예요. 이 인증 방식의 로그인 엔드포인트에 인증할 때 RoleID는 항상(role_id를 통해) 필수 인자예요. 기본적으로 RoleID는 고유 UUID이며, 이는 후보 크레덴셜 정보에 대한 2차 비밀로 작용할 수 있게 해요. 그러나 클라이언트가 인트로스펙션한 정보(예: 클라이언트의 도메인 이름)와 일치하도록 특정 값으로 설정할 수 있어요.
SecretID
SecretID는 기본적으로 모든 로그인(secret_id를 통해)에 요구되는 크레덴셜이며 항상 비밀인 것이 의도돼요. (고급 사용의 경우, SecretID 요구는 AppRole의 bind_secret_id 파라미터로 비활성화할 수 있어 RoleID만 아는 머신 또는 다른 설정된 제약을 일치시키는 머신이 토큰을 가져올 수 있게 해요). SecretID는 역할 자체가 128비트 순수 무작위 UUID를 생성해(푸시 모드 Pull) 만들거나, 특정 사용자 지정 값을(Push 모드) 만들어 AppRole에 대해 생성할 수 있어요. 토큰과 유사하게 SecretID는 사용 한도, TTL, 만료 같은 속성을 가져요.
Pull 및 Push SecretID 모드
로그인에 사용되는 SecretID를 AppRole에서 가져오면 Pull 모드로 작동하는 것이에요. 클라이언트가 AppRole에 대해 "custom" SecretID를 설정하면 Push 모드라고 해요. Push 모드는 폐기된 App-ID 인증 방식의 동작을 모방해요. 그러나 대부분의 경우 Pull 모드가 더 나은 접근이에요. 그 이유는 Push 모드는 항목을 만들기 위해 다른 시스템이 클라이언트 크레덴셜의 전체 집합(RoleID와 SecretID)을 알아야 하지만, 이 크레덴셜이 다른 경로로 배포되더라도 그렇기 때문이에요. 반면 Pull 모드는 RoleID를 클라이언트에 배포하려면 알아야 하지만, 응답 래핑(Response Wrapping)을 사용해 SecretID는 최종 인증 클라이언트를 제외한 모든 당사자에게 기밀로 유지할 수 있어요.
Push 모드는 App-ID 워크플로 호환성을 위해 제공되며, 일부 특정 경우에는 더 좋지만 대부분의 경우 Pull 모드가 더 안전하고 선호되어야 해요.
추가 제약 (Further constraints)
role_id는 로그인 엔드포인트의 필수 크레덴셜이에요. role_id가 가리키는 AppRole에는 제약이 설정돼 있을 거예요. 이는 로그인의 다른 required 크레덴셜을 결정해요. bind_secret_id 제약은 로그인 엔드포인트에 secret_id를 제시하도록 요구해요. 앞으로 이 인증 방식은 다양한 앱 집합을 지원하도록 더 많은 제약 파라미터를 지원할 수 있어요. 일부 제약은 크레덴셜을 요구하지 않지만 로그인에 대한 제약을 여전히 시행해요. 예를 들어 secret_id_bound_cidrs는 AppRole에 구성된 CIDR 블록에 속한 IP 주소에서 오는 로그인만 허용해요.
튜토리얼 (Tutorial)
더 알아보려면 다음 튜토리얼을 참고하세요.
- AppRole Pull 인증 튜토리얼 — AppRole 인증 방식으로 머신 또는 앱에 대한 토큰을 생성하는 방법.
- AppRole 사용 모범 사례 — 대상 Vault 클라이언트에 AppRole 크레덴셜을 배포하기 위한 권장 사항 이해.
사용자 잠금 (User lockout)
사용자가 짧은 시간에 여러 번 잘못된 크레덴셜을 제공하면 Vault는 한동안 크레덴셜 검증을 멈추고 즉시 permission denied 오류를 반환해요. 이 동작을 "사용자 잠금(user lockout)"이라 해요. 사용자가 잠기는 시간을 "잠금 기간(lockout duration)"이라 해요. 잠금 기간이 지나면 사용자가 로그인할 수 있어요. 사용자가 잠기는 실패 로그인 시도 횟수를 "잠금 임계값(lockout threshold)"이라 해요. 잠금 임계값 카운터는 몇 분간 로그인 시도가 없거나, 성공적인 로그인 시도 후 0으로 재설정돼요. 로그인 시도가 없을 때 카운터가 0으로 재설정되는 기간을 "잠금 카운터 재설정(lockout counter reset)"이라 해요. 이는 자동화된 요청과 표적 공격 모두, 즉 사용자 기반 비밀번호 추측 공격과 자동화 공격을 모두 무력화할 수 있어요.
참고: 사용자 잠금은 요청 처리 초기에 발생하며 유효하고 존재하는 기존 사용자 계정 이름의 유효성 또는 존재에 대한 정보를 누출할 수 있어요.
사용자 잠금 기능은 기본적으로 활성화돼 있어요. "lockout threshold"의 기본값은 5회 시도, "lockout duration"은 15분, "lockout counter reset"은 15분이에요.
사용자 잠금 기능은 다음과 같이 비활성화할 수 있어요.
VAULT_DISABLE_USER_LOCKOUT환경 변수로 전역적으로 비활성화할 수 있어요.- 구성 파일의
user_lockout스탠자 안에서disable_lockout파라미터로 지원되는 모든 인증 방식(ldap, userpass, approle) 또는 특정 지원 인증 방식에 대해 비활성화할 수 있어요. 자세한 내용은 user lockout 구성을 참고하세요. - 특정 인증 마운트에 대해 "auth tune"으로 비활성화할 수 있어요. 자세한 내용은 auth tune 명령 또는 auth tune API를 참고하세요.
참고: 이 기능은 Vault 1.13부터 제공되며 userpass, ldap, approle 인증 방식에서만 지원돼요.
API
AppRole 인증 방식은 완전한 HTTP API를 제공해요. 자세한 내용은 AppRole API 문서를 참고해 주세요.
Terraform
Vault Terraform 프로바이더로 AppRole 인증 리소스를 프로그래밍 방식으로 관리할 수 있어요. 자세한 내용은 Terraform Registry 문서를 참고하세요.
코드 예시 (Code example)
다음 예시는 응답 래핑을 사용한 AppRole 인증을 보여 줘요.
Go:
package main
import (
"context"
"fmt"
"os"
vault "github.com/hashicorp/vault/api"
auth "github.com/hashicorp/vault/api/auth/approle"
)
// Fetches a key-value secret (kv-v2) after authenticating via AppRole.
func getSecretWithAppRole() (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)
}
// A combination of a Role ID and Secret ID is required to log in to Vault
// with an AppRole.
// First, let's get the role ID given to us by our Vault administrator.
roleID := os.Getenv("APPROLE_ROLE_ID")
if roleID == "" {
return "", fmt.Errorf("no role ID was provided in APPROLE_ROLE_ID env var")
}
// The Secret ID is a value that needs to be protected, so instead of the
// app having knowledge of the secret ID directly, we have a trusted orchestrator (https://learn.hashicorp.com/tutorials/vault/secure-introduction?in=vault/app-integration#trusted-orchestrator)
// give the app access to a short-lived response-wrapping token (https://developer.hashicorp.com/vault/docs/concepts/response-wrapping).
// Read more at: https://learn.hashicorp.com/tutorials/vault/approle-best-practices?in=vault/auth-methods#secretid-delivery-best-practices
secretID := &auth.SecretID{FromFile: "path/to/wrapping-token"}
appRoleAuth, err := auth.NewAppRoleAuth(
roleID,
secretID,
auth.WithWrappingToken(), // Only required if the secret ID is response-wrapped.
)
if err != nil {
return "", fmt.Errorf("unable to initialize AppRole auth method: %w", err)
}
authInfo, err := client.Auth().Login(context.Background(), appRoleAuth)
if err != nil {
return "", fmt.Errorf("unable to login to AppRole 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 VaultSharp;
using VaultSharp.V1.AuthMethods;
using VaultSharp.V1.AuthMethods.AppRole;
using VaultSharp.V1.AuthMethods.Token;
using VaultSharp.V1.Commons;
namespace Examples
{
public class ApproleAuthExample
{
const string DefaultTokenPath = "../../../path/to/wrapping-token";
/// <summary>
/// Fetches a key-value secret (kv-v2) after authenticating to Vault via AppRole authentication
/// </summary>
public string GetSecretWithAppRole()
{
// A combination of a Role ID and Secret ID is required to log in to Vault with an AppRole.
// The Secret ID is a value that needs to be protected, so instead of the app having knowledge of the secret ID directly,
// we have a trusted orchestrator (https://developer.hashicorp.com/vault/tutorials/app-integration/secure-introduction?in=vault%2Fapp-integration#trusted-orchestrator)
// give the app access to a short-lived response-wrapping token (https://developer.hashicorp.com/vault/docs/concepts/response-wrapping).
// Read more at: https://learn.hashicorp.com/tutorials/vault/approle-best-practices?in=vault/auth-methods#secretid-delivery-best-practices
var vaultAddr = Environment.GetEnvironmentVariable("VAULT_ADDR");
if(String.IsNullOrEmpty(vaultAddr))
{
throw new System.ArgumentNullException("Vault Address");
}
var roleId = Environment.GetEnvironmentVariable("APPROLE_ROLE_ID");
if(String.IsNullOrEmpty(roleId))
{
throw new System.ArgumentNullException("AppRole Role Id");
}
// Get the path to wrapping token or fall back on default path
string pathToToken = !String.IsNullOrEmpty(Environment.GetEnvironmentVariable("WRAPPING_TOKEN_PATH")) ? Environment.GetEnvironmentVariable("WRAPPING_TOKEN_PATH") : DefaultTokenPath;
string wrappingToken = File.ReadAllText(pathToToken); // placed here by a trusted orchestrator
// We need to create two VaultClient objects for authenticating via AppRole. The first is for
// using the unwrap utility. We need to initialize the client with the wrapping token.
IAuthMethodInfo wrappedTokenAuthMethod = new TokenAuthMethodInfo(wrappingToken);
var vaultClientSettingsForUnwrapping = new VaultClientSettings(vaultAddr, wrappedTokenAuthMethod);
IVaultClient vaultClientForUnwrapping = new VaultClient(vaultClientSettingsForUnwrapping);
// We pass null here instead of the wrapping token to avoid depleting its single usage
// given that we already initialized our client with the wrapping token
Secret<Dictionary<string, object>> secretIdData = vaultClientForUnwrapping.V1.System
.UnwrapWrappedResponseDataAsync<Dictionary<string, object>>(null).Result;
var secretId = secretIdData.Data["secret_id"]; // Grab the secret_id
// We create a second VaultClient and initialize it with the AppRole auth method and our new credentials.
IAuthMethodInfo authMethod = new AppRoleAuthMethodInfo(roleId, secretId.ToString());
var vaultClientSettings = new VaultClientSettings(vaultAddr, authMethod);
IVaultClient vaultClient = new VaultClient(vaultClientSettings);
// We can retrieve the secret from VaultClient
Secret<SecretData> kv2Secret = null;
kv2Secret = vaultClient.V1.Secrets.KeyValue.V2.ReadSecretAsync(path: "/creds").Result;
var password = kv2Secret.Data.Data["password"];
return password.ToString();
}
}
}
더 알아보기 (Learn more)
- AppRole 인증 방식 전체 API는 AppRole API 문서를 참고하세요.
- AppRole 크레덴셜 배포 모범 사례는 AppRole best practices 튜토리얼을 참고하세요.