Commit e0a1163
authored
🤖 ci: add code and image security scanning (#88)
## Summary
Add repository and container security scanning by integrating CodeQL and
Trivy into CI/release workflows.
## Background
The repository already ran linting and vulnerability checks (`gosec`,
`govulncheck`, Terraform Trivy config scan), but it did not yet provide
first-class GitHub code scanning results or OCI image vulnerability
scanning for publish/release paths.
## Implementation
- Added a new CodeQL workflow for Go code scanning on PRs, pushes to
`main`, and a weekly schedule.
- Added a Trivy filesystem scan job in CI for repository-level scanning.
- Added a Trivy image scan job in CI that builds/scans the local Docker
image.
- Updated `publish-main` gating to include filesystem and image scanning
jobs.
- Added a post-publish Trivy scan for `ghcr.io/coder/coder-k8s:main`.
- Added a release-workflow Trivy scan for `ghcr.io/coder/coder-k8s:${{
github.event.release.tag_name }}`.
- Enabled GoReleaser image SBOM generation.
## Validation
- `go tool actionlint`
- `make build`
- `make test`
- `make lint`
- `make verify-vendor`
## Risks
- New Trivy gating can fail CI on newly disclosed HIGH/CRITICAL CVEs in
base/runtime dependencies.
- Additional scan jobs increase CI duration.
---
<details>
<summary>📋 Implementation Plan</summary>
# Plan: Add code scanning + OCI image scanning
## Context / Why
You want to add **(1) code scanning** and **(2) OCI container image
scanning** to the `coder-k8s` repository, ideally as part of GitHub
Actions CI/CD.
This repo already has good baseline security checks (gosec via
golangci-lint, govulncheck, Trivy config scan for Terraform, zizmor for
Actions). The main missing pieces are:
- **Image vulnerability scanning** for the built/published
`ghcr.io/coder/coder-k8s` images.
- **First-class “code scanning” results** surfaced in GitHub’s Security
UI (SARIF) rather than only console output.
## Evidence (what we verified)
From repo inspection (Explore task):
- `.github/workflows/ci.yaml` already runs:
- `golangci-lint` with `gosec` enabled
- `govulncheck ./...`
- Trivy **config** scan for `terraform/` (using
`aquasecurity/trivy-action@b6643a29...`)
- `zizmor` for workflow security
- `.github/workflows/release.yaml` uses GoReleaser to publish
artifacts/images.
- `.goreleaser.yaml` builds and pushes `ghcr.io/coder/coder-k8s` and has
`dockers_v2.*.sbom: false`.
- `Dockerfile.goreleaser` is `distroless/static:nonroot` and copies in
the built binary.
These files are sufficient to plan concrete changes to CI/CD for code +
image scanning.
## Implementation details (proposed changes)
### 1) Add GitHub CodeQL workflow (Go code scanning)
**Goal:** Add “code scanning” in the GitHub-native sense (SAST +
Security tab integration).
**Create:** `.github/workflows/codeql.yaml` (new)
- Triggers:
- `pull_request` (default branches)
- `push` to `main`
- `schedule` weekly (optional but recommended)
- Permissions:
- `security-events: write`
- `contents: read`
- `actions: read`
- Steps:
- `actions/checkout` (SHA-pinned, match repo style)
- `github/codeql-action/init` (Go)
- Build step (either `github/codeql-action/autobuild` or explicit `go
build ./...` with `GOFLAGS=-mod=vendor`)
- `github/codeql-action/analyze`
Shape:
```yaml
name: CodeQL
on:
push:
branches: [main]
pull_request:
schedule:
- cron: '0 6 * * 1'
permissions:
contents: read
security-events: write
actions: read
jobs:
analyze:
runs-on: depot-ubuntu-24.04-8
steps:
- uses: actions/checkout@<sha>
with:
persist-credentials: false
- uses: github/codeql-action/init@<sha>
with:
languages: go
- name: Build
env:
GOFLAGS: -mod=vendor
run: go build ./...
- uses: github/codeql-action/analyze@<sha>
```
Notes:
- Keep all Actions **SHA-pinned** (this repo already pins).
- If you expect external fork PRs, we should confirm CodeQL upload
permissions behavior; otherwise this is fine.
### 2) Add Trivy filesystem scan (repo/code + secrets + config)
**Goal:** Complement CodeQL with quick repo scanning for known
vulnerable dependencies, leaked secrets patterns, and misconfigurations.
**Update:** `.github/workflows/ci.yaml`
- Add a new job (or add steps to existing `lint` job) using the
already-pinned Trivy action.
- Use `scan-type: fs` and scan the repo root.
- Exclude `vendor/` (since deps are scanned via `govulncheck`, and
`vendor/` can create noise/slowdowns).
- Gate on `HIGH,CRITICAL` initially.
Shape:
```yaml
- name: Trivy filesystem scan
uses: aquasecurity/trivy-action@b6643a2
with:
scan-type: fs
scan-ref: .
severity: HIGH,CRITICAL
exit-code: '1'
skip-dirs: vendor
```
Optional (recommended if you want results in the GitHub Security UI):
- Output SARIF and upload it:
- Trivy: `format: sarif`, `output: trivy-fs.sarif`
- Upload: `github/codeql-action/upload-sarif` (SHA-pinned)
### 3) Add Trivy OCI image vulnerability scanning
**Goal:** Scan the built container image for vulnerabilities as part of
CI/CD.
There are two places to do this:
#### 3a) CI (pre-merge) image scan job
**Update:** `.github/workflows/ci.yaml`
- Add `image-scan` job gated on the existing `changes.outputs.publish ==
'true'` filter.
- Build a local image (linux/amd64 is enough to start) with
`Dockerfile.goreleaser`.
- Run Trivy `scan-type: image` against the local image tag.
Shape:
```yaml
image-scan:
needs: changes
if: github.event_name == 'merge_group' || needs.changes.outputs.publish == 'true'
runs-on: depot-ubuntu-24.04-8
steps:
- uses: actions/checkout@<sha>
with:
persist-credentials: false
- name: Build linux/amd64 binary
env:
GOFLAGS: -mod=vendor
CGO_ENABLED: '0'
GOOS: linux
GOARCH: amd64
run: |
mkdir -p linux/amd64
go build -o linux/amd64/coder-k8s ./
- name: Build image (local)
run: docker build -f Dockerfile.goreleaser -t coder-k8s:scan .
- name: Trivy image scan
uses: aquasecurity/trivy-action@b6643a2
with:
scan-type: image
image-ref: coder-k8s:scan
severity: HIGH,CRITICAL
exit-code: '1'
```
Optional: SARIF output + upload-sarif (same as filesystem scan).
#### 3b) Post-publish verification scan (main + releases)
**Update:**
- `.github/workflows/ci.yaml` `publish-main` job: add a Trivy image scan
step for `ghcr.io/coder/coder-k8s:main`.
- `.github/workflows/release.yaml`: add a Trivy image scan step for the
release tag `ghcr.io/coder/coder-k8s:${{ github.event.release.tag_name
}}`.
This ensures the image that actually gets published is scanned even if a
pre-merge job is skipped.
### 4) Enable SBOM generation for releases (optional but strongly
recommended)
**Goal:** Improve supply-chain posture and make scanning/auditing
easier.
**Update:** `.goreleaser.yaml`
- Turn on SBOM generation for the container image and/or release
archives.
Proposed change:
- In `dockers_v2` entry, set `sbom: true` (instead of `false`) if
supported by your GoReleaser version.
- Optionally also add a top-level `sboms:` section to generate SBOMs for
release artifacts.
### 5) Add ignore/config files for managing findings
**Goal:** Keep signal-to-noise high as scans are introduced.
**Add (as needed):**
- Repo root `.trivyignore` for image/fs scans (keep Terraform-specific
ignore in `terraform/.trivyignore` as-is).
- (Optional) `.trivy.yaml` to centralize excludes and scanner settings.
### 6) Validation plan (what to run before merging)
After implementing in Exec mode:
- `go tool actionlint` (or your existing workflow job) to validate
workflow YAML.
- Ensure CI passes for:
- `make test`
- `make build`
- `make lint`
- Smoke-test Trivy steps locally if desired (build image + run trivy).
<details>
<summary>Rationale / trade-offs</summary>
- CodeQL provides “code scanning” in the GitHub ecosystem and catches
classes of issues gosec won’t.
- Trivy `fs` scan is fast and broad (secrets/config/vuln), but can be
noisy; excluding `vendor/` and using `.trivyignore` helps.
- Image scanning on PRs requires building the image; the image is small
(distroless + static binary), so runtime impact should be acceptable.
- Post-publish scans are a safety net; pre-merge scans provide real
gating.
</details>
</details>
---
_Generated with [`mux`](https://github.com/coder/mux) • Model:
`openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: $0.34_
<!-- mux-attribution: model=openai:gpt-5.3-codex thinking=xhigh
costs=0.34 -->1 parent 8cb24dd commit e0a1163
4 files changed
Lines changed: 135 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
113 | 113 | | |
114 | 114 | | |
115 | 115 | | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
116 | 139 | | |
117 | 140 | | |
118 | 141 | | |
| |||
290 | 313 | | |
291 | 314 | | |
292 | 315 | | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
293 | 355 | | |
294 | 356 | | |
295 | 357 | | |
| |||
393 | 455 | | |
394 | 456 | | |
395 | 457 | | |
396 | | - | |
| 458 | + | |
397 | 459 | | |
398 | 460 | | |
399 | 461 | | |
400 | 462 | | |
401 | 463 | | |
402 | 464 | | |
403 | 465 | | |
| 466 | + | |
404 | 467 | | |
405 | 468 | | |
| 469 | + | |
406 | 470 | | |
407 | 471 | | |
408 | 472 | | |
| |||
464 | 528 | | |
465 | 529 | | |
466 | 530 | | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
41 | | - | |
| 41 | + | |
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
| |||
0 commit comments