Completed upto lesson 28
This commit is contained in:
37
lab/README.md
Normal file
37
lab/README.md
Normal 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
BIN
lab/bin/kind.exe
Normal file
Binary file not shown.
9
lab/kind-calico.yaml
Normal file
9
lab/kind-calico.yaml
Normal 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
57
lab/lesson-10-answer.yaml
Normal 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
|
||||
14
lab/lesson-10-fixture.yaml
Normal file
14
lab/lesson-10-fixture.yaml
Normal 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
42
lab/lesson-11-answer.yaml
Normal 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"]
|
||||
4
lab/lesson-11-fixture.yaml
Normal file
4
lab/lesson-11-fixture.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: runtime-hardening
|
||||
40
lab/lesson-12-audit-ans.jsonl
Normal file
40
lab/lesson-12-audit-ans.jsonl
Normal 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
|
||||
7
lab/lesson-12-audit.jsonl
Normal file
7
lab/lesson-12-audit.jsonl
Normal 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
26
lab/lesson-13-answer.yaml
Normal 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"]
|
||||
26
lab/lesson-13-fixture.yaml
Normal file
26
lab/lesson-13-fixture.yaml
Normal 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"]
|
||||
47
lab/lesson-13-solution.yaml
Normal file
47
lab/lesson-13-solution.yaml
Normal 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
|
||||
27
lab/lesson-14-audit-policy.yaml
Normal file
27
lab/lesson-14-audit-policy.yaml
Normal 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
|
||||
13
lab/lesson-15-kubelet-config-answer.yaml
Normal file
13
lab/lesson-15-kubelet-config-answer.yaml
Normal 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
|
||||
|
||||
12
lab/lesson-15-kubelet-config-insecure copy.yaml
Normal file
12
lab/lesson-15-kubelet-config-insecure copy.yaml
Normal 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
60
lab/lesson-16-answer.yaml
Normal 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
|
||||
27
lab/lesson-16-fixture.yaml
Normal file
27
lab/lesson-16-fixture.yaml
Normal 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
39
lab/lesson-17-answer.yaml
Normal 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
|
||||
6
lab/lesson-17-fixture.yaml
Normal file
6
lab/lesson-17-fixture.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: tenant-a
|
||||
labels:
|
||||
training.cks/lesson: tenant-a
|
||||
32
lab/lesson-17-solution.yaml
Normal file
32
lab/lesson-17-solution.yaml
Normal 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
14
lab/lesson-18-answer.yaml
Normal 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
|
||||
---
|
||||
11
lab/lesson-18-encryption-config.yaml
Normal file
11
lab/lesson-18-encryption-config.yaml
Normal 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
|
||||
13
lab/lesson-18-fixture.yaml
Normal file
13
lab/lesson-18-fixture.yaml
Normal 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
13
lab/lesson-19-answer.md
Normal 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
|
||||
2
lab/lesson-19-artifacts/SHA256SUMS
Normal file
2
lab/lesson-19-artifacts/SHA256SUMS
Normal file
@@ -0,0 +1,2 @@
|
||||
e67f13c4f37958bd8fd975cb870b8d245db5655b26621f878af353e8d79ab0e7 kubelet-v1.35.0-linux-amd64
|
||||
6d98653149dde123a1972890a6da297449f40606d70d38502aac8985e9b67eec kubectl-v1.35.0-linux-amd64
|
||||
1
lab/lesson-19-artifacts/kubectl-v1.35.0-linux-amd64
Normal file
1
lab/lesson-19-artifacts/kubectl-v1.35.0-linux-amd64
Normal file
@@ -0,0 +1 @@
|
||||
received-training-kubectl-artifact-v1.35.0-tampered
|
||||
1
lab/lesson-19-artifacts/kubelet-v1.35.0-linux-amd64
Normal file
1
lab/lesson-19-artifacts/kubelet-v1.35.0-linux-amd64
Normal file
@@ -0,0 +1 @@
|
||||
trusted-training-kubelet-artifact-v1.35.0
|
||||
15
lab/lesson-20-falco-rule.yaml
Normal file
15
lab/lesson-20-falco-rule.yaml
Normal 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]
|
||||
|
||||
|
||||
4
lab/lesson-20-runtime-event.json
Normal file
4
lab/lesson-20-runtime-event.json
Normal 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
34
lab/lesson-21-answer.yaml
Normal 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"]
|
||||
17
lab/lesson-21-fixture.yaml
Normal file
17
lab/lesson-21-fixture.yaml
Normal 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"]
|
||||
19
lab/lesson-22-kube-apiserver-answer.yaml
Normal file
19
lab/lesson-22-kube-apiserver-answer.yaml
Normal 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
|
||||
19
lab/lesson-22-kube-apiserver-insecure.yaml
Normal file
19
lab/lesson-22-kube-apiserver-insecure.yaml
Normal 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
0
lab/lesson-23-answer.md
Normal file
7
lab/lesson-23-cilium-scenario.md
Normal file
7
lab/lesson-23-cilium-scenario.md
Normal 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
34
lab/lesson-23-solution.md
Normal 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
34
lab/lesson-24-answer.yaml
Normal 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
|
||||
34
lab/lesson-24-fixture.yaml
Normal file
34
lab/lesson-24-fixture.yaml
Normal 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
|
||||
8
lab/lesson-25-answer.yaml
Normal file
8
lab/lesson-25-answer.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
apiVersion: security.istio.io/v1
|
||||
kind: PeerAuthentication
|
||||
metadata:
|
||||
name: default
|
||||
namespace: payments-mesh
|
||||
spec:
|
||||
mtls:
|
||||
mode: STRICT
|
||||
27
lab/lesson-25-fixture.yaml
Normal file
27
lab/lesson-25-fixture.yaml
Normal 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
17
lab/lesson-26-answer.yaml
Normal 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"]
|
||||
47
lab/lesson-26-fixture.yaml
Normal file
47
lab/lesson-26-fixture.yaml
Normal 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
61
lab/lesson-27-answer.md
Normal 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
|
||||
29
lab/lesson-27-fixture.yaml
Normal file
29
lab/lesson-27-fixture.yaml
Normal 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
11
lab/lesson-28-answer.yaml
Normal 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
|
||||
12
lab/lesson-28-kubelet-review.yaml
Normal file
12
lab/lesson-28-kubelet-review.yaml
Normal 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
|
||||
35
lab/lesson-29-fixture.yaml
Normal file
35
lab/lesson-29-fixture.yaml
Normal 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
93
lab/lesson-8-answer.yaml
Normal 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
55
lab/lesson-8-fixture.yaml
Normal 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"]
|
||||
62
lab/lesson-8-solution.yaml
Normal file
62
lab/lesson-8-solution.yaml
Normal 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
59
lab/lesson-9-answer.yaml
Normal 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
15
lab/lesson-9-fixture.yaml
Normal 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
|
||||
77
lab/networkpolicy-lab.yaml
Normal file
77
lab/networkpolicy-lab.yaml
Normal 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
|
||||
35
lab/payments-hardening-sanswer.yaml
Normal file
35
lab/payments-hardening-sanswer.yaml
Normal 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"]
|
||||
18
lab/payments-hardening-starter.yaml
Normal file
18
lab/payments-hardening-starter.yaml
Normal 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
|
||||
30
lab/pod-security-standards-answer.yaml
Normal file
30
lab/pod-security-standards-answer.yaml
Normal 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
|
||||
17
lab/pod-security-standards-starter.yaml
Normal file
17
lab/pod-security-standards-starter.yaml
Normal 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
28
lab/rbac-answer.yaml
Normal 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
10
lab/rbac-starter.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: cks-rbac
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: report-reader
|
||||
namespace: cks-rbac
|
||||
25
lab/serviceaccount-token-answer.yaml
Normal file
25
lab/serviceaccount-token-answer.yaml
Normal 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"]
|
||||
26
lab/workload-hardening-answer.yaml
Normal file
26
lab/workload-hardening-answer.yaml
Normal 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"]
|
||||
22
lab/workload-hardening-starter.yaml
Normal file
22
lab/workload-hardening-starter.yaml
Normal 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"]
|
||||
Reference in New Issue
Block a user