本篇是cicd-goat靶场系列的第四篇文章,涉及cicd-goat靶场环境是如何构建的和配置的。
解题记录参见第一篇文章第二篇文章,靶场环境中CI/CD的工作流解析参见第三篇文章

cicd-goat靶场构成

cicd-goat的架构图如下:

在本地运行cicd-goat靶场环境只需要使用项目README中给出的命令即可:

mkdir cicd-goat; cd cicd-goat
curl -o docker-compose.yaml https://raw.githubusercontent.com/cider-security-research/cicd-goat/main/docker-compose.yaml
get-content docker-compose.yaml | %{$_ -replace "bridge","nat"}
docker compose up -d

上述命令的意思是,下载项目的docker-compose文件到本地,将文件中的网络类型由bridge替换成nat,然后使用docker compose运行环境。

上面的命令有两个可能的错误:

  1. 修改网络类型的命令并没有将修改的结果写入docker-compose.yaml文件
  2. 在Docker官方文档Networking overviewNetwork drivers中,取消了nat类型的网络驱动器。

这两个错误结合在一起,命令却可以正常的工作🤣。

另外,我测试时发现Docker Hub对pull镜像进行了请求限制,不登录情况下每个IPv4地址每小时只允许pull 10次,略离谱。

概述

靶场由容器组成:答题环境,源代码管理,CICD环境和其他组件。

ctfd答题环境

ctfd是独立的答题环境,与靶场的CICD部分没有交互。通过给出任务说明和提示信息指引玩家进行攻击尝试,并校验flag信息、提供成就感。

其在docker-compose.yml文件中的配置信息如下:

ctfd:
image: cidersecurity/goat-ctfd:latest
container_name: ctfd
restart: always
ports:
- "8000:8000"
networks:
- goat

cicd-goat环境中的FLAG字段通过硬编码的形式给出:

  • 硬编码的flag对应于ctfd文档中的静态flag。
  • 而如果想要使用动态flag,需要使用Saas化部署的ctfd或者企业版的ctfd,大概率需要花钱;也可以考虑尝试CTFdOnlineChallenge插件。
  • 对于cicd-goat这种自己练习的环境来说,动态flag没特别大的必要。

ctfd的配置模式类似于虚拟机快照的模式:

  • 根据管理员账号密码台账admin/ciderland5#登录之后,进行题目、提示和flag的配置,然后将整个环境保存下来,形成Docker镜像以便后续使用。
  • 根据Git的变更记录,配置信息可能是存储在CTFd/ctfd.db中。
  • 上传的其他内容(如首页图片,logo信息等)则位于CTFd/uploads文件夹内。

源代码管理Gitea/Gitlab

靶场环境使用Gitea和Gitlab进行源代码管理,大部分项目都托管在Gitea上,少数需要使用Gitlab CI/CD能力的项目托管在Gitlab上。

Gitea

Gitea的配置信息如下:

gitea:
image: cidersecurity/goat-gitea:latest
container_name: gitea
restart: always
environment:
- USER_UID=1000
- USER_GID=1000
networks:
- goat
ports:
- "3000:3000"

项目中对Gitea的用户设置和repo内容、权限配置等,采用了配置即代码的理念,通过Yaml文件即可完成所有设置,整个实现十分优雅👍。

Gitea子目录中的Dockerfile文件主要内容如下:

FROM gitea/gitea:1.16.5 AS base

ENV PYTHONUNBUFFERED=1

RUN apk add --update --no-cache python3 py3-pip && \
ln -sf python3 /usr/bin/python && \
python3 --version
RUN python3 -m pip install --ignore-installed distlib --no-cache-dir pipenv==2022.8.30

COPY Pipfile* /setup/
RUN cd /setup && pipenv install --system --deploy

COPY . /setup/
RUN /setup/check_git.py && chmod 755 /setup/giteacasc/askpass.py /setup/run.sh
COPY --chown=git:git app.ini /data/gitea/conf/

ENTRYPOINT ["/setup/run.sh"]

check_git.py用于对repositories文件夹中的各个项目进行检测,确保项目不包含not.git文件夹。

run.sh为主要的配置代码,其功能是待gitea初始化完成之后,新建管理员信息,并使用giteacasc库根据gitea.yaml对gitea进行配置。

其内容如下:

