Completed upto lesson 28

This commit is contained in:
Mosiur Rahman
2026-08-23 15:29:06 +06:00
parent fb86f0b60a
commit 8e43c00cd6
104 changed files with 1888 additions and 0 deletions

37
lab/README.md Normal file
View File

@@ -0,0 +1,37 @@
# Isolated CKS NetworkPolicy lab
This lab runs in the local Kind context `kind-cks-lab`, separate from the pre-existing `monitoring-context` cluster.
## Scenario
`api` and `intruder` are clients. `db` serves HTTP on port 8080. The policy allows only pods labelled `app=api` to reach the database.
## Repeat the proof
```powershell
kubectl --context kind-cks-lab -n cks-lab exec api -- wget -qO- -T 5 http://db:8080
kubectl --context kind-cks-lab -n cks-lab exec intruder -- wget -qO- -T 5 http://db:8080
```
The first command must print `database`. The second must time out, which proves the denial.
## Inspect the answer
```powershell
kubectl --context kind-cks-lab -n cks-lab get networkpolicy db-only-from-api -o yaml
kubectl --context kind-cks-lab -n cks-lab get pods --show-labels
```
The policy is in `networkpolicy-lab.yaml`. Edit it for new drills, then run:
```powershell
kubectl --context kind-cks-lab apply -f .\lab\networkpolicy-lab.yaml
```
## Cleanup
This is destructive and removes only the isolated Kind cluster:
```powershell
.\lab\bin\kind.exe delete cluster --name cks-lab
```

BIN
lab/bin/kind.exe Normal file

Binary file not shown.

9
lab/kind-calico.yaml Normal file
View File

@@ -0,0 +1,9 @@
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: cks-lab
nodes:
- role: control-plane
- role: worker
networking:
disableDefaultCNI: true
podSubnet: 192.168.0.0/16

57
lab/lesson-10-answer.yaml Normal file
View File

@@ -0,0 +1,57 @@
apiVersion: v1
kind: Namespace
metadata:
name: billing
---
apiVersion: v1
kind: Secret
metadata:
name: billing-credentials
namespace: billing
type: Opaque
stringData:
api-key: test-key
legacy-password: do-not-mount
---
apiVersion: v1
kind: Pod
metadata:
name: reconciler
namespace: billing
spec:
automountServiceAccountToken: false
securityContext:
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
volumes:
- name: api-key
secret:
secretName: billing-credentials
items:
- key: api-key
path: api-key
- name: temp
emptyDir: {}
containers:
- name: billing-container
image: busybox:1.37
command: ["sh","-c","sleep 3600"]
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: api-key
readOnly: true
mountPath: /var/run/credentials
- name: temp
mountPath: /tmp
resources:
requests:
cpu: "100m"
memory: 128Mi
limits:
cpu: "100m"
memory: 128Mi

View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: Namespace
metadata:
name: billing
---
apiVersion: v1
kind: Secret
metadata:
name: billing-credentials
namespace: billing
type: Opaque
stringData:
api-key: test-key
legacy-password: do-not-mount

42
lab/lesson-11-answer.yaml Normal file
View File

@@ -0,0 +1,42 @@
apiVersion: v1
kind: Namespace
metadata:
name: runtime-hardening
---
apiVersion: v1
kind: Pod
metadata:
name: event-forwarder
namespace: runtime-hardening
spec:
volumes:
- name: tmp
emptyDir: {}
securityContext:
runAsUser: 1000
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
appArmorProfile:
type: RuntimeDefault
containers:
- name: forwarder
image: busybox:1.37
command: ["sh","-c","sleep 3600"]
volumeMounts:
- name: tmp
mountPath: /tmp
securityContext:
readOnlyRootFilesystem: true
- name: metrics
image: busybox:1.37
command: ["sh","-c","sleep 3600"]
volumeMounts:
- name: tmp
mountPath: /tmp
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]

View File

@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: runtime-hardening

View File

