opencode-skills-collection 3.0.31 → 3.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,561 @@
1
+ # Kubernetes Pod Security Reference
2
+
3
+ Full reference for hardening workloads in Kubernetes — NetworkPolicy, RBAC, Pod Security Admission, admission controllers (Kyverno/OPA), and service account hardening.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [Pod Security Admission (PSA)](#pod-security-admission)
8
+ 2. [NetworkPolicy — Zero-Trust Networking](#networkpolicy)
9
+ 3. [RBAC — Least Privilege](#rbac)
10
+ 4. [Admission Controllers (Kyverno / OPA Gatekeeper)](#admission-controllers)
11
+ 5. [Service Account Hardening](#service-account-hardening)
12
+ 6. [Runtime Security — Falco](#runtime-security--falco)
13
+ 7. [Secrets Management in K8s](#secrets-management-in-k8s)
14
+
15
+ ---
16
+
17
+ ## Pod Security Admission
18
+
19
+ Built-in K8s 1.25+ policy engine (replaces deprecated PodSecurityPolicy).
20
+
21
+ ### Three Built-In Policy Levels
22
+
23
+ | Level | What It Blocks |
24
+ |---|---|
25
+ | `privileged` | No restrictions (cluster default) |
26
+ | `baseline` | Blocks hostNetwork, hostPID, hostIPC, privileged containers, dangerous volume types, hostPath |
27
+ | `restricted` | Everything in baseline + requires non-root, read-only FS, drops capabilities, requires seccomp |
28
+
29
+ ### Three Modes Per Level
30
+
31
+ | Mode | Behavior |
32
+ |---|---|
33
+ | `enforce` | Reject pods that violate the policy |
34
+ | `audit` | Allow but log a violation in audit log |
35
+ | `warn` | Allow but return a warning to the user |
36
+
37
+ ### Applying PSA Labels
38
+
39
+ ```bash
40
+ # Audit before enforcing — find what would fail
41
+ kubectl label namespace production \
42
+ pod-security.kubernetes.io/audit=restricted \
43
+ pod-security.kubernetes.io/audit-version=latest
44
+
45
+ # Gradual rollout: warn in staging, enforce in production
46
+ kubectl label namespace staging \
47
+ pod-security.kubernetes.io/warn=restricted \
48
+ pod-security.kubernetes.io/warn-version=latest
49
+
50
+ kubectl label namespace production \
51
+ pod-security.kubernetes.io/enforce=restricted \
52
+ pod-security.kubernetes.io/enforce-version=latest
53
+ ```
54
+
55
+ ### Check What Would Fail Before Enforcing
56
+
57
+ ```bash
58
+ # Dry-run check against a namespace
59
+ kubectl --dry-run=server apply -f manifests/ --namespace production
60
+
61
+ # Check a specific pod spec
62
+ kubectl run test-pod --image=nginx --dry-run=server -n production
63
+ ```
64
+
65
+ ### Minimum Pod Spec for `restricted` Level
66
+
67
+ ```yaml
68
+ spec:
69
+ securityContext:
70
+ runAsNonRoot: true
71
+ runAsUser: 10001
72
+ runAsGroup: 10001
73
+ fsGroup: 10001
74
+ seccompProfile:
75
+ type: RuntimeDefault # or Localhost with a custom profile
76
+ containers:
77
+ - name: app
78
+ securityContext:
79
+ allowPrivilegeEscalation: false
80
+ readOnlyRootFilesystem: true
81
+ capabilities:
82
+ drop: ["ALL"]
83
+ # Resource limits are required for restricted PSA
84
+ resources:
85
+ requests:
86
+ memory: "64Mi"
87
+ cpu: "50m"
88
+ limits:
89
+ memory: "256Mi"
90
+ cpu: "250m"
91
+ ```
92
+
93
+ ---
94
+
95
+ ## NetworkPolicy — Zero-Trust Networking
96
+
97
+ By default all pods in a cluster can reach all other pods on any port. Lock down with NetworkPolicy.
98
+
99
+ > **Prerequisite:** Your CNI plugin must support NetworkPolicy (Calico, Cilium, Weave Net — but NOT Flannel by default).
100
+
101
+ ### Step 1: Default Deny All
102
+
103
+ Apply a default-deny to every namespace that holds workloads:
104
+
105
+ ```yaml
106
+ apiVersion: networking.k8s.io/v1
107
+ kind: NetworkPolicy
108
+ metadata:
109
+ name: default-deny-all
110
+ namespace: production
111
+ spec:
112
+ podSelector: {} # Selects all pods in this namespace
113
+ policyTypes:
114
+ - Ingress
115
+ - Egress
116
+ ```
117
+
118
+ ### Step 2: Allow Only Required Traffic
119
+
120
+ ```yaml
121
+ # Allow ingress from nginx ingress controller, egress to postgres + DNS
122
+ apiVersion: networking.k8s.io/v1
123
+ kind: NetworkPolicy
124
+ metadata:
125
+ name: allow-myapp
126
+ namespace: production
127
+ spec:
128
+ podSelector:
129
+ matchLabels:
130
+ app: myapp
131
+ policyTypes:
132
+ - Ingress
133
+ - Egress
134
+ ingress:
135
+ - from:
136
+ - namespaceSelector:
137
+ matchLabels:
138
+ kubernetes.io/metadata.name: ingress-nginx
139
+ - podSelector:
140
+ matchLabels:
141
+ app.kubernetes.io/name: ingress-nginx
142
+ ports:
143
+ - protocol: TCP
144
+ port: 3000
145
+ egress:
146
+ - to:
147
+ - podSelector:
148
+ matchLabels:
149
+ app: postgres
150
+ namespaceSelector:
151
+ matchLabels:
152
+ kubernetes.io/metadata.name: production
153
+ ports:
154
+ - protocol: TCP
155
+ port: 5432
156
+ - to: {} # Allow DNS resolution (required for service discovery)
157
+ ports:
158
+ - protocol: UDP
159
+ port: 53
160
+ - protocol: TCP
161
+ port: 53
162
+ ```
163
+
164
+ ### Allow Access to External Services (e.g., cloud APIs)
165
+
166
+ ```yaml
167
+ egress:
168
+ - to:
169
+ - ipBlock:
170
+ cidr: 0.0.0.0/0 # All external IPs
171
+ except:
172
+ - 10.0.0.0/8 # But not internal cluster ranges
173
+ - 172.16.0.0/12
174
+ - 192.168.0.0/16
175
+ ports:
176
+ - protocol: TCP
177
+ port: 443 # HTTPS only
178
+ ```
179
+
180
+ ### Validate NetworkPolicy with Cilium or Calico CLI
181
+
182
+ ```bash
183
+ # Cilium — test connectivity between pods
184
+ cilium connectivity test
185
+
186
+ # Calico — list effective policies
187
+ kubectl exec -it deploy/myapp -- calicoctl get networkpolicy -n production
188
+ ```
189
+
190
+ ---
191
+
192
+ ## RBAC — Least Privilege
193
+
194
+ ### Principle: Scope Narrowly, Avoid Wildcards
195
+
196
+ ```yaml
197
+ # ❌ DANGEROUS — grants everything to everything
198
+ apiVersion: rbac.authorization.k8s.io/v1
199
+ kind: ClusterRoleBinding
200
+ metadata:
201
+ name: full-admin
202
+ subjects:
203
+ - kind: ServiceAccount
204
+ name: myapp-sa
205
+ namespace: production
206
+ roleRef:
207
+ kind: ClusterRole
208
+ name: cluster-admin
209
+ apiGroup: rbac.authorization.k8s.io
210
+
211
+ ---
212
+ # ✅ CORRECT — minimal namespace-scoped role with specific resource names
213
+ apiVersion: rbac.authorization.k8s.io/v1
214
+ kind: Role
215
+ metadata:
216
+ name: myapp-role
217
+ namespace: production
218
+ rules:
219
+ - apiGroups: [""]
220
+ resources: ["configmaps"]
221
+ resourceNames: ["myapp-config"] # Lock to specific named resources
222
+ verbs: ["get", "list"] # Never ["*"]
223
+ - apiGroups: [""]
224
+ resources: ["secrets"]
225
+ resourceNames: ["myapp-db-creds"]
226
+ verbs: ["get"]
227
+
228
+ ---
229
+ apiVersion: rbac.authorization.k8s.io/v1
230
+ kind: RoleBinding
231
+ metadata:
232
+ name: myapp-rolebinding
233
+ namespace: production
234
+ subjects:
235
+ - kind: ServiceAccount
236
+ name: myapp-sa
237
+ namespace: production
238
+ roleRef:
239
+ kind: Role
240
+ name: myapp-role
241
+ apiGroup: rbac.authorization.k8s.io
242
+ ```
243
+
244
+ ### Audit RBAC
245
+
246
+ ```bash
247
+ # What can a service account do?
248
+ kubectl auth can-i --list \
249
+ --as=system:serviceaccount:production:myapp-sa \
250
+ -n production
251
+
252
+ # Find all cluster-admin bindings (security anti-pattern)
253
+ kubectl get clusterrolebindings -o json | \
254
+ jq '.items[] | select(.roleRef.name=="cluster-admin") | {name:.metadata.name, subjects:.subjects}'
255
+
256
+ # Find overly broad wildcard permissions
257
+ kubectl get roles,clusterroles -A -o json | \
258
+ jq '.items[] | select(.rules[]?.verbs[]? == "*") | .metadata.name'
259
+
260
+ # Use rbac-tool for a full audit
261
+ kubectl rbac-tool who-can get secrets -n production
262
+ ```
263
+
264
+ ---
265
+
266
+ ## Admission Controllers
267
+
268
+ ### Kyverno (Policy as Kubernetes Resources)
269
+
270
+ Kyverno validates, mutates, and generates resources — no Rego knowledge required.
271
+
272
+ ```bash
273
+ # Install Kyverno
274
+ helm repo add kyverno https://kyverno.github.io/kyverno/
275
+ helm install kyverno kyverno/kyverno -n kyverno --create-namespace
276
+ ```
277
+
278
+ **Essential Policies:**
279
+
280
+ ```yaml
281
+ # 1. Require non-root containers
282
+ apiVersion: kyverno.io/v1
283
+ kind: ClusterPolicy
284
+ metadata:
285
+ name: require-non-root
286
+ spec:
287
+ validationFailureAction: Enforce
288
+ rules:
289
+ - name: check-run-as-non-root
290
+ match:
291
+ resources:
292
+ kinds: [Pod]
293
+ validate:
294
+ message: "runAsNonRoot: true is required"
295
+ pattern:
296
+ spec:
297
+ containers:
298
+ - securityContext:
299
+ runAsNonRoot: true
300
+
301
+ ---
302
+ # 2. Require image digest pinning
303
+ apiVersion: kyverno.io/v1
304
+ kind: ClusterPolicy
305
+ metadata:
306
+ name: require-image-digest
307
+ spec:
308
+ validationFailureAction: Enforce
309
+ rules:
310
+ - name: check-digest
311
+ match:
312
+ resources:
313
+ kinds: [Pod]
314
+ validate:
315
+ message: "Images must use @sha256: digest, not floating tags"
316
+ pattern:
317
+ spec:
318
+ containers:
319
+ - image: "*@sha256:*"
320
+
321
+ ---
322
+ # 3. Disallow privileged containers
323
+ apiVersion: kyverno.io/v1
324
+ kind: ClusterPolicy
325
+ metadata:
326
+ name: disallow-privileged
327
+ spec:
328
+ validationFailureAction: Enforce
329
+ rules:
330
+ - name: check-privileged
331
+ match:
332
+ resources:
333
+ kinds: [Pod]
334
+ validate:
335
+ message: "Privileged containers are not allowed"
336
+ pattern:
337
+ spec:
338
+ containers:
339
+ - =(securityContext):
340
+ =(privileged): "false"
341
+
342
+ ---
343
+ # 4. Require resource limits (prevents resource starvation)
344
+ apiVersion: kyverno.io/v1
345
+ kind: ClusterPolicy
346
+ metadata:
347
+ name: require-resource-limits
348
+ spec:
349
+ validationFailureAction: Enforce
350
+ rules:
351
+ - name: check-limits
352
+ match:
353
+ resources:
354
+ kinds: [Pod]
355
+ validate:
356
+ message: "Resource limits (memory and cpu) must be set"
357
+ pattern:
358
+ spec:
359
+ containers:
360
+ - resources:
361
+ limits:
362
+ memory: "?*"
363
+ cpu: "?*"
364
+
365
+ ---
366
+ # 5. Auto-mutate: add drop ALL capabilities if not set
367
+ apiVersion: kyverno.io/v1
368
+ kind: ClusterPolicy
369
+ metadata:
370
+ name: drop-all-capabilities
371
+ spec:
372
+ rules:
373
+ - name: add-drop-all
374
+ match:
375
+ resources:
376
+ kinds: [Pod]
377
+ mutate:
378
+ patchStrategicMerge:
379
+ spec:
380
+ containers:
381
+ - (name): "*"
382
+ securityContext:
383
+ capabilities:
384
+ drop: ["ALL"]
385
+ ```
386
+
387
+ ### OPA Gatekeeper (Policy as Rego)
388
+
389
+ ```bash
390
+ # Install Gatekeeper
391
+ kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.17/deploy/gatekeeper.yaml
392
+ ```
393
+
394
+ ```yaml
395
+ # ConstraintTemplate — define the Rego policy
396
+ apiVersion: templates.gatekeeper.sh/v1
397
+ kind: ConstraintTemplate
398
+ metadata:
399
+ name: k8srequiredlabels
400
+ spec:
401
+ crd:
402
+ spec:
403
+ names:
404
+ kind: K8sRequiredLabels
405
+ validation:
406
+ openAPIV3Schema:
407
+ properties:
408
+ labels:
409
+ type: array
410
+ items:
411
+ type: string
412
+ targets:
413
+ - target: admission.k8s.gatekeeper.sh
414
+ rego: |
415
+ package k8srequiredlabels
416
+ violation[{"msg": msg}] {
417
+ provided := {label | input.review.object.metadata.labels[label]}
418
+ required := {label | label := input.parameters.labels[_]}
419
+ missing := required - provided
420
+ count(missing) > 0
421
+ msg := sprintf("Missing required labels: %v", [missing])
422
+ }
423
+
424
+ ---
425
+ # Constraint — apply the policy
426
+ apiVersion: constraints.gatekeeper.sh/v1beta1
427
+ kind: K8sRequiredLabels
428
+ metadata:
429
+ name: require-app-label
430
+ spec:
431
+ enforcementAction: deny
432
+ match:
433
+ kinds:
434
+ - apiGroups: ["apps"]
435
+ kinds: ["Deployment"]
436
+ parameters:
437
+ labels: ["app", "version", "owner"]
438
+ ```
439
+
440
+ ---
441
+
442
+ ## Service Account Hardening
443
+
444
+ ```yaml
445
+ # Dedicated service account per workload (never use 'default')
446
+ apiVersion: v1
447
+ kind: ServiceAccount
448
+ metadata:
449
+ name: myapp-sa
450
+ namespace: production
451
+ annotations:
452
+ # EKS — IAM Roles for Service Accounts (IRSA)
453
+ eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/myapp-role
454
+ # GKE — Workload Identity
455
+ iam.gke.io/gcp-service-account: myapp@my-project.iam.gserviceaccount.com
456
+ automountServiceAccountToken: false # Disable unless app calls K8s API
457
+
458
+ ---
459
+ # In the pod spec — also disable token mounting
460
+ spec:
461
+ serviceAccountName: myapp-sa
462
+ automountServiceAccountToken: false
463
+ ```
464
+
465
+ **Why use Workload Identity instead of K8s Secrets for cloud credentials?**
466
+ - Credentials are short-lived (1h) and auto-rotated
467
+ - No secret to leak, rotate, or store
468
+ - Audit trail tied to workload identity, not a shared key
469
+
470
+ ---
471
+
472
+ ## Runtime Security — Falco
473
+
474
+ Falco detects anomalous runtime behaviour (unexpected syscalls, network connections, file reads).
475
+
476
+ ```bash
477
+ # Install via Helm
478
+ helm repo add falcosecurity https://falcosecurity.github.io/charts
479
+ helm install falco falcosecurity/falco \
480
+ --namespace falco --create-namespace \
481
+ --set falco.grpc.enabled=true \
482
+ --set falco.grpcOutput.enabled=true
483
+ ```
484
+
485
+ **Example rules:**
486
+
487
+ ```yaml
488
+ # Alert on shell spawned inside a container
489
+ - rule: Terminal shell in container
490
+ desc: A shell was spawned in a container with an attached terminal
491
+ condition: >
492
+ spawned_process and container
493
+ and shell_procs and proc.tty != 0
494
+ and container_entrypoint
495
+ output: >
496
+ Shell spawned in a container (user=%user.name container=%container.name
497
+ shell=%proc.name parent=%proc.pname)
498
+ priority: WARNING
499
+
500
+ # Alert on sensitive file read
501
+ - rule: Read sensitive file untrusted
502
+ desc: An attempt to read a sensitive file by a non-trusted program
503
+ condition: >
504
+ open_read and sensitive_files
505
+ and not proc.name in (trusted_programs)
506
+ output: >
507
+ Sensitive file opened for reading (file=%fd.name user=%user.name
508
+ container=%container.name)
509
+ priority: WARNING
510
+ ```
511
+
512
+ ---
513
+
514
+ ## Secrets Management in K8s
515
+
516
+ **Kubernetes Secrets are base64-encoded, not encrypted by default.** Use one of these:
517
+
518
+ | Solution | Mechanism | Best For |
519
+ |---|---|---|
520
+ | **External Secrets Operator** | Sync from AWS Secrets Manager / GCP Secret Manager / Vault | Production — secrets never live in etcd |
521
+ | **Sealed Secrets (Bitnami)** | Asymmetric encryption of secrets in Git | GitOps workflows |
522
+ | **HashiCorp Vault** | Dynamic secrets, PKI, lease management | Complex multi-cloud setups |
523
+ | **SOPS + Age/GPG** | Encrypted secret files in Git | Small teams, simple workflows |
524
+
525
+ ```yaml
526
+ # External Secrets Operator — sync from AWS Secrets Manager
527
+ apiVersion: external-secrets.io/v1beta1
528
+ kind: ExternalSecret
529
+ metadata:
530
+ name: myapp-db-creds
531
+ namespace: production
532
+ spec:
533
+ refreshInterval: 1h
534
+ secretStoreRef:
535
+ name: aws-secrets-manager
536
+ kind: ClusterSecretStore
537
+ target:
538
+ name: myapp-db-creds
539
+ creationPolicy: Owner
540
+ data:
541
+ - secretKey: DB_PASSWORD
542
+ remoteRef:
543
+ key: production/myapp/db
544
+ property: password
545
+ ```
546
+
547
+ ```bash
548
+ # Enable etcd encryption at rest (K8s)
549
+ # In kube-apiserver: --encryption-provider-config=encryption-config.yaml
550
+ # encryption-config.yaml:
551
+ apiVersion: apiserver.config.k8s.io/v1
552
+ kind: EncryptionConfiguration
553
+ resources:
554
+ - resources: [secrets]
555
+ providers:
556
+ - aescbc:
557
+ keys:
558
+ - name: key1
559
+ secret: <base64-encoded-32-byte-key>
560
+ - identity: {}
561
+ ```