CI에서 외부 비밀 사용하기

Tier: Free, Premium, Ultimate Offering: GitLab.com, Self-managed, GitLab Dedicated

비밀은 CI 작업이 작업을 완료하는 데 필요한 민감한 정보를 나타냅니다.

이 민감한 정보는 API 토큰, 데이터베이스 자격 증명 또는 개인 키와 같은 항목일 수 있습니다.

비밀은 귀하의 비밀 공급자에서 공급됩니다.

CI/CD 변수와 달리, 항상 작업에 제공되는 변수와 달리, 비밀은 작업에 명시적으로 요구되어야 합니다.

구문에 대한 자세한 정보는 GitLab CI/CD 파이프라인 구성 참조를 읽어보세요.

GitLab은 다음 비밀 관리 공급자를 지원합니다:

  1. HashiCorp의 Vault
  2. Google Cloud Secret Manager
  3. Azure Key Vault

GitLab은 HashiCorp의 Vault를 첫 번째 지원 공급자로 선택하였고, KV-V2를 첫 번째 지원 비밀 엔진으로 선정하였습니다.

ID 토큰을 사용하여 Vault에 인증하기 위해서는 HashiCorp Vault로 비밀 인증 및 읽기 자습서를 참고하세요.

CI 작업에서 Vault 비밀을 사용하려면 Vault 서버를 구성해야 합니다.

HashiCorp Vault와 GitLab을 사용하는 흐름은 다음 다이어그램으로 요약됩니다:

GitLab과 HashiCorp 간의 흐름

  1. Vault 및 비밀을 구성합니다.
  2. JWT를 생성하고 CI 작업에 제공합니다.
  3. Runner가 HashiCorp Vault에 연락하여 JWT를 사용하여 인증합니다.
  4. HashiCorp Vault가 JWT를 검증합니다.
  5. HashiCorp Vault가 바운드 클레임을 확인하고 정책을 첨부합니다.
  6. HashiCorp Vault가 토큰을 반환합니다.
  7. Runner가 HashiCorp Vault에서 비밀을 읽습니다.
note
HashiCorp Vault로 비밀 인증 및 읽기 자습서를 읽어 이 기능의 버전을 참고하세요. 모든 구독 수준에서 사용할 수 있으며, Vault에 비밀을 작성하고 비밀을 삭제하는 것을 지원합니다. 여러 비밀 엔진을 지원합니다.

아래 vault.example.com URL을 귀하의 Vault 서버의 URL로, gitlab.example.com을 귀하의 GitLab 인스턴스의 URL로 바꿔야 합니다.

Vault 비밀 엔진

GitLab Runner에서 지원하는 Vault 비밀 엔진은 다음과 같습니다:

비밀 엔진 secrets:engine:name Runner 버전 세부 정보
KV 비밀 엔진 - 버전 2 kv-v2 13.4 kv-v2는 명시적으로 엔진 유형이 지정되지 않았을 때 GitLab Runner가 사용하는 기본 엔진입니다.
KV 비밀 엔진 - 버전 1 kv-v1 또는 generic 13.4 generic 키워드에 대한 지원은 GitLab 15.11에서 도입되었습니다.
AWS 비밀 엔진 generic 16.11  
Hashicorp Vault Artifactory 비밀 플러그인 generic 16.11 이 비밀 백엔드는 JFrog Artifactory 서버(5.0.0 이상)와 통신하며 지정된 범위로 액세스 토큰을 동적으로 프로비저닝합니다.

Vault 서버 구성하기

