2. 环境搭建:Docker靶场环境搭建、Jenkins CI/CD环境配置、GitLab仓库配置、Kubernetes集群部署

说实话,凭据注入这个攻击面,光讲理论是没用的。你得亲手搭一套环境,看着凭据怎么在流水线里流转,才能理解漏洞到底出在哪。这一章,我就带你把这套「靶场」搭起来。

我个人习惯把环境拆成四个部分:Docker 做底层容器、GitLab 存代码和凭据、Jenkins 跑 CI/CD 流水线、Kubernetes 做编排。你想想看,这正好是一条完整的攻击链路。

核心思路:所有组件都跑在 Docker 里,方便重置和销毁。生产环境别这么干,但做靶场,越简单越好。

凭据注入靶场环境架构图 凭据注入靶场环境架构图 Docker 靶场 容器化应用 GitLab 代码 + 凭据仓库 Jenkins CI/CD 流水线 K8s 集群 生产模拟 数据流方向:开发者 Push → GitLab Webhook → Jenkins 触发构建 → 部署到 K8s 凭据在 GitLab CI 变量、Jenkins Credentials、K8s Secrets 中流转 攻击面 1:GitLab CI 变量泄露 通过 .gitlab-ci.yml 或日志 获取明文凭据 攻击面 2:Jenkins 凭据注入 通过构建参数或环境变量 注入恶意凭据 攻击面 3:K8s Secrets 滥用 通过 Pod 环境变量或 Volume 窃取集群凭据

2.1 Docker 靶场环境搭建

我习惯用 docker-compose 一把梭。你想想看,三个服务写在一个文件里,一条命令全拉起来,多省事。

先创建一个项目目录:

mkdir -p ~/cred-injection-lab && cd ~/cred-injection-lab

然后写 docker-compose.yml。嗯,这里要注意版本和端口映射,别跟本地服务冲突了。

version: '3.8'
services:
  gitlab:
    image: gitlab/gitlab-ce:15.11.13-ce.0
    hostname: gitlab.local
    ports:
      - "8080:80"
      - "2222:22"
    volumes:
      - ./gitlab/config:/etc/gitlab
      - ./gitlab/logs:/var/log/gitlab
      - ./gitlab/data:/var/opt/gitlab
    environment:
      GITLAB_ROOT_PASSWORD: "Passw0rd!2024"

  jenkins:
    image: jenkins/jenkins:2.440.3-lts-jdk17
    ports:
      - "9090:8080"
      - "50000:50000"
    volumes:
      - ./jenkins/home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock

  k8s-control:
    image: kindest/node:v1.27.3
    privileged: true
    ports:
      - "6443:6443"

小技巧:GitLab 第一次启动要等 3-5 分钟,别急。你可以用 docker logs -f gitlab 盯着日志,看到 "GitLab is ready!" 就说明好了。

启动命令很简单:

docker-compose up -d

等所有容器都变成 healthy 状态,环境就绪了。我个人习惯用 docker-compose ps 检查状态。

2.2 GitLab 仓库配置

GitLab 起来之后,先登录 http://localhost:8080,用 root 和刚才设的密码进去。

接下来我们要创建一个项目,专门用来演示凭据注入。我建议你创建一个 私有项目,别让无关的人看到。

  1. 点击「Create a project」→「Create blank project」
  2. 项目名称填 cred-injection-demo
  3. 可见级别选 Private
  4. 初始化仓库时勾选「Initialize repository with a README」

项目建好后,我们要配置 CI/CD 变量。这是凭据注入的关键入口。

变量名 变量值 保护级别 掩码
DB_PASSWORD SuperSecret!2024 受保护分支 是
API_TOKEN glpat-xxxxxxxxxxxx 所有分支 否
SSH_PRIVATE_KEY -----BEGIN OPENSSH PRIVATE KEY-----... 受保护分支 是

注意:掩码(Masked)变量在 CI 日志里会显示为 ****,但如果你在脚本里 echo 出来,它还是会暴露。我曾经见过一个团队,掩码变量被 echo $VAR | base64 绕过了。

