opencode-skills-collection 3.0.31 → 3.0.33

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.
Files changed (51) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +14 -1
  2. package/bundled-skills/bilig-workpaper/SKILL.md +12 -3
  3. package/bundled-skills/bumblebee/SKILL.md +6 -2
  4. package/bundled-skills/bun-development/SKILL.md +5 -3
  5. package/bundled-skills/cloud-penetration-testing/SKILL.md +5 -3
  6. package/bundled-skills/container-security-hardening/SKILL.md +1001 -0
  7. package/bundled-skills/container-security-hardening/references/base-image-comparison.md +245 -0
  8. package/bundled-skills/container-security-hardening/references/kubernetes-pod-security.md +567 -0
  9. package/bundled-skills/container-security-hardening/references/seccomp-profile-template.json +337 -0
  10. package/bundled-skills/doc2math/SKILL.md +102 -0
  11. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  12. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  13. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  14. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  15. package/bundled-skills/docs/users/bundles.md +1 -1
  16. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  17. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  18. package/bundled-skills/docs/users/getting-started.md +6 -2
  19. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  20. package/bundled-skills/docs/users/usage.md +4 -4
  21. package/bundled-skills/docs/users/visual-guide.md +4 -4
  22. package/bundled-skills/environment-setup-guide/SKILL.md +10 -6
  23. package/bundled-skills/evolution/SKILL.md +5 -3
  24. package/bundled-skills/github-actions-advanced/SKILL.md +1100 -0
  25. package/bundled-skills/gitops-workflow/SKILL.md +5 -3
  26. package/bundled-skills/ii-commons/SKILL.md +15 -1
  27. package/bundled-skills/lemmaly/SKILL.md +15 -6
  28. package/bundled-skills/linkerd-patterns/SKILL.md +5 -3
  29. package/bundled-skills/longbridge/SKILL.md +95 -0
  30. package/bundled-skills/mercury-mcp/SKILL.md +9 -1
  31. package/bundled-skills/moatmri/SKILL.md +84 -0
  32. package/bundled-skills/nextjs-seo-indexing/SKILL.md +263 -0
  33. package/bundled-skills/openclaw-github-repo-commander/scripts/repo-audit.sh +42 -0
  34. package/bundled-skills/photopea-embedded-editor/SKILL.md +7 -3
  35. package/bundled-skills/runaway-guard/SKILL.md +331 -0
  36. package/bundled-skills/schema-markup-generator/SKILL.md +319 -0
  37. package/bundled-skills/sendblue/sendblue-api/SKILL.md +6 -1
  38. package/bundled-skills/sendblue/sendblue-cli/SKILL.md +6 -1
  39. package/bundled-skills/sendblue/sendblue-notify/SKILL.md +6 -1
  40. package/bundled-skills/sendblue/textme/SKILL.md +4 -0
  41. package/bundled-skills/social-metadata-hardening/SKILL.md +230 -0
  42. package/bundled-skills/socialclaw/SKILL.md +6 -1
  43. package/bundled-skills/uv-package-manager/resources/implementation-playbook.md +5 -3
  44. package/bundled-skills/varlock/SKILL.md +10 -6
  45. package/bundled-skills/vibe-code-cleanup/SKILL.md +231 -0
  46. package/bundled-skills/vibecode-production-qa-validator/SKILL.md +237 -0
  47. package/bundled-skills/wordpress-centric-high-seo-optimized-blogwriting-skill/SKILL.md +229 -162
  48. package/bundled-skills/yield-intelligence/SKILL.md +121 -0
  49. package/bundled-skills/youtube-full/SKILL.md +144 -0
  50. package/package.json +1 -1
  51. package/skills_index.json +330 -28