@@ -0,0 +1,40 @@
{"kind":"Event","apiVersion":"audit.k8s.io/v1",
"level":"RequestResponse",
"auditID":"a4",
"stage":"ResponseComplete",
"requestReceivedTimestamp":"2026-08-04T03:16:22Z",
"verb":"create",
"user":{"username":"system:serviceaccount:payments:api",
"groups":["system:serviceaccounts"]},
"sourceIPs":["10.244.3.77"],
"objectRef":{"apiGroup":"rbac.authorization.k8s.io",
"resource":"clusterrolebindings","name":"payments-api-admin"},
"responseStatus":{"code":201}}
{"kind":"Event",
"apiVersion":"audit.k8s.io/v1",
"level":"RequestResponse",
"auditID":"a5",
"stage":"ResponseComplete",
"requestReceivedTimestamp":"2026-08-04T03:16:40Z",
"verb":"get","user":{"username":"system:serviceaccount:payments:api",
"groups":["system:serviceaccounts"]},
"sourceIPs":["10.244.3.77"],
"objectRef":{"resource":"secrets",
"namespace":"payments",
"name":"prod-db"},"responseStatus":{"code":200}}
Which principal is suspicious?
a4 and a5 a6
What source IP is associated with the activity?
10.244.3.77
What is the earliest suspicious timestamp?
"2026-08-04T03:16:22Z"
Which ClusterRoleBinding was created?
payments-api-admin
Which Secret was accessed?
"prod-db"
What runtime action followed the Secret access?
exec
Give two immediate containment actions, in order.
get secret, exec in pod

View File

@@ -0,0 +1,7 @@
{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"Metadata","auditID":"a1","stage":"ResponseComplete","requestReceivedTimestamp":"2026-08-04T03:10:12Z","verb":"get","user":{"username":"system:serviceaccount:observability:metrics-reader","groups":["system:serviceaccounts"]},"sourceIPs":["10.244.1.18"],"objectRef":{"resource":"pods","namespace":"payments","name":"api-7f9d"},"responseStatus":{"code":200}}
{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"Metadata","auditID":"a2","stage":"ResponseComplete","requestReceivedTimestamp":"2026-08-04T03:12:46Z","verb":"create","user":{"username":"system:serviceaccount:payments:deployment-controller","groups":["system:serviceaccounts"]},"sourceIPs":["10.244.2.11"],"objectRef":{"resource":"pods","namespace":"payments","name":"api-7f9d-2"},"responseStatus":{"code":201}}
{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"RequestResponse","auditID":"a3","stage":"ResponseComplete","requestReceivedTimestamp":"2026-08-04T03:14:03Z","verb":"get","user":{"username":"system:serviceaccount:observability:metrics-reader","groups":["system:serviceaccounts"]},"sourceIPs":["10.244.1.18"],"objectRef":{"resource":"services","namespace":"payments","name":"api"},"responseStatus":{"code":200}}
{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"RequestResponse","auditID":"a4","stage":"ResponseComplete","requestReceivedTimestamp":"2026-08-04T03:16:22Z","verb":"create","user":{"username":"system:serviceaccount:payments:api","groups":["system:serviceaccounts"]},"sourceIPs":["10.244.3.77"],"objectRef":{"apiGroup":"rbac.authorization.k8s.io","resource":"clusterrolebindings","name":"payments-api-admin"},"responseStatus":{"code":201}}
{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"RequestResponse","auditID":"a5","stage":"ResponseComplete","requestReceivedTimestamp":"2026-08-04T03:16:40Z","verb":"get","user":{"username":"system:serviceaccount:payments:api","groups":["system:serviceaccounts"]},"sourceIPs":["10.244.3.77"],"objectRef":{"resource":"secrets","namespace":"payments","name":"prod-db"},"responseStatus":{"code":200}}
{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"Metadata","auditID":"a6","stage":"ResponseComplete","requestReceivedTimestamp":"2026-08-04T03:17:10Z","verb":"create","user":{"username":"system:serviceaccount:payments:api","groups":["system:serviceaccounts"]},"sourceIPs":["10.244.3.77"],"objectRef":{"subresource":"exec","resource":"pods","namespace":"payments","name":"api-7f9d"},"responseStatus":{"code":101}}
{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"Metadata","auditID":"a7","stage":"ResponseComplete","requestReceivedTimestamp":"2026-08-04T03:19:20Z","verb":"get","user":{"username":"system:serviceaccount:observability:metrics-reader","groups":["system:serviceaccounts"]},"sourceIPs":["10.244.1.18"],"objectRef":{"resource":"nodes","name":"cks-lab-worker"},"responseStatus":{"code":200}}

26
lab/lesson-13-answer.yaml Normal file
View File

@@ -0,0 +1,26 @@
apiVersion: v1
kind: Namespace
metadata:
name: supply-chain
labels:
training.cks/lesson: supply-chain
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: supply-chain
spec:
replicas: 1
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: busybox:1.37
command: ["sh", "-c", "sleep 3600"]

View File

