├── .gitignore ├── README.ja.md ├── README.md ├── docs ├── requirements.ja.md ├── requirements.md ├── setup.ja.md └── setup.md ├── manifests ├── 00_namespaces │ └── namespace.yml ├── 01_challenges │ ├── can-you-keep-a-secret │ │ ├── README.ja.md │ │ ├── README.md │ │ ├── flag.yaml │ │ └── victim.yaml │ ├── mountme │ │ ├── README.ja.md │ │ ├── README.md │ │ ├── job-sa.yaml │ │ └── victim.yaml │ ├── mountme2 │ │ ├── README.ja.md │ │ ├── README.md │ │ ├── flag │ │ │ └── FLAG.txt │ │ ├── job-sa.yaml │ │ └── victim.yaml │ ├── sniff │ │ ├── README.ja.md │ │ ├── README.md │ │ ├── client.yaml │ │ ├── server.yaml │ │ ├── service.yml │ │ └── victim.yaml │ └── treasure-hunt │ │ ├── README.md │ │ ├── docker-registry │ │ ├── certs │ │ │ ├── ca.crt │ │ │ └── ca.key │ │ ├── deployment.yml │ │ ├── htpasswd │ │ ├── registry-config.yml │ │ ├── secrets.yml │ │ └── service.yml │ │ ├── flag │ │ ├── Dockerfile │ │ ├── FLAG.txt │ │ └── docker-registry-password │ │ └── victim.yaml ├── flags │ ├── mountme │ │ └── FLAG.txt │ └── mountme2 │ │ └── FLAG.txt └── gatekeeper │ ├── 00_gatekeeper.yaml │ └── hostpath │ ├── 00_template.yaml │ └── 01_constraint.yaml └── setup.sh /.gitignore: -------------------------------------------------------------------------------- 1 | manifests/01_challenges/can-you-keep-a-secret/*.crt 2 | manifests/01_challenges/can-you-keep-a-secret/*.key 3 | 4 | manifests/01_challenges/can-you-keep-a-secret/writeup 5 | manifests/01_challenges/mountme/writeup 6 | manifests/01_challenges/mountme2/writeup 7 | manifests/01_challenges/sniff/writeup 8 | manifests/01_challenges/treasure-hunt/writeup 9 | -------------------------------------------------------------------------------- /README.ja.md: -------------------------------------------------------------------------------- 1 | # kubectf 2 | 3 | # Requirements 4 | 5 | * minikube 6 | * kubectl 7 | * Docker Engine 8 | * Add `{ "insecure-registries": ["docker.for.mac.localhost:5000"] }` to Docker configuration. 9 | 10 | more information [docs/requirements.md](docs/requirements.md) 11 | 12 | # Getting Started 13 | 14 | ```shell 15 | $ minikube start --driver=virtualbox 16 | $ ./setup.sh 17 | ``` 18 | 19 | # Rule 20 | 21 | Pod の一つに侵入することができたという想定で、権限昇格を行って `k8sctf{...}` というフォーマットの文字列(flag)を探し出します。 22 | 各問題の namespace に victim- プレフィックスのついた名前を持つ Pod がいますので、その Pod に入って問題に取り組んでください。 23 | 24 | ```shell 25 | $ kubens mountme 26 | ❯ kubectl get pods 27 | NAME READY STATUS RESTARTS AGE 28 | victim-7c5745b4dc-jxd5t 1/1 Running 0 14m 29 | 30 | ❯ kubectl exec -it victim-7c5745b4dc-jxd5t bash 31 | I have no name!@victim-7c5745b4dc-jxd5t:/$ 32 | ``` 33 | 34 | 以下細かい点です。 35 | 36 | * このリポジトリの中には答えである flag が入っているので、真面目に問題を解きたいひとは見ないようにしてください。 37 | * クラスタ管理者という立場ではなく、victim という Pod に何らかの手段で侵入できた攻撃者という立場で取り組んでください。 38 | * なので manifest の変更等は禁止です。victim Pod の中から実行可能な範囲のみで取り組んでください。 39 | 40 | # Challenges 41 | 42 | | Title | Difficulty | Description | 43 | |:-----:|:---------:|:----------:| 44 | | Mount me | 🔥 | [Link](manifests/01_challenges/mountme/README.md) | 45 | | Can You Keep A Secret? | 🔥 | [Link](manifests/01_challenges/can-you-keep-a-secret/README.md) | 46 | | Treasure Hunt | 🔥🔥 | [Link](manifests/01_challenges/treasure-hunt/README.md) | 47 | | Mount me 2 | 🔥🔥 | [Link](manifests/01_challenges/mountme2/README.md) | 48 | | Sniff | 🔥🔥 | [Link](manifests/01_challenges/sniff/README.md) | 49 | 50 | # Writeups 51 | 52 | 2020 年年末に公開します 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kubectf 2 | 3 | # Requirements 4 | 5 | * minikube 6 | * kubectl 7 | * Docker Engine 8 | * Add `{ "insecure-registries": ["docker.for.mac.localhost:5000"] }` to Docker configuration. 9 | 10 | more information [docs/requirements.md](docs/requirements.md) 11 | 12 | # Getting Started 13 | 14 | ```shell 15 | $ minikube start --driver=virtualbox 16 | $ ./setup.sh 17 | ``` 18 | 19 | # Rule 20 | 21 | Assuming you've penetrated a vulnerable Pod, perform a privilege escalation and look for a Flag (a string of the format `k8sctf{...}` ). 22 | There is a victim pod in the namespace for each problem, and you enter into this pod to challenge the problem. 23 | No changes should be made to anifests. You should work within Pod as a attacker. 24 | 25 | ```shell 26 | $ kubens mountme 27 | ❯ kubectl get pods 28 | NAME READY STATUS RESTARTS AGE 29 | victim-7c5745b4dc-jxd5t 1/1 Running 0 14m 30 | 31 | ❯ kubectl exec -it victim-7c5745b4dc-jxd5t bash 32 | I have no name!@victim-7c5745b4dc-jxd5t:/$ 33 | ``` 34 | 35 | # Challenges 36 | 37 | | Title | Difficulty | Description | 38 | |:-----:|:---------:|:----------:| 39 | | Mount me | 🔥 | [Link](manifests/01_challenges/mountme/README.md) | 40 | | Can You Keep A Secret? | 🔥 | [Link](manifests/01_challenges/can-you-keep-a-secret/README.md) | 41 | | Treasure Hunt | 🔥🔥 | [Link](manifests/01_challenges/treasure-hunt/README.md) | 42 | | Mount me 2 | 🔥🔥 | [Link](manifests/01_challenges/mountme2/README.md) | 43 | | Sniff | 🔥🔥 | [Link](manifests/01_challenges/sniff/README.md) | 44 | 45 | # Writeups 46 | 47 | TBD 48 | 49 | -------------------------------------------------------------------------------- /docs/requirements.ja.md: -------------------------------------------------------------------------------- 1 | # Requirements 2 | 3 | ## 1. minikube のインストール 4 | 5 | https://kubernetes.io/ja/docs/tasks/tools/install-minikube/ を参考に minikube をインストールします。 6 | 7 | ## 2. insecure registry の登録 8 | 9 | 手元の Docker for Mac の Docker Engine の設定に次の内容を追加します。 10 | アプリケーションバーから Docker を選択して Preferences -> Docker Engine で追加できます。 11 | 12 | ```json 13 | { 14 | "insecure-registries": [ 15 | "docker.for.mac.localhost:5000" 16 | ] 17 | } 18 | ``` 19 | 20 | -------------------------------------------------------------------------------- /docs/requirements.md: -------------------------------------------------------------------------------- 1 | # Requirements 2 | 3 | ## 1. Install minikube 4 | 5 | see https://kubernetes.io/ja/docs/tasks/tools/install-minikube 6 | 7 | ## 2. Register Insecure Registry 8 | 9 | Add Insecure Registry to Docker Engine on your local machine (e.g. On Mac, Preferences -> Docker Engine). 10 | 11 | ```json 12 | { 13 | "insecure-registries": [ 14 | "docker.for.mac.localhost:5000" 15 | ] 16 | } 17 | ``` 18 | 19 | -------------------------------------------------------------------------------- /docs/setup.ja.md: -------------------------------------------------------------------------------- 1 | # Requirements 2 | 3 | ## 1. クラスタの構築 4 | 5 | ```shell 6 | $ minikube start --driver=virtualbox 7 | ``` 8 | 9 | ## 2. 問題の登録 10 | 11 | ```shell 12 | $ ./setup.sh 13 | ``` 14 | 15 | 特にエラーもなく、次のような namespace が作成されていれば OK です。 16 | 17 | ```shell 18 | ❯ kubectl get ns 19 | NAME STATUS AGE 20 | can-you-keep-a-secret Active 13m 21 | default Active 14m 22 | kube-node-lease Active 14m 23 | kube-public Active 14m 24 | kube-system Active 14m 25 | mountme Active 13m 26 | mountme2 Active 13m 27 | treasure-hunt Active 13m 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/setup.md: -------------------------------------------------------------------------------- 1 | # Requirements 2 | 3 | ## 1. Create a cluster 4 | 5 | ```shell 6 | $ minikube start --driver=virtualbox 7 | ``` 8 | 9 | ## 2. Setup challenges 10 | 11 | ```shell 12 | $ ./setup.sh 13 | ``` 14 | 15 | Make sure you have the following namespace. 16 | 17 | ``` 18 | ❯ kubectl get ns 19 | NAME STATUS AGE 20 | can-you-keep-a-secret Active 13m 21 | default Active 14m 22 | kube-node-lease Active 14m 23 | kube-public Active 14m 24 | kube-system Active 14m 25 | mountme Active 13m 26 | mountme2 Active 13m 27 | treasure-hunt Active 13m 28 | ``` 29 | -------------------------------------------------------------------------------- /manifests/00_namespaces/namespace.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | creationTimestamp: null 5 | name: treasure-hunt 6 | 7 | --- 8 | apiVersion: v1 9 | kind: Namespace 10 | metadata: 11 | creationTimestamp: null 12 | name: mountme 13 | 14 | --- 15 | apiVersion: v1 16 | kind: Namespace 17 | metadata: 18 | creationTimestamp: null 19 | name: mountme2 20 | 21 | --- 22 | apiVersion: v1 23 | kind: Namespace 24 | metadata: 25 | creationTimestamp: null 26 | name: can-you-keep-a-secret 27 | 28 | --- 29 | apiVersion: v1 30 | kind: Namespace 31 | metadata: 32 | creationTimestamp: null 33 | name: sniff 34 | 35 | -------------------------------------------------------------------------------- /manifests/01_challenges/can-you-keep-a-secret/README.ja.md: -------------------------------------------------------------------------------- 1 | # Can You Keep A Secret? 2 | 3 | victim Pod は Kubernetes クラスタの etcd を操作するための Pod です。 4 | `kube-system` に flag という secret があるので、それを取得してください。 5 | 6 | ```shell 7 | $ kubens can-you-keep-a-secret 8 | ❯ kubectl get pods 9 | NAME READY STATUS RESTARTS AGE 10 | victim-56958dffc6-8chjx 1/1 Running 0 27m 11 | ❯ kubectl exec -it victim-56958dffc6-8chjx sh 12 | / # 13 | ``` 14 | 15 |
16 | Hint 1 17 | Network Namespace が分離されていないということは...? 18 |
19 | -------------------------------------------------------------------------------- /manifests/01_challenges/can-you-keep-a-secret/README.md: -------------------------------------------------------------------------------- 1 | # Can You Keep A Secret? 2 | 3 | The victim pod is for operating etcd on Kubernetes cluster. 4 | Flag is in `secret` in `kube-system` namespace. 5 | 6 | ```shell 7 | $ kubens can-you-keep-a-secret 8 | ❯ kubectl get pods 9 | NAME READY STATUS RESTARTS AGE 10 | victim-56958dffc6-8chjx 1/1 Running 0 27m 11 | ❯ kubectl exec -it victim-56958dffc6-8chjx sh 12 | / # 13 | ``` 14 | 15 |
16 | Hint 1 17 | Network namespace is not isolated. 18 |
19 | 20 | -------------------------------------------------------------------------------- /manifests/01_challenges/can-you-keep-a-secret/flag.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | flag: azhzY3Rmezk0ZDViY2RmNjFlMDRjNjNjNGU3ZGRkNTIzZDIyMzI1ZmNmMzNkZWZ9 4 | kind: Secret 5 | metadata: 6 | creationTimestamp: null 7 | name: flag 8 | namespace: kube-system 9 | -------------------------------------------------------------------------------- /manifests/01_challenges/can-you-keep-a-secret/victim.yaml: -------------------------------------------------------------------------------- 1 | kind: Deployment 2 | apiVersion: apps/v1 3 | metadata: 4 | name: victim 5 | namespace: can-you-keep-a-secret 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: victim 11 | template: 12 | metadata: 13 | labels: 14 | app: victim 15 | spec: 16 | hostNetwork: true 17 | containers: 18 | - name: victim 19 | image: gcr.io/etcd-development/etcd 20 | command: ["tail", "-f", "/dev/null"] 21 | volumeMounts: 22 | - name: etcd-credentials 23 | mountPath: /mnt/etcd-credentials 24 | volumes: 25 | - name: etcd-credentials 26 | secret: 27 | secretName: etcd-credentials 28 | 29 | -------------------------------------------------------------------------------- /manifests/01_challenges/mountme/README.ja.md: -------------------------------------------------------------------------------- 1 | # Mount me 2 | 3 | ## Red Challenge 4 | 5 | フラグはノードの /home/docker/mount1 にあります。 6 | 7 | ```shell 8 | $ kubens mountme 9 | $ kubectl exec -it vicitm-7c5745b4dc-jxd5t bash 10 | ``` 11 | 12 |
13 | Hint 1 14 | マウントされている ServiceAccount の権限は? 15 |
16 | 17 |
18 | Hint 2 19 | hostPath マウント 20 |
21 | 22 | ## Blue Challenge 23 | 24 | * Disable hostPath mount with OPA 25 | * Detect hostPath mount with falco 26 | -------------------------------------------------------------------------------- /manifests/01_challenges/mountme/README.md: -------------------------------------------------------------------------------- 1 | # Mount me 2 | 3 | ## Red Challenge 4 | 5 | Flag is in /home/docker/mount1 directory at node. 6 | 7 | ```shell 8 | $ kubens mountme 9 | $ kubectl exec -it vicitm-7c5745b4dc-jxd5t bash 10 | ``` 11 | 12 |
13 | Hint 1 14 | What are the privileges of the mounted ServiceAccount? 15 |
16 | 17 |
18 | Hint 2 19 | hostPath mount 20 |
21 | 22 | ## Blue Challenge 23 | 24 | * Disable hostPath mount with OPA 25 | * Detect hostPath mount with falco 26 | -------------------------------------------------------------------------------- /manifests/01_challenges/mountme/job-sa.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: runner 6 | namespace: mountme 7 | automountServiceAccountToken: false 8 | 9 | --- 10 | kind: Role 11 | apiVersion: rbac.authorization.k8s.io/v1 12 | metadata: 13 | name: job-runner 14 | namespace: mountme 15 | rules: 16 | - apiGroups: ["batch", "extensions"] 17 | resources: ["jobs", "job/status"] 18 | verbs: ["*"] 19 | - apiGroups: [""] 20 | resources: ["pods", "pods/binding", "pods/log", "pods/status"] 21 | verbs: ["get", "list"] 22 | 23 | --- 24 | kind: RoleBinding 25 | apiVersion: rbac.authorization.k8s.io/v1 26 | metadata: 27 | name: job-runner 28 | namespace: mountme 29 | subjects: 30 | - kind: ServiceAccount 31 | name: runner 32 | namespace: mountme 33 | roleRef: 34 | kind: Role 35 | name: job-runner 36 | apiGroup: rbac.authorization.k8s.io 37 | -------------------------------------------------------------------------------- /manifests/01_challenges/mountme/victim.yaml: -------------------------------------------------------------------------------- 1 | kind: Deployment 2 | apiVersion: apps/v1 3 | metadata: 4 | name: victim 5 | namespace: mountme 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: victim 11 | template: 12 | metadata: 13 | labels: 14 | app: victim 15 | spec: 16 | serviceAccountName: runner 17 | automountServiceAccountToken: true 18 | containers: 19 | - name: victim 20 | image: bitnami/kubectl:1.19.4 21 | command: ["tail", "-f", "/dev/null"] 22 | -------------------------------------------------------------------------------- /manifests/01_challenges/mountme2/README.ja.md: -------------------------------------------------------------------------------- 1 | # Mount me 2 2 | 3 | ## Red Challenge 4 | 5 | Node の `/home/docker/mountme2` に flag があります。 6 | 7 | ```shell 8 | ❯ kubens mountme2 9 | 10 | ❯ kubectl get pods 11 | NAME READY STATUS RESTARTS AGE 12 | victim-7c5745b4dc-42mx8 1/1 Running 0 53m 13 | 14 | ❯ kubectl exec -it victim-7c5745b4dc-42mx8 bash 15 | I have no name!@victim-7c5745b4dc-42mx8:/$ 16 | ``` 17 | 18 |
19 | Hint 1 20 | hostPath マウントは Gatekeeper で禁止されています。 21 |
22 | 23 |
24 | Hint 2 25 | Namespace 26 |
27 | 28 | 29 | ## Blue Challenge 30 | 31 | * Disable create privileged container with OPA 32 | * Detect create privileged container with falco 33 | -------------------------------------------------------------------------------- /manifests/01_challenges/mountme2/README.md: -------------------------------------------------------------------------------- 1 | # Mount me 2 2 | 3 | ## Red Challenge 4 | 5 | Flag is in /home/docker/mount2 directory at node. 6 | 7 | ```shell 8 | ❯ kubens mountme2 9 | 10 | ❯ kubectl get pods 11 | NAME READY STATUS RESTARTS AGE 12 | victim-7c5745b4dc-42mx8 1/1 Running 0 53m 13 | 14 | ❯ kubectl exec -it victim-7c5745b4dc-42mx8 bash 15 | I have no name!@victim-7c5745b4dc-42mx8:/$ 16 | ``` 17 | 18 |
19 | Hint 1 20 | hostPath mount is prohibited by Gatekeeper. 21 |
22 | 23 |
24 | Hint 2 25 | Namespace 26 |
27 | 28 | 29 | ## Blue Challenge 30 | 31 | * Disable create privileged container with OPA 32 | * Detect create privileged container with falco 33 | -------------------------------------------------------------------------------- /manifests/01_challenges/mountme2/flag/FLAG.txt: -------------------------------------------------------------------------------- 1 | k8sctf{396b1bc80afa7234a2977f709d35e787b9c7cfa3} 2 | -------------------------------------------------------------------------------- /manifests/01_challenges/mountme2/job-sa.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: runner 6 | namespace: mountme2 7 | 8 | --- 9 | kind: ClusterRole 10 | apiVersion: rbac.authorization.k8s.io/v1 11 | metadata: 12 | name: job-runner 13 | rules: 14 | - apiGroups: ["policy"] 15 | resources: ["podsecuritypolicies"] 16 | resourceNames: ["restricted"] 17 | verbs: ["use"] 18 | - apiGroups: ["batch", "extensions"] 19 | resources: ["jobs", "job/status"] 20 | verbs: ["*"] 21 | - apiGroups: [""] 22 | resources: ["pods", "pods/binding", "pods/log", "pods/status"] 23 | verbs: ["get", "list", "create"] 24 | 25 | --- 26 | kind: RoleBinding 27 | apiVersion: rbac.authorization.k8s.io/v1 28 | metadata: 29 | name: runner 30 | namespace: mountme2 31 | subjects: 32 | - kind: ServiceAccount 33 | name: runner 34 | namespace: mountme2 35 | roleRef: 36 | kind: ClusterRole 37 | name: job-runner 38 | apiGroup: rbac.authorization.k8s.io 39 | -------------------------------------------------------------------------------- /manifests/01_challenges/mountme2/victim.yaml: -------------------------------------------------------------------------------- 1 | kind: Deployment 2 | apiVersion: apps/v1 3 | metadata: 4 | name: victim 5 | namespace: mountme2 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: victim 11 | template: 12 | metadata: 13 | labels: 14 | app: victim 15 | spec: 16 | serviceAccountName: runner 17 | automountServiceAccountToken: true 18 | containers: 19 | - name: victim 20 | image: bitnami/kubectl:1.19.4 21 | command: ["tail", "-f", "/dev/null"] 22 | -------------------------------------------------------------------------------- /manifests/01_challenges/sniff/README.ja.md: -------------------------------------------------------------------------------- 1 | # Sniff 2 | 3 | ## Red Challenge 4 | 5 | find a flag. 6 | 7 | ```shell 8 | $ kubectl get pods -n sniff 9 | NAME READY STATUS RESTARTS AGE 10 | client-66b6f5cdcf-v5n9j 1/1 Running 0 3m24s 11 | server-79b88f567-v4xsp 1/1 Running 0 3m26s 12 | victim-cb9b4c946-d7vkq 1/1 Running 0 15m 13 | 14 | $ kubectl exec -it victim-cb9b4c946-d7vkq bash 15 | ``` 16 | 17 |
18 | Hint 1 19 | Client から Server に HTTP リクエストが送られているので盗聴しよう 20 |
21 | 22 |
23 | Hint 2 24 | DNS Spoofing 25 |
26 | 27 | ## Blue Challenge 28 | 29 | * Drop `CAP_NET_RAW` 30 | * Add a OPA policy to validate `CAP_NET_RAW` 31 | -------------------------------------------------------------------------------- /manifests/01_challenges/sniff/README.md: -------------------------------------------------------------------------------- 1 | # Sniff 2 | 3 | ## Red Challenge 4 | 5 | Find a flag. 6 | 7 | ```shell 8 | $ kubectl get pods -n sniff 9 | NAME READY STATUS RESTARTS AGE 10 | client-66b6f5cdcf-v5n9j 1/1 Running 0 3m24s 11 | server-79b88f567-v4xsp 1/1 Running 0 3m26s 12 | victim-cb9b4c946-d7vkq 1/1 Running 0 15m 13 | 14 | $ kubectl exec -it victim-cb9b4c946-d7vkq bash 15 | ``` 16 | 17 |
18 | Hint 1 19 | An HTTP request is being sent from the client to the server. 20 |
21 | 22 |
23 | Hint 2 24 | DNS Spoofing 25 |
26 | 27 | ## Blue Challenge 28 | 29 | * Drop `CAP_NET_RAW` 30 | * Add a OPA policy to validate `CAP_NET_RAW` 31 | -------------------------------------------------------------------------------- /manifests/01_challenges/sniff/client.yaml: -------------------------------------------------------------------------------- 1 | kind: Deployment 2 | apiVersion: apps/v1 3 | metadata: 4 | name: client 5 | namespace: sniff 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: client 11 | template: 12 | metadata: 13 | labels: 14 | app: client 15 | spec: 16 | containers: 17 | - name: client 18 | image: nicolaka/netshoot:latest 19 | command: ["sh", "-c", "while true; do curl -X POST -d \"k8sctf{25cb1d440f87baf0cf09e8bd3d349df2204c4481}\" server.sniff.svc.cluster.local; sleep 5; done"] 20 | -------------------------------------------------------------------------------- /manifests/01_challenges/sniff/server.yaml: -------------------------------------------------------------------------------- 1 | kind: Deployment 2 | apiVersion: apps/v1 3 | metadata: 4 | name: server 5 | namespace: sniff 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: server 11 | template: 12 | metadata: 13 | labels: 14 | app: server 15 | spec: 16 | containers: 17 | - name: server 18 | image: nginx:latest 19 | -------------------------------------------------------------------------------- /manifests/01_challenges/sniff/service.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: server 5 | namespace: sniff 6 | spec: 7 | selector: 8 | app: server 9 | ports: 10 | - port: 80 11 | -------------------------------------------------------------------------------- /manifests/01_challenges/sniff/victim.yaml: -------------------------------------------------------------------------------- 1 | kind: Deployment 2 | apiVersion: apps/v1 3 | metadata: 4 | name: victim 5 | namespace: sniff 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: victim 11 | template: 12 | metadata: 13 | labels: 14 | app: victim 15 | spec: 16 | containers: 17 | - name: victim 18 | image: python:latest 19 | command: ["tail", "-f", "/dev/null"] 20 | -------------------------------------------------------------------------------- /manifests/01_challenges/treasure-hunt/README.md: -------------------------------------------------------------------------------- 1 | # Treasure Hunt 2 | 3 | ## Red Challenge 4 | 5 | Find a flag. 6 | 7 | ```shell 8 | ❯ kubens treasure-hunt 9 | 10 | ❯ kubectl get pods 11 | NAME READY STATUS RESTARTS AGE 12 | docker-registry-6b5b55b44-zgvxf 1/1 Running 0 42m 13 | victim-68bcd4465f-q4pkb 1/1 Running 0 42m 14 | 15 | ❯ kubectl exec -it victim-68bcd4465f-q4pkb sh 16 | / # 17 | ``` 18 | 19 |
20 | Hint 1 21 | Where are the Docker Registry credentials? 22 |
23 | 24 |
25 | Hint 2 26 | Overlayfs 27 |
28 | 29 | ## Blue Challenge 30 | 31 | * Restricted access betweeen pods 32 | * Detect access to Docker Registry with falco 33 | -------------------------------------------------------------------------------- /manifests/01_challenges/treasure-hunt/docker-registry/certs/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICljCCAX4CCQCp4Seu4/CjazANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJK 3 | UDAeFw0yMDExMjQwNjUwMzZaFw0zMDExMjIwNjUwMzZaMA0xCzAJBgNVBAYTAkpQ 4 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp1t6BfCXf8EKry29x7ZD 5 | LUJSYQ7aW3WhoW70rz9XZobx5028EazH371qVcrk1xiBDouE4FyzZph2PL6QkN7S 6 | 9Kfic9QT8DvYv3MSkRTkzwO8EotGiQzkWbHCs/ewboq48BiXgcgAB45zexNg2Q92 7 | qTPry7axtKcv5CuejnKjrM+Gh6BZlnP9+wfzk9VIffYWXOwqV+PfzexeAn0rnlW/ 8 | sQl8gcQtf3SibbwSzvZU5v6edLHjo9ykCDTu5K0gJGOEMIMhwn5Ou6V0+RVDs2hZ 9 | 753mb9KT193aeh8kxuhjchSGchVf7DwfsBgZ2uUffov45eBd52CicjKt9jFwCbAq 10 | HQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQB0eeDxhqcz3NRfnJcix2ekdu0K0aoT 11 | sctOs2zAnSIZuAH6d7CQVscW6I387VWlKZPWqq9KogaeZm1sF80Wey+Oxjzno9KG 12 | 0ZJuz0iGzyrQ3esZegyWUxCaLBrHihIIQKl9Pk302os/QEeK7lW8jrIsKhF0YIGj 13 | UUUju8fSwJbUEKjRx2B2XtkJGuNZX0OHCQCwIqh/poj6X4sWT/3sZptJimokkzU9 14 | K6n4AeDHVS65RT15A1gVapf5PFUB5cSVD2L6dKx1Nl1HuAatgZkUqleTGCHVqK62 15 | 3ZB6wShdaxYLlHM1utZ2MWpMO+8eZ4549E4y4q2mpgnUYJ/9kiLLVUf+ 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /manifests/01_challenges/treasure-hunt/docker-registry/certs/ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQCnW3oF8Jd/wQqv 3 | Lb3HtkMtQlJhDtpbdaGhbvSvP1dmhvHnTbwRrMffvWpVyuTXGIEOi4TgXLNmmHY8 4 | vpCQ3tL0p+Jz1BPwO9i/cxKRFOTPA7wSi0aJDORZscKz97BuirjwGJeByAAHjnN7 5 | E2DZD3apM+vLtrG0py/kK56OcqOsz4aHoFmWc/37B/OT1Uh99hZc7CpX49/N7F4C 6 | fSueVb+xCXyBxC1/dKJtvBLO9lTm/p50seOj3KQINO7krSAkY4QwgyHCfk67pXT5 7 | FUOzaFnvneZv0pPX3dp6HyTG6GNyFIZyFV/sPB+wGBna5R9+i/jl4F3nYKJyMq32 8 | MXAJsCodAgMBAAECggEBAJnUq8Tp8VesyXVcRiVck0L2w2ITlQKcjYUlyCONxXio 9 | cZJ7EXPtrYsrQKK7bL2J+Ja2B91zWDPl0EjloVyIdMAN0UlGl00qtYDE9TwGSItA 10 | k484Fg3Dm+IWUzG5aqMxP+zxrdHNVLmdZuiqV0stu5Sr9QG7XH0BcDP7JDMrjOc6 11 | HY8B2Z2uW2U9iZRqyDygIZ/xg3mJyLGW3UiME/Y469a3xdk8aZ7QUZy7OQ1kFxKc 12 | Thnbf8fpXUTmz9SM+5bvWi5SNzlWNSvbVcmob3zCKwBVXdj/ZqAscK9u5RFHJa/Z 13 | ij/svN7dhzUmO/mmnPKGIR30UK/MR4JXg0y6BpZSxgECgYEA0gWgv7Dpcvd8PLPt 14 | VekCYesiZ6H4QUhTNo2Da1GuaPiIlVjTXPRwejpi2EeT/FtsDIAIinbCuJXfvjpn 15 | J5X6qI4PfN/13xR81bUrxjgWzyJx5Lp9dH4iRhS9nieRXL4O7EWAPowkSZHcq8U/ 16 | f0zgvsy+wxNzv8vvHkMS0pKMUIUCgYEAy/7FbdaMaIZ3L1pYj547T4pAvJy0vO9m 17 | XexVoHfX8jnhz1uE5RkD0LQ2QTlFOaWrC5Jvt0QuOmVUFQLQ4itc4toWMiD2hqBb 18 | aDyCGXCAFehvq0tFwdwQxftPn8eb4A1wLMxoihsaML5e2bh7toaDXdiUyyeSWnZD 19 | iY/o4p1QMrkCgYEAp4w72JFPPBt57odwELhp4SoPcXOCtLn5QK0WqbgpFq8nb1XE 20 | kQSk8q2/i2FXCtz4TuSZa7Ug1Ncj/wNipe79YBis/GdDJCuZB3iYKnM2pxqIl/u6 21 | +ANkXlpPh2tziPRKvW0YyU1N6a9Gx3X9mRYAudZnXQMYtWsBIThLPNgSvm0CgYEA 22 | gdq95ntqiab++3+xXErHSoyhRO8kBhkGuscUgr+HEURTP+HsKMHRserlloNUU6vn 23 | w8gFoU2fd8DE01MlkLM1v5pAAs0E4RH23dWWo4b/ofKCEzIpR4xEiSK+vuEbknY7 24 | b+j2quMxutGUI1fSOJBL01No9CFOgpNBzrFy975YLNECgYEAjcpg64eV3z6on2Jn 25 | DQRRPbkUzhg+Wmo2h6lUUdi/uSuS1ntCRnIUH6FY+2l6AIGxiIH4InL7oAhgJzi1 26 | tRXYJHh0X1s6JDr8FnXVL/A7ApvesMTaRLXNt3ySTFmb9KOmp0JXnJqfrvNdjUlb 27 | 2sY4yPTZZGL0zlM5W+aUadQGEuE= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /manifests/01_challenges/treasure-hunt/docker-registry/deployment.yml: -------------------------------------------------------------------------------- 1 | kind: Deployment 2 | apiVersion: apps/v1 3 | metadata: 4 | name: docker-registry 5 | namespace: treasure-hunt 6 | labels: 7 | app: docker-registry 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: docker-registry 13 | template: 14 | metadata: 15 | labels: 16 | app: docker-registry 17 | spec: 18 | containers: 19 | - name: docker-registry 20 | image: registry:2.7 21 | volumeMounts: 22 | - mountPath: "/var/lib/registry" 23 | name: registry 24 | - mountPath: "/auth/" 25 | name: docker-registry-secret 26 | - name: registry-config 27 | mountPath: /etc/docker/registry/ 28 | ports: 29 | - containerPort: 5000 30 | env: 31 | - name: REGISTRY_AUTH 32 | value: htpasswd 33 | - name: REGISTRY_AUTH_HTPASSWD_REALM 34 | value: "Registry Realm" 35 | - name: REGISTRY_AUTH_HTPASSWD_PATH 36 | value: "/auth/docker-registry-htpasswd" 37 | - name: REGISTRY_HTTP_TLS_CERTIFICATE 38 | value: "/auth/ca.crt" 39 | - name: REGISTRY_HTTP_TLS_KEY 40 | value: "/auth/ca.key" 41 | - name: REGISTRY_HTTP_ADDR 42 | value: 0.0.0.0:5000 43 | volumes: 44 | - name: registry-config 45 | configMap: 46 | name: registry-config 47 | - name: registry 48 | hostPath: 49 | path: /data/registry 50 | type: DirectoryOrCreate 51 | - name: docker-registry-secret 52 | secret: 53 | secretName: docker-registry-secret 54 | items: 55 | - key: REGISTRY_AUTH_HTPASSWD 56 | path: "docker-registry-htpasswd" 57 | - key: REGISTRY_HTTP_TLS_CERTIFICATE 58 | path: "ca.crt" 59 | - key: REGISTRY_HTTP_TLS_KEY 60 | path: "ca.key" 61 | -------------------------------------------------------------------------------- /manifests/01_challenges/treasure-hunt/docker-registry/htpasswd: -------------------------------------------------------------------------------- 1 | ctf:$2y$05$4hTo.jGtSsQc7cuW5bWz0.CQQ.pCQ6df6L8pokeXNo1f9gVAgx2bK 2 | 3 | -------------------------------------------------------------------------------- /manifests/01_challenges/treasure-hunt/docker-registry/registry-config.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | config.yml: | 4 | version: 0.1 5 | log: 6 | level: debug 7 | fields: 8 | service: registry 9 | storage: 10 | cache: 11 | blobdescriptor: inmemory 12 | filesystem: 13 | rootdirectory: /var/lib/registry 14 | http: 15 | addr: :5000 16 | headers: 17 | X-Content-Type-Options: [nosniff] 18 | health: 19 | storagedriver: 20 | enabled: true 21 | interval: 10s 22 | threshold: 3 23 | kind: ConfigMap 24 | metadata: 25 | creationTimestamp: null 26 | name: registry-config 27 | namespace: treasure-hunt 28 | -------------------------------------------------------------------------------- /manifests/01_challenges/treasure-hunt/docker-registry/secrets.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | REGISTRY_AUTH_HTPASSWD: Y3RmOiQyeSQwNSQ0aFRvLmpHdFNzUWM3Y3VXNWJXejAuQ1FRLnBDUTZkZjZMOHBva2VYTm8xZjlnVkFneDJiSwoK 4 | REGISTRY_HTTP_TLS_CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNsakNDQVg0Q0NRQ3A0U2V1NC9DamF6QU5CZ2txaGtpRzl3MEJBUXNGQURBTk1Rc3dDUVlEVlFRR0V3SksKVURBZUZ3MHlNREV4TWpRd05qVXdNelphRncwek1ERXhNakl3TmpVd016WmFNQTB4Q3pBSkJnTlZCQVlUQWtwUQpNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXAxdDZCZkNYZjhFS3J5Mjl4N1pECkxVSlNZUTdhVzNXaG9XNzByejlYWm9ieDUwMjhFYXpIMzcxcVZjcmsxeGlCRG91RTRGeXpacGgyUEw2UWtON1MKOUtmaWM5UVQ4RHZZdjNNU2tSVGt6d084RW90R2lRemtXYkhDcy9ld2JvcTQ4QmlYZ2NnQUI0NXpleE5nMlE5MgpxVFByeTdheHRLY3Y1Q3Vlam5LanJNK0doNkJabG5QOSt3ZnprOVZJZmZZV1hPd3FWK1BmemV4ZUFuMHJubFcvCnNRbDhnY1F0ZjNTaWJid1N6dlpVNXY2ZWRMSGpvOXlrQ0RUdTVLMGdKR09FTUlNaHduNU91NlYwK1JWRHMyaFoKNzUzbWI5S1QxOTNhZWg4a3h1aGpjaFNHY2hWZjdEd2ZzQmdaMnVVZmZvdjQ1ZUJkNTJDaWNqS3Q5akZ3Q2JBcQpIUUlEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQjBlZUR4aHFjejNOUmZuSmNpeDJla2R1MEswYW9UCnNjdE9zMnpBblNJWnVBSDZkN0NRVnNjVzZJMzg3VldsS1pQV3FxOUtvZ2FlWm0xc0Y4MFdleStPeGp6bm85S0cKMFpKdXowaUd6eXJRM2VzWmVneVdVeENhTEJySGloSUlRS2w5UGszMDJvcy9RRWVLN2xXOGpySXNLaEYwWUlHagpVVVVqdThmU3dKYlVFS2pSeDJCMlh0a0pHdU5aWDBPSENRQ3dJcWgvcG9qNlg0c1dULzNzWnB0Smltb2trelU5Cks2bjRBZURIVlM2NVJUMTVBMWdWYXBmNVBGVUI1Y1NWRDJMNmRLeDFObDFIdUFhdGdaa1VxbGVUR0NIVnFLNjIKM1pCNndTaGRheFlMbEhNMXV0WjJNV3BNTys4ZVo0NTQ5RTR5NHEybXBnblVZSi85a2lMTFZVZisKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= 5 | REGISTRY_HTTP_TLS_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV3QUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktvd2dnU21BZ0VBQW9JQkFRQ25XM29GOEpkL3dRcXYKTGIzSHRrTXRRbEpoRHRwYmRhR2hidlN2UDFkbWh2SG5UYndSck1mZnZXcFZ5dVRYR0lFT2k0VGdYTE5tbUhZOAp2cENRM3RMMHArSnoxQlB3TzlpL2N4S1JGT1RQQTd3U2kwYUpET1Jac2NLejk3QnVpcmp3R0plQnlBQUhqbk43CkUyRFpEM2FwTSt2THRyRzBweS9rSzU2T2NxT3N6NGFIb0ZtV2MvMzdCL09UMVVoOTloWmM3Q3BYNDkvTjdGNEMKZlN1ZVZiK3hDWHlCeEMxL2RLSnR2QkxPOWxUbS9wNTBzZU9qM0tRSU5PN2tyU0FrWTRRd2d5SENmazY3cFhUNQpGVU96YUZudm5lWnYwcFBYM2RwNkh5VEc2R055RklaeUZWL3NQQit3R0JuYTVSOStpL2psNEYzbllLSnlNcTMyCk1YQUpzQ29kQWdNQkFBRUNnZ0VCQUpuVXE4VHA4VmVzeVhWY1JpVmNrMEwydzJJVGxRS2NqWVVseUNPTnhYaW8KY1pKN0VYUHRyWXNyUUtLN2JMMkorSmEyQjkxeldEUGwwRWpsb1Z5SWRNQU4wVWxHbDAwcXRZREU5VHdHU0l0QQprNDg0RmczRG0rSVdVekc1YXFNeFArenhyZEhOVkxtZFp1aXFWMHN0dTVTcjlRRzdYSDBCY0RQN0pETXJqT2M2CkhZOEIyWjJ1VzJVOWlaUnF5RHlnSVoveGczbUp5TEdXM1VpTUUvWTQ2OWEzeGRrOGFaN1FVWnk3T1Exa0Z4S2MKVGhuYmY4ZnBYVVRtejlTTSs1YnZXaTVTTnpsV05TdmJWY21vYjN6Q0t3QlZYZGovWnFBc2NLOXU1UkZISmEvWgppai9zdk43ZGh6VW1PL21tblBLR0lSMzBVSy9NUjRKWGcweTZCcFpTeGdFQ2dZRUEwZ1dndjdEcGN2ZDhQTFB0ClZla0NZZXNpWjZINFFVaFRObzJEYTFHdWFQaUlsVmpUWFBSd2VqcGkyRWVUL0Z0c0RJQUlpbmJDdUpYZnZqcG4KSjVYNnFJNFBmTi8xM3hSODFiVXJ4amdXenlKeDVMcDlkSDRpUmhTOW5pZVJYTDRPN0VXQVBvd2tTWkhjcThVLwpmMHpndnN5K3d4Tnp2OHZ2SGtNUzBwS01VSVVDZ1lFQXkvN0ZiZGFNYUlaM0wxcFlqNTQ3VDRwQXZKeTB2TzltClhleFZvSGZYOGpuaHoxdUU1UmtEMExRMlFUbEZPYVdyQzVKdnQwUXVPbVZVRlFMUTRpdGM0dG9XTWlEMmhxQmIKYUR5Q0dYQ0FGZWh2cTB0Rndkd1F4ZnRQbjhlYjRBMXdMTXhvaWhzYU1MNWUyYmg3dG9hRFhkaVV5eWVTV25aRAppWS9vNHAxUU1ya0NnWUVBcDR3NzJKRlBQQnQ1N29kd0VMaHA0U29QY1hPQ3RMbjVRSzBXcWJncEZxOG5iMVhFCmtRU2s4cTIvaTJGWEN0ejRUdVNaYTdVZzFOY2ovd05pcGU3OVlCaXMvR2RESkN1WkIzaVlLbk0ycHhxSWwvdTYKK0FOa1hscFBoMnR6aVBSS3ZXMFl5VTFONmE5R3gzWDltUllBdWRablhRTVl0V3NCSVRoTFBOZ1N2bTBDZ1lFQQpnZHE5NW50cWlhYisrMyt4WEVySFNveWhSTzhrQmhrR3VzY1VncitIRVVSVFArSHNLTUhSc2VybGxvTlVVNnZuCnc4Z0ZvVTJmZDhERTAxTWxrTE0xdjVwQUFzMEU0UkgyM2RXV280Yi9vZktDRXpJcFI0eEVpU0srdnVFYmtuWTcKYitqMnF1TXh1dEdVSTFmU09KQkwwMU5vOUNGT2dwTkJ6ckZ5OTc1WUxORUNnWUVBamNwZzY0ZVYzejZvbjJKbgpEUVJSUGJrVXpoZytXbW8yaDZsVVVkaS91U3VTMW50Q1JuSVVINkZZKzJsNkFJR3hpSUg0SW5MN29BaGdKemkxCnRSWFlKSGgwWDFzNkpEcjhGblhWTC9BN0FwdmVzTVRhUkxYTnQzeVNURm1iOUtPbXAwSlhuSnFmcnZOZGpVbGIKMnNZNHlQVFpaR0wwemxNNVcrYVVhZFFHRXVFPQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg== 6 | kind: Secret 7 | metadata: 8 | creationTimestamp: null 9 | name: docker-registry-secret 10 | namespace: treasure-hunt 11 | -------------------------------------------------------------------------------- /manifests/01_challenges/treasure-hunt/docker-registry/service.yml: -------------------------------------------------------------------------------- 1 | kind: Service 2 | apiVersion: v1 3 | metadata: 4 | name: docker-registry 5 | namespace: treasure-hunt 6 | spec: 7 | selector: 8 | app: docker-registry 9 | type: ClusterIP 10 | ports: 11 | - protocol: TCP 12 | port: 5000 13 | targetPort: 5000 14 | name: docker-registry-http 15 | -------------------------------------------------------------------------------- /manifests/01_challenges/treasure-hunt/flag/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | ADD FLAG.txt /FLAG.txt 4 | RUN rm /FLAG.txt 5 | -------------------------------------------------------------------------------- /manifests/01_challenges/treasure-hunt/flag/FLAG.txt: -------------------------------------------------------------------------------- 1 | k8sctf{cb5639e30f2f7ae7af0b319e6bc5571cb81a4e49} 2 | -------------------------------------------------------------------------------- /manifests/01_challenges/treasure-hunt/flag/docker-registry-password: -------------------------------------------------------------------------------- 1 | fj8ur3gy837nbt8g7r89yfbrwye89 2 | -------------------------------------------------------------------------------- /manifests/01_challenges/treasure-hunt/victim.yaml: -------------------------------------------------------------------------------- 1 | kind: Deployment 2 | apiVersion: apps/v1 3 | metadata: 4 | name: victim 5 | namespace: treasure-hunt 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: victim 11 | template: 12 | metadata: 13 | labels: 14 | app: victim 15 | spec: 16 | containers: 17 | - name: victim 18 | image: nicolaka/netshoot:latest 19 | command: ["tail", "-f", "/dev/null"] 20 | env: 21 | - name: DOCKER_USERNAME 22 | value: ctf 23 | - name: DOCKER_PASSWORD 24 | value: fj8ur3gy837nbt8g7r89yfbrwye89 25 | 26 | -------------------------------------------------------------------------------- /manifests/flags/mountme/FLAG.txt: -------------------------------------------------------------------------------- 1 | k8sctf{396b1bc80afa7234a2977f709d35e787b9c7cfa3} 2 | -------------------------------------------------------------------------------- /manifests/flags/mountme2/FLAG.txt: -------------------------------------------------------------------------------- 1 | k8sctf{ec7738c6d0ca58dd8005906a5c8da8129be4bf74} 2 | -------------------------------------------------------------------------------- /manifests/gatekeeper/00_gatekeeper.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | admission.gatekeeper.sh/ignore: no-self-managing 6 | control-plane: controller-manager 7 | gatekeeper.sh/system: "yes" 8 | name: gatekeeper-system 9 | --- 10 | apiVersion: apiextensions.k8s.io/v1beta1 11 | kind: CustomResourceDefinition 12 | metadata: 13 | annotations: 14 | controller-gen.kubebuilder.io/version: v0.3.0 15 | creationTimestamp: null 16 | labels: 17 | gatekeeper.sh/system: "yes" 18 | name: configs.config.gatekeeper.sh 19 | spec: 20 | group: config.gatekeeper.sh 21 | names: 22 | kind: Config 23 | listKind: ConfigList 24 | plural: configs 25 | singular: config 26 | scope: Namespaced 27 | validation: 28 | openAPIV3Schema: 29 | description: Config is the Schema for the configs API 30 | properties: 31 | apiVersion: 32 | description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 33 | type: string 34 | kind: 35 | description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 36 | type: string 37 | metadata: 38 | type: object 39 | spec: 40 | description: ConfigSpec defines the desired state of Config 41 | properties: 42 | match: 43 | description: Configuration for namespace exclusion 44 | items: 45 | properties: 46 | excludedNamespaces: 47 | items: 48 | type: string 49 | type: array 50 | processes: 51 | items: 52 | type: string 53 | type: array 54 | type: object 55 | type: array 56 | readiness: 57 | description: Configuration for readiness tracker 58 | properties: 59 | statsEnabled: 60 | type: boolean 61 | type: object 62 | sync: 63 | description: Configuration for syncing k8s objects 64 | properties: 65 | syncOnly: 66 | description: If non-empty, only entries on this list will be replicated into OPA 67 | items: 68 | properties: 69 | group: 70 | type: string 71 | kind: 72 | type: string 73 | version: 74 | type: string 75 | type: object 76 | type: array 77 | type: object 78 | validation: 79 | description: Configuration for validation 80 | properties: 81 | traces: 82 | description: List of requests to trace. Both "user" and "kinds" must be specified 83 | items: 84 | properties: 85 | dump: 86 | description: Also dump the state of OPA with the trace. Set to `All` to dump everything. 87 | type: string 88 | kind: 89 | description: Only trace requests of the following GroupVersionKind 90 | properties: 91 | group: 92 | type: string 93 | kind: 94 | type: string 95 | version: 96 | type: string 97 | type: object 98 | user: 99 | description: Only trace requests from the specified user 100 | type: string 101 | type: object 102 | type: array 103 | type: object 104 | type: object 105 | status: 106 | description: ConfigStatus defines the observed state of Config 107 | type: object 108 | type: object 109 | version: v1alpha1 110 | versions: 111 | - name: v1alpha1 112 | served: true 113 | storage: true 114 | status: 115 | acceptedNames: 116 | kind: "" 117 | plural: "" 118 | conditions: [] 119 | storedVersions: [] 120 | --- 121 | apiVersion: apiextensions.k8s.io/v1beta1 122 | kind: CustomResourceDefinition 123 | metadata: 124 | annotations: 125 | controller-gen.kubebuilder.io/version: v0.3.0 126 | creationTimestamp: null 127 | labels: 128 | gatekeeper.sh/system: "yes" 129 | name: constraintpodstatuses.status.gatekeeper.sh 130 | spec: 131 | group: status.gatekeeper.sh 132 | names: 133 | kind: ConstraintPodStatus 134 | listKind: ConstraintPodStatusList 135 | plural: constraintpodstatuses 136 | singular: constraintpodstatus 137 | scope: Namespaced 138 | validation: 139 | openAPIV3Schema: 140 | description: ConstraintPodStatus is the Schema for the constraintpodstatuses API 141 | properties: 142 | apiVersion: 143 | description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 144 | type: string 145 | kind: 146 | description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 147 | type: string 148 | metadata: 149 | type: object 150 | status: 151 | description: ConstraintPodStatusStatus defines the observed state of ConstraintPodStatus 152 | properties: 153 | constraintUID: 154 | description: Storing the constraint UID allows us to detect drift, such as when a constraint has been recreated after its CRD was deleted out from under it, interrupting the watch 155 | type: string 156 | enforced: 157 | type: boolean 158 | errors: 159 | items: 160 | description: Error represents a single error caught while adding a constraint to OPA 161 | properties: 162 | code: 163 | type: string 164 | location: 165 | type: string 166 | message: 167 | type: string 168 | required: 169 | - code 170 | - message 171 | type: object 172 | type: array 173 | id: 174 | type: string 175 | observedGeneration: 176 | format: int64 177 | type: integer 178 | operations: 179 | items: 180 | type: string 181 | type: array 182 | type: object 183 | type: object 184 | version: v1beta1 185 | versions: 186 | - name: v1beta1 187 | served: true 188 | storage: true 189 | status: 190 | acceptedNames: 191 | kind: "" 192 | plural: "" 193 | conditions: [] 194 | storedVersions: [] 195 | --- 196 | apiVersion: apiextensions.k8s.io/v1beta1 197 | kind: CustomResourceDefinition 198 | metadata: 199 | annotations: 200 | controller-gen.kubebuilder.io/version: v0.3.0 201 | creationTimestamp: null 202 | labels: 203 | gatekeeper.sh/system: "yes" 204 | name: constrainttemplatepodstatuses.status.gatekeeper.sh 205 | spec: 206 | group: status.gatekeeper.sh 207 | names: 208 | kind: ConstraintTemplatePodStatus 209 | listKind: ConstraintTemplatePodStatusList 210 | plural: constrainttemplatepodstatuses 211 | singular: constrainttemplatepodstatus 212 | scope: Namespaced 213 | validation: 214 | openAPIV3Schema: 215 | description: ConstraintTemplatePodStatus is the Schema for the constrainttemplatepodstatuses API 216 | properties: 217 | apiVersion: 218 | description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 219 | type: string 220 | kind: 221 | description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 222 | type: string 223 | metadata: 224 | type: object 225 | status: 226 | description: ConstraintTemplatePodStatusStatus defines the observed state of ConstraintTemplatePodStatus 227 | properties: 228 | errors: 229 | items: 230 | description: CreateCRDError represents a single error caught during parsing, compiling, etc. 231 | properties: 232 | code: 233 | type: string 234 | location: 235 | type: string 236 | message: 237 | type: string 238 | required: 239 | - code 240 | - message 241 | type: object 242 | type: array 243 | id: 244 | description: 'Important: Run "make" to regenerate code after modifying this file' 245 | type: string 246 | observedGeneration: 247 | format: int64 248 | type: integer 249 | operations: 250 | items: 251 | type: string 252 | type: array 253 | templateUID: 254 | description: UID is a type that holds unique ID values, including UUIDs. Because we don't ONLY use UUIDs, this is an alias to string. Being a type captures intent and helps make sure that UIDs and names do not get conflated. 255 | type: string 256 | type: object 257 | type: object 258 | version: v1beta1 259 | versions: 260 | - name: v1beta1 261 | served: true 262 | storage: true 263 | status: 264 | acceptedNames: 265 | kind: "" 266 | plural: "" 267 | conditions: [] 268 | storedVersions: [] 269 | --- 270 | apiVersion: apiextensions.k8s.io/v1beta1 271 | kind: CustomResourceDefinition 272 | metadata: 273 | creationTimestamp: null 274 | labels: 275 | controller-tools.k8s.io: "1.0" 276 | gatekeeper.sh/system: "yes" 277 | name: constrainttemplates.templates.gatekeeper.sh 278 | spec: 279 | group: templates.gatekeeper.sh 280 | names: 281 | kind: ConstraintTemplate 282 | plural: constrainttemplates 283 | scope: Cluster 284 | subresources: 285 | status: {} 286 | validation: 287 | openAPIV3Schema: 288 | properties: 289 | apiVersion: 290 | description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 291 | type: string 292 | kind: 293 | description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 294 | type: string 295 | metadata: 296 | type: object 297 | spec: 298 | properties: 299 | crd: 300 | properties: 301 | spec: 302 | properties: 303 | names: 304 | properties: 305 | kind: 306 | type: string 307 | shortNames: 308 | items: 309 | type: string 310 | type: array 311 | type: object 312 | validation: 313 | type: object 314 | type: object 315 | type: object 316 | targets: 317 | items: 318 | properties: 319 | libs: 320 | items: 321 | type: string 322 | type: array 323 | rego: 324 | type: string 325 | target: 326 | type: string 327 | type: object 328 | type: array 329 | type: object 330 | status: 331 | properties: 332 | byPod: 333 | items: 334 | properties: 335 | errors: 336 | items: 337 | properties: 338 | code: 339 | type: string 340 | location: 341 | type: string 342 | message: 343 | type: string 344 | required: 345 | - code 346 | - message 347 | type: object 348 | type: array 349 | id: 350 | description: a unique identifier for the pod that wrote the status 351 | type: string 352 | observedGeneration: 353 | format: int64 354 | type: integer 355 | type: object 356 | type: array 357 | created: 358 | type: boolean 359 | type: object 360 | version: v1beta1 361 | versions: 362 | - name: v1beta1 363 | served: true 364 | storage: true 365 | - name: v1alpha1 366 | served: true 367 | storage: false 368 | status: 369 | acceptedNames: 370 | kind: "" 371 | plural: "" 372 | conditions: [] 373 | storedVersions: [] 374 | --- 375 | apiVersion: v1 376 | kind: ServiceAccount 377 | metadata: 378 | labels: 379 | gatekeeper.sh/system: "yes" 380 | name: gatekeeper-admin 381 | namespace: gatekeeper-system 382 | --- 383 | apiVersion: rbac.authorization.k8s.io/v1 384 | kind: Role 385 | metadata: 386 | creationTimestamp: null 387 | labels: 388 | gatekeeper.sh/system: "yes" 389 | name: gatekeeper-manager-role 390 | namespace: gatekeeper-system 391 | rules: 392 | - apiGroups: 393 | - "" 394 | resources: 395 | - events 396 | verbs: 397 | - create 398 | - patch 399 | - apiGroups: 400 | - "" 401 | resources: 402 | - secrets 403 | verbs: 404 | - create 405 | - delete 406 | - get 407 | - list 408 | - patch 409 | - update 410 | - watch 411 | --- 412 | apiVersion: rbac.authorization.k8s.io/v1 413 | kind: ClusterRole 414 | metadata: 415 | creationTimestamp: null 416 | labels: 417 | gatekeeper.sh/system: "yes" 418 | name: gatekeeper-manager-role 419 | rules: 420 | - apiGroups: 421 | - '*' 422 | resources: 423 | - '*' 424 | verbs: 425 | - get 426 | - list 427 | - watch 428 | - apiGroups: 429 | - apiextensions.k8s.io 430 | resources: 431 | - customresourcedefinitions 432 | verbs: 433 | - create 434 | - delete 435 | - get 436 | - list 437 | - patch 438 | - update 439 | - watch 440 | - apiGroups: 441 | - config.gatekeeper.sh 442 | resources: 443 | - configs 444 | verbs: 445 | - create 446 | - delete 447 | - get 448 | - list 449 | - patch 450 | - update 451 | - watch 452 | - apiGroups: 453 | - config.gatekeeper.sh 454 | resources: 455 | - configs/status 456 | verbs: 457 | - get 458 | - patch 459 | - update 460 | - apiGroups: 461 | - constraints.gatekeeper.sh 462 | resources: 463 | - '*' 464 | verbs: 465 | - create 466 | - delete 467 | - get 468 | - list 469 | - patch 470 | - update 471 | - watch 472 | - apiGroups: 473 | - policy 474 | resources: 475 | - podsecuritypolicies 476 | verbs: 477 | - use 478 | - apiGroups: 479 | - status.gatekeeper.sh 480 | resources: 481 | - '*' 482 | verbs: 483 | - create 484 | - delete 485 | - get 486 | - list 487 | - patch 488 | - update 489 | - watch 490 | - apiGroups: 491 | - templates.gatekeeper.sh 492 | resources: 493 | - constrainttemplates 494 | verbs: 495 | - create 496 | - delete 497 | - get 498 | - list 499 | - patch 500 | - update 501 | - watch 502 | - apiGroups: 503 | - templates.gatekeeper.sh 504 | resources: 505 | - constrainttemplates/finalizers 506 | verbs: 507 | - delete 508 | - get 509 | - patch 510 | - update 511 | - apiGroups: 512 | - templates.gatekeeper.sh 513 | resources: 514 | - constrainttemplates/status 515 | verbs: 516 | - get 517 | - patch 518 | - update 519 | - apiGroups: 520 | - admissionregistration.k8s.io 521 | resourceNames: 522 | - gatekeeper-validating-webhook-configuration 523 | resources: 524 | - validatingwebhookconfigurations 525 | verbs: 526 | - create 527 | - delete 528 | - get 529 | - list 530 | - patch 531 | - update 532 | - watch 533 | --- 534 | apiVersion: rbac.authorization.k8s.io/v1 535 | kind: RoleBinding 536 | metadata: 537 | labels: 538 | gatekeeper.sh/system: "yes" 539 | name: gatekeeper-manager-rolebinding 540 | namespace: gatekeeper-system 541 | roleRef: 542 | apiGroup: rbac.authorization.k8s.io 543 | kind: Role 544 | name: gatekeeper-manager-role 545 | subjects: 546 | - kind: ServiceAccount 547 | name: gatekeeper-admin 548 | namespace: gatekeeper-system 549 | --- 550 | apiVersion: rbac.authorization.k8s.io/v1 551 | kind: ClusterRoleBinding 552 | metadata: 553 | labels: 554 | gatekeeper.sh/system: "yes" 555 | name: gatekeeper-manager-rolebinding 556 | roleRef: 557 | apiGroup: rbac.authorization.k8s.io 558 | kind: ClusterRole 559 | name: gatekeeper-manager-role 560 | subjects: 561 | - kind: ServiceAccount 562 | name: gatekeeper-admin 563 | namespace: gatekeeper-system 564 | --- 565 | apiVersion: v1 566 | kind: Secret 567 | metadata: 568 | labels: 569 | gatekeeper.sh/system: "yes" 570 | name: gatekeeper-webhook-server-cert 571 | namespace: gatekeeper-system 572 | --- 573 | apiVersion: v1 574 | kind: Service 575 | metadata: 576 | labels: 577 | gatekeeper.sh/system: "yes" 578 | name: gatekeeper-webhook-service 579 | namespace: gatekeeper-system 580 | spec: 581 | ports: 582 | - port: 443 583 | targetPort: 8443 584 | selector: 585 | control-plane: controller-manager 586 | gatekeeper.sh/operation: webhook 587 | gatekeeper.sh/system: "yes" 588 | --- 589 | apiVersion: apps/v1 590 | kind: Deployment 591 | metadata: 592 | labels: 593 | control-plane: controller-manager 594 | gatekeeper.sh/operation: audit 595 | gatekeeper.sh/system: "yes" 596 | name: gatekeeper-audit 597 | namespace: gatekeeper-system 598 | spec: 599 | replicas: 1 600 | selector: 601 | matchLabels: 602 | control-plane: audit-controller 603 | gatekeeper.sh/operation: audit 604 | gatekeeper.sh/system: "yes" 605 | template: 606 | metadata: 607 | annotations: 608 | container.seccomp.security.alpha.kubernetes.io/manager: runtime/default 609 | labels: 610 | control-plane: audit-controller 611 | gatekeeper.sh/operation: audit 612 | gatekeeper.sh/system: "yes" 613 | spec: 614 | containers: 615 | - args: 616 | - --operation=audit 617 | - --operation=status 618 | - --logtostderr 619 | command: 620 | - /manager 621 | env: 622 | - name: POD_NAMESPACE 623 | valueFrom: 624 | fieldRef: 625 | apiVersion: v1 626 | fieldPath: metadata.namespace 627 | - name: POD_NAME 628 | valueFrom: 629 | fieldRef: 630 | fieldPath: metadata.name 631 | image: openpolicyagent/gatekeeper:v3.1.3 632 | imagePullPolicy: Always 633 | livenessProbe: 634 | httpGet: 635 | path: /healthz 636 | port: 9090 637 | name: manager 638 | ports: 639 | - containerPort: 8888 640 | name: metrics 641 | protocol: TCP 642 | - containerPort: 9090 643 | name: healthz 644 | protocol: TCP 645 | readinessProbe: 646 | httpGet: 647 | path: /readyz 648 | port: 9090 649 | resources: 650 | limits: 651 | cpu: 1000m 652 | memory: 512Mi 653 | requests: 654 | cpu: 100m 655 | memory: 256Mi 656 | securityContext: 657 | allowPrivilegeEscalation: false 658 | capabilities: 659 | drop: 660 | - all 661 | runAsGroup: 999 662 | runAsNonRoot: true 663 | runAsUser: 1000 664 | nodeSelector: 665 | kubernetes.io/os: linux 666 | serviceAccountName: gatekeeper-admin 667 | terminationGracePeriodSeconds: 60 668 | --- 669 | apiVersion: apps/v1 670 | kind: Deployment 671 | metadata: 672 | labels: 673 | control-plane: controller-manager 674 | gatekeeper.sh/operation: webhook 675 | gatekeeper.sh/system: "yes" 676 | name: gatekeeper-controller-manager 677 | namespace: gatekeeper-system 678 | spec: 679 | replicas: 3 680 | selector: 681 | matchLabels: 682 | control-plane: controller-manager 683 | gatekeeper.sh/operation: webhook 684 | gatekeeper.sh/system: "yes" 685 | template: 686 | metadata: 687 | annotations: 688 | container.seccomp.security.alpha.kubernetes.io/manager: runtime/default 689 | labels: 690 | control-plane: controller-manager 691 | gatekeeper.sh/operation: webhook 692 | gatekeeper.sh/system: "yes" 693 | spec: 694 | affinity: 695 | podAntiAffinity: 696 | preferredDuringSchedulingIgnoredDuringExecution: 697 | - podAffinityTerm: 698 | labelSelector: 699 | matchExpressions: 700 | - key: gatekeeper.sh/operation 701 | operator: In 702 | values: 703 | - webhook 704 | topologyKey: kubernetes.io/hostname 705 | weight: 100 706 | containers: 707 | - args: 708 | - --port=8443 709 | - --logtostderr 710 | - --exempt-namespace=gatekeeper-system 711 | - --operation=webhook 712 | command: 713 | - /manager 714 | env: 715 | - name: POD_NAMESPACE 716 | valueFrom: 717 | fieldRef: 718 | apiVersion: v1 719 | fieldPath: metadata.namespace 720 | - name: POD_NAME 721 | valueFrom: 722 | fieldRef: 723 | fieldPath: metadata.name 724 | image: openpolicyagent/gatekeeper:v3.1.3 725 | imagePullPolicy: Always 726 | livenessProbe: 727 | httpGet: 728 | path: /healthz 729 | port: 9090 730 | name: manager 731 | ports: 732 | - containerPort: 8443 733 | name: webhook-server 734 | protocol: TCP 735 | - containerPort: 8888 736 | name: metrics 737 | protocol: TCP 738 | - containerPort: 9090 739 | name: healthz 740 | protocol: TCP 741 | readinessProbe: 742 | httpGet: 743 | path: /readyz 744 | port: 9090 745 | resources: 746 | limits: 747 | cpu: 1000m 748 | memory: 512Mi 749 | requests: 750 | cpu: 100m 751 | memory: 256Mi 752 | securityContext: 753 | allowPrivilegeEscalation: false 754 | capabilities: 755 | drop: 756 | - all 757 | runAsGroup: 999 758 | runAsNonRoot: true 759 | runAsUser: 1000 760 | volumeMounts: 761 | - mountPath: /certs 762 | name: cert 763 | readOnly: true 764 | nodeSelector: 765 | kubernetes.io/os: linux 766 | serviceAccountName: gatekeeper-admin 767 | terminationGracePeriodSeconds: 60 768 | volumes: 769 | - name: cert 770 | secret: 771 | defaultMode: 420 772 | secretName: gatekeeper-webhook-server-cert 773 | --- 774 | apiVersion: admissionregistration.k8s.io/v1beta1 775 | kind: ValidatingWebhookConfiguration 776 | metadata: 777 | creationTimestamp: null 778 | labels: 779 | gatekeeper.sh/system: "yes" 780 | name: gatekeeper-validating-webhook-configuration 781 | webhooks: 782 | - clientConfig: 783 | caBundle: Cg== 784 | service: 785 | name: gatekeeper-webhook-service 786 | namespace: gatekeeper-system 787 | path: /v1/admit 788 | failurePolicy: Ignore 789 | name: validation.gatekeeper.sh 790 | namespaceSelector: 791 | matchExpressions: 792 | - key: admission.gatekeeper.sh/ignore 793 | operator: DoesNotExist 794 | rules: 795 | - apiGroups: 796 | - '*' 797 | apiVersions: 798 | - '*' 799 | operations: 800 | - CREATE 801 | - UPDATE 802 | resources: 803 | - '*' 804 | sideEffects: None 805 | timeoutSeconds: 3 806 | - clientConfig: 807 | caBundle: Cg== 808 | service: 809 | name: gatekeeper-webhook-service 810 | namespace: gatekeeper-system 811 | path: /v1/admitlabel 812 | failurePolicy: Fail 813 | name: check-ignore-label.gatekeeper.sh 814 | rules: 815 | - apiGroups: 816 | - "" 817 | apiVersions: 818 | - '*' 819 | operations: 820 | - CREATE 821 | - UPDATE 822 | resources: 823 | - namespaces 824 | sideEffects: None 825 | timeoutSeconds: 3 826 | -------------------------------------------------------------------------------- /manifests/gatekeeper/hostpath/00_template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: templates.gatekeeper.sh/v1beta1 2 | kind: ConstraintTemplate 3 | metadata: 4 | name: k8spsphostfilesystem 5 | spec: 6 | crd: 7 | spec: 8 | names: 9 | kind: K8sPSPHostFilesystem 10 | validation: 11 | openAPIV3Schema: 12 | properties: 13 | allowedHostPaths: 14 | type: array 15 | items: 16 | type: object 17 | properties: 18 | readOnly: 19 | type: boolean 20 | pathPrefix: 21 | type: string 22 | targets: 23 | - target: admission.k8s.gatekeeper.sh 24 | rego: | 25 | package k8spsphostfilesystem 26 | violation[{"msg": msg, "details": {}}] { 27 | volume := input_hostpath_volumes[_] 28 | allowedPaths := get_allowed_paths(input) 29 | input_hostpath_violation(allowedPaths, volume) 30 | msg := sprintf("HostPath volume %v is not allowed, pod: %v. Allowed path: %v", [volume, input.review.object.metadata.name, allowedPaths]) 31 | } 32 | input_hostpath_violation(allowedPaths, volume) { 33 | allowedPaths == [] 34 | } 35 | input_hostpath_violation(allowedPaths, volume) { 36 | not input_hostpath_allowed(allowedPaths, volume) 37 | } 38 | get_allowed_paths(arg) = out { 39 | not arg.parameters 40 | out = [] 41 | } 42 | get_allowed_paths(arg) = out { 43 | not arg.parameters.allowedHostPaths 44 | out = [] 45 | } 46 | get_allowed_paths(arg) = out { 47 | out = arg.parameters.allowedHostPaths 48 | } 49 | input_hostpath_allowed(allowedPaths, volume) { 50 | allowedHostPath := allowedPaths[_] 51 | path_matches(allowedHostPath.pathPrefix, volume.hostPath.path) 52 | not allowedHostPath.readOnly == true 53 | } 54 | input_hostpath_allowed(allowedPaths, volume) { 55 | allowedHostPath := allowedPaths[_] 56 | path_matches(allowedHostPath.pathPrefix, volume.hostPath.path) 57 | allowedHostPath.readOnly 58 | not writeable_input_volume_mounts(volume.name) 59 | } 60 | writeable_input_volume_mounts(volume_name) { 61 | container := input_containers[_] 62 | mount := container.volumeMounts[_] 63 | mount.name == volume_name 64 | not mount.readOnly 65 | } 66 | path_matches(prefix, path) { 67 | a := split(trim(prefix, "/"), "/") 68 | b := split(trim(path, "/"), "/") 69 | prefix_matches(a, b) 70 | } 71 | prefix_matches(a, b) { 72 | count(a) <= count(b) 73 | not any_not_equal_upto(a, b, count(a)) 74 | } 75 | any_not_equal_upto(a, b, n) { 76 | a[i] != b[i] 77 | i < n 78 | } 79 | input_hostpath_volumes[v] { 80 | v := input.review.object.spec.volumes[_] 81 | has_field(v, "hostPath") 82 | } 83 | has_field(object, field) = true { 84 | object[field] 85 | } 86 | input_containers[c] { 87 | c := input.review.object.spec.containers[_] 88 | } 89 | input_containers[c] { 90 | c := input.review.object.spec.initContainers[_] 91 | } 92 | -------------------------------------------------------------------------------- /manifests/gatekeeper/hostpath/01_constraint.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: constraints.gatekeeper.sh/v1beta1 2 | kind: K8sPSPHostFilesystem 3 | metadata: 4 | name: psp-host-filesystem 5 | spec: 6 | match: 7 | kinds: 8 | - apiGroups: [""] 9 | kinds: ["Pod"] 10 | parameters: 11 | allowedHostPaths: 12 | - readOnly: true 13 | pathPrefix: "/data" 14 | - readOnly: true 15 | pathPrefix: "/home/docker/mountme1" 16 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | kubectl apply -Rf manifests/00_namespaces/ 4 | echo "✅ Create namespace" 5 | 6 | kubectl apply -f manifests/gatekeeper/00_gatekeeper.yaml 7 | kubectl apply -f manifests/gatekeeper/hostpath/00_template.yaml 8 | echo "✅ Create Gatekeeper" 9 | 10 | # mountme 11 | minikube ssh -- mkdir -p /home/docker/mountme1 12 | scp -o StrictHostKeyChecking=no -i $(minikube ssh-key) manifests/flags/mountme/FLAG.txt docker@$(minikube ip):/home/docker/mountme1/FLAG.txt 13 | 14 | kubectl apply -f manifests/01_challenges/mountme/job-sa.yaml 15 | kubectl apply -f manifests/01_challenges/mountme/victim.yaml 16 | 17 | echo "✅ mountme OK" 18 | 19 | # mountme2 20 | minikube ssh -- mkdir -p /home/docker/mountme2 21 | scp -o StrictHostKeyChecking=no -i $(minikube ssh-key) manifests/flags/mountme2/FLAG.txt docker@$(minikube ip):/home/docker/mountme2/FLAG.txt 22 | 23 | kubectl apply -f manifests/01_challenges/mountme2/job-sa.yaml 24 | kubectl apply -f manifests/01_challenges/mountme2/victim.yaml 25 | echo "✅ mountme2 OK" 26 | 27 | # treasure hunt 28 | kubectl apply -f manifests/01_challenges/treasure-hunt/victim.yaml 29 | kubectl apply -f manifests/01_challenges/treasure-hunt/docker-registry/secrets.yml 30 | kubectl apply -f manifests/01_challenges/treasure-hunt/docker-registry/deployment.yml 31 | kubectl apply -f manifests/01_challenges/treasure-hunt/docker-registry/registry-config.yml 32 | kubectl apply -f manifests/01_challenges/treasure-hunt/docker-registry/service.yml 33 | 34 | cd manifests/01_challenges/treasure-hunt/flag 35 | 36 | while : 37 | do 38 | kubectl get pods -n treasure-hunt | grep docker-registry | grep Running 39 | if [ $? = 0 ]; then 40 | break 41 | fi 42 | echo "🕒 Waiting for a pod to running..." 43 | sleep 5 44 | done 45 | 46 | kubectl -n treasure-hunt port-forward svc/docker-registry 5000 & 47 | sleep 10 48 | 49 | cat docker-registry-password | docker -D login --username ctf --password-stdin https://docker.for.mac.localhost:5000 50 | docker build -t docker.for.mac.localhost:5000/flag/image:latest . 51 | docker push docker.for.mac.localhost:5000/flag/image:latest 52 | pkill -f 'kubectl -n treasure-hunt port-forward' 53 | cd ../../../../ 54 | 55 | echo "✅ treasure-hunt OK" 56 | 57 | # can you keep a secret? 58 | minikube ssh -- sudo cat /var/lib/minikube/certs/etcd/ca.crt > manifests/01_challenges/can-you-keep-a-secret/ca.crt 59 | minikube ssh -- sudo cat /var/lib/minikube/certs/etcd/healthcheck-client.key > manifests/01_challenges/can-you-keep-a-secret/healthcheck-client.key 60 | minikube ssh -- sudo cat /var/lib/minikube/certs/etcd/healthcheck-client.crt > manifests/01_challenges/can-you-keep-a-secret/healthcheck-client.crt 61 | kubectl -n can-you-keep-a-secret create secret generic etcd-credentials --from-file=ETCD_CA=manifests/01_challenges/can-you-keep-a-secret/ca.crt --from-file=ETCD_CRT=manifests/01_challenges/can-you-keep-a-secret/healthcheck-client.crt --from-file=ETCD_KEY=manifests/01_challenges/can-you-keep-a-secret/healthcheck-client.key 62 | 63 | kubectl apply -f manifests/01_challenges/can-you-keep-a-secret/victim.yaml 64 | kubectl apply -f manifests/01_challenges/can-you-keep-a-secret/flag.yaml 65 | echo "✅ can you keep a secret? OK" 66 | 67 | # sniff 68 | kubectl apply -f manifests/01_challenges/sniff/service.yml 69 | kubectl apply -f manifests/01_challenges/sniff/server.yaml 70 | kubectl apply -f manifests/01_challenges/sniff/victim.yaml 71 | kubectl apply -f manifests/01_challenges/sniff/client.yaml 72 | echo "✅ sniff OK" 73 | 74 | kubectl apply -f manifests/gatekeeper/hostpath/01_constraint.yaml 75 | echo "✅ Create Gatekeeper Constraint" 76 | echo "✅ All Done !!1" 77 | --------------------------------------------------------------------------------