本篇是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运行环境。
上面的命令有两个可能的错误:
- 修改网络类型的命令并没有将修改的结果写入
docker-compose.yaml
文件
- 在Docker官方文档Networking overview和Network 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的配置模式类似于虚拟机快照的模式:
源代码管理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
|
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文件夹内。
其他组件
待补充。。。