@@ -0,0 +1,26 @@
apiVersion: v1
kind: Namespace
metadata:
name: supply-chain
labels:
training.cks/lesson: supply-chain
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: supply-chain
spec:
replicas: 1
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: busybox:1.37
command: ["sh", "-c", "sleep 3600"]

View File

@@ -0,0 +1,47 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: supply-chain
spec:
replicas: 1
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: docker.io/library/busybox@sha256:9532d8c39891ca2ecde4d30d7710e01fb739c87a8b9299685c63704296b16028
command: ["sh", "-c", "sleep 3600"]
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: supply-chain-digest-only
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
validations:
- expression: "object.spec.containers.all(c, c.image.startsWith('docker.io/library/') && c.image.contains('@sha256:'))"
message: "all container images must use docker.io/library and an immutable sha256 digest"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: supply-chain-digest-only
spec:
policyName: supply-chain-digest-only
validationActions: [Deny]
matchResources:
namespaceSelector:
matchLabels:
training.cks/lesson: supply-chain

View File

@@ -0,0 +1,27 @@
apiVersion: audit.k8s.io/v1 # This is required.
kind: Policy
# Don't generate audit events for all requests in RequestReceived stage.
omitStages:
- "RequestReceived"
rules:
# Log pod changes at RequestResponse level
- level: RequestResponse
resources:
- group: ""
# Resource "pods" doesn't match requests to any subresource of pods,
# which is consistent with the RBAC policy.
resources: ["secrets"]
namespaces: ["payments"]
- level: Metadata
verbs:
- create
- update
- patch
- delete
- deletecollection
resources:
- group: "rbac.authorization.k8s.io" # core API group
resources: ["roles", "rolebindings","clusterroles","clusterrolebindings"]
- level: Metadata

View File

@@ -0,0 +1,13 @@
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
anonymous:
enabled: false
webhook:
enabled: true
authorization:
mode: Webhook
readOnlyPort: 0
protectKernelDefaults: true
rotateCertificates: true

View File

@@ -0,0 +1,12 @@
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
anonymous:
enabled: true
webhook:
enabled: false
authorization:
mode: AlwaysAllow
readOnlyPort: 10255
protectKernelDefaults: false
rotateCertificates: false

60
lab/lesson-16-answer.yaml Normal file
View File

@@ -0,0 +1,60 @@
apiVersion: v1
kind: Namespace
metadata:
name: ingress-drill
---
apiVersion: v1
kind: Secret
metadata:
name: orders-tls
namespace: ingress-drill
type: kubernetes.io/tls
stringData:
tls.crt: placeholder-certificate-for-api-validation
tls.key: placeholder-key-for-api-validation
---
apiVersion: v1
kind: Service
metadata:
name: orders
namespace: ingress-drill
spec:
selector:
app: orders
ports:
- name: http
port: 8080
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: orders
namespace: ingress-drill
spec:
ingressClassName: nginx
tls:
- hosts:
- orders.training.local
secretName: orders-tls
rules:
- host: orders.training.local
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: orders
port:
number: 8080
- host: orders.training.local
http:
paths:
- path: /healthz
pathType: Exact
backend:
service:
name: orders
port:
number: 8080

View File

@@ -0,0 +1,27 @@
apiVersion: v1
kind: Namespace
metadata:
name: ingress-drill
---
apiVersion: v1
kind: Secret
metadata:
name: orders-tls
namespace: ingress-drill
type: kubernetes.io/tls
stringData:
tls.crt: placeholder-certificate-for-api-validation
tls.key: placeholder-key-for-api-validation
---
apiVersion: v1
kind: Service
metadata:
name: orders
namespace: ingress-drill
spec:
selector:
app: orders
ports:
- name: http
port: 8080
targetPort: 8080

39
lab/lesson-17-answer.yaml Normal file
View File

@@ -0,0 +1,39 @@
apiVersion: v1
kind: Namespace
metadata:
name: tenant-a
labels:
training.cks/lesson: tenant-a
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: "validate-namespace-tenant-a"
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
validations:
- expression: >-
(!has(object.spec.hostNetwork) || object.spec.hostNetwork==false) &&
(!has(object.spec.hostPID) || object.spec.hostPID==false) &&
(!has(object.spec.hostIPC) || object.spec.hostIPC==false) &&
(!has(object.spec.volumes) || object.spec.volumes.all(v, !has(v.hostPath))) &&
object.spec.containers.all(c, !has(c.securityContext) || !has(c.securityContext.privileged) || c.securityContext.privileged==false)
message: "host namespaces, hostPath volumes, and privileged containers are not allowed"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: validate-namespace-tenant-a
spec:
policyName: validate-namespace-tenant-a
validationActions: [Deny]
matchResources:
namespaceSelector:
matchLabels:
training.cks/lesson: tenant-a

