A security gate that checks the whole box, not just the image.
Drop jakelamon/keelix@v0.1.0 into your pipeline. In one step it scans your Docker Compose stack — host config, port exposure, and AI-agent/MCP posture — and blocks the promote-to-production job when a critical finding or a sub-threshold posture score is present.
Quick start
The action installs the Keelix CLI binary into the runner's temp directory, runs a scan against your Compose file, and exits non-zero on any critical finding. No API token. No network calls to Keelix servers. Nothing leaves the runner.
jobs:
security-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Keelix posture gate
uses: jakelamon/keelix@v0.1.0
with:
compose: docker-compose.ymlThe action fails (exit 1) when any critical finding is present. The full list of inputs is below — you can add outside-in probing, a baseline file for delta gating, and a posture report artifact.
Inputs reference
| Input | Required | Default | Description |
|---|---|---|---|
| compose | yes | docker-compose.yml | Path to the docker-compose.yml to audit |
| host | no | (empty) | Host to probe outside-in. Leave empty for static analysis only. |
| no-probe | no | true | Disable outside-in probing (default on; set false + host to enable) |
| firewall | no | (empty) | Path to a UFW/iptables rules dump to correlate |
| baseline | no | (empty) | Path to a previous scan JSON; fail only on NEW criticals |
| version | no | latest | Keelix CLI version to install |
| report | no | (empty) | Write a posture report artifact: md, html, or pdf |
Output: json— path to the machine-readable scan result JSON written to the runner's temp directory. Pass it to downstream steps or upload as an artifact.
Two-step rollout: monitoring then enforcement
Dropping a hard gate into a live pipeline cold is a recipe for frustrated engineers. The practical rollout is two steps.
Step 1 — monitor. Run the action with continue-on-error: true. The scan runs, the JSON result is uploaded as an artifact, and the workflow always passes. Review the findings for a sprint or two and triage what's real vs noise.
Step 2 — enforce. Remove continue-on-error and optionally pass a baseline JSON so the gate only fires on findings that are new since you started monitoring. That keeps your backlog from blocking new work while still catching regressions.
- name: Keelix posture scan (monitor)
uses: jakelamon/keelix@v0.1.0
continue-on-error: true
with:
compose: docker-compose.yml
report: html
- name: Upload posture report
uses: actions/upload-artifact@v4
with:
name: keelix-report
path: ${{ runner.temp }}/keelix-report.html - name: Restore baseline
uses: actions/cache@v4
with:
path: keelix-baseline.json
key: keelix-baseline-${{ github.ref_name }}
- name: Keelix posture gate (enforce)
uses: jakelamon/keelix@v0.1.0
with:
compose: docker-compose.yml
baseline: keelix-baseline.json
- name: Save new baseline
run: cp ${{ runner.temp }}/keelix-scan.json keelix-baseline.jsonWhat image-only actions cannot catch
Actions like aquasecurity/trivy-action are excellent at what they do: scanning container layers for CVEs and Dockerfile misconfigurations. Use them. But an image-layer scan runs before the container is deployed — it has no visibility into what happens on the box after provisioning.
- An MCP server that auto-approves all tool calls (an all-or-nothing trust escalation that turns any prompt injection into full agent takeover) is configured at deploy time, not baked into an image layer.
- A plaintext Anthropic API key sitting in a Claude Code or Cursor config file lives on the host, not in any container image.
- A Redis container published to
0.0.0.0:6379— bypassing UFW via Docker's iptables rules — becomes internet-reachable the moment the stack is brought up. The compose file sets that; the image doesn't know about it.
These are deployment-context issues, not image issues. Keelix catches them at the point where they actually exist: the running box. See how Docker bypasses UFW and the full AI/MCP security surface for details.
Full pipeline: Trivy image scan + Keelix box gate
The recommended setup runs Trivy in the build stage and Keelix in the deploy-to-staging stage. Trivy catches CVEs in the image. Keelix catches the misconfigured deployment. Neither finds what the other finds.
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Trivy image scan
uses: aquasecurity/trivy-action@0.35.0 # pin to SHA in prod
with:
image-ref: myapp:${{ github.sha }}
exit-code: "1"
severity: CRITICAL
deploy-to-staging:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# … your staging deploy steps …
- name: Keelix posture gate
uses: jakelamon/keelix@v0.1.0
with:
compose: docker-compose.yml
report: html
- name: Upload posture report
if: always()
uses: actions/upload-artifact@v4
with:
name: keelix-posture-report
path: ${{ runner.temp }}/keelix-report.html
promote-to-production:
needs: deploy-to-staging
runs-on: ubuntu-latest
steps:
# … promotion only runs if the gate passed …Frequently asked
- How do I add a security gate to GitHub Actions?
- Add a step after your deploy-to-staging job that runs `uses: jakelamon/keelix@v0.1.0` with your compose file path. The action installs the Keelix CLI, runs keelix scan --ci against the stack, and exits non-zero if a critical finding is present. Your workflow fails and the promote-to-production job is blocked until the finding is resolved.
- What does the Keelix GitHub Action scan that image-only actions miss?
- Image scanners like trivy-action check CVEs inside container layers. They cannot see host config, open port bindings, or the AI agents and MCP servers deployed on the same box. Keelix checks all of those: whether an MCP server auto-approves all tool calls, whether a plaintext API key is sitting in a Claude Code or Cursor config file, whether a port is accidentally bound to 0.0.0.0, and how all of it rolls up to a 0–100 posture score. None of those findings live in an image layer.
- What is the difference between monitoring and enforcement in a CI security gate?
- Monitoring runs the scanner, collects results, and surfaces a report — but the build passes regardless of findings. Enforcement adds --ci to the scan command and sets an exit code policy: the build fails if a critical finding is present or if the posture score falls below your threshold. Most teams start with monitoring to establish a baseline, then switch to enforcement once they have cleared the initial findings.
- Can I fail only on new findings instead of every finding?
- Yes. Pass a saved baseline JSON from a previous scan via the `baseline` input. The action will only gate on findings that are new relative to that baseline — useful when you are managing a backlog of known issues and want the gate to catch regressions without failing on findings you already know about.
- Does the Keelix Action send any data outside my runner?
- No. The action installs the Keelix CLI binary into the runner's temp directory, runs the scan entirely on-runner, and writes the result to a local JSON file. No scan data, no secrets, no posture score leaves the runner. The CLI is Apache-2.0 and the source is public at github.com/jakelamon/keelix.
Related
- AI/MCP security — the posture surface image actions can't see
- How to fail a CI build on security findings — thresholds and exit codes
- GitHub Actions compliance gate — CIS Docker Benchmark in CI
- Keelix vs Trivy — complementary tools, different surfaces
- Is my Docker container exposed to the internet?
- Pricing — the CLI and Action are free and Apache-2.0
Gate your next deploy in under five minutes.
Free, local-first, Apache-2.0. The CLI runs locally; the Action runs on-runner. Nothing leaves your box.