Stage 7 · Master
Security Hardening
Dependency and Image Scanning
How supply-chain scanning fits into a Go microservices workflow, what different scanners actually catch, and the security pipeline Meridian should add once Dockerfiles and CI exist.
Why Supply-Chain Scanning Matters
A backend service depends on more code than its own repository. Go modules, container base images, transitive dependencies, and CI build steps all become part of the software supply chain. Scanning exists to answer a narrow question early: does this change introduce a known dependency or image risk that should be fixed before merge or before deployment? The discipline is valuable because the person best positioned to fix the issue is usually the engineer currently changing the code, not an on-call responder weeks later.
Scan Types and Their Jobs
| Control | What it catches well | What it does not cover by itself |
|---|---|---|
| govulncheck | Known Go vulnerabilities that are reachable from actual code paths | OS-package issues and base-image vulnerabilities. |
| Image scanner such as Trivy or Grype | Runtime-layer vulnerabilities in the built container image | Whether the Go code actually reaches a vulnerable function at runtime. |
| Dependency update bot | A steady stream of version hygiene through pull requests | Whether a particular version is actively exploitable in this service or whether an image layer still carries risk. |
Current Meridian Status
The meridian repository currently has no Dockerfiles, no .github/workflows directory, and no scanner configuration. That means the supply-chain surface is still small but already worth planning for. There are five Go modules to scan today: apps/gateway, apps/identity-service, apps/community-service, apps/billing-service, and libs/platform. Once container builds are added, each of the four deployable services also gains an image-scanning surface.
Planned Security Assets
.github/workflows/security.ymlcreateResponsibility: Runs govulncheck across each Go module and scans built images once Dockerfiles exist.
Why now: Security findings are cheapest to fix while a pull request is still open and the author still owns the context.
.github/dependabot.ymlcreateResponsibility: Automates routine version update proposals for Go modules and GitHub Actions.
Why now: Staying close to supported versions reduces the backlog of urgent upgrades later.
.trivy.yamlcreateResponsibility: Documents image-scanning policy such as severity thresholds and failure conditions.
Why now: A checked-in policy makes scanner behavior reviewable instead of living in tribal CI knowledge.
.trivyignorecreateResponsibility: Records rare, reviewed exceptions with context and an expectation of revisit.
Why now: Real systems occasionally need a time-bounded exception, but silent suppression destroys trust in the security lane.
Reference Workflow
name: security\n\non:\n pull_request:\n push:\n branches: [main]\n\njobs:\n govulncheck:\n runs-on: ubuntu-latest\n strategy:\n fail-fast: false\n matrix:\n module:\n - libs/platform\n - apps/gateway\n - apps/identity-service\n - apps/community-service\n - apps/billing-service\n defaults:\n run:\n working-directory: ${{ matrix.module }}\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-go@v5\n with:\n go-version-file: go.mod\n - name: Reachable vulnerability scan\n run: go run golang.org/x/vuln/cmd/govulncheck@latest ./...\n\n image-scan:\n runs-on: ubuntu-latest\n if: ${{ hashFiles('apps/**/Dockerfile') != '' }}\n strategy:\n fail-fast: false\n matrix:\n service: [gateway, identity-service, community-service, billing-service]\n steps:\n - uses: actions/checkout@v4\n - name: Build image\n run: docker build -f apps/${{ matrix.service }}/Dockerfile -t meridian/${{ matrix.service }}:${{ github.sha }} .\n - name: Trivy scan\n uses: aquasecurity/trivy-action@0.28.0\n with:\n image-ref: meridian/${{ matrix.service }}:${{ github.sha }}\n severity: HIGH,CRITICAL\n ignore-unfixed: trueThe image-scan job is conditional because the real repository has no Dockerfiles yet. That is an honest way to teach the intended future design without claiming the images already exist.
version: 2
updates:
- package-ecosystem: gomod
directory: /apps/gateway
schedule:
interval: weekly
- package-ecosystem: gomod
directory: /apps/identity-service
schedule:
interval: weekly
- package-ecosystem: gomod
directory: /apps/community-service
schedule:
interval: weekly
- package-ecosystem: gomod
directory: /apps/billing-service
schedule:
interval: weekly
- package-ecosystem: gomod
directory: /libs/platform
schedule:
interval: weeklyEach Go module gets its own update lane because each has its own go.mod today. Workspaces coordinate modules; they do not erase their independent manifests.
Making Results Actionable
Security tooling becomes noise when it cannot tell the engineer what to do next. Actionable findings have a clear owner, a severity threshold that matches actual risk, and a visible path to remediation or exception review. For Meridian that means failing pull requests on high-confidence reachable Go issues and serious runtime image findings, while keeping temporary exceptions narrow and documented. The objective is not a permanently red dashboard; it is a workflow in which important problems are surfaced soon enough to fix cheaply.
Nightly reports are useful for estate-wide visibility, but PR-time scanning is where remediation is cheapest. The engineer introducing the change still remembers why the dependency or image layer was added.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.