Vault 서버를 구성하려면:

  1. Vault 서버가 버전 1.2.0 이상으로 실행되고 있는지 확인합니다.

  2. 다음 명령어를 실행하여 인증 방법을 활성화합니다. 이를 통해 Vault 서버는 GitLab 인스턴스에 대한 OIDC Discovery URL을 제공받아, Vault가 공개 서명 키를 가져오고 JWT(JSON Web Token)를 인증할 수 있습니다:

    $ vault auth enable jwt
    
    $ vault write auth/jwt/config \
      oidc_discovery_url="https://gitlab.example.com" \
      bound_issuer="gitlab.example.com"
    
  3. Vault 서버에서 특정 경로 및 작업에 대한 접근을 허용하거나 금지하는 정책을 구성합니다. 이 예제는 프로덕션 환경에서 필요한 비밀 집합에 대한 읽기 권한을 부여합니다:

    vault policy write myproject-production - <<EOF
    # 'ops/data/production/*' 경로에 대한 읽기 전용 권한
    
    path "ops/data/production/*" {
      capabilities = [ "read" ]
    }
    EOF
    
  4. Vault 서버에서 역할을 구성하고, 역할을 프로젝트 또는 네임스페이스로 제한합니다. 이 페이지의 Vault 서버 역할 구성하기를 참조하세요.

  5. Vault 서버에 대한 세부 정보를 제공하는 다음 CI/CD 변수를 생성합니다:

    • VAULT_SERVER_URL - Vault 서버의 URL, 예: https://vault.example.com:8200. 필수.
    • VAULT_AUTH_ROLE - 선택 사항. 인증을 시도할 때 사용할 역할입니다. 역할이 지정되지 않으면, Vault는 인증 방법 구성 시 지정된 기본 역할을 사용합니다.
    • VAULT_AUTH_PATH - 선택 사항. 인증 방법이 장착된 경로, 기본값은 jwt입니다.
    • VAULT_NAMESPACE - 선택 사항. 비밀 읽기 및 인증에 사용할 Vault Enterprise 네임스페이스. 다음과 같습니다:
      • Vault는 네임스페이스가 지정되지 않은 경우 root (“/”) 네임스페이스를 사용합니다.
      • Vault Open source에서 이 설정은 무시됩니다.
      • HashiCorp Cloud Platform (HCP) Vault에서는 네임스페이스가 필요합니다. HCP Vault는 기본적으로 admin 네임스페이스를 루트 네임스페이스로 사용합니다. 예를 들어, VAULT_NAMESPACE=admin.
    note
    사용자 인터페이스에서 이 값을 제공하는 지원 이 이슈에서 추적됩니다.

CI 작업에서 Vault 비밀 사용하기

Tier: Premium, Ultimate Offering: GitLab.com, Self-managed, GitLab Dedicated

Vault 서버 구성 후, vault 키워드를 사용하여 Vault에 저장된 비밀을 정의하여 사용할 수 있습니다:

job_using_vault:
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.example.com
  secrets:
    DATABASE_PASSWORD:
      vault: production/db/password@ops  # 비밀 `ops/data/production/db`, 필드 `password`로 변환
      token: $VAULT_ID_TOKEN

이 예제에서:

  • production/db - 비밀.
  • password - 필드.
  • ops - 비밀 엔진이 장착된 경로.

GitLab이 Vault에서 비밀을 가져오면, 해당 값이 임시 파일에 저장됩니다. 이 파일의 경로는 DATABASE_PASSWORD라는 CI/CD 변수에 저장됩니다. 이는 파일 유형의 변수와 유사합니다.

기본 동작을 덮어쓰려면, file 옵션을 명시적으로 설정합니다:

secrets:
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.example.com
  DATABASE_PASSWORD:
    vault: production/db/password@ops
    file: false
    token: $VAULT_ID_TOKEN

이 예제에서 비밀 값은 파일이 아닌 DATABASE_PASSWORD 변수에 직접 저장됩니다.

다른 비밀 엔진 사용하기

기본적으로 kv-v2 비밀 엔진이 사용됩니다. 다른 엔진을 사용하려면 구성의 vault 아래에 engine 섹션을 추가하세요.

예를 들어, Artifactory의 비밀 엔진과 경로를 설정하려면:

job_using_vault:
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.example.com
  secrets:
    JFROG_TOKEN:
      vault:
        engine:
          name: generic
          path: artifactory
        path: production/jfrog
        field: access_token
      file: false

이 예에서 비밀 값은 artifactory/production/jfrog에서 access_token 필드로 가져옵니다. generic 비밀 엔진은 kv-v1, AWS, Artifactory 및 기타 유사한 비밀 엔진을 위해 사용할 수 있습니다.

Vault 서버 역할 구성하기

CI 작업이 인증을 시도할 때 역할을 지정합니다. 역할을 사용하여 서로 다른 정책을 그룹화할 수 있습니다. 인증이 성공하면 이러한 정책이 생성된 Vault 토큰에 첨부됩니다.