View File

@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: tenant-a
labels:
training.cks/lesson: tenant-a

View File

@@ -0,0 +1,32 @@
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: validate-namespace-tenant-a
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
validations:
- expression: >-
(!has(object.spec.hostNetwork) || object.spec.hostNetwork == false) &&
(!has(object.spec.hostPID) || object.spec.hostPID == false) &&
(!has(object.spec.hostIPC) || object.spec.hostIPC == false) &&
(!has(object.spec.volumes) || object.spec.volumes.all(v, !has(v.hostPath))) &&
object.spec.containers.all(c, !has(c.securityContext) || !has(c.securityContext.privileged) || c.securityContext.privileged == false)
message: host namespaces, hostPath volumes, and privileged containers are not allowed
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: validate-namespace-tenant-a
spec:
policyName: validate-namespace-tenant-a
validationActions: [Deny]
matchResources:
namespaceSelector:
matchLabels:
training.cks/lesson: tenant-a

14
lab/lesson-18-answer.yaml Normal file
View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: Namespace
metadata:
name: data-protection
---
apiVersion: v1
kind: Secret
metadata:
name: payment-token
namespace: data-protection
type: Opaque
stringData:
token: replace-me-after-encryption-is-enabled
---

View File

@@ -0,0 +1,11 @@
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: H5avyAWUn8F0kWtQURZoHGBE1BaBcwKZgtmKu5SAUi4=
- identity: {} # plain text, in other words NO encryption

View File

@@ -0,0 +1,13 @@
apiVersion: v1
kind: Namespace
metadata:
name: data-protection
---
apiVersion: v1
kind: Secret
metadata:
name: payment-token
namespace: data-protection
type: Opaque
stringData:
token: replace-me-after-encryption-is-enabled

13
lab/lesson-19-answer.md Normal file
View File

@@ -0,0 +1,13 @@
$ sha256sum.exe lab/lesson-19-artifacts/kubectl-v1.35.0-linux-amd64
9bb6389a5f72cdb071541c40c8df41028a4dae8987f0af003d17e326ceb635d7 *lab/lesson-19-artifacts/kubectl-v1.35.0-linux-amd64
$ sha256sum.exe lab/lesson-19-artifacts/kubelet-v1.35.0-linux-amd64
e67f13c4f37958bd8fd975cb870b8d245db5655b26621f878af353e8d79ab0e7 *lab/lesson-19-artifacts/kubelet-v1.35.0-linux-amd64
Which artifact is verified and may proceed to the next deployment-review stage.
answer:
kubelet-v1.35.0-linux-amd64
Which artifact must not be deployed.
kubectl-v1.35.0-linux-amd64

View File

@@ -0,0 +1,2 @@
e67f13c4f37958bd8fd975cb870b8d245db5655b26621f878af353e8d79ab0e7 kubelet-v1.35.0-linux-amd64
6d98653149dde123a1972890a6da297449f40606d70d38502aac8985e9b67eec kubectl-v1.35.0-linux-amd64

View File

@@ -0,0 +1 @@
received-training-kubectl-artifact-v1.35.0-tampered

View File

@@ -0,0 +1 @@
trusted-training-kubelet-artifact-v1.35.0

View File

@@ -0,0 +1,15 @@
- rule: custom_rule
desc: custom_rule
source: syscall
condition: >
(evt.type in (execve, execveat)) and
container.id != host and
k8s.ns.name = "payments" and
(proc.name = bash or
proc.name = sh)
output: >
%k8s.pod.name,%k8s.ns.name,%proc.cmdline,%container.name
priority: WARNING
tags: [container,mitre_execution]

View File

@@ -0,0 +1,4 @@
{"evt.time":"2026-08-10T09:25:12.441Z","container":true,"proc.name":"sh",
"proc.cmdline":"sh -c cat /var/run/secrets/kubernetes.io/serviceaccount/token",
"user.name":"root","k8s.ns.name":"payments","k8s.pod.name":"api-7f9d","container.name":"api"}

34
lab/lesson-21-answer.yaml Normal file
View File

@@ -0,0 +1,34 @@
apiVersion: v1
kind: Namespace
metadata:
name: partner-workloads
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: v1.35
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: v1.35
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: v1.35
---
apiVersion: v1
kind: Pod
metadata:
name: exporter-v2
namespace: partner-workloads
labels:
app: exporter
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: exporter
image: busybox:1.37
command: ["sh","-c","sleep 3600"]
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]