#!/bin/bash
set -m
USERNAME=red_queen
PASSWORD=ciderland5#
/usr/bin/entrypoint &
while true
do
gitea_status_code=$(curl --write-out %{http_code} --silent --output /dev/null localhost:3000/ )
if [ "$gitea_status_code" -eq 200 ]; then
echo "Gitea ready. Continue with setup..."
break
fi
echo "Gitea is not ready. Waiting 5 seconds..."
sleep 5
done
su -c "gitea admin user create --username $USERNAME --password $PASSWORD --email queen@localhost --admin" git
cd /setup
python3 -m giteacasc /setup/gitea.yaml -u $USERNAME -p $PASSWORD
fg #/usr/bin/entrypoint

giteacasc是一个专门为Gitea实现Configuration as Code的Python库,它通过调用Gitea的API接口实现了用户、组织和代码仓库的创建及代码的同步。

run.sh中,实际起Gitea配置功能的代码前后,先将/usr/bin/entrypoint进程置于后台,配置完成之后再置于前台,这种单独的设置是为了避免环境变量中的用户名和密码泄露吗?🤔

todo:这里需要单独再测试验证一下

Gitlab

Gitlab不仅是一个源代码管理仓库,而且还包含了CI/CD工具、镜像仓库等,提供了相对完成的集成环境。

Gitlab的配置如下:

gitlab:
image: cidersecurity/goat-gitlab:latest
container_name: gitlab
restart: always
hostname: 'gitlab'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://gitlab'
registry_external_url 'https://gitlab:5050'
letsencrypt['enable'] = false
gitlab_rails['initial_shared_runners_registration_token'] = "GR1348hansd87fyzDiqyZeuHuxe"
gitlab_rails['smtp_enable'] = false
gitlab_kas['enable'] = false
sentinel['enable'] = false
logrotate['enable'] = false
pages_nginx['enable'] = false
grafana['enable'] = false
prometheus_monitoring['enable'] = false
alertmanager['enable'] = false
networks:
- goat
ports:
- '4000:80'
- '5050:5050'
shm_size: '256m'

cicd-goat环境中的gitlab使用Terraform进行依据gitlab/gitlab.tf配置,代码仓库使用gitlab/repositories.sh进行push。

Terraform是一个很强大的多云环境下基础设置即代码的工具,可以文件声明的形式创建各种资源。
我自己还未深入使用这个工具,找到了一些可能有用的资料,后续可以尝试下:

CICD环境

Jenkins

jenkins-server是用来部署大部分CI/CD任务的Jenkins服务器,具体的CICD任务则在对应的jenkins-agent节点上运行。

他们的配置信息如下:

jenkins-server:
image: cidersecurity/goat-jenkins-server:latest
networks:
- goat
container_name: jenkins-server
restart: always
ports:
- "8080:8080"
- "50000:50000"
jenkins-agent:
image: cidersecurity/goat-jenkins-agent:latest
container_name: jenkins-agent
restart: always
networks:
- goat
environment:
- JENKINS_AGENT_SSH_PUBKEY=ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC33zkdtvwp8giZLp1mVUbqzCKi0KIiWD8/towT0+9k1SCDYjJ/YVPqKidkSefYaKBgJ1yFcWa9qroXAUd5nACXN3Xdes2fe4w+xZ8GQTpqHKyStEHy6R9QXi00r/VxGcmBYZWLifEyzV//PiRC+hUaG27JeeBnkZ1FEOyXUunpaPixNaDkfnLbCimflkd2uYH2arMY+FdOH950ezow/+v4vsNrzoMwVVCCSg/dIJBS2G/JaoAbbQgIGo63Kyz0j++rIInsXMFmxhy9hEpViX/tEorFzGh4gUvJPLy3IDjWjUz/Nfte9By6usQjN/1plJcuP+rUqrjjGeMpfhDP6aq5ZvfuPTmXOVkWJ9vJZaK2BCtSZk1vOJR4luxCyUZQbKgb3jS9YZ4N1ZS26z3EKwJGP/acNtEMx2u2zhY7zXdG7ca1Qo1yeVDMRctlvH++KMEYX/ZR6LqYlJyV6TFICZVUT7dLF65gq68UyIFswMu9pQ8/VvIMkkO6eiU1cqFr8gc=

配置中服务端除了开放8080端口用于WEB界面访问之外,还开放了50000端口用于服务端与Agent端的通信。

代码配置层面jenkins-server使用了configuration-as-code: 1346.ve8cfa_3473c94代码即配置插件,通过文件对Jenkins进行配置。

根据本系列的第三篇文章中的探索,Jenkins上大部分CICD任务都是通过事件/变更来触发的,基本没有定时任务。这些任务都是使用Groovy文件编写,存放于jenkins-server/jobdsl文件夹内。

其他组件

待补充。。。