SPIRE로 SPIFFE 인증 메서드를 사용해 Vault에 인증하기

SPIRE로 SPIFFE 인증 메서드를 사용해 Vault에 인증하기

SPIRE(SPIFFE Runtime Environment, 애플리케이션 서비스 간 통신을 식별하고 보호하기 위한 전체 SPIFFE 프레임워크와 표준 집합의 프로덕션 준비 구현)를 사용해 SPIFFE 인증 메서드로 Vault에 인증해 봐요.

출처: 문서

본문

SPIFFE에 익숙하지 않다면 Vault에서 SPIFFE 인증이 동작하는 방식에 대한 정보를 SPIFFE 개요에서 확인하세요.

이 단계를 그대로 따라 하세요. 프로덕션 호스트에서 실행하면 해당 호스트의 구성이 변경돼요. 일반적인 구성 지침은 설정 가이드를 참조하세요.

요구 사항

  • 활성화된 Vault Enterprise 라이선스.
  • Vault Enterprise 설치.
  • SPIRE 서버 설치.
  • SPIRE 에이전트 설치.
  • 이 워크스루를 실행할 Linux 서버 또는 인스턴스.

Vault와 SPIRE 설정

이 섹션에서는 SPIRE를 사용해 워크로드가 Vault에 인증하도록 하는 최소 구성 요소를 살펴봐요. 이 섹션은 SPIRE 설치나 강화(hardening)를 다루지 않아요. 자세한 내용은 SPIRE 문서를 참조하세요.

SPIRE 서버 설정

SPIRE 서버를 설정해요. SPIRE 에이전트가 서버에 연결해 워크로드에 SVID를 발급해요.

  • SPIRE 서버 작업 디렉터리를 만드세요. 이 단계는 이 문서에 표시된 최소 설정에 필요해요. 일반적으로 SPIRE 서버와 SPIRE 에이전트는 다른 노드에서 실행돼요. 여기서는 충돌을 피하기 위해 구성과 데이터 디렉터리를 분리하고 있어요.
$ mkdir ~/spire-server && cd ~/spire-server
  • 최소 SPIRE 서버 구성을 만드세요. 경고: bind_address = "0.0.0.0"와 페더레이션 주소 "0.0.0.0"은 모든 네트워크 인터페이스에 바인딩돼요. 이는 SPIRE가 워크로드와 같은 머신에서 실행되는 로컬 랩 환경에서 허용돼요. 프로덕션에서는 두 값을 SPIRE 서버가 수신해야 하는 특정 IP 주소나 호스트 이름으로 바꾸세요. 참고: 번들 엔드포인트는 항상 TLS를 사용해요 — 평문 HTTP 모드는 없어요. https_spiffe 프로파일은 SPIRE 서버 자체의 SVID를 사용해 엔드포인트를 인증하므로 외부 CA의 인증서나 실제 해석 가능한 도메인이 필요 없어요. 그래서 여기서 사용하는 거예요. 대안인 https_web 프로파일은 외부 발급 인증서(또는 ACME 자동화)가 필요하며, Vault가 공개 인터넷을 통해 도달하는 엔드포인트와 페더레이션할 때 더 적합해요. 두 프로파일에 대한 추가 정보는 SPIRE의 페더레이션 문서를 참조하세요.
$ tee spire-server.conf <<EOF
# spire-server.conf
server {
  trust_domain = "example.org"
  bind_address = "0.0.0.0"
  bind_port    = "8081"
  data_dir     = "./.data"

  federation {
    bundle_endpoint {
      address = "0.0.0.0"
      port    = 8443

      profile "https_spiffe" {}
    }
  }
}

plugins {
  DataStore "sql" {
    plugin_data {
      database_type     = "sqlite3"
      connection_string = "./.data/datastore.sqlite3"
    }
  }
  NodeAttestor "join_token" {
    plugin_data {}
  }
  KeyManager "disk" {
    plugin_data {
      keys_path = "./.data/keys.json"
    }
  }
}
EOF
  • SPIRE 서버를 시작하세요.
