linwarden 0.13.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. linwarden-0.13.1/LICENSE +21 -0
  2. linwarden-0.13.1/MANIFEST.in +11 -0
  3. linwarden-0.13.1/PKG-INFO +292 -0
  4. linwarden-0.13.1/README.md +261 -0
  5. linwarden-0.13.1/action.yml +155 -0
  6. linwarden-0.13.1/docs/architecture.md +84 -0
  7. linwarden-0.13.1/docs/comparison.md +28 -0
  8. linwarden-0.13.1/docs/configuration.md +62 -0
  9. linwarden-0.13.1/docs/contributor-ideas.md +27 -0
  10. linwarden-0.13.1/docs/development.md +57 -0
  11. linwarden-0.13.1/docs/fixture-roots.md +29 -0
  12. linwarden-0.13.1/docs/github-actions.md +189 -0
  13. linwarden-0.13.1/docs/launch.md +57 -0
  14. linwarden-0.13.1/docs/package-vulnerability-feed.md +108 -0
  15. linwarden-0.13.1/docs/positioning.md +39 -0
  16. linwarden-0.13.1/docs/release.md +89 -0
  17. linwarden-0.13.1/docs/report-schema.md +130 -0
  18. linwarden-0.13.1/docs/roadmap.md +15 -0
  19. linwarden-0.13.1/docs/rules.md +215 -0
  20. linwarden-0.13.1/docs/ssh.md +47 -0
  21. linwarden-0.13.1/examples/README.md +29 -0
  22. linwarden-0.13.1/examples/fixture-report.json +314 -0
  23. linwarden-0.13.1/examples/fixture-report.md +133 -0
  24. linwarden-0.13.1/examples/fixture-report.sarif +572 -0
  25. linwarden-0.13.1/examples/linwarden.json +10 -0
  26. linwarden-0.13.1/pyproject.toml +75 -0
  27. linwarden-0.13.1/schemas/report.schema.json +541 -0
  28. linwarden-0.13.1/scripts/__init__.py +1 -0
  29. linwarden-0.13.1/scripts/release_assets.py +43 -0
  30. linwarden-0.13.1/scripts/verify_release_version.py +108 -0
  31. linwarden-0.13.1/setup.cfg +4 -0
  32. linwarden-0.13.1/src/linwarden/__init__.py +1 -0
  33. linwarden-0.13.1/src/linwarden/__main__.py +3 -0
  34. linwarden-0.13.1/src/linwarden/cli.py +160 -0
  35. linwarden-0.13.1/src/linwarden/collectors.py +1292 -0
  36. linwarden-0.13.1/src/linwarden/config.py +174 -0
  37. linwarden-0.13.1/src/linwarden/models.py +139 -0
  38. linwarden-0.13.1/src/linwarden/parsers.py +147 -0
  39. linwarden-0.13.1/src/linwarden/reporters.py +196 -0
  40. linwarden-0.13.1/src/linwarden/rules.py +503 -0
  41. linwarden-0.13.1/src/linwarden.egg-info/PKG-INFO +292 -0
  42. linwarden-0.13.1/src/linwarden.egg-info/SOURCES.txt +167 -0
  43. linwarden-0.13.1/src/linwarden.egg-info/dependency_links.txt +1 -0
  44. linwarden-0.13.1/src/linwarden.egg-info/entry_points.txt +2 -0
  45. linwarden-0.13.1/src/linwarden.egg-info/requires.txt +5 -0
  46. linwarden-0.13.1/src/linwarden.egg-info/top_level.txt +1 -0
  47. linwarden-0.13.1/tests/fixtures/alpine-root/etc/hostname +1 -0
  48. linwarden-0.13.1/tests/fixtures/alpine-root/etc/nftables.conf +6 -0
  49. linwarden-0.13.1/tests/fixtures/alpine-root/etc/os-release +6 -0
  50. linwarden-0.13.1/tests/fixtures/alpine-root/etc/ssh/sshd_config +6 -0
  51. linwarden-0.13.1/tests/fixtures/alpine-root/proc/loadavg +1 -0
  52. linwarden-0.13.1/tests/fixtures/alpine-root/proc/meminfo +5 -0
  53. linwarden-0.13.1/tests/fixtures/alpine-root/proc/mounts +2 -0
  54. linwarden-0.13.1/tests/fixtures/alpine-root/proc/sys/fs/protected_hardlinks +1 -0
  55. linwarden-0.13.1/tests/fixtures/alpine-root/proc/sys/fs/protected_symlinks +1 -0
  56. linwarden-0.13.1/tests/fixtures/alpine-root/proc/sys/kernel/kptr_restrict +1 -0
  57. linwarden-0.13.1/tests/fixtures/alpine-root/proc/sys/kernel/osrelease +1 -0
  58. linwarden-0.13.1/tests/fixtures/alpine-root/proc/sys/kernel/randomize_va_space +1 -0
  59. linwarden-0.13.1/tests/fixtures/alpine-root/proc/sys/net/ipv4/conf/all/accept_redirects +1 -0
  60. linwarden-0.13.1/tests/fixtures/alpine-root/proc/sys/net/ipv4/ip_forward +1 -0
  61. linwarden-0.13.1/tests/fixtures/alpine-root/proc/sys/net/ipv6/conf/all/accept_redirects +1 -0
  62. linwarden-0.13.1/tests/fixtures/alpine-root/proc/sys/net/ipv6/conf/all/forwarding +1 -0
  63. linwarden-0.13.1/tests/fixtures/alpine-root/proc/sys/vm/mmap_min_addr +1 -0
  64. linwarden-0.13.1/tests/fixtures/alpine-root/proc/uptime +1 -0
  65. linwarden-0.13.1/tests/fixtures/alpine-root/var/cache/apk/APKINDEX.fixture.tar.gz +1 -0
  66. linwarden-0.13.1/tests/fixtures/arch-root/etc/hostname +1 -0
  67. linwarden-0.13.1/tests/fixtures/arch-root/etc/nftables.conf +6 -0
  68. linwarden-0.13.1/tests/fixtures/arch-root/etc/os-release +6 -0
  69. linwarden-0.13.1/tests/fixtures/arch-root/etc/ssh/sshd_config +6 -0
  70. linwarden-0.13.1/tests/fixtures/arch-root/etc/systemd/system/multi-user.target.wants/nftables.service +2 -0
  71. linwarden-0.13.1/tests/fixtures/arch-root/proc/loadavg +1 -0
  72. linwarden-0.13.1/tests/fixtures/arch-root/proc/meminfo +5 -0
  73. linwarden-0.13.1/tests/fixtures/arch-root/proc/mounts +2 -0
  74. linwarden-0.13.1/tests/fixtures/arch-root/proc/sys/fs/protected_hardlinks +1 -0
  75. linwarden-0.13.1/tests/fixtures/arch-root/proc/sys/fs/protected_symlinks +1 -0
  76. linwarden-0.13.1/tests/fixtures/arch-root/proc/sys/kernel/kptr_restrict +1 -0
  77. linwarden-0.13.1/tests/fixtures/arch-root/proc/sys/kernel/osrelease +1 -0
  78. linwarden-0.13.1/tests/fixtures/arch-root/proc/sys/kernel/randomize_va_space +1 -0
  79. linwarden-0.13.1/tests/fixtures/arch-root/proc/sys/net/ipv4/conf/all/accept_redirects +1 -0
  80. linwarden-0.13.1/tests/fixtures/arch-root/proc/sys/net/ipv4/conf/br-lan/forwarding +1 -0
  81. linwarden-0.13.1/tests/fixtures/arch-root/proc/sys/net/ipv4/ip_forward +1 -0
  82. linwarden-0.13.1/tests/fixtures/arch-root/proc/sys/net/ipv6/conf/all/accept_redirects +1 -0
  83. linwarden-0.13.1/tests/fixtures/arch-root/proc/sys/net/ipv6/conf/all/forwarding +1 -0
  84. linwarden-0.13.1/tests/fixtures/arch-root/proc/sys/net/ipv6/conf/br-lan/forwarding +1 -0
  85. linwarden-0.13.1/tests/fixtures/arch-root/proc/sys/vm/mmap_min_addr +1 -0
  86. linwarden-0.13.1/tests/fixtures/arch-root/proc/uptime +1 -0
  87. linwarden-0.13.1/tests/fixtures/arch-root/sys/class/net/br-lan/bridge +1 -0
  88. linwarden-0.13.1/tests/fixtures/arch-root/sys/class/net/br-lan/brif/lan0 +1 -0
  89. linwarden-0.13.1/tests/fixtures/arch-root/var/lib/pacman/sync/core.db +1 -0
  90. linwarden-0.13.1/tests/fixtures/arch-root/var/lib/pacman/sync/extra.db +1 -0
  91. linwarden-0.13.1/tests/fixtures/config/invalid-profile.json +3 -0
  92. linwarden-0.13.1/tests/fixtures/config/missing-reason.json +7 -0
  93. linwarden-0.13.1/tests/fixtures/debian-root/etc/hostname +1 -0
  94. linwarden-0.13.1/tests/fixtures/debian-root/etc/os-release +7 -0
  95. linwarden-0.13.1/tests/fixtures/debian-root/etc/ssh/sshd_config +6 -0
  96. linwarden-0.13.1/tests/fixtures/debian-root/etc/systemd/system/multi-user.target.wants/fixture-local.service +5 -0
  97. linwarden-0.13.1/tests/fixtures/debian-root/etc/ufw/ufw.conf +2 -0
  98. linwarden-0.13.1/tests/fixtures/debian-root/proc/loadavg +1 -0
  99. linwarden-0.13.1/tests/fixtures/debian-root/proc/meminfo +5 -0
  100. linwarden-0.13.1/tests/fixtures/debian-root/proc/mounts +2 -0
  101. linwarden-0.13.1/tests/fixtures/debian-root/proc/sys/fs/protected_hardlinks +1 -0
  102. linwarden-0.13.1/tests/fixtures/debian-root/proc/sys/fs/protected_symlinks +1 -0
  103. linwarden-0.13.1/tests/fixtures/debian-root/proc/sys/kernel/kptr_restrict +1 -0
  104. linwarden-0.13.1/tests/fixtures/debian-root/proc/sys/kernel/osrelease +1 -0
  105. linwarden-0.13.1/tests/fixtures/debian-root/proc/sys/kernel/randomize_va_space +1 -0
  106. linwarden-0.13.1/tests/fixtures/debian-root/proc/sys/net/ipv4/conf/all/accept_redirects +1 -0
  107. linwarden-0.13.1/tests/fixtures/debian-root/proc/sys/net/ipv4/ip_forward +1 -0
  108. linwarden-0.13.1/tests/fixtures/debian-root/proc/sys/net/ipv6/conf/all/accept_redirects +1 -0
  109. linwarden-0.13.1/tests/fixtures/debian-root/proc/sys/net/ipv6/conf/all/forwarding +1 -0
  110. linwarden-0.13.1/tests/fixtures/debian-root/proc/sys/vm/mmap_min_addr +1 -0
  111. linwarden-0.13.1/tests/fixtures/debian-root/proc/uptime +1 -0
  112. linwarden-0.13.1/tests/fixtures/debian-root/var/lib/apt/periodic/update-success-stamp +1 -0
  113. linwarden-0.13.1/tests/fixtures/debian-root/var/lib/update-notifier/updates-available +2 -0
  114. linwarden-0.13.1/tests/fixtures/fedora-root/etc/firewalld/firewalld.conf +1 -0
  115. linwarden-0.13.1/tests/fixtures/fedora-root/etc/hostname +1 -0
  116. linwarden-0.13.1/tests/fixtures/fedora-root/etc/os-release +8 -0
  117. linwarden-0.13.1/tests/fixtures/fedora-root/etc/ssh/sshd_config +6 -0
  118. linwarden-0.13.1/tests/fixtures/fedora-root/etc/systemd/system/multi-user.target.wants/firewalld.service +1 -0
  119. linwarden-0.13.1/tests/fixtures/fedora-root/etc/systemd/system/multi-user.target.wants/fixture-api.service +5 -0
  120. linwarden-0.13.1/tests/fixtures/fedora-root/proc/loadavg +1 -0
  121. linwarden-0.13.1/tests/fixtures/fedora-root/proc/meminfo +5 -0
  122. linwarden-0.13.1/tests/fixtures/fedora-root/proc/mounts +2 -0
  123. linwarden-0.13.1/tests/fixtures/fedora-root/proc/sys/fs/protected_hardlinks +1 -0
  124. linwarden-0.13.1/tests/fixtures/fedora-root/proc/sys/fs/protected_symlinks +1 -0
  125. linwarden-0.13.1/tests/fixtures/fedora-root/proc/sys/kernel/kptr_restrict +1 -0
  126. linwarden-0.13.1/tests/fixtures/fedora-root/proc/sys/kernel/osrelease +1 -0
  127. linwarden-0.13.1/tests/fixtures/fedora-root/proc/sys/kernel/randomize_va_space +1 -0
  128. linwarden-0.13.1/tests/fixtures/fedora-root/proc/sys/net/ipv4/conf/all/accept_redirects +1 -0
  129. linwarden-0.13.1/tests/fixtures/fedora-root/proc/sys/net/ipv4/ip_forward +1 -0
  130. linwarden-0.13.1/tests/fixtures/fedora-root/proc/sys/net/ipv6/conf/all/accept_redirects +1 -0
  131. linwarden-0.13.1/tests/fixtures/fedora-root/proc/sys/net/ipv6/conf/all/forwarding +1 -0
  132. linwarden-0.13.1/tests/fixtures/fedora-root/proc/sys/vm/mmap_min_addr +1 -0
  133. linwarden-0.13.1/tests/fixtures/fedora-root/proc/uptime +1 -0
  134. linwarden-0.13.1/tests/fixtures/fedora-root/var/cache/dnf/expired_repos.json +1 -0
  135. linwarden-0.13.1/tests/fixtures/grype-vulnerability-report.json +67 -0
  136. linwarden-0.13.1/tests/fixtures/invalid-vulnerability-feed.json +9 -0
  137. linwarden-0.13.1/tests/fixtures/linux-root/etc/hostname +1 -0
  138. linwarden-0.13.1/tests/fixtures/linux-root/etc/os-release +9 -0
  139. linwarden-0.13.1/tests/fixtures/linux-root/etc/passwd +3 -0
  140. linwarden-0.13.1/tests/fixtures/linux-root/etc/ssh/sshd_config +7 -0
  141. linwarden-0.13.1/tests/fixtures/linux-root/etc/ssh/sshd_config.d/hardening.conf +3 -0
  142. linwarden-0.13.1/tests/fixtures/linux-root/etc/ufw/ufw.conf +1 -0
  143. linwarden-0.13.1/tests/fixtures/linux-root/proc/loadavg +1 -0
  144. linwarden-0.13.1/tests/fixtures/linux-root/proc/meminfo +7 -0
  145. linwarden-0.13.1/tests/fixtures/linux-root/proc/mounts +3 -0
  146. linwarden-0.13.1/tests/fixtures/linux-root/proc/sys/fs/protected_hardlinks +1 -0
  147. linwarden-0.13.1/tests/fixtures/linux-root/proc/sys/fs/protected_symlinks +1 -0
  148. linwarden-0.13.1/tests/fixtures/linux-root/proc/sys/kernel/kptr_restrict +1 -0
  149. linwarden-0.13.1/tests/fixtures/linux-root/proc/sys/kernel/osrelease +1 -0
  150. linwarden-0.13.1/tests/fixtures/linux-root/proc/sys/kernel/randomize_va_space +1 -0
  151. linwarden-0.13.1/tests/fixtures/linux-root/proc/sys/net/ipv4/conf/all/accept_redirects +1 -0
  152. linwarden-0.13.1/tests/fixtures/linux-root/proc/sys/net/ipv4/ip_forward +1 -0
  153. linwarden-0.13.1/tests/fixtures/linux-root/proc/sys/net/ipv6/conf/all/accept_redirects +1 -0
  154. linwarden-0.13.1/tests/fixtures/linux-root/proc/sys/net/ipv6/conf/all/forwarding +1 -0
  155. linwarden-0.13.1/tests/fixtures/linux-root/proc/sys/vm/mmap_min_addr +1 -0
  156. linwarden-0.13.1/tests/fixtures/linux-root/proc/uptime +1 -0
  157. linwarden-0.13.1/tests/fixtures/linux-root/var/lib/update-notifier/updates-available +2 -0
  158. linwarden-0.13.1/tests/fixtures/linwarden.json +10 -0
  159. linwarden-0.13.1/tests/fixtures/osv-vulnerability-report.json +160 -0
  160. linwarden-0.13.1/tests/fixtures/trivy-vulnerability-report.json +44 -0
  161. linwarden-0.13.1/tests/fixtures/vulnerability-feed.json +24 -0
  162. linwarden-0.13.1/tests/test_action_metadata.py +176 -0
  163. linwarden-0.13.1/tests/test_collectors.py +405 -0
  164. linwarden-0.13.1/tests/test_config.py +92 -0
  165. linwarden-0.13.1/tests/test_distro_fixtures.py +147 -0
  166. linwarden-0.13.1/tests/test_parsers.py +41 -0
  167. linwarden-0.13.1/tests/test_release_assets.py +122 -0
  168. linwarden-0.13.1/tests/test_reporters_cli.py +421 -0
  169. linwarden-0.13.1/tests/test_rules.py +184 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Linwarden contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,11 @@
