튜토리얼: HashiCorp Vault 구성을 ID 토큰으로 업데이트하기

튜토리얼: HashiCorp Vault 구성을 ID 토큰으로 업데이트하기

기존 CI/CD 시크릿 구성을 ID 토큰을 사용하도록 전환하는 방법을 보여주는 튜토리얼이에요. CI_JOB_JWT 변수는 더 이상 사용되지 않으므로, ID 토큰을 쓰려면 Vault 인증 메서드와 역할 구성을 업데이트해야 해요.

두 가지 마이그레이션 경로(새 인증 경로 생성 vs 역할 수준 클레임 사용)를 구체적인 Vault 명령과 함께 옆에서 설명해 주는 방식으로 진행할게요.

출처: 문서

본문

이 튜토리얼은 현재 CI/CD 시크릿 구성을 ID 토큰을 사용하도록 전환하는 방법을 보여줘요.

CI_JOB_JWT 변수는 더 이상 사용되지 않아요. 대신 ID 토큰을 사용하려면 Vault 인증 메서드와 역할 구성을 업데이트해야 해요.

ID 토큰으로 전환하려면:

  1. Vault 구성을 업데이트하세요. 하나를 선택하세요:
  2. CI/CD 작업 업데이트

시작하기 전에

이 튜토리얼은 GitLab CI/CD와 Vault에 익숙하다고 가정해요.

따라 하려면 다음이 필요해요.

  • 이미 사용 중인 Vault 서버.
  • CI_JOB_JWT로 Vault에서 시크릿을 가져오는 CI/CD 작업.

Vault 1.17 이상에서, JWT에 aud 클레임이 포함되어 있으면 JWT auth login은 역할에 bound audiences가 있어야 해요. aud 클레임은 단일 문자열 또는 문자열 목록일 수 있어요.

JWT 역할을 새 인증 경로로 마이그레이션

이 접근 방식은 현재 인증 메서드와 병렬로 두 번째 JWT 인증 메서드를 만들고, 역할들이 이를 사용하도록 다시 만들어요. 인증 메서드를 복제하고 싶지 않다면 대신 역할 수준 클레임을 사용하세요.

Vault에서 두 번째 JWT 인증 경로 만들기

이 예시들에서 gitlab.example.com을 GitLab 인스턴스의 URL로 바꾸세요.

CI_JOB_JWT에서 ID 토큰으로 전환하는 과정의 일부로, Vault의 bound_issuerhttps://를 포함하도록 업데이트해야 해요.

$ vault write auth/jwt/config \
    oidc_discovery_url="https://gitlab.example.com" \
    bound_issuer="https://gitlab.example.com"

이 변경 후 CI_JOB_JWT를 사용하는 작업들은 실패하기 시작해요.

중단 없이 프로젝트 또는 작업 단위로 ID 토큰으로 전환하려면 Vault에 여러 인증 경로를 만들 수 있어요.

  1. jwt_v2라는 이름의 새 인증 경로를 구성하세요.
    vault auth enable -path jwt_v2 jwt
    
    다른 이름을 선택할 수 있지만 나머지 예시들은 jwt_v2를 사용한다고 가정하므로 필요에 따라 업데이트하세요.
  2. 인스턴스를 위해 새 인증 경로를 구성하세요.
    $ vault write auth/jwt_v2/config \
        oidc_discovery_url="https://gitlab.example.com" \
        bound_issuer="https://gitlab.example.com"
    

새 인증 경로를 사용하도록 역할 다시 만들기

이 예시들에서 vault.example.com을 Vault 서버의 URL로 바꾸세요.

