Skip to content

CI gate for systemd service hardening (systemd-analyze security offline)

License

Notifications You must be signed in to change notification settings

teunlao/systemd-security-gate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

systemd-security-gate

CLI + GitHub Action: fail CI when required systemd .service units in your repo exceed a security exposure threshold, using systemd-analyze security in offline mode.

What it does

  • Finds .service files by glob(s)
  • Builds a temporary --root layout and runs:
    • systemd-analyze security --offline=yes --root=... --threshold=... <unit>
    • systemd-analyze security --offline=yes --root=... --json=short <unit> (for reports)
  • Produces:
    • Markdown summary (stdout + $GITHUB_STEP_SUMMARY if set)
    • Optional JSON report
    • Optional SARIF report (for GitHub Code Scanning upload)

Requirements

  • systemd-analyze v250+ (offline mode + --json=short + --security-policy + --threshold)
  • v1 scope: only .service units (no .socket/.timer mapping yet)

CLI usage

Build:

go build ./cmd/ssg

Scan:

./ssg scan \
  --repo-root . \
  --paths 'deploy/systemd/**/*.service' \
  --threshold 6.0 \
  --policy .ci/systemd-security-policy.json \
  --allowlist .ci/ssg-allowlist.json \
  --json-report ssg.json \
  --sarif-report ssg.sarif

Modes:

  • --mode enforce (default): exit non-zero if any unit fails and is not allowlisted
  • --mode report: never fail on threshold checks (adoption mode), but still fails on analysis errors

Allowlist format (v1)

--allowlist <path> points to a JSON file:

{
  "allowUnits": [
    "deploy/systemd/legacy.service",
    "legacy.service"
  ],
  "allowTests": [
    { "unit": "deploy/systemd/myapp.service", "test": "PrivateNetwork" },
    { "unit": "myapp.service", "test": "ProtectSystem" }
  ]
}

Notes:

  • unit may be either repo-relative path or just the unit filename.
  • test should match json_field / name from systemd-analyze security --json=short.
  • Current semantics: if a unit exceeds --threshold, it is treated as allowed if:
    • the unit is in allowUnits, or
    • all non-zero-exposure checks are listed in allowTests for that unit.

GitHub Action usage (container action)

This repo includes a container action at repo root (action.yml + Dockerfile). To use it, publish it to GitHub and reference it in workflows.

Example workflow:

name: systemd security gate
on:
  pull_request:
  push:
    branches: [ main ]

jobs:
  ssg:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: teunlao/systemd-security-gate@v0
        with:
          paths: |
            deploy/systemd/**/*.service
          threshold: "6.0"
          policy: .ci/systemd-security-policy.json
          allowlist: .ci/ssg-allowlist.json
          json_report: ssg.json
          sarif_report: ssg.sarif

      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: ssg.sarif

License

MIT (see LICENSE).

Versioning (Action + CLI)

GitHub Actions should be pinned to a tag (or SHA) instead of main.

Recommended:

  • use @v0 for “latest stable v0.x”
  • use @v0.x.y for fully reproducible builds

Example:

- uses: teunlao/systemd-security-gate@v0

About

CI gate for systemd service hardening (systemd-analyze security offline)

Resources

License

Stars

Watchers

Forks

Packages

No packages published