View File

@@ -0,0 +1,17 @@
apiVersion: v1
kind: Namespace
metadata:
name: partner-workloads
---
apiVersion: v1
kind: Pod
metadata:
name: legacy-exporter
namespace: partner-workloads
labels:
app: legacy-exporter
spec:
containers:
- name: exporter
image: busybox:1.37
command: ["sh", "-c", "sleep 3600"]

View File

@@ -0,0 +1,19 @@
apiVersion: v1
kind: Pod
metadata:
name: kube-apiserver
namespace: kube-system
spec:
hostNetwork: true
containers:
- name: kube-apiserver
image: registry.k8s.io/kube-apiserver:v1.35.0
command:
- kube-apiserver
- --etcd-servers=https://127.0.0.1:2379
- --service-cluster-ip-range=10.96.0.0/12
- --anonymous-auth=false
- --authorization-mode=Node,RBAC
- --profiling=false
- --audit-log-path=/var/log/kubernetes/audit/audit.log
- --service-account-lookup=true

View File

@@ -0,0 +1,19 @@
apiVersion: v1
kind: Pod
metadata:
name: kube-apiserver
namespace: kube-system
spec:
hostNetwork: true
containers:
- name: kube-apiserver
image: registry.k8s.io/kube-apiserver:v1.35.0
command:
- kube-apiserver
- --etcd-servers=https://127.0.0.1:2379
- --service-cluster-ip-range=10.96.0.0/12
- --anonymous-auth=true
- --authorization-mode=AlwaysAllow
- --service-account-lookup=false
- --profiling=true
- --audit-log-path=-

0
lab/lesson-23-answer.md Normal file
View File

View File

@@ -0,0 +1,7 @@
# Cilium encryption rollout scenario
Cluster `prod-east` already runs Cilium 1.19.6 from the official Helm chart in namespace `kube-system`.
The security team requires transparent encryption for traffic between Cilium-managed Pods on different nodes. The team does **not** require node-to-node encryption. The worker-node firewall currently allows TCP 6443 and UDP 8472, but not WireGuard traffic.
The cluster's operating-system image has WireGuard kernel support. There is no CNI chaining.

34
lab/lesson-23-solution.md Normal file
View File

@@ -0,0 +1,34 @@
# Cilium WireGuard rollout answer
## 1. Helm upgrade command
```bash
helm upgrade cilium cilium/cilium \
--namespace kube-system \
--version 1.19.6 \
--reuse-values \
--set encryption.enabled=true \
--set encryption.type=wireguard
```
## 2. Firewall change
Allow **UDP port 51871** between every cluster node. This is the WireGuard tunnel port used by Cilium.
## 3. Preflight checks
```bash
cilium status --wait
kubectl -n kube-system get daemonset cilium
```
The first confirms Cilium is healthy before the change. The second confirms that the Cilium agent is scheduled on the nodes that will participate in encrypted Pod networking. Also confirm the node firewall allows UDP 51871 in both directions before the upgrade.
## 4. Post-change validation
```bash
cilium status --wait
cilium encrypt status
```
`cilium status --wait` confirms the Cilium deployment is healthy after the rollout. `cilium encrypt status` confirms WireGuard encryption is enabled and reports the encryption state for the nodes.

34
lab/lesson-24-answer.yaml Normal file
View File

@@ -0,0 +1,34 @@
apiVersion: v1
kind: Namespace
metadata:
name: release
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: release-bot
namespace: release
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: release-bot-reader
namespace: release
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: release-bot-reader
namespace: release
subjects:
- kind: ServiceAccount
name: release-bot
namespace: release
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: release-bot-reader

View File

@@ -0,0 +1,34 @@
apiVersion: v1
kind: Namespace
metadata:
name: release
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: release-bot
namespace: release
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: release-bot-reader
namespace: release
rules:
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: release-bot-reader
namespace: release
subjects:
- kind: ServiceAccount
name: release-bot
namespace: release
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: release-bot-reader

View File

@@ -0,0 +1,8 @@
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: payments-mesh
spec:
mtls:
mode: STRICT

View File

@@ -0,0 +1,27 @@
apiVersion: v1
kind: Namespace
metadata:
name: payments-mesh
labels:
istio-injection: enabled
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: payments-api
namespace: payments-mesh
spec:
replicas: 1
selector:
matchLabels:
app: payments-api
template:
metadata:
labels:
app: payments-api
spec:
containers:
- name: api
image: nginxinc/nginx-unprivileged:1.27
ports:
- containerPort: 8080

