[AEWS] 10주차 - Jenkins + Vault (AppRole) - CI
이번 실습에서는 Jenkins에서 Vault를 연동하여 Vault KV Store에 저장한 username, password을 Jenkins을 활용해서 획득하는 방안에 대해 알아보고, 정적(Static) 시크릿을 외부에 저장하고 관리하는 방법을 알아보겠습니다.
요즘 CI/CD를 많이 활용하고 있는데, CI/CD 정보의 경우 유출될 경우 막강한 권한과 중대한 위험이 발생할 수 있기 때문에 더욱 더 조심해야합니다. 그래서 Vault를 사용하는 것을 고민해보는 것이 도움이 될 겁니다.
Jenkins Plugin with AppRole 인증방식 워크플로우
해당 내용은 공식문서에서도 잘 설명되어 있습니다. Jenkins에서는 vault 플러그인을 제공하고 있어 손쉽게 연동할 수 있습니다.
이때 AppRole 방식을 활용하고 다음과 같은 워크플로우를 가집니다.
- 젠킨스 워커가 Vault에 인증
- Vault는 토큰을 반환
- 워커는 이 토큰을 사용해 작업에 해당하는 역할의 Wrapped SecretID를 요청
- Vault는 Wrapped SecretID를 반환
- 워커는 작업 러너를 생성하고, Wrapped SecretID를 변수로 전달
- 러너 컨테이너는 Wrapped SecretID의 unwrap을 요청
- Vault는 SecretID를 반환
- 러너는 RoleID와 SecretID를 사용해 Vault에 인증
- Vault는 필요한 시크릿 정보를 읽을 수 있는 정책이 포함된 토큰을 반환
- 러너는 이 토큰을 사용해 Vault에서 시크릿을 가져옴
Jenkins는 Vault에 시크릿으로 분류된 데이터를 필요로 하는 작업(job)을 실행해야 합니다. Jenkins는 마스터 노드와 워커 노드를 가지고 있으며, 워커 노드는 짧은 시간 동안 실행되는 컨테이너 러너에서 작업을 실행하는 구조입니다.
1. Jenkins에서 Vault Plugin 설치
Jenkins 설치에 대해서는 기존에 포스팅한 블로그에서 설치한 방식 그대로 활용하겠습니다.
https://devops-james.tistory.com/517
[AEWS-3기] Kubernetes CI/CD - Jenkins, Gogs, ArgoCD 배포
Jenkins 및 Gogs 설치하기docker-compose를 통해서 컨테이너로 설치한다.cat docker-compose.yamlservices: jenkins: container_name: jenkins image: jenkins/jenkins restart: unless-stopped networks: - cicd-network ports: - "8080:8080" - "50000:50
devops-james.tistory.com
Jenkins UI 접속하여 상단 메뉴에서 Manage Jenkins → Plugins에 접속합니다.
Available 탭에서 Vault 검색하고 Install 버튼을 눌러줍니다.
HashiCorp Vault Plugin 설치 후 Jenkins 재시작합니다.
참고로 설치 중에 맨 마지막 보면 재시작하는 옵션을 추가할 수도 있습니다.
Vault AppRole 정보 확인
이제 plugin은 설치를 완료하였고, Manage Jenkins에서 System 설정으로 들어갑니다.
밑에 쭉 내리게 되면 Vault plugin에 해당 credential을 입력할 수 있도록 하겠습니다. Vault URL에는 Vault 서버의 엔드포인트 정보를 넣어주면 되고, Credential은 추가하기 위해 Add 버튼을 눌러줍니다. 그럼 다음과 같이 Jenkins가 나오게 됩니다.
그리고 주소는 localhost 말고 ip주소로 하시기 바랍니다.
Kind에는 vault app role credential 을 선택합니다.
이제 해당 정보를 입력하면 됩니다.
참고로 namespace는 'vault의 네임스페이스'이며, 엔터프라이즈 버전에서 사용하는 기능이라 여기서는 사용하지 않았습니다.
ID는 추후에도 이력관리나 추적을 위해서 인식하기 편한 내용으로 입력합니다.
기존에 만들었던 해당 정보를 입력해주고 Add 버튼을 눌러줍니다.
# 4. 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"
# 5. 파일로 저장
mkdir -p approle-creds
echo "$ROLE_ID" > approle-creds/role_id.txt
echo "$SECRET_ID" > approle-creds/secret_id.txt
plugin 에 해당정보를 설정하고 apply 와 save버튼을 눌러줍니다.
만약 아까 설정한 해당 정보를 수정하고 싶다면 manage jenkins에서 crednetials에 들어가 수정하면됩니다.
이렇게 credentials에 들어가있는것을 볼 수 있습니다.
Jenkins Pipeline Job 생성
이제 pipeline을 생성해서 넣어보도록 하겠습니다. New Item에 들어간 뒤 jenkins-vault-kv를 이름으로 지정하고 pipeline을 선택합니다.
파이프라인 script 쪽에 들어가서 아래 코드를 넣습니다.
해당 파이프라인 스크립트를 명확하게 살펴보겠습니다.
pipeline {
agent any
environment {
VAULT_ADDR = 'http://192.0.0.2: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}"
}
}
}
}
}
}
먼저 vault_addr은 localhost가 아니라 ip로 설정해야 합니다. 따라서 해당 ip를 확인하고 넣어주면됩니다.
environment {
VAULT_ADDR = 'http://192.0.0.2:30000' // 실제 Vault 주소로 변경!!!
}
이제 stage에서 확인하면
withvault의 vaultsecrets를 통해서 path를 지정하고, 버전정보나 실제로 가져올 값의 내용을 지정합니다.
stage('Read Vault Secret') {
steps {
withVault([
vaultSecrets: [
[
path: 'secret/sampleapp/config',
engineVersion: 2,
secretValues: [
[envVar: 'USERNAME', vaultKey: 'username'],
[envVar: 'PASSWORD', vaultKey: 'password']
]
]
],
쉘 방식과 script 방식을 사용해서 echo를 통해 확인해보도록 하겠습니다. 당연히 해당 내용은 필요한 대로 수정을 하면되고, 인프라 환경에 맞춰 사용하시면 됩니다.
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}"
}
}
}
이렇게 파이프라인 구성을 마치고 콘솔 출력을 해보면 다음과 같습니다.
sh방식과 script 방식을 둘다 적용해서 테스트해봤기 때문에 다음과 같이 형식에 맞는 출력값을 각각 가져올 수 있었습니다.
즉 이로써 CI단계에서 vault를 이용한 환경변수 주입 및 관리가 가능함을 알 수 있었습니다.