역할은 특정 인증 경로에 바인딩되므로 각 작업에 새 역할을 추가해야 해요. 역할의 bound_audiences 매개변수는 JWT에 오디언스가 포함되면 필수이며, JWT의 관련 aud 클레임 중 하나 이상과 일치해야 해요.

  1. myproject-staging이라는 스테이징 역할을 다시 만드세요.
    $ vault write auth/jwt_v2/role/myproject-staging - {
      "role_type": "jwt",
      "policies": ["myproject-staging"],
      "token_explicit_max_ttl": 60,
      "user_claim": "user_email",
      "bound_audiences": ["https://vault.example.com"],
      "bound_claims": {
        "project_id": "22",
        "ref": "master",
        "ref_type": "branch"
      }
    }
    EOF
    
  2. myproject-production이라는 프로덕션 역할을 다시 만드세요.
    $ vault write auth/jwt_v2/role/myproject-production - {
      "role_type": "jwt",
      "policies": ["myproject-production"],
      "token_explicit_max_ttl": 60,
      "user_claim": "user_email",
      "bound_audiences": ["https://vault.example.com"],
      "bound_claims_type": "glob",
      "bound_claims": {
        "project_id": "22",
        "ref_protected": "true",
        "ref_type": "branch",
        "ref": "auto-deploy-*"
      }
    }
    EOF
    

vault 명령에서 jwtjwt_v2로 바꾸기만 하면 돼요. 역할 내부의 role_type은 변경하지 마세요.

iss 클레임을 역할 수준 클레임으로 이동

이 접근 방식은 두 번째 JWT 인증 메서드를 만들거나 역할을 다시 만들 필요가 없어요. 대신 새 iss 클레임 값을 현재 역할에 직접 추가해요. 역할당 단일 클레임 값을 유지하고 싶다면 새 인증 경로를 대신 사용하세요.

각 역할에 bound_issuers 클레임 맵 추가

이 예시들에서 gitlab.example.com을 GitLab 인스턴스의 URL로, vault.example.com을 Vault 서버의 URL로, jwt를 현재 인증 메서드 이름으로 바꾸세요.

Vault는 JWT 인증 메서드 수준에서 여러 iss 클레임을 허용하지 않아요. 이 수준의 bound_issuer 지시문은 단일 값만 수용하기 때문이에요. 그러나 역할 수준에서는 bound_claims 맵 구성 지시문으로 여러 클레임을 구성할 수 있어요.

이 접근 방식으로 Vault에 iss 클레임 검증을 위한 여러 옵션을 제공해요. ID 토큰과 함께 제공되는 https:// 접두사 GitLab 인스턴스 호스트명 클레임과, 이전의 접두사 없는 클레임이에요.

필요한 역할에 bound_claims 구성을 추가하려면 다음을 실행하세요.

$ vault write auth/jwt/role/myproject-staging - {
  "role_type": "jwt",
  "policies": ["myproject-staging"],
  "token_explicit_max_ttl": 60,
  "user_claim": "user_email",
  "bound_audiences": ["https://vault.example.com"],
  "bound_claims": {
    "iss": [
      "https://gitlab.example.com",
      "gitlab.example.com"
    ],
    "project_id": "22",
    "ref": "master",
    "ref_type": "branch"
  }
}
EOF

bound_claims 섹션을 제외하고 현재 역할 구성을 변경할 필요는 없어요. 앞서 보여준 대로 iss 구성을 추가해 Vault가 이 역할에 대해 접두사 있는/없는 iss 클레임을 수용하도록 해야 해요.

다음 단계로 진행하기 전에 이 변경을 GitLab 통합에 사용되는 모든 JWT 역할에 적용해야 해요.

인증 메서드에서 bound_issuers 클레임 제거

모든 역할이 bound_claims.iss 클레임으로 업데이트된 후에는 이 검증에 대한 인증 메서드 수준 구성을 제거할 수 있어요.

$ vault write auth/jwt/config \
    oidc_discovery_url="https://gitlab.example.com" \
    bound_issuer=""

bound_issuer 값은 인증 메서드 수준에서 발급자 검증을 제거해요. 하지만 이 검증이 이제 역할 수준에 있으므로 구성은 여전히 안전해요.