17
lab/lesson-26-answer.yaml Normal file
View File

@@ -0,0 +1,17 @@
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: payments-api-only-frontend
namespace: payments-mesh
spec:
selector:
matchLabels:
app: payments-api
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/web-mesh/sa/frontend"]
to:
- operation:
ports: ["8080"]

View File

@@ -0,0 +1,47 @@
apiVersion: v1
kind: Namespace
metadata:
name: web-mesh
labels:
istio-injection: enabled
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: frontend
namespace: web-mesh
---
apiVersion: v1
kind: Namespace
metadata:
name: payments-mesh
labels:
istio-injection: enabled
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: payments-api
namespace: payments-mesh
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: payments-api
namespace: payments-mesh
spec:
replicas: 1
selector:
matchLabels:
app: payments-api
template:
metadata:
labels:
app: payments-api
spec:
serviceAccountName: payments-api
containers:
- name: api
image: nginxinc/nginx-unprivileged:1.27
ports:
- containerPort: 8080

61
lab/lesson-27-answer.md Normal file
View File

@@ -0,0 +1,61 @@
docker run --rm -v "${PWD}:/work" aquasec/trivy:0.68.1 config /work/lab/lesson-27-fixture.yaml
AVD-KSV-0014 (HIGH): Container 'api' of Deployment 'checkout-api' should set 'securityContext.readOnlyRootFilesystem' to true
════════════════════════════════════════
────────────────────────────────────────
lesson-27-fixture.yaml:21-28
────────────────────────────────────────
21 ┌ containers:
22 │ - name: api
23 │ image: nginx:latest
24 │ ports:
25 │ - containerPort: 8080
26 │ securityContext:
27 │ privileged: true
28 └ capabilities:
────────────────────────────────────────
explanation: rootFileSystem not readOnly so if the attacker gains access the the pod it can change the root directory
remedy: enable readOnlyRootFilesystem in container security context
AVD-KSV-0017 (HIGH): Container 'api' of Deployment 'checkout-api' should set 'securityContext.privileged' to false
════════════════════════════════════════
────────────────────────────────────────
lesson-27-fixture.yaml:21-28
────────────────────────────────────────
21 ┌ containers:
22 │ - name: api
23 │ image: nginx:latest
24 │ ports:
25 │ - containerPort: 8080
26 │ securityContext:
27 │ privileged: true
28 └ capabilities:
────────────────────────────────────────
explanation: allow hacker to get privilege escalation inside the pod if the pod can get service token then it can hack the kubernetes system
remedy: privileged: false
AVD-KSV-0118 (HIGH): deployment checkout-api in checkout namespace is using the default security context, which allows root privileges
════════════════════════════════════════
────────────────────────────────────────
lesson-27-fixture.yaml:19-28
────────────────────────────────────────
19 ┌ app: checkout-api
20 │ spec:
21 │ containers:
22 │ - name: api
23 │ image: nginx:latest
24 │ ports:
25 │ - containerPort: 8080
26 │ securityContext:
27 │ privileged: true
28 └ capabilities:
────────────────────────────────────────
explanation: allow hacker to get privilege escalation inside the pod if the pod can get service token then it can hack the kubernetes system
remedy: runAsNonRoot: true

View File

@@ -0,0 +1,29 @@
apiVersion: v1
kind: Namespace
metadata:
name: checkout
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkout-api
namespace: checkout
spec:
replicas: 2
selector:
matchLabels:
app: checkout-api
template:
metadata:
labels:
app: checkout-api
spec:
containers:
- name: api
image: nginx:latest
ports:
- containerPort: 8080
securityContext:
privileged: true
capabilities:
add: ["NET_ADMIN"]

11
lab/lesson-28-answer.yaml Normal file
View File

@@ -0,0 +1,11 @@
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
anonymous:
enabled: false
webhook:
enabled: true
authorization:
mode: Webhook
protectKernelDefaults: true
rotateCertificates: true

View File

@@ -0,0 +1,12 @@
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
anonymous:
enabled: true
webhook:
enabled: false
authorization:
mode: AlwaysAllow
readOnlyPort: 10255
protectKernelDefaults: false
rotateCertificates: false

View File