配置路径:项目 → Settings → CI/CD → Variables → Add variable。

然后写一个简单的 .gitlab-ci.yml,触发流水线:

stages:
  - build
  - deploy

build-job:
  stage: build
  script:
    - echo "Building with DB_PASSWORD=$DB_PASSWORD"
    - echo "API_TOKEN=$API_TOKEN"
  only:
    - main

deploy-job:
  stage: deploy
  script:
    - echo "Deploying to K8s..."
    - echo "Using SSH key length: ${#SSH_PRIVATE_KEY}"
  only:
    - main

你想想看,这个流水线直接把凭据打印到日志里了。这就是典型的凭据泄露。

2.3 Jenkins CI/CD 环境配置

Jenkins 的初始密码在容器日志里:

docker logs jenkins 2>&1 | grep "Initial admin password"

拿到密码后,访问 http://localhost:9090,解锁 Jenkins。我建议你安装「推荐的插件」,省得后面缺东西。

接下来我们要做三件事:

  1. 安装必要插件:GitLab、Kubernetes、Credentials Binding
  2. 配置 GitLab 连接:系统管理 → 系统配置 → GitLab → 添加 API Token
  3. 创建凭据:用于演示凭据注入

创建凭据的路径:系统管理 → Manage Credentials → 全局 → Add Credentials。

凭据 ID 类型 值 用途
gitlab-token GitLab API Token glpat-xxxxxxxx 触发 GitLab Webhook
k8s-config Secret file kubeconfig 文件 连接 K8s 集群
docker-hub Username with password dockerhub 账号 推送镜像

然后创建一个 Pipeline 任务,配置 GitLab 仓库地址和 Webhook。嗯,这里要注意 Jenkinsfile 的写法:

pipeline {
    agent any
    stages {
        stage('Checkout') {
            steps {
                checkout scmGit(
                    branches: [[name: '*/main']],
                    userRemoteConfigs: [[url: 'http://gitlab.local/root/cred-injection-demo.git']]
                )
            }
        }
        stage('Build') {
            steps {
                withCredentials([string(credentialsId: 'gitlab-token', variable: 'TOKEN')]) {
                    sh 'echo "Using token: $TOKEN"'
                }
            }
        }
    }
}

关键点:withCredentials 会把凭据注入到环境变量里。如果脚本里不小心 echo 出来,凭据就暴露了。我在项目中遇到过好几次这种问题。

2.4 Kubernetes 集群部署

本地跑 K8s,我推荐用 kind(Kubernetes in Docker)。轻量、快速、好重置。

先安装 kind:

curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind

然后创建一个集群配置文件 kind-config.yaml:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    extraPortMappings:
      - containerPort: 30080
        hostPort: 30080
  - role: worker
  - role: worker

创建集群:

kind create cluster --config kind-config.yaml --name cred-lab

等集群就绪后,验证一下:

kubectl cluster-info --context kind-cred-lab
kubectl get nodes

接下来,我们要创建一个 Namespace 和 Secrets,模拟生产环境:

kubectl create namespace cred-injection
kubectl -n cred-injection create secret generic app-secrets \
  --from-literal=db-password='ProdDB!2024' \
  --from-literal=api-key='sk-prod-xxxxx'

避坑指南:我曾经见过有人把 Secrets 直接写在 YAML 里提交到 Git 仓库。千万别这么干!Secrets 的 value 是 base64 编码,不是加密。随便一个 echo xxx | base64 -d 就能解开。

最后,把 Jenkins 和 K8s 连起来。在 Jenkins 里配置 K8s 云:系统管理 → 系统配置 → 云 → 添加 Kubernetes。填上 K8s API 地址和凭据。

这样,整套环境就搭好了。你想想看,从 GitLab 的 CI 变量,到 Jenkins 的凭据,再到 K8s 的 Secrets,凭据在这条链路上到处流转。后面的章节,我们就拿这套环境做攻击和防御演练。