제한된 클레임은 JWT 클레임에 맞춰진 미리 정의된 값입니다. 제한된 클레임을 사용하면 특정 GitLab 사용자, 특정 프로젝트 또는 특정 Git 참조를 위해 실행 중인 작업에 대한 접근을 제한할 수 있습니다. 필요한 만큼 많은 제한된 클레임을 가질 수 있지만, 인증이 성공적으로 이루어지려면 모두 일치해야 합니다.

제한된 클레임을 사용자 역할보호된 브랜치와 같은 GitLab 기능과 결합하면, 이러한 규칙을 특정 사용 사례에 맞게 조정할 수 있습니다. 이 예에서 인증은 프로덕션 릴리스에 대한 패턴과 일치하는 이름을 가진 보호된 태그에 대해서만 허용됩니다:

$ vault write auth/jwt/role/myproject-production - <<EOF
{
  "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": "42",
    "ref_protected": "true",
    "ref_type": "tag",
    "ref": "auto-deploy-*"
  }
}
EOF

경고:

항상 project_id 또는 namespace_id와 같은 제공된 클레임을 사용하여 역할을 프로젝트 또는 네임스페이스에 제한하세요. 이러한 제한 없이는 이 GitLab 인스턴스에서 생성된 모든 JWT가 이 역할을 사용하여 인증될 수 있습니다.

ID 토큰 JWT 클레임의 전체 목록은 How It Works 섹션을 읽어보세요. HashiCorp Vault로 비밀 인증 및 읽기 튜토리얼.

결과 Vault 토큰에 대한 몇 가지 속성도 지정할 수 있으며, 여기에는 생애 주기, IP 주소 범위 및 사용 횟수 등이 포함됩니다. 전체 옵션 목록은 역할 생성에 대한 Vault 문서에서 확인할 수 있습니다.

자기 서명된 Vault 서버 사용하기

Vault 서버가 자기 서명된 인증서를 사용하는 경우 작업 로그에서 다음과 같은 오류를 확인할 수 있습니다:

ERROR: Job failed (system failure): resolving secrets: initializing Vault service: preparing authenticated client: checking Vault server health: Get https://vault.example.com:8000/v1/sys/health?drsecondarycode=299&performancestandbycode=299&sealedcode=299&standbycode=299&uninitcode=299: x509: certificate signed by unknown authority

이 오류를 해결할 수 있는 두 가지 방법이 있습니다:

  • GitLab Runner 서버의 CA 저장소에 자기 서명된 인증서를 추가하세요. GitLab Runner를 Helm 차트를 사용하여 배포한 경우, 직접 GitLab Runner 이미지를 생성해야 합니다.

  • VAULT_CACERT 환경 변수를 사용하여 GitLab Runner가 인증서를 신뢰하도록 구성하세요:

    • GitLab Runner를 관리하는 데 systemd를 사용하고 있다면, GitLab Runner에 환경 변수를 추가하는 방법을 참조하세요.
    • GitLab Runner를 Helm 차트를 사용하여 배포한 경우:
      1. GitLab에 접근하기 위한 사용자 정의 인증서 제공하기를 수행하고, GitLab에 대한 인증서 대신 Vault 서버의 인증서를 추가하세요. GitLab 인스턴스가 자기 서명된 인증서를 사용하는 경우, 같은 Secret에 두 개 모두 추가할 수 있어야 합니다.

      2. values.yaml 파일에 다음 줄을 추가하세요:

        ## <SECRET_NAME> 및 <VAULT_CERTIFICATE>를 생성한 비밀의 실제 값으로 교체하세요.
        
        certsSecretName: <SECRET_NAME>
        
        envVars:
          - name: VAULT_CACERT
            value: "/home/gitlab-runner/.gitlab-runner/certs/<VAULT_CERTIFICATE>"
        

문제 해결

resolving secrets: secret not found: MY_SECRET 오류

GitLab이 vault에서 비밀을 찾을 수 없을 때, 다음과 같은 오류가 발생할 수 있습니다:

ERROR: Job failed (system failure): resolving secrets: secret not found: MY_SECRET

vault 값이 CI/CD 작업에 올바르게 구성되었는지 확인하세요.

비밀을 검색 가능 여부를 확인하기 위해 kv 명령어를 Vault CLI와 함께 사용할 수 있으며, 이는 CI/CD 구성에서 vault 값의 구문을 결정하는 데 도움이 됩니다. 예를 들어, 비밀을 검색하려면:

$ vault kv get -field=password -namespace=admin -mount=ops "production/db"
this-is-a-password