@@ -0,0 +1,35 @@
apiVersion: v1
kind: Namespace
metadata:
name: storefront
---
apiVersion: v1
kind: Namespace
metadata:
name: payments-mesh
---
apiVersion: v1
kind: Pod
metadata:
name: frontend
namespace: storefront
labels:
app: frontend
spec:
containers:
- name: app
image: nginxinc/nginx-unprivileged:1.27
---
apiVersion: v1
kind: Pod
metadata:
name: payments-api
namespace: payments-mesh
labels:
app: payments-api
spec:
containers:
- name: api
image: nginxinc/nginx-unprivileged:1.27
ports:
- containerPort: 8080

93
lab/lesson-8-answer.yaml Normal file
View File

@@ -0,0 +1,93 @@
apiVersion: v1
kind: Namespace
metadata:
name: cks-net-hard
---
apiVersion: v1
kind: Service
metadata:
name: ledger
namespace: cks-net-hard
spec:
selector:
app: ledger
ports:
- port: 8080
targetPort: 8080
---
apiVersion: v1
kind: Pod
metadata:
name: ledger
namespace: cks-net-hard
labels:
app: ledger
spec:
containers:
- name: ledger
image: busybox:1.37
command: ["sh", "-c", "mkdir -p /www; echo ledger > /www/index.html; httpd -f -p 8080 -h /www"]
---
apiVersion: v1
kind: Pod
metadata:
name: checkout
namespace: cks-net-hard
labels:
app: checkout
spec:
containers:
- name: checkout
image: busybox:1.37
command: ["sh", "-c", "sleep 3600"]
---
apiVersion: v1
kind: Pod
metadata:
name: intruder
namespace: cks-net-hard
labels:
app: intruder
spec:
containers:
- name: intruder
image: busybox:1.37
command: ["sh", "-c", "sleep 3600"]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: cks-net-hard
namespace: cks-net-hard
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: checkout
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: ledger
ports:
- protocol: TCP
port: 8080
- to:
- podSelector:
matchLabels:
app: checkout
ports:
- port: 53
protocol: TCP
- port: 53
protocol: UDP

55
lab/lesson-8-fixture.yaml Normal file
View File

@@ -0,0 +1,55 @@
apiVersion: v1
kind: Namespace
metadata:
name: cks-net-hard
---
apiVersion: v1
kind: Service
metadata:
name: ledger
namespace: cks-net-hard
spec:
selector:
app: ledger
ports:
- port: 8080
targetPort: 8080
---
apiVersion: v1
kind: Pod
metadata:
name: ledger
namespace: cks-net-hard
labels:
app: ledger
spec:
containers:
- name: ledger
image: busybox:1.37
command: ["sh", "-c", "mkdir -p /www; echo ledger > /www/index.html; httpd -f -p 8080 -h /www"]
---
apiVersion: v1
kind: Pod
metadata:
name: checkout
namespace: cks-net-hard
labels:
app: checkout
spec:
containers:
- name: checkout
image: busybox:1.37
command: ["sh", "-c", "sleep 3600"]
---
apiVersion: v1
kind: Pod
metadata:
name: intruder
namespace: cks-net-hard
labels:
app: intruder
spec:
containers:
- name: intruder
image: busybox:1.37
command: ["sh", "-c", "sleep 3600"]

View File

@@ -0,0 +1,62 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: cks-net-hard
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ledger-ingress-from-checkout
namespace: cks-net-hard
spec:
podSelector:
matchLabels:
app: ledger
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: checkout
ports:
- protocol: TCP
port: 8080
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-checkout-egress
namespace: cks-net-hard
spec:
podSelector:
matchLabels:
app: checkout
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: ledger
ports:
- protocol: TCP
port: 8080
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53

59
lab/lesson-9-answer.yaml Normal file
View File

@@ -0,0 +1,59 @@
apiVersion: v1
kind: Namespace
metadata:
name: incident-response
---
apiVersion: v1
kind: Namespace
metadata:
name: security-ops
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: audit-bot
namespace: security-ops
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: incidnet-response-role
namespace: incident-response
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- pods/log
verbs:
- get
- apiGroups:
- ""
resourceNames:
- incident-playbook
resources:
- configmaps
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: incident-response-rolebinding
namespace: incident-response
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: incidnet-response-role
subjects:
- kind: ServiceAccount
name: audit-bot
namespace: security-ops

15
lab/lesson-9-fixture.yaml Normal file
View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Namespace
metadata:
name: incident-response
---
apiVersion: v1
kind: Namespace
metadata:
name: security-ops
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: audit-bot
namespace: security-ops

View File