1
+ include LICENSE
2
+ include README.md
3
+ include action.yml
4
+ include pyproject.toml
5
+ recursive-include docs *.md
6
+ recursive-include examples *.json *.md *.sarif
7
+ recursive-include schemas *.json
8
+ recursive-include scripts *.py
9
+ recursive-include tests *.py
10
+ recursive-include tests/fixtures *
11
+ recursive-exclude docs/checkpoints *
@@ -0,0 +1,292 @@
1
+ Metadata-Version: 2.4
2
+ Name: linwarden
3
+ Version: 0.13.1
4
+ Summary: Rootless Linux host inventory and hardening audit CLI.
5
+ Author: Linwarden contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/kingkyylian/linwarden
8
+ Project-URL: Documentation, https://github.com/kingkyylian/linwarden/tree/main/docs
9
+ Project-URL: Issues, https://github.com/kingkyylian/linwarden/issues
10
+ Keywords: linux,security,hardening,inventory,audit,cli,sysadmin
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Security
22
+ Classifier: Topic :: System :: Monitoring
23
+ Requires-Python: >=3.9
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Provides-Extra: dev
27
+ Requires-Dist: build>=1.2; extra == "dev"
28
+ Requires-Dist: mypy<2,>=1.13; extra == "dev"
29
+ Requires-Dist: ruff>=0.8; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ # Linwarden
33
+
34
+ [![CI](https://github.com/kingkyylian/linwarden/actions/workflows/ci.yml/badge.svg)](https://github.com/kingkyylian/linwarden/actions/workflows/ci.yml)
35
+ [![Python](https://img.shields.io/badge/python-3.9%2B-blue.svg)](pyproject.toml)
36
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
37
+
38
+ Linwarden is a rootless Linux hardening scanner for CI and fleet triage. It reads ordinary system files such as `/etc/os-release`, `/etc/ssh/sshd_config`, and selected `/proc/sys` values, then produces Markdown, JSON, or SARIF artifacts without installing an agent, daemon, privileged helper, database, or network service.
39
+
40
+ 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.
41
+
42
+ ## Why Linwarden
43
+
44
+ Use Linwarden when you need a fast security posture signal, not a heavyweight compliance platform.
45
+
46
+ | Need | Linwarden approach |
47
+ | --- | --- |
48
+ | CI-friendly output | JSON, Markdown, and SARIF for GitHub code scanning. |
49
+ | Low operational risk | Read-only collection from ordinary Linux files. |
50
+ | Offline analysis | Scan mounted roots, image extracts, containers, and fixtures. |
51
+ | Explainable findings | Every rule includes evidence, impact, remediation, and references. |
52
+ | Small supply chain | Zero runtime dependencies beyond Python 3.9+. |
53
+
54
+ 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.
55
+
56
+ ## Features
57
+
58
+ - Rootless collection from `/etc`, `/proc`, and procfs sysctl paths.
59
+ - Deterministic JSON output for CI pipelines and scheduled host scans.
60
+ - Markdown output suitable for GitHub job summaries and issue attachments.
61
+ - SARIF output suitable for GitHub-native security ingestion.
62
+ - JSON config support for profiles, disabled rules, and justified suppressions.
63
+ - Optional effective OpenSSH config collection through `sshd -T`, including Match context.
64
+ - Package update and host firewall posture signals where rootless files expose them.
65
+ - Package metadata freshness checks for common package manager cache markers.
66
+ - Bridge interface and bridge firewall hook posture checks for container hosts.
67
+ - Static container runtime posture checks for exposed Docker or Podman APIs and Docker group membership.
68
+ - Optional package vulnerability findings from a local JSON feed.
69
+ - Release checksum manifests with optional detached GPG signatures.
70
+ - Severity scoring with `critical`, `high`, `medium`, and `low` buckets.
71
+ - CI-friendly exit thresholds through `--fail-on`.
72
+ - Composite GitHub Action wrapper through `uses: kingkyylian/linwarden@v0.13.1`.
73
+ - Fixture-root scanning for tests, containers, forensic copies, and offline analysis.
74
+ - Zero runtime dependencies beyond Python 3.9+.
75
+
76
+ ## Quick Start
77
+
78
+ From a checkout:
79
+
80
+ ```bash
81
+ python3 -m venv .venv
82
+ . .venv/bin/activate
83
+ python -m pip install -e .
84
+ linwarden scan --format markdown
85
+ ```
86
+
87
+ Run against the included fixture:
88
+
89
+ ```bash
90
+ PYTHONPATH=src python3 -m linwarden scan \
91
+ --root tests/fixtures/linux-root \
92
+ --format json \
93
+ --fail-on high
94
+ ```
95
+
96
+ Exit code `2` means at least one finding matched the selected threshold.
97
+
98
+ ## Common Workflows
99
+
100
+ | Workflow | Command or doc |
101
+ | --- | --- |
102
+ | Local triage | `linwarden scan --format markdown` |
103
+ | CI failure threshold | `linwarden scan --format json --fail-on high` |
104
+ | GitHub code scanning | `uses: kingkyylian/linwarden@v0.13.1` |
105
+ | Mounted image scan | `linwarden scan --root /mnt/server-image --format json` |
106
+ | Effective SSH scan | `linwarden scan --sshd-mode effective --sshd-match user=deploy` |
107
+ | Tool positioning | [docs/comparison.md](docs/comparison.md) |
108
+
109
+ ## CLI
110
+
111
+ ```text
112
+ linwarden profiles [--format markdown|json]
113
+
114
+ linwarden scan [--root PATH] [--proc-root PATH] [--etc-root PATH] [--sys-root PATH]
115
+ [--config PATH] [--format markdown|json|sarif]
116
+ [--vulnerability-feed PATH] [--vulnerability-feed-format linwarden|trivy|grype|osv]
117
+ [--sshd-mode static|effective|auto] [--sshd-binary PATH]
118
+ [--sshd-match KEY=VALUE]
119
+ [--output PATH]
120
+ [--fail-on off|low|medium|high|critical]
121
+ ```
122
+
123
+ Common examples:
124
+
125
+ ```bash
126
+ linwarden profiles
127
+ linwarden scan --format markdown --output linwarden-report.md
128
+ linwarden scan --format json --fail-on high
129
+ linwarden scan --config linwarden.json --format sarif --output linwarden.sarif
130
+ linwarden scan --sshd-mode effective --format json
131
+ linwarden scan --sshd-mode effective --sshd-match user=deploy --sshd-match addr=203.0.113.10
132
+ linwarden scan --root /mnt/server-image --format json
133
+ linwarden scan --proc-root /host/proc --etc-root /host/etc --sys-root /host/sys --format markdown
134
+ linwarden scan --vulnerability-feed ./linwarden-vulnerabilities.json --format sarif
135
+ linwarden scan --vulnerability-feed ./trivy-report.json --vulnerability-feed-format trivy --format sarif
136
+ linwarden scan --vulnerability-feed ./grype-report.json --vulnerability-feed-format grype --format sarif
137
+ linwarden scan --vulnerability-feed ./osv-scanner-report.json --vulnerability-feed-format osv --format sarif
138
+ ```
139
+
140
+ ## Configuration
141
+
142
+ Linwarden accepts a zero-dependency JSON config file:
143
+
144
+ ```json
145
+ {
146
+ "profile": "router",
147
+ "disabled_rules": ["LNX-NET-002"],
148
+ "suppressions": [
149
+ {
150
+ "rule_id": "LNX-SSH-002",
151
+ "reason": "Temporary migration host; password auth removed after cutover."
152
+ }
153
+ ]
154
+ }
155
+ ```
156
+
157
+ Profiles:
158
+
159
+ | Profile | Behavior |
160
+ | --- | --- |
161
+ | `server` | Default for general Linux servers. No profile suppressions. |
162
+ | `workstation` | Interactive desktop or laptop posture. No profile suppressions; SSH, firewall, package, and kernel findings stay visible. |
163
+ | `router` | For hosts that intentionally route traffic. Suppresses IPv4, IPv6, and bridge forwarding findings. |
164
+ | `container` | For container or image-root scans where host-kernel sysctl values may be inherited. Suppresses kernel and filesystem sysctl findings. |
165
+
166
+ Suppressed findings remain visible in JSON and Markdown reports. SARIF output includes active findings only.
167
+
168
+ ## Exit Codes
169
+
170
+ | Code | Meaning |
171
+ | --- | --- |
172
+ | `0` | Scan completed and no selected threshold was met. |
173
+ | `1` | CLI usage error from argument parsing. |
174
+ | `2` | Scan completed and `--fail-on` threshold was met. |
175
+
176
+ ## Current Rules
177
+
178
+ | Rule | Severity | Area | Summary |
179
+ | --- | --- | --- | --- |
180
+ | `LNX-SSH-001` | high | SSH | `PermitRootLogin yes` is enabled. |
181
+ | `LNX-SSH-002` | medium | SSH | `PasswordAuthentication yes` is enabled. |
182
+ | `LNX-SSH-003` | high | SSH | `PermitEmptyPasswords yes` is enabled. |
183
+ | `LNX-SSH-004` | medium | SSH | `MaxAuthTries` is above `4`. |
184
+ | `LNX-SSH-005` | medium | SSH | `AllowTcpForwarding yes` or `all` is enabled. |
185
+ | `LNX-KRN-001` | high | Kernel | `kernel.randomize_va_space=0` disables ASLR. |
186
+ | `LNX-KRN-002` | high | Kernel | `vm.mmap_min_addr` is below `65536`. |
187
+ | `LNX-KRN-003` | medium | Kernel | `kernel.kptr_restrict=0` exposes kernel pointers. |
188
+ | `LNX-FS-001` | high | Filesystem | `fs.protected_hardlinks=0` disables hardlink protection. |
189
+ | `LNX-FS-002` | high | Filesystem | `fs.protected_symlinks=0` disables symlink protection. |
190
+ | `LNX-NET-001` | medium | Network | `net.ipv4.ip_forward=1` is enabled. |
191
+ | `LNX-NET-002` | low | Network | `net.ipv4.conf.all.accept_redirects=1` is enabled. |
192
+ | `LNX-NET-003` | medium | Network | `net.ipv6.conf.all.forwarding=1` is enabled. |
193
+ | `LNX-NET-004` | low | Network | `net.ipv6.conf.all.accept_redirects=1` is enabled. |
194
+ | `LNX-NET-005` | medium | Network | Bridge IPv4 firewall hooks are disabled while bridge interfaces exist. |
195
+ | `LNX-NET-006` | medium | Network | Bridge IPv6 firewall hooks are disabled while bridge interfaces exist. |
196
+ | `LNX-NET-007` | medium | Network | A bridge interface has forwarding enabled. |
197
+ | `LNX-PKG-001` | medium | Packages | Package updates are available. |
198
+ | `LNX-PKG-002` | high | Packages | Security package updates are available. |
199
+ | `LNX-PKG-003` | medium | Packages | Package metadata is stale. |
200
+ | `LNX-PKG-004` | feed severity | Packages | A local vulnerability feed reports an affected package. |
201
+ | `LNX-FW-001` | medium | Firewall | A known host firewall is disabled. |
202
+ | `LNX-SVC-001` | medium | Services | An enabled systemd service appears externally bound. |
203
+ | `LNX-CTR-001` | high | Containers | A container runtime API is bound to non-loopback TCP. |
204
+ | `LNX-CTR-002` | high | Containers | The Docker group grants daemon-level access to non-root users. |
205
+
206
+ Rule details live in [docs/rules.md](docs/rules.md).
207
+
208
+ ## Report Score
209
+
210
+ Linwarden starts each report at `100` and subtracts a fixed penalty per finding:
211
+
212
+ | Severity | Penalty |
213
+ | --- | --- |
214
+ | critical | 35 |
215
+ | high | 20 |
216
+ | medium | 10 |
217
+ | low | 3 |
218
+
219
+ The score is intentionally simple. It is a triage signal, not a compliance rating.
220
+
221
+ ## Project Layout
222
+
223
+ ```text
224
+ src/linwarden/
225
+ cli.py command line entry point
226
+ config.py profiles, disabled rules, and suppressions
227
+ collectors.py host snapshot collection
228
+ parsers.py small parsers for Linux files
229
+ rules.py built-in hardening checks
230
+ reporters.py JSON, Markdown, and SARIF rendering
231
+ models.py report data structures
232
+ tests/
233
+ fixtures/ deterministic Linux fixture root
234
+ docs/
235
+ architecture.md implementation overview
236
+ configuration.md profile and suppression config
237
+ comparison.md positioning against adjacent Linux security tools
238
+ contributor-ideas.md scoped contribution backlog
239
+ github-actions.md CI and SARIF workflow examples
240
+ launch.md copy and checklist for public announcements
241
+ positioning.md maintainer messaging guide
242
+ release.md release artifact and publishing workflow
243
+ rules.md rule catalog
244
+ report-schema.md JSON report contract
245
+ schemas/
246
+ report.schema.json machine-readable JSON report schema
247
+ ```
248
+
249
+ ## Development
250
+
251
+ ```bash
252
+ make test
253
+ make compile
254
+ make lint
255
+ make typecheck
256
+ make smoke
257
+ make smoke-sarif
258
+ make check
259
+ ```
260
+
261
+ Use `make check PYTHON=.venv/bin/python` when running through a project virtualenv.
262
+
263
+ No network services or privileged permissions are required for the test suite.
264
+
265
+ ## Security Model
266
+
267
+ 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.
268
+
269
+ ## Known Limits
270
+
271
+ - Static SSH mode reads `sshd_config` plus simple `Include` directives; `Match` behavior may differ from effective OpenSSH config.
272
+ - Effective SSH mode executes `sshd -T`; use it only when scanning the live host intentionally.
273
+ - Package metadata age relies on local cache marker mtimes and does not call package manager commands.
274
+ - Package vulnerability findings require an explicit local JSON feed and never fetch remote CVE data.
275
+ - Bridge posture checks rely on procfs and sysfs files inside the scanned root; missing bridge data is treated as unknown.
276
+ - Firewalld and nftables service state is inferred from systemd enablement markers when present; config-only detection leaves enabled state unknown.
277
+ - Enabled systemd service exposure detection is static and only flags common wildcard bind options in service unit `ExecStart` lines.
278
+ - Container runtime posture checks only report explicit static evidence from config, group, or enabled unit files; missing runtime files are unknown, not safe.
279
+ - Missing files are treated as absent data so scans can run in containers and fixture roots.
280
+ - Linwarden is a hardening triage tool, not a full CIS or DISA STIG compliance scanner.
281
+
282
+ Please report vulnerabilities using [SECURITY.md](SECURITY.md).
283
+
284
+ ## Roadmap
285
+
286
+ - PyPI trusted publishing.
287
+
288
+ Contributor-ready ideas live in [docs/contributor-ideas.md](docs/contributor-ideas.md).
289
+
290
+ ## License
291
+
292
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,261 @@
1
+ # Linwarden
2
+
3
+ [![CI](https://github.com/kingkyylian/linwarden/actions/workflows/ci.yml/badge.svg)](https://github.com/kingkyylian/linwarden/actions/workflows/ci.yml)
4
+ [![Python](https://img.shields.io/badge/python-3.9%2B-blue.svg)](pyproject.toml)
5
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
6
+
7
+ Linwarden is a rootless Linux hardening scanner for CI and fleet triage. It reads ordinary system files such as `/etc/os-release`, `/etc/ssh/sshd_config`, and selected `/proc/sys` values, then produces Markdown, JSON, or SARIF artifacts without installing an agent, daemon, privileged helper, database, or network service.
8
+
9
+ 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.
10
+
11
+ ## Why Linwarden
12
+
13
+ Use Linwarden when you need a fast security posture signal, not a heavyweight compliance platform.
14
+
15
+ | Need | Linwarden approach |
16
+ | --- | --- |
17
+ | CI-friendly output | JSON, Markdown, and SARIF for GitHub code scanning. |
18
+ | Low operational risk | Read-only collection from ordinary Linux files. |
19
+ | Offline analysis | Scan mounted roots, image extracts, containers, and fixtures. |
20
+ | Explainable findings | Every rule includes evidence, impact, remediation, and references. |
21
+ | Small supply chain | Zero runtime dependencies beyond Python 3.9+. |
22
+
23
+ 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.
24
+
25
+ ## Features
26
+
27
+ - Rootless collection from `/etc`, `/proc`, and procfs sysctl paths.
28
+ - Deterministic JSON output for CI pipelines and scheduled host scans.
29
+ - Markdown output suitable for GitHub job summaries and issue attachments.
30
+ - SARIF output suitable for GitHub-native security ingestion.
31
+ - JSON config support for profiles, disabled rules, and justified suppressions.
32
+ - Optional effective OpenSSH config collection through `sshd -T`, including Match context.
33
+ - Package update and host firewall posture signals where rootless files expose them.
34
+ - Package metadata freshness checks for common package manager cache markers.
35
+ - Bridge interface and bridge firewall hook posture checks for container hosts.
36
+ - Static container runtime posture checks for exposed Docker or Podman APIs and Docker group membership.
37
+ - Optional package vulnerability findings from a local JSON feed.
38
+ - Release checksum manifests with optional detached GPG signatures.
39
+ - Severity scoring with `critical`, `high`, `medium`, and `low` buckets.
40
+ - CI-friendly exit thresholds through `--fail-on`.
41
+ - Composite GitHub Action wrapper through `uses: kingkyylian/linwarden@v0.13.1`.
42
+ - Fixture-root scanning for tests, containers, forensic copies, and offline analysis.
43
+ - Zero runtime dependencies beyond Python 3.9+.
44
+
45
+ ## Quick Start
46
+
47
+ From a checkout:
48
+
49
+ ```bash
50
+ python3 -m venv .venv
51
+ . .venv/bin/activate
52
+ python -m pip install -e .
53
+ linwarden scan --format markdown
54
+ ```
55
+
56
+ Run against the included fixture:
57
+
58
+ ```bash
59
+ PYTHONPATH=src python3 -m linwarden scan \
60
+ --root tests/fixtures/linux-root \
61
+ --format json \
62
+ --fail-on high
63
+ ```
64
+
65
+ Exit code `2` means at least one finding matched the selected threshold.
66
+
67
+ ## Common Workflows
68
+
69
+ | Workflow | Command or doc |
70
+ | --- | --- |
71
+ | Local triage | `linwarden scan --format markdown` |
72
+ | CI failure threshold | `linwarden scan --format json --fail-on high` |
73
+ | GitHub code scanning | `uses: kingkyylian/linwarden@v0.13.1` |
74
+ | Mounted image scan | `linwarden scan --root /mnt/server-image --format json` |
75
+ | Effective SSH scan | `linwarden scan --sshd-mode effective --sshd-match user=deploy` |
76
+ | Tool positioning | [docs/comparison.md](docs/comparison.md) |
77
+
78
+ ## CLI
79
+
80
+ ```text
81
+ linwarden profiles [--format markdown|json]
82
+
83
+ linwarden scan [--root PATH] [--proc-root PATH] [--etc-root PATH] [--sys-root PATH]
84
+ [--config PATH] [--format markdown|json|sarif]
85
+ [--vulnerability-feed PATH] [--vulnerability-feed-format linwarden|trivy|grype|osv]
86
+ [--sshd-mode static|effective|auto] [--sshd-binary PATH]
87
+ [--sshd-match KEY=VALUE]
88
+ [--output PATH]
89
+ [--fail-on off|low|medium|high|critical]
90
+ ```
91
+
92
+ Common examples:
93
+
94
+ ```bash
95
+ linwarden profiles
96
+ linwarden scan --format markdown --output linwarden-report.md
97
+ linwarden scan --format json --fail-on high
98
+ linwarden scan --config linwarden.json --format sarif --output linwarden.sarif
99
+ linwarden scan --sshd-mode effective --format json
100
+ linwarden scan --sshd-mode effective --sshd-match user=deploy --sshd-match addr=203.0.113.10
101
+ linwarden scan --root /mnt/server-image --format json
102
+ linwarden scan --proc-root /host/proc --etc-root /host/etc --sys-root /host/sys --format markdown
103
+ linwarden scan --vulnerability-feed ./linwarden-vulnerabilities.json --format sarif
104
+ linwarden scan --vulnerability-feed ./trivy-report.json --vulnerability-feed-format trivy --format sarif
105
+ linwarden scan --vulnerability-feed ./grype-report.json --vulnerability-feed-format grype --format sarif
106
+ linwarden scan --vulnerability-feed ./osv-scanner-report.json --vulnerability-feed-format osv --format sarif
107
+ ```
108
+
109
+ ## Configuration
110
+
111
+ Linwarden accepts a zero-dependency JSON config file:
112
+
113
+ ```json
114
+ {
115
+ "profile": "router",
116
+ "disabled_rules": ["LNX-NET-002"],
117
+ "suppressions": [
118
+ {
119
+ "rule_id": "LNX-SSH-002",
120
+ "reason": "Temporary migration host; password auth removed after cutover."
121
+ }
122
+ ]
123
+ }
124
+ ```
125
+
126
+ Profiles:
127
+
128
+ | Profile | Behavior |
129
+ | --- | --- |
130
+ | `server` | Default for general Linux servers. No profile suppressions. |
131
+ | `workstation` | Interactive desktop or laptop posture. No profile suppressions; SSH, firewall, package, and kernel findings stay visible. |
132
+ | `router` | For hosts that intentionally route traffic. Suppresses IPv4, IPv6, and bridge forwarding findings. |
133
+ | `container` | For container or image-root scans where host-kernel sysctl values may be inherited. Suppresses kernel and filesystem sysctl findings. |
134
+
135
+ Suppressed findings remain visible in JSON and Markdown reports. SARIF output includes active findings only.
136
+
137
+ ## Exit Codes
138
+
139
+ | Code | Meaning |
140
+ | --- | --- |
141
+ | `0` | Scan completed and no selected threshold was met. |
142
+ | `1` | CLI usage error from argument parsing. |
143
+ | `2` | Scan completed and `--fail-on` threshold was met. |
144
+
145
+ ## Current Rules
146
+
147
+ | Rule | Severity | Area | Summary |
148
+ | --- | --- | --- | --- |
149
+ | `LNX-SSH-001` | high | SSH | `PermitRootLogin yes` is enabled. |
150
+ | `LNX-SSH-002` | medium | SSH | `PasswordAuthentication yes` is enabled. |
151
+ | `LNX-SSH-003` | high | SSH | `PermitEmptyPasswords yes` is enabled. |
152
+ | `LNX-SSH-004` | medium | SSH | `MaxAuthTries` is above `4`. |
153
+ | `LNX-SSH-005` | medium | SSH | `AllowTcpForwarding yes` or `all` is enabled. |
154
+ | `LNX-KRN-001` | high | Kernel | `kernel.randomize_va_space=0` disables ASLR. |
155
+ | `LNX-KRN-002` | high | Kernel | `vm.mmap_min_addr` is below `65536`. |
156
+ | `LNX-KRN-003` | medium | Kernel | `kernel.kptr_restrict=0` exposes kernel pointers. |
157
+ | `LNX-FS-001` | high | Filesystem | `fs.protected_hardlinks=0` disables hardlink protection. |
158
+ | `LNX-FS-002` | high | Filesystem | `fs.protected_symlinks=0` disables symlink protection. |
159
+ | `LNX-NET-001` | medium | Network | `net.ipv4.ip_forward=1` is enabled. |
160
+ | `LNX-NET-002` | low | Network | `net.ipv4.conf.all.accept_redirects=1` is enabled. |
161
+ | `LNX-NET-003` | medium | Network | `net.ipv6.conf.all.forwarding=1` is enabled. |
162
+ | `LNX-NET-004` | low | Network | `net.ipv6.conf.all.accept_redirects=1` is enabled. |
163
+ | `LNX-NET-005` | medium | Network | Bridge IPv4 firewall hooks are disabled while bridge interfaces exist. |
164
+ | `LNX-NET-006` | medium | Network | Bridge IPv6 firewall hooks are disabled while bridge interfaces exist. |
165
+ | `LNX-NET-007` | medium | Network | A bridge interface has forwarding enabled. |
166
+ | `LNX-PKG-001` | medium | Packages | Package updates are available. |
167
+ | `LNX-PKG-002` | high | Packages | Security package updates are available. |
168
+ | `LNX-PKG-003` | medium | Packages | Package metadata is stale. |
169
+ | `LNX-PKG-004` | feed severity | Packages | A local vulnerability feed reports an affected package. |
170
+ | `LNX-FW-001` | medium | Firewall | A known host firewall is disabled. |
171
+ | `LNX-SVC-001` | medium | Services | An enabled systemd service appears externally bound. |
172
+ | `LNX-CTR-001` | high | Containers | A container runtime API is bound to non-loopback TCP. |
173
+ | `LNX-CTR-002` | high | Containers | The Docker group grants daemon-level access to non-root users. |
174
+
175
+ Rule details live in [docs/rules.md](docs/rules.md).
176
+
177
+ ## Report Score
178
+
179
+ Linwarden starts each report at `100` and subtracts a fixed penalty per finding:
180
+
181
+ | Severity | Penalty |
182
+ | --- | --- |
183
+ | critical | 35 |
184
+ | high | 20 |
185
+ | medium | 10 |
186
+ | low | 3 |
187
+
188
+ The score is intentionally simple. It is a triage signal, not a compliance rating.
189
+
190
+ ## Project Layout
191
+
192
+ ```text
193
+ src/linwarden/
194
+ cli.py command line entry point
195
+ config.py profiles, disabled rules, and suppressions
196
+ collectors.py host snapshot collection
197
+ parsers.py small parsers for Linux files
198
+ rules.py built-in hardening checks
199
+ reporters.py JSON, Markdown, and SARIF rendering
200
+ models.py report data structures
201
+ tests/
202
+ fixtures/ deterministic Linux fixture root
203
+ docs/
204
+ architecture.md implementation overview
205
+ configuration.md profile and suppression config
206
+ comparison.md positioning against adjacent Linux security tools
207
+ contributor-ideas.md scoped contribution backlog
208
+ github-actions.md CI and SARIF workflow examples
209
+ launch.md copy and checklist for public announcements
210
+ positioning.md maintainer messaging guide
211
+ release.md release artifact and publishing workflow
212
+ rules.md rule catalog
213
+ report-schema.md JSON report contract
214
+ schemas/
215
+ report.schema.json machine-readable JSON report schema
216
+ ```
217
+
218
+ ## Development
219
+
220
+ ```bash
221
+ make test
222
+ make compile
223
+ make lint
224
+ make typecheck
225
+ make smoke
226
+ make smoke-sarif
227
+ make check
228
+ ```
229
+
230
+ Use `make check PYTHON=.venv/bin/python` when running through a project virtualenv.
231
+
232
+ No network services or privileged permissions are required for the test suite.
233
+
234
+ ## Security Model
235
+
236
+ 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.
237
+
238
+ ## Known Limits
239
+
240
+ - Static SSH mode reads `sshd_config` plus simple `Include` directives; `Match` behavior may differ from effective OpenSSH config.
241
+ - Effective SSH mode executes `sshd -T`; use it only when scanning the live host intentionally.
242
+ - Package metadata age relies on local cache marker mtimes and does not call package manager commands.
243
+ - Package vulnerability findings require an explicit local JSON feed and never fetch remote CVE data.
244
+ - Bridge posture checks rely on procfs and sysfs files inside the scanned root; missing bridge data is treated as unknown.
245
+ - Firewalld and nftables service state is inferred from systemd enablement markers when present; config-only detection leaves enabled state unknown.
246
+ - Enabled systemd service exposure detection is static and only flags common wildcard bind options in service unit `ExecStart` lines.
247
+ - Container runtime posture checks only report explicit static evidence from config, group, or enabled unit files; missing runtime files are unknown, not safe.
248
+ - Missing files are treated as absent data so scans can run in containers and fixture roots.
249
+ - Linwarden is a hardening triage tool, not a full CIS or DISA STIG compliance scanner.
250
+
251
+ Please report vulnerabilities using [SECURITY.md](SECURITY.md).
252
+
253
+ ## Roadmap
254
+
255
+ - PyPI trusted publishing.
256
+
257
+ Contributor-ready ideas live in [docs/contributor-ideas.md](docs/contributor-ideas.md).
258
+
259
+ ## License
260
+
261
+ MIT. See [LICENSE](LICENSE).