Skip to content

Commit f636b48

Browse files
authored
feat: deploy containers to Google kCTF (#120)
Only Google Kubernetes Engine deployment type, with patched kCTF to support image caching and Artifact Registry Signed-off-by: Tom Plant <[email protected]>
1 parent 91db2d6 commit f636b48

File tree

2 files changed

+129
-6
lines changed

2 files changed

+129
-6
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
name: Deploy containers to kCTF
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
9+
jobs:
10+
# based on https://github.com/google/kctf/blob/5d0f830d6adae029322570601f145a1866a50669/.github/workflows/update-images.yaml#L205
11+
main:
12+
runs-on: ubuntu-latest
13+
permissions:
14+
contents: read
15+
id-token: write
16+
packages: write
17+
steps:
18+
- uses: actions/[email protected]
19+
20+
- uses: google-github-actions/auth@v2
21+
with:
22+
workload_identity_provider: ${{ vars.KCTF_IDENTITY }}
23+
- uses: google-github-actions/setup-gcloud@v2
24+
with:
25+
install_components: gke-gcloud-auth-plugin
26+
27+
- uses: docker/[email protected]
28+
id: buildkit
29+
- name: Get auth params for buildx cache
30+
uses: crazy-max/[email protected]
31+
32+
- name: Deploy containers
33+
# Artefact Registry support https://github.com/google/kctf/pull/406
34+
# have to --push for cache to hit https://github.com/moby/buildkit/issues/2887
35+
run: |
36+
curl -sSL https://github.com/HlynurOskar/kctf/archive/3976fdec31aeabc10d41dfeaa2337f0a789f6698.tar.gz | tar xz -C /tmp
37+
mv /tmp/kctf-3976fdec31aeabc10d41dfeaa2337f0a789f6698/dist kctf
38+
source kctf/activate
39+
40+
sed -i 's|docker build|docker build --load --cache-from type=gha,scope=${CHALLENGE_NAME}-${CONTAINER_NAME} --cache-to type=gha,mode=max,scope=${CHALLENGE_NAME}-${CONTAINER_NAME} --builder ${{ steps.buildkit.outputs.name }}|' kctf/bin/kctf-challenge
41+
sed -i 's|IMAGE_URL="$.*|IMAGE_URL="${REGISTRY}/${PROJECT}/${CLUSTER_NAME}/${CHALLENGE_NAME}-${IMAGE_NAME}:${IMAGE_ID}"|' kctf/bin/kctf-challenge
42+
mkdir kctf/config
43+
echo "${{ vars.KCTF_CONFIG }}" | tr '\r\n' '\n' > kctf/config/kctf-cluster
44+
source kctf/config/kctf-cluster
45+
gcloud auth configure-docker $REGISTRY
46+
kctf cluster load kctf-cluster
47+
48+
shopt -s extglob
49+
for template in !(kctf)/*/; do
50+
pushd $template
51+
if [[ ! -e "challenge.yaml" ]]; then
52+
continue
53+
fi
54+
if [[ -e "challenge/Makefile" ]]; then
55+
make -C "challenge"
56+
fi
57+
CHALLENGE_NAME="$("${KCTF_BIN}/yq" eval '.metadata.name' challenge.yaml)"
58+
echo "starting challenge ${CHALLENGE_NAME}"
59+
kctf chal start
60+
echo "challenge started, waiting for it to become available"
61+
# We want to wait for the deployment to be available, but it
62+
# might not have been created yet by the operator and wait will fail.
63+
# So try to "kubectl get" the challenge a few times to make sure it exists.
64+
# Ideally, we would expose the condition in the operator but I
65+
# don't think that's currently possible.
66+
for i in {1..5}; do
67+
kubectl get "deployment/${CHALLENGE_NAME}" && break
68+
echo "deployment/${CHALLENGE_NAME} doesn't exist yet, sleeping"
69+
sleep 5
70+
done
71+
kubectl wait --for=condition=available --timeout=5m "deployment/${CHALLENGE_NAME}"
72+
popd
73+
done
74+
75+
- name: Setup GitHub container registry
76+
uses: docker/[email protected]
77+
with:
78+
registry: ghcr.io
79+
username: ${{ github.actor }}
80+
password: ${{ github.token }}
81+
82+
- name: Push container images to GitHub
83+
run: |-
84+
source kctf/config/kctf-cluster
85+
86+
images=$(docker images $REGISTRY/*:latest --format "{{.Repository}}")
87+
for image in $images; do
88+
lowercase=${GITHUB_REPOSITORY_OWNER,,}
89+
newtag=${image//$REGISTRY/ghcr\.io\/$lowercase}:latest
90+
docker tag $image:latest $newtag
91+
docker push $newtag
92+
done

README.md

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ Automatically deploy your CTF challenges from GitHub to CTFd. Also supports cont
1212

1313
1. [Click here](https://github.com/new?template_name=auto-ctfd&template_owner=pl4nty) to create a repository for your CTF. Select "Private" to prevent public access
1414
2. [Allow GitHub Actions to create pull requests](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#preventing-github-actions-from-creating-or-approving-pull-requests)
15-
3. [Create the following secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository):
15+
3. [Create the following secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository)
1616

1717
| Name | Value |
1818
| ---- | ----- |
1919
| `CTFD_TOKEN` | [CTFd admin access token](https://docs.ctfd.io/docs/api/getting-started#generating-an-admin-access-token) |
2020
| `CTFD_SITE_PASSWORD` (optional) | [CTFd site password](https://docs.ctfd.io/hosted/security/setting-site-password), if enabled |
2121

22-
4. [Create the following variables](https://docs.github.com/en/actions/learn-github-actions/variables#creating-configuration-variables-for-a-repository):
22+
4. [Create the following variables](https://docs.github.com/en/actions/learn-github-actions/variables#creating-configuration-variables-for-a-repository)
2323

2424
| Name | Value |
2525
| ---- | ----- |
@@ -53,7 +53,7 @@ Some challenges, like pwn or web, may need to run services in containers. These
5353

5454
Note that managed CTFd has certain Dockerfile requirements and limitations. Please see the [CTFd documentation](https://docs.ctfd.io/tutorials/challenges/deploying-challenges) for more details.
5555

56-
Create the following variables:
56+
Create the following variables
5757

5858
| Name | Value |
5959
| ---- | ----- |
@@ -63,14 +63,14 @@ Create the following variables:
6363

6464
1. Add a Compose file like `docker-compose.yml` to each of your challenge(s)
6565
2. Ensure TCP challenges have unique ports
66-
3. Create the following variables:
66+
3. Create the following variables
6767

6868
| Name | Value |
6969
| ---- | ----- |
7070
| `REGISTRY` | A container registry accessible by the Kubernetes cluster |
7171
| `KUBE_HOST` | Hostname for challenges. HTTP challenges will be available via ingress on `example.KUBE_HOST`, and TCP challenges via load balancer service on `KUBE_HOST:port` |
7272

73-
4. Create the following secrets:
73+
4. Create the following secrets
7474

7575
| Name | Value |
7676
| ---- | ----- |
@@ -91,7 +91,7 @@ Create the following variables:
9191
4. [Create a user-assigned managed identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-manage-user-assigned-managed-identities#create-a-user-assigned-managed-identity) and
9292
5. [Create an Azure Container Registry](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-get-started-portal) and assign the `AcrPull` role on it to the managed identity
9393
6. (Optional) [Add a custom DNS suffix](https://learn.microsoft.com/en-us/azure/container-apps/environment-custom-dns-suffix) to the Container Apps environment
94-
6. Create the following variables:
94+
6. Create the following variables
9595

9696
| Name | Value |
9797
| ---- | ----- |
@@ -101,3 +101,34 @@ Create the following variables:
101101
| `AZURE_CONTAINER_ENV` | Container Apps enviroment [resource ID](https://learn.microsoft.com/en-us/azure/storage/common/storage-account-get-info?tabs=portal#get-the-resource-id-for-a-storage-account) |
102102
| `AZURE_CONTAINER_IDENTITY` | Managed identity resource ID |
103103
| `AZURE_CONTAINER_SUFFIX` | Container Apps environment DNS suffix, eg chals.example.com |
104+
105+
### Google kCTF
106+
107+
1. [Set up kCTF infrastructure](https://google.github.io/kctf/google-cloud.html)
108+
2. [Create a Workload Identity Pool and Provider](https://github.com/google-github-actions/auth#preferred-direct-workload-identity-federation)
109+
3. Grant Kubernetes Engine Developer and Artifact Registry Writer roles to the Pool
110+
111+
```sh
112+
# TODO: replace ${PROJECT_ID}, ${WORKLOAD_IDENTITY_POOL_ID}, and ${REPO}
113+
# with your values below.
114+
#
115+
# ${REPO} is the full repo name including the parent GitHub organization,
116+
# such as "my-org/my-repo".
117+
#
118+
# ${WORKLOAD_IDENTITY_POOL_ID} is the full pool id, such as
119+
# "projects/123456789/locations/global/workloadIdentityPools/github".
120+
121+
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
122+
--role="roles/container.developer" \
123+
--member="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL_ID}/attribute.repository/${REPO}"
124+
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
125+
--role="roles/artifactregistry.writer" \
126+
--member="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL_ID}/attribute.repository/${REPO}"
127+
```
128+
129+
4. Create the following variables
130+
131+
| Name | Value |
132+
| ---- | ----- |
133+
| `KCTF_CONFIG` | Contents of the kCTF config file `kctf/config/.lastconfig` |
134+
| `KCTF_IDENTITY` | Workload Identity Provider resource name |

0 commit comments

Comments
 (0)