본문 바로가기

DevOps

[AWS EKS] (27) EKS 스터디 10주차 ( Jenkins + Vault (AppRole) )

Vault KV Store에 저장한 username, password을 Jenkins을 활용해서 획득하는 방안

CI 파이프라인에서 정적(Static) 시크릿을 외부에 저장하고 관리할 경우 사용할 수 있습니다.

CloudNet@팀의 EKS 스터디 AEWS 2기에 작성된 자료를 토대로 작성합니다.

 

5. Jenkins + Vault (AppRole) - CI

 

최근 CI/CD의 공격사례(CVE-2025-30066) : GitHub Action tj-actions/changed-files 공격

Vault - Jenkins Plugin with AppRole 인증방식 워크플로우 - Docs

 

Best practices for AppRole authentication | Vault | HashiCorp Developer

Follow best practices for AppRole authentication to secure access and validate application workload identity.

developer.hashicorp.com

 

젠킨스는 Vault에 시크릿으로 분류된 데이터를 필요로 하는 작업(job)을 실행해야 합니다. 젠킨스는 마스터 노드와 워커 노드를 가지고 있으며, 워커 노드는 짧은 시간 동안 실행되는 컨테이너 러너에서 작업을 실행합니다.

  • 프로세스는 다음과 같습니다:
    1. 젠킨스 워커가 Vault에 인증
    2. Vault는 토큰을 반환
    3. 워커는 이 토큰을 사용해 작업에 해당하는 역할의 Wrapped SecretID를 요청
    4. Vault는 Wrapped SecretID를 반환
    5. 워커는 작업 러너를 생성하고, Wrapped SecretID를 변수로 전달
    6. 러너 컨테이너는 Wrapped SecretID의 unwrap을 요청
    7. Vault는 SecretID를 반환
    8. 러너는 RoleID와 SecretID를 사용해 Vault에 인증
    9. Vault는 필요한 시크릿 정보를 읽을 수 있는 정책이 포함된 토큰을 반환
    10. 러너는 이 토큰을 사용해 Vault에서 시크릿을 가져옴

Vault - Jenkins Plugin with AppRole 실습

Step1. Jenkins에서 Vault Plugin 설치

  • Jenkins UI 접속
  • 상단 메뉴에서 Manage JenkinsPlugins

  • Available 탭에서 Vault 검색

  • HashiCorp Vault Plugin 설치 후 Jenkins 재시작

Step2. Vault AppRole 정보 확인 Secret ID는 1시간 만료이므로, 그냥 다시 생성해서, 해당 값을 젠킨스에 설정하고 빌드 실습 하자.

ROLE_ID=$(vault read -field=role_id auth/approle/role/sampleapp-role/role-id)
SECRET_ID=$(vault write -f -field=secret_id auth/approle/role/sampleapp-role/secret-id)

echo "ROLE_ID: $ROLE_ID"
echo "SECRET_ID: $SECRET_ID"

Step3. Jenkins에서 Vault 설정 및 Credentials 추가

  • Jenkins UI(admin/qwe123) → Manage Jenkins → Configure System

  • 스크롤 하단의 Vault Plugin Configuration 섹션으로 이동
# Vault URL 입력 후 [Add] 버튼 클릭 : IP입력할것!
http://localhost:30000

  • Vault Credential 다음 값 입력:
  종류: Vault AppRole Credential

Role ID & Secret ID 입력 → 생성해놓은 변수 또는 파일참고
ID는 기억하기 쉬운 이름으로 지정 (vault-approle-creds 등)

  • Jenkins UI → New Item → Pipeline 선택
  • jenkins-vault-kv 입력 후 생성

  • Jenkinsfile 작성
pipeline {
  agent any

  environment {
    VAULT_ADDR = 'http://172.30.1.93:30000' // 실제 Vault 주소로 변경!!!
  }

  stages {
    stage('Read Vault Secret') {
      steps {
        withVault([
          vaultSecrets: [
            [
              path: 'secret/sampleapp/config',
              engineVersion: 2,
              secretValues: [
                [envVar: 'USERNAME', vaultKey: 'username'],
                [envVar: 'PASSWORD', vaultKey: 'password']
              ]
            ]
          ],
          configuration: [
            vaultUrl: "${VAULT_ADDR}",
            vaultCredentialId: 'vault-approle-creds'
          ]
        ]) {
          sh '''
            echo "Username from Vault: $USERNAME"
            echo "Password from Vault: $PASSWORD"
          '''
          script {
            echo "Username (env): ${env.USERNAME}"
            echo "Password (env): ${env.PASSWORD}"
          }
        }
      }
    }
  }
}

한시간 지나면 error 발생 

# 갱신할 대상 
# ROLE_ID
# SECRET_ID

ROLE_ID=$(vault read -field=role_id auth/approle/role/sampleapp-role/role-id)
SECRET_ID=$(vault write -f -field=secret_id auth/approle/role/sampleapp-role/secret-id)

echo "ROLE_ID: $ROLE_ID"
echo "SECRET_ID: $SECRET_ID"
ROLE_ID: e0df71e9-e32f-cf95-dddf-dfc70261bf65
SECRET_ID: 42e3d61a-df73-ebc0-3917-0d199eaacb85


vault write auth/approle/role/sampleapp-role2 \
  token_policies="sampleapp-policy" \
  secret_id_ttl="1h" \
  token_ttl="1h" \
  token_max_ttl="4h"
  
Success! Data written to: auth/approle/role/sampleapp-role2

ROLE_ID=$(vault read -field=role_id auth/approle/role/sampleapp-role2/role-id)
SECRET_ID=$(vault write -f -field=secret_id auth/approle/role/sampleapp-role2/secret-id)

echo "ROLE_ID: $ROLE_ID"
echo "SECRET_ID: $SECRET_ID"

ROLE_ID: fca4c04d-4aa0-eff7-9a2e-e9c1745859e8
SECRET_ID: aea52e8f-e3f1-11ad-a804-ea8db90f431a