모든 프로젝트를 ID 토큰으로 마이그레이션하고 병렬로 CI_JOB_JWT를 더 이상 지원할 필요가 없게 되면, 이 구성을 되돌릴 수 있어요. 각 역할의 bound_claims에서 iss 클레임을 제거하고 인증 메서드의 bound_issuer를 다시 단일 값으로 설정하세요.

CI/CD 작업 업데이트

Vault에는 두 가지 다른 KV Secrets Engine이 있어요. 사용하는 버전이 CI/CD에서 시크릿을 정의하는 방식에 영향을 줘요.

Vault 서버를 확인하려면 HashiCorp 지원 포털의 Which Version is my Vault KV Mount? 문서를 확인하세요.

또한 필요하다면 다음 CI/CD 문서를 검토할 수 있어요.

이 예시들은 스테이징 데이터베이스 비밀번호를 얻는 방법을 보여줘요. secret/myproject/staging/dbpassword 필드에 저장되어 있어요. 이 예시들에서 vault.example.com을 Vault 서버의 URL로 바꾸세요.

VAULT_AUTH_PATH 변수의 값은 사용한 마이그레이션 접근 방식에 따라 달라져요.

  • 새 인증 경로: jwt_v2를 사용하세요.
  • 역할 수준 클레임: jwt를 사용하세요.

KV Secrets Engine v1

secrets:vault 키워드는 기본적으로 KV Mount의 v2를 사용하므로 작업이 v1 엔진을 사용하도록 명시적으로 구성해야 해요.

job:
  variables:
    VAULT_SERVER_URL: https://vault.example.com
    VAULT_AUTH_PATH: jwt_v2  # or "jwt" if you used the role-level claims approach
    VAULT_AUTH_ROLE: myproject-staging
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.example.com
  secrets:
    PASSWORD:
      vault:
        engine:
          name: kv-v1
          path: secret
        field: password
        path: myproject/staging/db
      file: false

선호한다면 VAULT_SERVER_URLVAULT_AUTH_PATH 모두 프로젝트 또는 그룹 CI/CD 변수로 정의할 수 있어요.

ID 토큰은 기본적으로 시크릿을 파일에 두기 때문에 secrets:filefalse로 설정돼 있어요. 이전 동작과 일치하려면 시크릿이 대신 일반 변수로 동작해야 해요.

KV Secrets Engine v2

v2 엔진에는 두 가지 형식을 사용할 수 있어요.

긴 형식:

job:
  variables:
    VAULT_SERVER_URL: https://vault.example.com
    VAULT_AUTH_PATH: jwt_v2  # or "jwt" if you used the role-level claims approach
    VAULT_AUTH_ROLE: myproject-staging
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.example.com
  secrets:
    PASSWORD:
      vault:
        engine:
          name: kv-v2
          path: secret
        field: password
        path: myproject/staging/db
      file: false

이 긴 형식은 v1 엔진 예시와 같지만, 엔진과 일치하도록 secrets:vault:engine:name:kv-v2로 설정되어 있어요.

짧은 형식:

job:
  variables:
    VAULT_SERVER_URL: https://vault.example.com
    VAULT_AUTH_PATH: jwt_v2  # or "jwt" if you used the role-level claims approach
    VAULT_AUTH_ROLE: myproject-staging
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.example.com
  secrets:
    PASSWORD:
      vault: myproject/staging/db/password@secret
      file: false

업데이트된 CI/CD 구성을 커밋하면 작업들이 ID 토큰으로 시크릿을 가져와요. 축하해요!

더 알아보기

ID 토큰 기반 시크릿 인증의 원리를 더 알고 싶다면 ID 토큰 인증 문서를, 전반적인 시크릿 제공자 구성은 HashiCorp Vault로 인증하고 비밀 읽기 튜토리얼을 함께 보시면 좋아요.