@@ -0,0 +1,77 @@
apiVersion: v1
kind: Namespace
metadata:
name: cks-lab
---
apiVersion: v1
kind: Pod
metadata:
name: db
namespace: cks-lab
labels:
app: db
spec:
containers:
- name: http
image: hashicorp/http-echo:1.0
args: ["-text=database", "-listen=:8080"]
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: db
namespace: cks-lab
spec:
selector:
app: db
ports:
- port: 8080
targetPort: 8080
---
apiVersion: v1
kind: Pod
metadata:
name: api
namespace: cks-lab
labels:
app: api
spec:
containers:
- name: tools
image: busybox:1.37
command: ["sh", "-c", "sleep 3600"]
---
apiVersion: v1
kind: Pod
metadata:
name: intruder
namespace: cks-lab
labels:
app: intruder
spec:
containers:
- name: tools
image: busybox:1.37
command: ["sh", "-c", "sleep 3600"]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-only-from-api
namespace: cks-lab
spec:
podSelector:
matchLabels:
app: db
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: api
ports:
- protocol: TCP
port: 8080

View File

@@ -0,0 +1,35 @@
apiVersion: v1
kind: Namespace
metadata:
name: payments-prod
---
apiVersion: v1
kind: Pod
metadata:
name: payments
namespace: payments-prod
labels:
app: payments
spec:
securityContext:
runAsNonRoot: true
runAsUser: 101
seccompProfile:
type: RuntimeDefault
volumes:
- name: secure-tmp
emptyDir: {}
containers:
- name: payments
image: nginxinc/nginx-unprivileged:1.27
volumeMounts:
- name: secure-tmp
mountPath: /tmp
ports:
- containerPort: 8080
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]

View File

@@ -0,0 +1,18 @@
apiVersion: v1
kind: Namespace
metadata:
name: payments-prod
---
apiVersion: v1
kind: Pod
metadata:
name: payments
namespace: payments-prod
labels:
app: payments
spec:
containers:
- name: payments
image: nginxinc/nginx-unprivileged:1.27
ports:
- containerPort: 8080

View File

@@ -0,0 +1,30 @@
apiVersion: v1
kind: Namespace
metadata:
name: cks-pss
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
---
apiVersion: v1
kind: Pod
metadata:
name: orders
namespace: cks-pss
labels:
app: orders
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: orders
image: busybox:1.37
command: ["sh", "-c", "sleep 3600"]
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL

View File

@@ -0,0 +1,17 @@
apiVersion: v1
kind: Namespace
metadata:
name: cks-pss
---
apiVersion: v1
kind: Pod
metadata:
name: orders
namespace: cks-pss
labels:
app: orders
spec:
containers:
- name: orders
image: busybox:1.37
command: ["sh", "-c", "sleep 3600"]

28
lab/rbac-answer.yaml Normal file
View File

@@ -0,0 +1,28 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: reporter-role
namespace: cks-rbac
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: reporter-rolebinding
namespace: cks-rbac
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: reporter-role
subjects:
- kind: ServiceAccount
name: report-reader
namespace: cks-rbac

10
lab/rbac-starter.yaml Normal file
View File

@@ -0,0 +1,10 @@
apiVersion: v1
kind: Namespace
metadata:
name: cks-rbac
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: report-reader
namespace: cks-rbac

View File

@@ -0,0 +1,25 @@
apiVersion: v1
kind: Namespace
metadata:
name: checkout
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: checkout-api
namespace: checkout
---
apiVersion: v1
kind: Pod
metadata:
name: checkout
namespace: checkout
labels:
app: checkout
spec:
automountServiceAccountToken: false
serviceAccountName: checkout-api
containers:
- name: checkout
image: busybox:1.37
command: ["sh", "-c", "sleep 3600"]

View File

@@ -0,0 +1,26 @@
apiVersion: v1
kind: Namespace
metadata:
name: cks-workload
---
apiVersion: v1
kind: Pod
metadata:
name: inventory
namespace: cks-workload
labels:
app: inventory
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: inventory
image: busybox:1.37
command: ["sh", "-c", "id; sleep 3600"]
securityContext:
runAsUser: 1000
runAsNonRoot: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]

View File

@@ -0,0 +1,22 @@
apiVersion: v1
kind: Namespace
metadata:
name: cks-workload
---
apiVersion: v1
kind: Pod
metadata:
name: inventory
namespace: cks-workload
labels:
app: inventory
spec:
containers:
- name: inventory
image: busybox:1.37
command: ["sh", "-c", "id; sleep 3600"]
securityContext:
runAsUser: 0
allowPrivilegeEscalation: true
capabilities:
add: ["NET_ADMIN"]