@@ -0,0 +1,1001 @@
1
+ ---
2
+ name: container-security-hardening
3
+ description: >
4
+ Harden Docker/container images and runtime deployments with secure base images,
5
+ non-root users, CVE scanning, SBOM/signing, seccomp/AppArmor, and Kubernetes
6
+ pod security controls. Use for Dockerfile security reviews, container CVEs,
7
+ image scanning, distroless images, or production hardening.
8
+ category: security
9
+ risk: safe
10
+ source: community
11
+ date_added: "2026-05-30"
12
+ ---
13
+
14
+ # Container Security Hardening Skill
15
+
16
+ A production-focused guide for building, scanning, and running containers securely — from Dockerfile authoring through runtime enforcement and supply chain integrity.
17
+
18
+ ---
19
+
20
+ ## When to Use This Skill
21
+
22
+ - User mentions Docker security, container hardening, or Dockerfile security review
23
+ - User asks about distroless images, non-root containers, or read-only filesystems
24
+ - User wants to scan images for CVEs with Trivy, Grype, or Snyk
25
+ - User mentions seccomp, AppArmor, Linux capabilities, or runtime security
26
+ - User asks "is my Dockerfile secure?" or "how do I reduce my image attack surface?"
27
+ - User wants to sign/verify images with Cosign or generate SBOMs
28
+ - User asks about Kubernetes pod security, NetworkPolicy, or RBAC hardening
29
+ - User says "fix container CVEs" or "harden my container for production"
30
+
31
+ ## When NOT to Use This Skill
32
+
33
+ - The user is primarily asking about GitHub Actions CI/CD → recommend `github-actions-advanced`
34
+ - The user needs general Docker usage help (not security) → recommend `docker-expert`
35
+ - The user is working with Kubernetes orchestration beyond security → recommend `kubernetes-architect`
36
+ - The user needs application-level security (SQL injection, XSS) → recommend `api-security-best-practices`
37
+
38
+ ---
39
+
40
+ ## Step 1: Understand Context Before Responding
41
+
42
+ When invoked, first detect the current state:
43
+
44
+ ```bash
45
+ # Find Dockerfiles in the project
46
+ find . -name "Dockerfile*" -not -path "*/node_modules/*" | head -10
47
+
48
+ # Check for existing security tooling
49
+ ls .trivyignore .hadolint.yaml .snyk docker-compose*.yml 2>/dev/null
50
+
51
+ # Inspect base images currently in use
52
+ grep -r "^FROM" $(find . -name "Dockerfile*") 2>/dev/null
53
+
54
+ # Check if Kubernetes manifests exist
55
+ find . -name "*.yaml" -path "*/k8s/*" -o -name "*.yaml" -path "*/manifests/*" | head -10
56
+ ```
57
+
58
+ Then adapt recommendations to:
59
+ - The tech stack (Node, Python, Go, Java — affects base image choice)
60
+ - Whether this is Docker-only or Kubernetes-deployed
61
+ - The CI platform in use (for scanner integration)
62
+ - The existing base images and how far they are from best practice
63
+
64
+ ---
65
+
66
+ ## The Five Layers of Container Security
67
+
68
+ ```
69
+ 1. Image Build → Minimal base, no secrets, non-root, read-only FS
70
+ 2. Image Scanning → CVE scanning, SBOM, secret detection, Dockerfile lint
71
+ 3. Runtime Security → Capabilities, seccomp, AppArmor, resource limits
72
+ 4. Supply Chain → Signed images, pinned digests, trusted registries
73
+ 5. Kubernetes Layer → Pod Security Admission, NetworkPolicy, RBAC, Kyverno
74
+ ```
75
+
76
+ > Work through layers in order — hardening the image first gives the most leverage.
77
+ > See `references/base-image-comparison.md` for a full size/CVE trade-off table.
78
+
79
+ ---
80
+
81
+ ## Layer 1: Dockerfile Hardening
82
+
83
+ ### 1.1 Use a Minimal Base Image
84
+
85
+ ```dockerfile
86
+ # ❌ AVOID — massive attack surface (~100–200 CVEs typical)
87
+ FROM ubuntu:latest
88
+ FROM node:20
89
+
90
+ # ✅ BETTER — slim variants (glibc, smaller apt footprint)
91
+ FROM node:20-slim
92
+ FROM python:3.12-slim
93
+
94
+ # ✅ BEST — distroless (no shell, no package manager, built-in nonroot user)
95
+ FROM gcr.io/distroless/nodejs20-debian12
96
+ FROM gcr.io/distroless/python3-debian12
97
+ FROM gcr.io/distroless/static-debian12 # Go/Rust fully-static binaries
98
+
99
+ # ✅ ALSO GREAT — Alpine (musl libc; verify app compatibility first)
100
+ FROM alpine:3.20
101
+
102
+ # ✅ ZERO ATTACK SURFACE — for fully static binaries only
103
+ FROM scratch
104
+ ```
105
+
106
+ See `references/base-image-comparison.md` for the full trade-off matrix.
107
+
108
+ ### 1.2 Multi-Stage Build — Separate Build from Runtime
109
+
110
+ Never ship build tools, compilers, or dev dependencies in a production image.
111
+
112
+ ```dockerfile
113
+ # syntax=docker/dockerfile:1
114
+
115
+ # ── Stage 1: Install & Build ──────────────────────────────
116
+ FROM node:20-slim AS builder
117
+ WORKDIR /build
118
+ COPY package*.json ./
119
+ RUN npm ci # Install all deps (including devDeps)
120
+ COPY . .
121
+ RUN npm run build && npm prune --production
122
+
123
+ # ── Stage 2: Runtime — minimal, no build tools ────────────
124
+ FROM gcr.io/distroless/nodejs20-debian12@sha256:<digest>
125
+ LABEL org.opencontainers.image.source="https://github.com/org/repo"
126
+ LABEL org.opencontainers.image.revision="${BUILD_SHA}"
127
+ LABEL org.opencontainers.image.licenses="MIT"
128
+ WORKDIR /app
129
+ COPY --from=builder --chown=nonroot:nonroot /build/dist ./dist
130
+ COPY --from=builder --chown=nonroot:nonroot /build/node_modules ./node_modules
131
+ USER nonroot:nonroot # UID 65532 — built into distroless
132
+ EXPOSE 3000
133
+ CMD ["dist/server.js"]
134
+ ```
135
+
136
+ **Go / Rust static binary pattern:**
137
+ ```dockerfile
138
+ FROM golang:1.22-alpine AS builder
139
+ WORKDIR /build
140
+ COPY go.* ./
141
+ RUN go mod download
142
+ COPY . .
143
+ RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o app .
144
+
145
+ FROM scratch # Zero attack surface
146
+ COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
147
+ COPY --from=builder /build/app /app
148
+ USER 65532:65532
149
+ ENTRYPOINT ["/app"]
150
+ ```
151
+
152
+ ### 1.3 Run as Non-Root User
153
+
154
+ ```dockerfile
155
+ # For debian/ubuntu-based images — create dedicated user
156
+ RUN groupadd -r appgroup --gid 10001 && \
157
+ useradd -r -g appgroup --uid 10001 --no-log-init appuser
158
+
159
+ COPY --chown=appuser:appgroup . /app
160
+
161
+ USER appuser # Switch before CMD/ENTRYPOINT — never run as root
162
+
163
+ # ─────────────────────────────────────────────────────────
164
+ # For Alpine-based images
165
+ RUN addgroup -g 10001 -S appgroup && \
166
+ adduser -u 10001 -S appuser -G appgroup
167
+
168
+ # For distroless — nonroot (UID 65532) is already built in
169
+ USER nonroot:nonroot
170
+ ```
171
+
172
+ ### 1.4 Pin Base Images to Digest
173
+
174
+ ```dockerfile
175
+ # ❌ UNSAFE — tags are mutable; image can be silently overwritten (supply chain attack)
176
+ FROM node:20-slim
177
+
178
+ # ✅ SAFE — SHA256 digest is cryptographically immutable
179
+ FROM node:20-slim@sha256:a1b2c3d4e5f6789abcdef0123456789abcdef0123456789abcdef0123456789ab
180
+ ```
181
+
182
+ **Get the current digest:**
183
+ ```bash
184
+ docker pull node:20-slim
185
+ docker inspect node:20-slim --format='{{index .RepoDigests 0}}'
186
+ ```
187
+
188
+ **Automate digest pinning** with Renovate or Dependabot:
189
+ ```json
190
+ // .renovaterc.json
191
+ {
192
+ "extends": ["config:base"],
193
+ "dockerfile": { "enabled": true },
194
+ "pinDigests": true
195
+ }
196
+ ```
197
+
198
+ ### 1.5 Never Bake Secrets into Images
199
+
200
+ ```dockerfile
201
+ # ❌ NEVER — secret in ENV or RUN; visible in `docker history` and layer cache
202
+ ENV AWS_SECRET_ACCESS_KEY=supersecret
203
+ RUN curl -H "Authorization: Bearer $TOKEN" https://api.example.com > config.json
204
+ ARG API_KEY # Also unsafe — visible in build args history
205
+
206
+ # ✅ CORRECT — BuildKit secret mount (never persisted in any layer)
207
+ # syntax=docker/dockerfile:1
208
+ RUN --mount=type=secret,id=api_token \
209
+ curl -H "Authorization: Bearer $(cat /run/secrets/api_token)" \
210
+ https://api.example.com/config > config.json
211
+ ```
212
+
213
+ Build with: `docker build --secret id=api_token,src=./token.txt .`
214
+
215
+ **Check your image for leaked secrets:**
216
+ ```bash
217
+ docker history --no-trunc myapp:latest | grep -iE "secret|key|password|token"
218
+ trivy image --scanners secret myapp:latest
219
+ ```
220
+
221
+ ### 1.6 Read-Only Filesystem & No New Privileges
222
+
223
+ ```dockerfile
224
+ # In the Dockerfile — use exec form (no shell interpretation)
225
+ ENTRYPOINT ["node", "server.js"] # ✅ exec form
226
+ # ENTRYPOINT /bin/sh -c "node..." # ❌ shell form — spawns extra process
227
+
228
+ # Define a HEALTHCHECK
229
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
230
+ CMD ["node", "-e", "require('http').get('http://localhost:3000/health', r => process.exit(r.statusCode === 200 ? 0 : 1))"]
231
+ ```
232
+
233
+ Enforce read-only at runtime (see Layer 3).
234
+
235
+ ### 1.7 Minimal .dockerignore
236
+
237
+ ```dockerignore
238
+ # Always exclude these from build context
239
+ .git
240
+ .github
241
+ .env
242
+ .env.*
243
+ *.pem
244
+ *.key
245
+ node_modules
246
+ __pycache__
247
+ .pytest_cache
248
+ coverage/
249
+ dist/
250
+ *.log
251
+ .DS_Store
252
+ Dockerfile*
253
+ docker-compose*
254
+ README.md
255
+ docs/
256
+ tests/
257
+ ```
258
+
259
+ ### 1.8 Full Hardened Dockerfile Example
260
+
261
+ ```dockerfile
262
+ # syntax=docker/dockerfile:1
263
+
264
+ # ── Build stage ───────────────────────────────────────────
265
+ FROM node:20-slim AS builder
266
+ WORKDIR /build
267
+ COPY package*.json ./
268
+ RUN --mount=type=cache,target=/root/.npm \
269
+ npm ci
270
+ COPY . .
271
+ RUN npm run build && npm prune --production
272
+
273
+ # ── Runtime stage ─────────────────────────────────────────
274
+ FROM gcr.io/distroless/nodejs20-debian12@sha256:<pin-digest-here>
275
+
276
+ LABEL org.opencontainers.image.source="https://github.com/org/repo"
277
+ LABEL org.opencontainers.image.revision="${BUILD_SHA}"
278
+ LABEL org.opencontainers.image.licenses="MIT"
279
+
280
+ WORKDIR /app
281
+ COPY --from=builder --chown=nonroot:nonroot /build/dist ./dist
282
+ COPY --from=builder --chown=nonroot:nonroot /build/node_modules ./node_modules
283
+
284
+ USER nonroot:nonroot
285
+ EXPOSE 3000
286
+
287
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
288
+ CMD ["node", "-e", "require('http').get('http://localhost:3000/health', r => process.exit(r.statusCode===200?0:1))"]
289
+
290
+ CMD ["dist/server.js"]
291
+ ```
292
+
293
+ ---
294
+
295
+ ## Layer 2: Image Scanning
296
+
297
+ ### 2.1 Trivy (Recommended — Fast, Comprehensive)
298
+
299
+ ```bash
300
+ # Install
301
+ brew install trivy # macOS
302
+ apt install trivy # Debian/Ubuntu
303
+ tmpdir="$(mktemp -d)"
304
+ trap 'rm -rf "$tmpdir"' EXIT
305
+ curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh \
306
+ -o "$tmpdir/trivy-install.sh"
307
+ sed -n '1,160p' "$tmpdir/trivy-install.sh"
308
+ sh "$tmpdir/trivy-install.sh"
309
+
310
+ # Scan an image for CVEs
311
+ trivy image myapp:latest
312
+
313
+ # Fail CI on HIGH/CRITICAL severity
314
+ trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest
315
+
316
+ # Scan Dockerfile for misconfigurations
317
+ trivy config ./Dockerfile
318
+
319
+ # Scan entire repo (vulnerabilities + secrets + misconfigs)
320
+ trivy fs --scanners vuln,secret,misconfig .
321
+
322
+ # Generate SBOM (CycloneDX or SPDX)
323
+ trivy image --format cyclonedx --output sbom.json myapp:latest
324
+ trivy image --format spdx-json --output sbom.spdx.json myapp:latest
325
+
326
+ # Ignore specific CVEs (add justification comments)
327
+ trivy image --ignorefile .trivyignore myapp:latest
328
+ ```
329
+
330
+ **.trivyignore example:**
331
+ ```
332
+ # CVE-2023-1234 — only exploitable via X feature, not used in this app
333
+ CVE-2023-1234
334
+
335
+ # CVE-2023-5678 — fix not yet available; tracked in issue #42
336
+ CVE-2023-5678
337
+ ```
338
+
339
+ ### 2.2 Grype (Anchore Alternative)
340
+
341
+ ```bash
342
+ # Install
343
+ tmpdir="$(mktemp -d)"
344
+ trap 'rm -rf "$tmpdir"' EXIT
345
+ curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh \
346
+ -o "$tmpdir/grype-install.sh"
347
+ sed -n '1,160p' "$tmpdir/grype-install.sh"
348
+ sh "$tmpdir/grype-install.sh"
349
+
350
+ # Scan image
351
+ grype myapp:latest
352
+
353
+ # Fail on critical
354
+ grype myapp:latest --fail-on critical
355
+
356
+ # Output SARIF for GitHub Security tab
357
+ grype myapp:latest -o sarif > results.sarif
358
+
359
+ # Pair with Syft for SBOM generation
360
+ syft myapp:latest -o cyclonedx-json > sbom.json
361
+ grype sbom:sbom.json # Scan the SBOM directly
362
+ ```
363
+
364
+ ### 2.3 Hadolint — Dockerfile Linting
365
+
366
+ ```bash
367
+ # Run directly
368
+ docker run --rm -i hadolint/hadolint < Dockerfile
369
+
370
+ # With config file
371
+ hadolint --config .hadolint.yaml --failure-threshold warning Dockerfile
372
+ ```
373
+
374
+ **.hadolint.yaml:**
375
+ ```yaml
376
+ failure-threshold: warning
377
+ ignore:
378
+ - DL3008 # Pin versions in apt-get (allow floating for base layer)
379
+ trustedRegistries:
380
+ - gcr.io
381
+ - ghcr.io
382
+ - public.ecr.aws
383
+ ```
384
+
385
+ ### 2.4 Secret Scanning in Images
386
+
387
+ ```bash
388
+ # Trivy covers secrets too
389
+ trivy image --scanners secret myapp:latest
390
+
391
+ # Dedicated: TruffleHog
392
+ trufflehog docker --image myapp:latest
393
+
394
+ # git-secrets to prevent committing secrets
395
+ git secrets --scan
396
+ ```
397
+
398
+ ### 2.5 CI Integration (GitHub Actions — SHA-Pinned)
399
+
400
+ ```yaml
401
+ permissions:
402
+ contents: read
403
+ security-events: write # Required for uploading SARIF
404
+
405
+ jobs:
406
+ security-scan:
407
+ runs-on: ubuntu-24.04
408
+ timeout-minutes: 20
409
+ steps:
410
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
411
+
412
+ - name: Build image
413
+ run: docker build -t myapp:${{ github.sha }} .
414
+
415
+ - name: Lint Dockerfile
416
+ uses: hadolint/hadolint-action@54c9adbab1582c2ef04b2016b760714a4bfde3cf # v3.1.0
417
+ with:
418
+ dockerfile: Dockerfile
419
+ failure-threshold: warning
420
+
421
+ - name: Scan with Trivy
422
+ uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # v0.28.0
423
+ with:
424
+ image-ref: myapp:${{ github.sha }}
425
+ format: sarif
426
+ output: trivy-results.sarif
427
+ severity: HIGH,CRITICAL
428
+ exit-code: '1'
429
+
430
+ - name: Upload results to GitHub Security tab
431
+ uses: github/codeql-action/upload-sarif@4f3212b61783c3c68e8309a0f18a699764811cda # v3.27.1
432
+ if: always() # Upload even if scan found issues
433
+ with:
434
+ sarif_file: trivy-results.sarif
435
+ ```
436
+
437
+ ---
438
+
439
+ ## Layer 3: Runtime Security
440
+
441
+ ### 3.1 docker run Hardening Flags
442
+
443
+ ```bash
444
+ docker run \
445
+ --read-only \ # Read-only root filesystem
446
+ --tmpfs /tmp:noexec,nosuid,size=100m \ # Writable tmpfs for /tmp only
447
+ --tmpfs /var/run \ # For PID files if needed
448
+ --user 10001:10001 \ # Non-root UID:GID
449
+ --cap-drop ALL \ # Drop ALL Linux capabilities
450
+ --cap-add NET_BIND_SERVICE \ # Re-add only what's truly needed
451
+ --security-opt no-new-privileges:true \ # Prevent privilege escalation via setuid
452
+ --security-opt seccomp=seccomp.json \ # Custom seccomp profile
453
+ --security-opt apparmor=docker-default \ # AppArmor profile
454
+ --pids-limit 100 \ # Prevent fork bombs
455
+ --memory 512m \ # OOM protection
456
+ --memory-swap 512m \ # Disable swap
457
+ --cpus 1.0 \ # CPU limit
458
+ --network none \ # No network (if not needed)
459
+ --health-cmd "curl -f http://localhost:3000/health || exit 1" \
460
+ --health-interval 30s \
461
+ myapp:latest
462
+ ```
463
+
464
+ ### 3.2 Linux Capabilities — What to Drop and Keep
465
+
466
+ Drop ALL, then explicitly add only what your app requires:
467
+
468
+ | Capability | Purpose | Keep? |
469
+ |---|---|---|
470
+ | `NET_BIND_SERVICE` | Bind ports < 1024 | Only if binding a privileged port |
471
+ | `CHOWN` | Change file ownership | No — set ownership at build time |
472
+ | `SETUID` / `SETGID` | Switch user identity | No — drop always |
473
+ | `SYS_ADMIN` | Broad privileged operations | No — most dangerous capability |
474
+ | `NET_ADMIN` | Configure network interfaces | No (only network tools) |
475
+ | `SYS_PTRACE` | Debug/trace processes | No (only debugger containers) |
476
+ | `DAC_OVERRIDE` | Override file permissions | No — runs as correct user |
477
+ | `NET_RAW` | Raw sockets (ping) | No (blocked by default seccomp anyway) |
478
+
479
+ > **Most web apps need zero capabilities.** `--cap-drop ALL` alone is often sufficient.
480
+
481
+ ### 3.3 Docker Compose Hardening
482
+
483
+ ```yaml
484
+ services:
485
+ app:
486
+ image: myapp:latest
487
+ read_only: true
488
+ user: "10001:10001"
489
+ tmpfs:
490
+ - /tmp:noexec,nosuid,size=100m
491
+ - /var/run:noexec,nosuid,size=10m
492
+ cap_drop:
493
+ - ALL
494
+ cap_add:
495
+ - NET_BIND_SERVICE # Only if binding port < 1024
496
+ security_opt:
497
+ - no-new-privileges:true
498
+ - seccomp:./references/seccomp-profile-template.json
499
+ pids_limit: 100
500
+ mem_limit: 512m
501
+ memswap_limit: 512m
502
+ cpus: 1.0
503
+ healthcheck:
504
+ test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
505
+ interval: 30s
506
+ timeout: 5s
507
+ retries: 3
508
+ start_period: 10s
509
+ networks:
510
+ - backend
511
+ # Only expose externally if truly required
512
+ # ports: ["8080:8080"]
513
+ restart: unless-stopped
514
+ logging:
515
+ driver: json-file
516
+ options:
517
+ max-size: "10m"
518
+ max-file: "3"
519
+
520
+ networks:
521
+ backend:
522
+ driver: bridge
523
+ internal: true # No external connectivity unless needed
524
+ ```
525
+
526
+ ### 3.4 Seccomp Profiles
527
+
528
+ The Docker default seccomp profile blocks ~44 dangerous syscalls. For stricter control:
529
+
530
+ ```bash
531
+ # Step 1: Audit syscalls your app actually makes
532
+ docker run --security-opt seccomp=unconfined \
533
+ --name audit-run myapp:latest &
534
+
535
+ # Capture with strace
536
+ strace -c -p $(docker inspect --format '{{.State.Pid}}' audit-run)
537
+
538
+ # Or with sysdig (more container-friendly)
539
+ sysdig -p "%syscall.type" container.name=audit-run | sort -u
540
+
541
+ # Step 2: Build a custom profile from references/seccomp-profile-template.json
542
+ # Step 3: Apply it
543
+ docker run --security-opt seccomp=references/seccomp-profile-template.json myapp:latest
544
+ ```
545
+
546
+ See `references/seccomp-profile-template.json` for a minimal starting allowlist for typical web servers.
547
+
548
+ ### 3.5 AppArmor Profile (Linux hosts)
549
+
550
+ ```bash
551
+ # Load Docker's default AppArmor profile
552
+ sudo apparmor_parser -r /etc/apparmor.d/docker-default
553
+
554
+ # Apply at runtime
555
+ docker run --security-opt apparmor=docker-default myapp:latest
556
+
557
+ # Generate a custom profile
558
+ aa-genprof myapp # Interactive — run app under aa-complain mode first
559
+ ```
560
+
561
+ ---
562
+
563
+ ## Layer 4: Supply Chain Security
564
+
565
+ ### 4.1 Sign Images with Cosign (Sigstore — Keyless)
566
+
567
+ ```bash
568
+ # Install cosign
569
+ brew install cosign # macOS
570
+ # or: https://github.com/sigstore/cosign/releases
571
+
572
+ # Sign after push — keyless via OIDC (no long-lived keys)
573
+ cosign sign ghcr.io/org/myapp:latest
574
+
575
+ # Verify before deploy
576
+ cosign verify ghcr.io/org/myapp:latest \
577
+ --certificate-identity-regexp="https://github.com/org/repo" \
578
+ --certificate-oidc-issuer="https://token.actions.githubusercontent.com"
579
+ ```
580
+
581
+ **GitHub Actions — Sign & Verify Pipeline:**
582
+ ```yaml
583
+ permissions:
584
+ id-token: write # Required for OIDC keyless signing
585
+ packages: write
586
+
587
+ steps:
588
+ - uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
589
+
590
+ - name: Sign image (keyless via OIDC)
591
+ run: |
592
+ cosign sign --yes \
593
+ ghcr.io/${{ github.repository }}:${{ github.sha }}
594
+ env:
595
+ COSIGN_EXPERIMENTAL: "true"
596
+
597
+ - name: Attach SBOM attestation
598
+ run: |
599
+ cosign attest --yes \
600
+ --predicate sbom.json \
601
+ --type cyclonedx \
602
+ ghcr.io/${{ github.repository }}:${{ github.sha }}
603
+ ```
604
+
605
+ ### 4.2 SBOM Generation & Attestation
606
+
607
+ ```bash
608
+ # Generate SBOM with Syft
609
+ syft myapp:latest -o cyclonedx-json > sbom.json
610
+ syft myapp:latest -o spdx-json > sbom.spdx.json
611
+
612
+ # Attach to image as attestation
613
+ cosign attest --predicate sbom.json --type cyclonedx ghcr.io/org/myapp:latest
614
+
615
+ # Verify SBOM attestation before deployment
616
+ cosign verify-attestation \
617
+ --type cyclonedx \
618
+ --certificate-identity-regexp="https://github.com/org/repo" \
619
+ --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
620
+ ghcr.io/org/myapp:latest
621
+ ```
622
+
623
+ ### 4.3 Use Trusted Registries & Enable Registry Scanning
624
+
625
+ | Registry | Built-in Scanning | Notes |
626
+ |---|---|---|
627
+ | GHCR (GitHub Container Registry) | No (use Trivy in CI) | Best for OSS, OIDC auth |
628
+ | AWS ECR | Yes (enhanced scanning via Inspector) | Enable per-repo |
629
+ | GCP Artifact Registry | Yes (Container Analysis) | Enabled by default |
630
+ | Azure ACR | Yes (Defender for Containers) | Premium tier |
631
+ | Docker Hub | Yes (limited on free tier) | Avoid for private images |
632
+
633
+ ```bash
634
+ # Enable ECR enhanced scanning
635
+ aws ecr put-registry-scanning-configuration \
636
+ --scan-type ENHANCED \
637
+ --rules '[{"repositoryFilters":[{"filter":"*","filterType":"WILDCARD"}],"scanFrequency":"CONTINUOUS_SCAN"}]'
638
+ ```
639
+
640
+ ### 4.4 Admission Control — Block Unsigned/Unscanned Images
641
+
642
+ ```yaml
643
+ # Kyverno policy — require signed images before admission
644
+ apiVersion: kyverno.io/v1
645
+ kind: ClusterPolicy
646
+ metadata:
647
+ name: require-signed-images
648
+ spec:
649
+ validationFailureAction: Enforce
650
+ rules:
651
+ - name: verify-image-signature
652
+ match:
653
+ resources:
654
+ kinds: [Pod]
655
+ verifyImages:
656
+ - imageReferences:
657
+ - "ghcr.io/org/*"
658
+ attestors:
659
+ - entries:
660
+ - keyless:
661
+ subject: "https://github.com/org/repo/.github/workflows/*"
662
+ issuer: "https://token.actions.githubusercontent.com"
663
+ ```
664
+
665
+ ---
666
+
667
+ ## Layer 5: Kubernetes Pod Security
668
+
669
+ > Full reference: `references/kubernetes-pod-security.md`
670
+
671
+ ### 5.1 Pod Security Context
672
+
673
+ ```yaml
674
+ apiVersion: apps/v1
675
+ kind: Deployment
676
+ metadata:
677
+ name: myapp
678
+ namespace: production
679
+ spec:
680
+ replicas: 3
681
+ template:
682
+ spec:
683
+ # ── Pod-level security context ─────────────────────
684
+ securityContext:
685
+ runAsNonRoot: true
686
+ runAsUser: 10001
687
+ runAsGroup: 10001
688
+ fsGroup: 10001
689
+ fsGroupChangePolicy: OnRootMismatch
690
+ seccompProfile:
691
+ type: RuntimeDefault # Use containerd/runc default seccomp
692
+ supplementalGroups: []
693
+
694
+ automountServiceAccountToken: false # Disable unless needed
695
+
696
+ # ── Container-level security context ──────────────
697
+ containers:
698
+ - name: app
699
+ image: ghcr.io/org/myapp@sha256:<digest> # Always use digest
700
+ securityContext:
701
+ allowPrivilegeEscalation: false
702
+ readOnlyRootFilesystem: true
703
+ capabilities:
704
+ drop: ["ALL"]
705
+ add: [] # Add nothing unless absolutely required
706
+ runAsNonRoot: true
707
+ runAsUser: 10001
708
+ seccompProfile:
709
+ type: RuntimeDefault
710
+
711
+ # ── Resource limits (required for restricted PSA) ──
712
+ resources:
713
+ requests:
714
+ memory: "128Mi"
715
+ cpu: "100m"
716
+ limits:
717
+ memory: "512Mi"
718
+ cpu: "500m"
719
+
720
+ # ── Writable tmpfs mounts ──────────────────────
721
+ volumeMounts:
722
+ - name: tmp
723
+ mountPath: /tmp
724
+ - name: varrun
725
+ mountPath: /var/run
726
+
727
+ volumes:
728
+ - name: tmp
729
+ emptyDir:
730
+ medium: Memory
731
+ sizeLimit: 100Mi
732
+ - name: varrun
733
+ emptyDir:
734
+ medium: Memory
735
+ sizeLimit: 10Mi
736
+ ```
737
+
738
+ ### 5.2 Pod Security Admission (K8s 1.25+)
739
+
740
+ ```bash
741
+ # Audit existing workloads before enforcing
742
+ kubectl label namespace production \
743
+ pod-security.kubernetes.io/audit=restricted \
744
+ pod-security.kubernetes.io/audit-version=latest
745
+
746
+ # Warn in staging, enforce in production
747
+ kubectl label namespace staging \
748
+ pod-security.kubernetes.io/warn=restricted
749
+
750
+ kubectl label namespace production \
751
+ pod-security.kubernetes.io/enforce=restricted \
752
+ pod-security.kubernetes.io/enforce-version=latest
753
+ ```
754
+
755
+ | PSA Level | What It Blocks |
756
+ |---|---|
757
+ | `privileged` | No restrictions |
758
+ | `baseline` | Blocks hostNetwork, hostPID, privileged containers, hostPath |
759
+ | `restricted` | Also requires non-root, read-only FS, drops capabilities, seccomp |
760
+
761
+ ### 5.3 NetworkPolicy — Zero-Trust Networking
762
+
763
+ ```yaml
764
+ # Step 1: Deny all ingress and egress by default in the namespace
765
+ apiVersion: networking.k8s.io/v1
766
+ kind: NetworkPolicy
767
+ metadata:
768
+ name: default-deny-all
769
+ namespace: production
770
+ spec:
771
+ podSelector: {}
772
+ policyTypes: [Ingress, Egress]
773
+
774
+ ---
775
+ # Step 2: Selectively allow only required traffic
776
+ apiVersion: networking.k8s.io/v1
777
+ kind: NetworkPolicy
778
+ metadata:
779
+ name: allow-app
780
+ namespace: production
781
+ spec:
782
+ podSelector:
783
+ matchLabels:
784
+ app: myapp
785
+ policyTypes: [Ingress, Egress]
786
+ ingress:
787
+ - from:
788
+ - namespaceSelector:
789
+ matchLabels:
790
+ kubernetes.io/metadata.name: ingress-nginx
791
+ podSelector:
792
+ matchLabels:
793
+ app.kubernetes.io/name: ingress-nginx
794
+ ports:
795
+ - port: 3000
796
+ egress:
797
+ - to:
798
+ - podSelector:
799
+ matchLabels:
800
+ app: postgres
801
+ ports:
802
+ - port: 5432
803
+ - to: # Allow only cluster DNS
804
+ - namespaceSelector:
805
+ matchLabels:
806
+ kubernetes.io/metadata.name: kube-system
807
+ podSelector:
808
+ matchLabels:
809
+ k8s-app: kube-dns
810
+ ports:
811
+ - port: 53
812
+ protocol: UDP
813
+ - port: 53
814
+ protocol: TCP
815
+ ```
816
+
817
+ ### 5.4 RBAC — Least Privilege
818
+
819
+ ```yaml
820
+ # Create minimal role — never use wildcards
821
+ apiVersion: rbac.authorization.k8s.io/v1
822
+ kind: Role
823
+ metadata:
824
+ name: app-reader
825
+ namespace: production
826
+ rules:
827
+ - apiGroups: [""]
828
+ resources: ["configmaps", "secrets"]
829
+ resourceNames: ["myapp-config"] # Lock to specific resource names
830
+ verbs: ["get"] # Never ["*"]
831
+
832
+ ---
833
+ apiVersion: rbac.authorization.k8s.io/v1
834
+ kind: RoleBinding
835
+ metadata:
836
+ name: app-reader-binding
837
+ namespace: production
838
+ subjects:
839
+ - kind: ServiceAccount
840
+ name: myapp-sa
841
+ namespace: production
842
+ roleRef:
843
+ kind: Role
844
+ name: app-reader
845
+ apiGroup: rbac.authorization.k8s.io
846
+ ```
847
+
848
+ ```bash
849
+ # Audit what permissions a service account has
850
+ kubectl auth can-i --list --as=system:serviceaccount:production:myapp-sa
851
+
852
+ # Find overly-permissive cluster roles
853
+ kubectl get clusterrolebindings -o json | \
854
+ jq '.items[] | select(.roleRef.name == "cluster-admin") | .subjects'
855
+ ```
856
+
857
+ ### 5.5 Kyverno Policy Examples
858
+
859
+ ```yaml
860
+ # Require non-root containers
861
+ apiVersion: kyverno.io/v1
862
+ kind: ClusterPolicy
863
+ metadata:
864
+ name: require-non-root
865
+ spec:
866
+ validationFailureAction: Enforce
867
+ rules:
868
+ - name: check-run-as-non-root
869
+ match:
870
+ resources:
871
+ kinds: [Pod]
872
+ validate:
873
+ message: "Containers must not run as root (runAsNonRoot: true required)"
874
+ pattern:
875
+ spec:
876
+ containers:
877
+ - securityContext:
878
+ runAsNonRoot: true
879
+
880
+ ---
881
+ # Require image digest pinning
882
+ apiVersion: kyverno.io/v1
883
+ kind: ClusterPolicy
884
+ metadata:
885
+ name: require-image-digest
886
+ spec:
887
+ validationFailureAction: Enforce
888
+ rules:
889
+ - name: check-digest
890
+ match:
891
+ resources:
892
+ kinds: [Pod]
893
+ validate:
894
+ message: "Images must be pinned to a SHA256 digest, not just a tag"
895
+ pattern:
896
+ spec:
897
+ containers:
898
+ - image: "*@sha256:*"
899
+
900
+ ---
901
+ # Block privileged containers
902
+ apiVersion: kyverno.io/v1
903
+ kind: ClusterPolicy
904
+ metadata:
905
+ name: disallow-privileged
906
+ spec:
907
+ validationFailureAction: Enforce
908
+ rules:
909
+ - name: check-privileged
910
+ match:
911
+ resources:
912
+ kinds: [Pod]
913
+ validate:
914
+ message: "Privileged containers are not allowed"
915
+ pattern:
916
+ spec:
917
+ containers:
918
+ - =(securityContext):
919
+ =(privileged): "false"
920
+ ```
921
+
922
+ ---
923
+
924
+ ## Common Pitfalls & Fixes
925
+
926
+ | Problem | Root Cause | Fix |
927
+ |---|---|---|
928
+ | Image runs as root | No `USER` directive | Add `RUN useradd ...` and `USER appuser` |
929
+ | Secret in `docker history` | `ENV` or `RUN curl -H "Bearer $TOKEN"` | Use `RUN --mount=type=secret` |
930
+ | Large image with many CVEs | Full base image (`node:20`, `ubuntu`) | Switch to `node:20-slim` or `distroless` |
931
+ | App crashes with `--read-only` | Writes to `/tmp` or app directory | Add `--tmpfs /tmp` for writable temp space |
932
+ | Trivy scan blocks CI on unfixable CVEs | No ignore file | Add `.trivyignore` with justified entries |
933
+ | Container needs `SYS_ADMIN` | Missing `--cap-drop` context | Investigate why — almost always avoidable |
934
+ | Tag-based images drift over time | Mutable tags | Pin to `@sha256:` digest; use Renovate to update |
935
+ | K8s pod rejected by PSA | Missing security context fields | Add `runAsNonRoot`, `readOnlyRootFilesystem`, `allowPrivilegeEscalation: false` |
936
+ | App can't write to filesystem | `readOnlyRootFilesystem: true` | Mount `emptyDir` volumes for writable paths |
937
+
938
+ ---
939
+
940
+ ## Security Checklist
941
+
942
+ ### Dockerfile
943
+ - [ ] Minimal base image (distroless, slim, or alpine — not full debian/ubuntu)
944
+ - [ ] Multi-stage build — no build tools, devDependencies, or compilers in runtime image
945
+ - [ ] Non-root `USER` declared before `CMD`/`ENTRYPOINT`
946
+ - [ ] Base image pinned to `@sha256:...` digest (not just tag)
947
+ - [ ] No secrets in `ENV`, `ARG`, or `RUN` commands
948
+ - [ ] `HEALTHCHECK` defined
949
+ - [ ] OCI labels present (`org.opencontainers.image.*`)
950
+ - [ ] `.dockerignore` excludes `.git`, `.env`, secrets, tests
951
+ - [ ] `ENTRYPOINT` uses exec form, not shell form
952
+
953
+ ### Image Scanning
954
+ - [ ] Trivy or Grype scan in CI (fails on HIGH/CRITICAL)
955
+ - [ ] Hadolint passes with no warnings
956
+ - [ ] Secret scan run on image (`trivy --scanners secret`)
957
+ - [ ] SBOM generated and stored
958
+ - [ ] `.trivyignore` has justified entries for accepted CVEs
959
+
960
+ ### Runtime
961
+ - [ ] `--read-only` filesystem
962
+ - [ ] `--cap-drop ALL` (add back only what's documented as required)
963
+ - [ ] `--security-opt no-new-privileges:true`
964
+ - [ ] `--security-opt seccomp=<profile>` applied
965
+ - [ ] Resource limits set (`--memory`, `--cpus`, `--pids-limit`)
966
+ - [ ] Image signed with Cosign; verified before deploy
967
+
968
+ ### Kubernetes
969
+ - [ ] `readOnlyRootFilesystem: true`
970
+ - [ ] `allowPrivilegeEscalation: false`
971
+ - [ ] `runAsNonRoot: true` with explicit UID
972
+ - [ ] `capabilities.drop: ["ALL"]`
973
+ - [ ] Resource `requests` and `limits` defined
974
+ - [ ] `automountServiceAccountToken: false`
975
+ - [ ] Namespace PSA enforced at `restricted` level
976
+ - [ ] `NetworkPolicy` default-deny applied
977
+ - [ ] RBAC uses specific resource names and minimal verbs
978
+
979
+ ---
980
+
981
+ ## Reference Files
982
+
983
+ - `references/base-image-comparison.md` — Size, CVE count, shell/pkg-manager trade-offs: distroless vs alpine vs slim vs scratch
984
+ - `references/seccomp-profile-template.json` — Minimal syscall allowlist for typical web servers; start here and extend
985
+ - `references/kubernetes-pod-security.md` — NetworkPolicy, RBAC, OPA/Kyverno policies, service account hardening, PSA
986
+
987
+ ## Related Skills
988
+
989
+ - `docker-expert` — General Docker usage, Compose orchestration, image optimization
990
+ - `gha-security-review` — Security audit of GitHub Actions workflows
991
+ - `github-actions-advanced` — CI pipeline patterns including scanner integration
992
+ - `kubernetes-architect` — Full Kubernetes architecture, not just security
993
+ - `api-security-best-practices` — Application-level security (injection, auth, OWASP)
994
+ - `k8s-security-policies` — Extended Kubernetes security policies
995
+
996
+ ## Limitations
997
+
998
+ - Use this skill only when the task clearly matches the scope described above.
999
+ - Do not treat the output as a substitute for environment-specific penetration testing or a formal security audit.
1000
+ - Seccomp profiles and AppArmor are Linux-only; macOS/Windows Docker Desktop uses different mechanisms.
1001
+ - Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.