$ spire-server run -config ./spire-server.conf &
  • SPIRE 에이전트가 연결할 1회용 조인 토큰을 만드세요. 이 예시는 조인 토큰 노드 증명을 사용해요. 조인 토큰은 Vault 래핑 토큰과 유사하게 1회용이에요. 정확히 한 에이전트를 부트스트랩한 뒤 더 이상 유효하지 않아요. 프로덕션 배포는 공유 조인 토큰 대신 플랫폼별 노드 증명자(aws_iid, gcp_iit, k8s_psat, x509pop 또는 다른 지원 증명자)를 사용해야 해요.
$ JOIN_TOKEN=$(spire-server token generate -spiffeID spiffe://example.org/agent/example-node | sed 's/^Token: //')

SPIRE 에이전트 설정

실제로 워크로드에 SVID를 발급하는 것은 에이전트예요. SPIRE 서버와 등록 엔트리만으로는 인증이 활성화되지 않아요.

  • SPIRE 에이전트 작업 디렉터리를 만드세요.
$ mkdir ~/spire-agent && cd ~/spire-agent
  • SPIRE 서버에서 트러스트 번들을 가져와 파일로 저장하세요. SPIRE 에이전트는 에이전트를 시작할 때 이 파일이 필요해요.
$ spire-server bundle show > initial_bundle.crt
  • 최소 SPIRE 에이전트 구성을 만드세요. trust_bundle_path는 에이전트의 첫 시작 전에 서버의 초기 트러스트 번들을 노드에 수동으로 복사해야 해요. 프로덕션 환경에서는 프로비저닝 시점에 기존 구성 관리 및 시크릿 주입 워크플로를 통해 이 파일을 프로비저닝하세요. 또는 trust_bundle_url로 번들을 가져올 수도 있어요. 두 옵션 모두 SPIRE의 에이전트 구성 레퍼런스를 참조하세요.
$ tee spire-agent.conf <<EOF
# agent.conf
agent {
  data_dir          = "./.data"
  server_address    = "127.0.0.1"
  server_port       = "8081"
  socket_path       = "/tmp/spire-agent/public/api.sock"
  trust_bundle_path = "./initial_bundle.crt"
  trust_domain      = "example.org"
}

plugins {
  NodeAttestor "join_token" {
    plugin_data {}
  }
  KeyManager "disk" {
    plugin_data {
      directory = "./.data"
    }
  }
  WorkloadAttestor "unix" {
    plugin_data {}
  }
}
EOF
  • 서버 구성 섹션의 조인 토큰으로 에이전트를 시작하세요.
$ spire-agent run -config spire-agent.conf -joinToken $JOIN_TOKEN &

워크로드 등록

  • SPIRE 서버에 워크로드를 등록하세요. 워크로드 등록 시 사용하는 셀렉터는 에이전트 구성과 일치해야 해요. 에이전트 구성의 WorkloadAttestor unix 플러그인은 unix:uid:... 셀렉터와 일치해요. 워크로드를 다른 방식(쿠버네티스, Docker 또는 다른 지원 플랫폼)으로 증명한다면 둘을 함께 바꾸세요. 예시 명령은 런타임에 자신의 UID를 잡기 위해 $(id -u)를 사용해요. 이는 엔트리를 등록하고 나중에 SVID를 가져오는 사람이 모두 본인일 때만 동작해요. 운영자가 별도의 서비스 계정용 엔트리를 등록하는 실제 배포에서는 그 계정의 UID를 명시적으로 대체하세요. $(id -u)는 워크로드의 UID가 아니라 운영자의 UID를 잡아요.
$ spire-server entry create \
   -parentID spiffe://example.org/agent/example-node \
   -spiffeID  spiffe://example.org/ns/prod/sa/payment-service \
   -selector  unix:uid:$(id -u)
  • 워크로드 SVID를 요청하세요. X.509: 이렇게 하면 ~/spire-agent/svid.0.pem~/spire-agent/svid.0.key가 작성돼요. JWT: JWT-SVID에는 -write 플래그가 없어요. 토큰이 stdout으로 출력돼요. SVID를 디스크에 수동으로 가져와 작성하는 것은 설정이 작동하는지 확인하는 용도예요. 이는 SVID를 회전하거나 갱신하지 않아요. 프로덕션 워크로드는 SPIFFE SDK를 사용해 Workload API를 직접 호출하거나 SPIFFE Helper를 사용해야 해요.
$ spire-agent api fetch x509 -write ~/spire-agent
$ JWT=$(spire-agent api fetch jwt -audience vault -output json | jq -r '.[0].svids[0].svid')

Vault 인증 메서드 구성

SPIRE 구성과 일치하도록 SPIFFE 인증 메서드로 Vault를 구성해요.

  • Vault 서버 작업 디렉터리를 만드세요.
$ mkdir ~/vault-dev && cd ~/vault-dev
  • Vault Enterprise 라이선스를 환경 변수로 내보내세요.
$ export VAULT_LICENSE=C10WMSH0W...snip...S3ASM3STR33T
  • Vault dev 모드 서버를 시작하세요. dev 서버는 완전히 메모리에서 실행되며 TLS가 활성화된 TCP 포트 8200의 localhost에서 수신해요. 런타임에 dev 서버는 자동으로 언실(Unseal)되고 언실 키와 초기 루트 토큰 값을 stdout으로 출력해요. 루트 토큰: dev 모드 서버는 초기 루트 토큰 값으로 시작해요. 프로덕션 환경에서 루트 토큰 사용은 Vault 서버에 대한 전체 접근을 제공하므로 극히 주의해야 해요. 이 튜토리얼의 학습 목표에 집중할 수 있도록 편의상 루트 토큰 값을 제공해 dev 모드로 Vault를 시작할 수 있어요.
$ vault server -dev -dev-tls -dev-root-token-id root &
  • Vault dev 모드 서버의 환경 변수를 복사하세요. 참고: dev 모드 서버 출력에서 전체 VAULT_CACERT=...를 복사하세요.
  • 필요한 Vault 환경 변수를 내보내세요.
$ export VAULT_TOKEN=root VAULT_ADDR=https://127.0.0.1:8200 VAULT_CACERT='/path/from/stdout'
  • Vault용 트러스트 번들을 요청하세요.
$ spire-server bundle show -format spiffe > initial_bundle.jwks
  • SPIFFE 인증 메서드를 활성화하세요.
$ vault auth enable \
  -passthrough-request-headers="Authorization" \
  spiffe
  • SPIFFE 인증 메서드 구성을 작성하세요. audience는 마운트 전체에 적용되며 역할별이 아니에요. 이 마운트의 모든 역할이 같은 JWT audience 허용 목록을 공유해요. X.509-SVID에는 aud 클레임이 없으므로 X.509-SVID 인증은 이 설정의 영향을 받지 않아요.
$ vault write auth/spiffe/config \
    trust_domain="example.org" \
    profile="https_spiffe_bundle" \
    bundle=@initial_bundle.jwks \
    endpoint_url="https://127.0.0.1:8443" \
    endpoint_spiffe_id="spiffe://example.org/spire/server" \
    audience="vault"
  • 워크로드용 역할을 만드세요. workload_id_patterns는 워크로드 ID — spiffe://<trust_domain>/ 프리픽스를 뗀 SPIFFE ID, 전체 URI가 아님 — 와 일치해요. *(프리픽스)와 +(단일 경로 세그먼트) 와일드카드를 지원해요.
$ vault write auth/spiffe/role/payment-role \
  workload_id_patterns="ns/prod/sa/payment-service" \
  token_policies="payment-vault-policy" \
  token_ttl="1h"

워크로드를 Vault에 인증

프로덕션 서비스는 SPIFFE SDK(Go용 go-spiffe, Python용 py-spiffe, Java용 java-spiffe 또는 다른 SPIFFE SDK 구현)를 사용해 Workload API를 직접 호출해요.

이 워크스루는 Vault에 인증하기 위해 spire-agent CLI와 curl을 사용해요.

  • curl로 Vault에 인증하세요. X.509: 예시 출력. JWT-SVID: 워크로드가 SPIRE Workload API에서 JWT-SVID를 요청할 때는 auth/spiffe/config에 구성된 audience 값과 일치하는 audience를 지정해야 해요. 이는 SPIRE 서버의 어떤 것도 아니라 워크로드가 요청 시점에 설정해요. 여기서 불일치가 발생하면 SPIRE의 오류가 아니라 Vault 쪽에서 거부된 로그인으로 나타나요. 예시 출력: 선택적으로 요청 본문에 "type"(auto, cert, jwt)을 설정해 Vault가 사용할 SVID 소스를 강제할 수 있어요. auto(기본값)는 JWT-SVID가 있으면 선호하고, 없으면 피어 X.509 인증서로 폴백해요. 성공적인 검증 후 Vault는 역할의 정책에 바인딩된 클라이언트 토큰을 반환해요.
$ curl \
    --cacert "$VAULT_CACERT" \
    --cert ~/spire-agent/svid.0.pem \
    --key ~/spire-agent/svid.0.key \
    --request POST \
    --data '{"role": "payment-role"}' \
    $VAULT_ADDR/v1/auth/spiffe/login | jq
{
  "request_id": "0f7905fb-6194-c4c7-5f0a-5c5bf4f042c5",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": null,
  "wrap_info": null,
  "warnings": null,
  "auth": {
    "client_token": "hvs.CAESID_Uge-45...example...WRzUmdicTJ3UEpVZ2o",
    "accessor": "MTBqnsBvoRuhqMN3kYFt0iRS",
    "policies": [
      "default",
      "payment-vault-policy"
    ],
    "token_policies": [
      "default",
      "payment-vault-policy"
    ],
    "metadata": {
      "role": "payment-role",
      "spiffe_id": "spiffe://example.org/ns/prod/sa/payment-service",
      "trust_domain": "example.org"
    },
    "lease_duration": 3600,
    "renewable": true,
    "entity_id": "707d32fd-8021-7fc7-9d08-c3c9a7ae7374",
    "token_type": "service",
    "orphan": true,
    "mfa_requirement": null,
    "num_uses": 0
  },
  "mount_type": ""
}
$ curl \
  --cacert "$VAULT_CACERT" \
  --header "Authorization: Bearer ***" \
  --request POST \
  --data '{"role": "payment-role"}' \
  $VAULT_ADDR/v1/auth/spiffe/login | jq
{
  "request_id": "532107ce-39f4-7d26-6a02-54dd9d567ada",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": null,
  "wrap_info": null,
  "warnings": null,
  "auth": {
    "client_token": "hvs.CAESID_Uge-45...example...WRzUmdicTJ3UEpVZ2o",
    "accessor": "sDokTIFDY619Yj1dOLWWmIgw",
    "policies": [
      "default",
      "payment-vault-policy"
    ],
    "token_policies": [
      "default",
      "payment-vault-policy"
    ],
    "metadata": {
      "role": "payment-role",
      "spiffe_id": "spiffe://example.org/ns/prod/sa/payment-service",
      "trust_domain": "example.org"
    },
    "lease_duration": 3600,
    "renewable": true,
    "entity_id": "707d32fd-8021-7fc7-9d08-c3c9a7ae7374",
    "token_type": "service",
    "orphan": true,
    "mfa_requirement": null,
    "num_uses": 0
  },
  "mount_type": ""
}

SPIFFE 플러그인 API

SPIFFE 인증 메서드는 완전한 HTTP API를 제공해요. 추가 정보는 SPIFFE 인증 API 문서를 참조하세요.

Terraform

vault_auth_backend 리소스로 SPIFFE 인증 메서드를 활성화하고 vault_spiffe_auth_backend_config로 구성을 관리할 수 있어요.

  • Vault auth backend 리소스
  • Vault SPIFFE auth backend config 리소스
  • Vault SPIFFE auth backend role 리소스

더 알아보기 (Learn more)