kingkyylian/linwarden

GitHub: kingkyylian/linwarden

Stars: 2 | Forks: 2

# Linwarden [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/3eb15eadc9163750.svg)](https://github.com/kingkyylian/linwarden/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/linwarden.svg)](https://pypi.org/project/linwarden/) [![Python](https://img.shields.io/badge/python-3.9%2B-blue.svg)](pyproject.toml) [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) The project goal is practical: give maintainers a small, auditable tool that explains risky Linux defaults without needing an agent, daemon, privileged service, external database, or network access. ## Why Linwarden Use Linwarden when you need a fast security posture signal, not a heavyweight compliance platform. | Need | Linwarden approach | | --- | --- | | CI-friendly output | JSON, Markdown, and SARIF for GitHub code scanning. | | Low operational risk | Read-only collection from ordinary Linux files. | | Offline analysis | Scan mounted roots, image extracts, containers, and fixtures. | | Explainable findings | Every rule includes evidence, impact, remediation, and references. | | Small supply chain | Zero runtime dependencies beyond Python 3.9+. | Linwarden is not a CIS or STIG replacement. It is the lightweight first pass that tells operators what deserves attention before they reach for heavier scanners. ## 30-Second Signal Linwarden is meant to make risky Linux defaults visible before a reviewer has to read a long report. Against the synthetic fixture root: $ linwarden scan --root tests/fixtures/linux-root --format markdown --fail-on off # Linwarden Report - Host: `fixture-box` - Score: `0/100` - Findings: `17` | Severity | Rule | Title | Evidence | | --- | --- | --- | --- | | HIGH | LNX-SSH-001 | SSH root login is permitted | PermitRootLogin yes | | MEDIUM | LNX-SSH-002 | SSH password authentication is enabled | PasswordAuthentication yes | | HIGH | LNX-KRN-001 | Address space layout randomization is disabled | kernel.randomize_va_space=0 | | MEDIUM | LNX-FW-001 | Host firewall is disabled | ufw firewall disabled | See the full [synthetic terminal demo](docs/assets/terminal-demo.md) and the [GitHub Actions examples](docs/github-actions.md) for SARIF/code-scanning workflows. ## Release Provenance Current release artifacts are published on the [GitHub release page](https://github.com/kingkyylian/linwarden/releases/tag/v0.15.0) and the package is available on [PyPI](https://pypi.org/project/linwarden/). Each tag release includes `SHA256SUMS` and GitHub artifact attestations for the built source and wheel files. Verification guidance lives in [docs/release.md#artifact-attestations](docs/release.md#artifact-attestations), including the `gh attestation verify` command. ## Features - Rootless collection from `/etc`, `/proc`, and procfs sysctl paths. - Deterministic JSON output for CI pipelines and scheduled host scans. - Markdown output suitable for GitHub job summaries and issue attachments. - SARIF output suitable for GitHub-native security ingestion. - JSON config support for profiles, disabled rules, and justified suppressions. - Optional effective OpenSSH config collection through `sshd -T`, including Match context. - Package update and host firewall posture signals where rootless files expose them. - Package metadata freshness checks for common package manager cache markers. - Reverse-path filtering and source-route posture checks from procfs sysctls. - Bridge interface and bridge firewall hook posture checks for container hosts. - Static container runtime posture checks for exposed Docker or Podman APIs and Docker group membership. - Optional package vulnerability findings from a local JSON feed. - Release checksum manifests with optional detached GPG signatures. - Severity scoring with `critical`, `high`, `medium`, and `low` buckets. - CI-friendly exit thresholds through `--fail-on`. - Composite GitHub Action wrapper through `uses: kingkyylian/linwarden@v0.15.0`. - Fixture-root scanning for tests, containers, forensic copies, and offline analysis. - Zero runtime dependencies beyond Python 3.9+. ## Quick Start Install from PyPI: python3 -m pip install linwarden linwarden --version linwarden scan --format markdown For an isolated CLI install: pipx install linwarden From a development checkout: python3 -m venv .venv . .venv/bin/activate python -m pip install -e . linwarden scan --format markdown Run against the included fixture: PYTHONPATH=src python3 -m linwarden scan \ --root tests/fixtures/linux-root \ --format json \ --fail-on high Exit code `2` means at least one finding matched the selected threshold. See the [synthetic terminal demo](docs/assets/terminal-demo.md) for a Markdown scan transcript generated from the fixture root. ## Common Workflows | Workflow | Command or doc | | --- | --- | | Documentation index | [docs/index.md](docs/index.md) | | Local triage | `linwarden scan --format markdown` | | CI failure threshold | `linwarden scan --format json --fail-on high` | | GitHub code scanning | `uses: kingkyylian/linwarden@v0.15.0` | | Actions troubleshooting | [docs/github-actions.md#troubleshooting](docs/github-actions.md#troubleshooting) | | JSON report shape | [docs/assets/json-report-example.json](docs/assets/json-report-example.json) | | Report artifact handling | [docs/report-artifacts.md](docs/report-artifacts.md) | | Mounted image scan | [docs/mounted-root-scans.md](docs/mounted-root-scans.md) | | Effective SSH scan | `linwarden scan --sshd-mode effective --sshd-match user=deploy` | | Tool positioning | [docs/comparison.md](docs/comparison.md) | ## CLI linwarden profiles [--format markdown|json] linwarden scan [--root PATH] [--proc-root PATH] [--etc-root PATH] [--sys-root PATH] [--config PATH] [--format markdown|json|sarif] [--vulnerability-feed PATH] [--vulnerability-feed-format linwarden|trivy|grype|osv] [--sshd-mode static|effective|auto] [--sshd-binary PATH] [--sshd-match KEY=VALUE] [--output PATH] [--fail-on off|low|medium|high|critical] Common examples: linwarden profiles linwarden scan --format markdown --output linwarden-report.md linwarden scan --format json --fail-on high linwarden scan --config linwarden.json --format sarif --output linwarden.sarif linwarden scan --sshd-mode effective --format json linwarden scan --sshd-mode effective --sshd-match user=deploy --sshd-match addr=203.0.113.10 linwarden scan --root /mnt/server-image --format json linwarden scan --proc-root /host/proc --etc-root /host/etc --sys-root /host/sys --format markdown linwarden scan --vulnerability-feed ./linwarden-vulnerabilities.json --format sarif linwarden scan --vulnerability-feed ./trivy-report.json --vulnerability-feed-format trivy --format sarif linwarden scan --vulnerability-feed ./grype-report.json --vulnerability-feed-format grype --format sarif linwarden scan --vulnerability-feed ./osv-scanner-report.json --vulnerability-feed-format osv --format sarif ## Configuration Linwarden accepts a zero-dependency JSON config file: { "profile": "router", "disabled_rules": ["LNX-NET-002"], "suppressions": [ { "rule_id": "LNX-SSH-002", "reason": "Temporary migration host; password auth removed after cutover." } ] } Profiles: | Profile | Behavior | | --- | --- | | `server` | Default for general Linux servers. No profile suppressions. | | `workstation` | Interactive desktop or laptop posture. No profile suppressions; SSH, firewall, package, and kernel findings stay visible. | | `router` | For hosts that intentionally route traffic. Suppresses IPv4, IPv6, and bridge forwarding findings. | | `container` | For container or image-root scans where host-kernel sysctl values may be inherited. Suppresses kernel and filesystem sysctl findings. | Suppressed findings remain visible in JSON and Markdown reports. SARIF output includes active findings only. ## Exit Codes | Code | Meaning | | --- | --- | | `0` | Scan completed and no selected threshold was met. | | `1` | CLI usage error from argument parsing. | | `2` | Scan completed and `--fail-on` threshold was met. | ## Current Rules | Rule | Severity | Area | Summary | | --- | --- | --- | --- | | `LNX-SSH-001` | high | SSH | `PermitRootLogin yes` is enabled. | | `LNX-SSH-002` | medium | SSH | `PasswordAuthentication yes` is enabled. | | `LNX-SSH-003` | high | SSH | `PermitEmptyPasswords yes` is enabled. | | `LNX-SSH-004` | medium | SSH | `MaxAuthTries` is above `4`. | | `LNX-SSH-005` | medium | SSH | `AllowTcpForwarding yes` or `all` is enabled. | | `LNX-KRN-001` | high | Kernel | `kernel.randomize_va_space=0` disables ASLR. | | `LNX-KRN-002` | high | Kernel | `vm.mmap_min_addr` is below `65536`. | | `LNX-KRN-003` | medium | Kernel | `kernel.kptr_restrict=0` exposes kernel pointers. | | `LNX-FS-001` | high | Filesystem | `fs.protected_hardlinks=0` disables hardlink protection. | | `LNX-FS-002` | high | Filesystem | `fs.protected_symlinks=0` disables symlink protection. | | `LNX-NET-001` | medium | Network | `net.ipv4.ip_forward=1` is enabled. | | `LNX-NET-002` | low | Network | `net.ipv4.conf.all.accept_redirects=1` is enabled. | | `LNX-NET-003` | medium | Network | `net.ipv6.conf.all.forwarding=1` is enabled. | | `LNX-NET-004` | low | Network | `net.ipv6.conf.all.accept_redirects=1` is enabled. | | `LNX-NET-005` | medium | Network | Bridge IPv4 firewall hooks are disabled while bridge interfaces exist. | | `LNX-NET-006` | medium | Network | Bridge IPv6 firewall hooks are disabled while bridge interfaces exist. | | `LNX-NET-007` | medium | Network | A bridge interface has forwarding enabled. | | `LNX-NET-008` | medium | Network | `net.ipv4.conf.all.rp_filter=0` disables IPv4 reverse-path filtering. | | `LNX-NET-009` | medium | Network | `net.ipv4.conf.all.accept_source_route=1` accepts IPv4 source-routed packets. | | `LNX-NET-010` | medium | Network | `net.ipv6.conf.all.accept_source_route` accepts IPv6 source-routed packets. | | `LNX-PKG-001` | medium | Packages | Package updates are available. | | `LNX-PKG-002` | high | Packages | Security package updates are available. | | `LNX-PKG-003` | medium | Packages | Package metadata is stale. | | `LNX-PKG-004` | feed severity | Packages | A local vulnerability feed reports an affected package. | | `LNX-FW-001` | medium | Firewall | A known host firewall is disabled. | | `LNX-SVC-001` | medium | Services | An enabled systemd unit appears externally bound. | | `LNX-CTR-001` | high | Containers | A container runtime API is bound to non-loopback TCP. | | `LNX-CTR-002` | high | Containers | The Docker group grants daemon-level access to non-root users. | | `LNX-CTR-003` | medium | Containers | Docker user namespace remapping is explicitly disabled. | Rule details live in [docs/rules.md](docs/rules.md). ## Report Score Linwarden starts each report at `100` and subtracts a fixed penalty per finding: | Severity | Penalty | | --- | --- | | critical | 35 | | high | 20 | | medium | 10 | | low | 3 | The score is intentionally simple. It is a triage signal, not a compliance rating. ## Project Layout src/linwarden/ cli.py command line entry point config.py profiles, disabled rules, and suppressions collectors.py host snapshot collection parsers.py small parsers for Linux files rules.py built-in hardening checks reporters.py JSON, Markdown, and SARIF rendering models.py report data structures tests/ fixtures/ deterministic Linux fixture root docs/ architecture.md implementation overview configuration.md profile and suppression config comparison.md positioning against adjacent Linux security tools contributor-ideas.md scoped contribution backlog github-actions.md CI and SARIF workflow examples launch.md copy and checklist for public announcements positioning.md maintainer messaging guide release.md release artifact and publishing workflow rules.md rule catalog report-schema.md JSON report contract schemas/ report.schema.json machine-readable JSON report schema ## Development make test make compile make lint make typecheck make smoke make smoke-sarif make check Use `make check PYTHON=.venv/bin/python` when running through a project virtualenv. No network services or privileged permissions are required for the test suite. ## Security Model Linwarden is read-only by default. It does not modify host state, load kernel modules, call package managers, or send telemetry. Reports can contain host configuration details, so treat generated artifacts as operationally sensitive. Report vulnerabilities using [SECURITY.md](SECURITY.md); do not open public issues for sensitive reports. ## Known Limits ## Roadmap - Fixture-backed distro coverage for package metadata and firewall posture. - Static container runtime signals only when evidence is reliable and rootless. - Release checks that keep GitHub artifacts, attestations, and PyPI installs verifiable. Launch feedback and rootless rule ideas can go through the [launch discussion](https://github.com/kingkyylian/linwarden/discussions/57). Contributor-ready ideas live in [docs/contributor-ideas.md](docs/contributor-ideas.md), starting with the [Ubuntu fixture issue](https://github.com/kingkyylian/linwarden/issues/56). ## License MIT. See [LICENSE](LICENSE).