capitan0n/sshd-lint

GitHub: capitan0n/sshd-lint

一款零依赖的 OpenSSH sshd_config 静态分析工具,基于 CIS、Mozilla 和 NIST 安全基线离线审计 SSH 服务器配置并支持 CI/CD 集成。

Stars: 2 | Forks: 0

# sshd_lint 一个零依赖、快速且健壮的 OpenSSH `sshd_config` 文件静态分析工具。 `sshd_lint` 根据行业标准安全基线评估您的 SSH 服务器配置,包括 **CIS Benchmark for Linux**、**Mozilla OpenSSH Guidelines** 和 **NIST SP 800-53**。 ## 💡 动机 这个项目源于一个反复出现的实际需求:每当设置新的测试 VM 或管理生产服务器时,需要快速审计 SSH 配置和状态。 为了高效地解决这个问题,我构建了这个轻量级程序来提供即时结果。它是完全独立的,运行时不需要互联网连接,并且绝对零依赖。 ## 🤖 AI 辅助项目 该工具的构思和开发均借助了人工智能的辅助。 在人类的指导和审查下,AI 被用于代码生成、逻辑优化以及边缘情况的处理(例如累积指令和 `Match` 块作用域)。 ## ✨ 功能 - **零依赖** — 完全基于 Python 标准库构建。无需 `pip install`。 - **上下文感知解析** — 理解 OpenSSH 语义:`Match` 块、`Match All` 重置、指令遮蔽(directive shadowing)和 `Include` 通配符扩展。 - **作用域限定的 Match 块发现** — 区分全局配置错误与仅适用于特定用户、地址或组的风险。 - **重复指令检测** — 当某个指令在全局中出现多次时发出警告,因为 sshd 只会静默使用第一次出现的配置。 - **详细且具可操作性的报告** — 解释问题所在、其重要性,并引用相关标准。 - **支持 CI/CD** — 结构化的 JSON 输出和有意义的退出码,便于集成到 pipeline 中。 - **版本感知规则** — 根据目标 OpenSSH 版本调整预期。 ## 📦 环境要求 - Python **3.9+** - 无需外部包 ## ⚙️ 安装 由于没有外部依赖,您可以直接运行它: ``` wget https://raw.githubusercontent.com/capitan0n/sshd-lint/main/sshd_lint.py -O sshd_lint chmod +x sshd_lint ./sshd_lint ``` 或者克隆代码仓库: ``` git clone https://github.com/capitan0n/sshd-lint.git cd YOUR_REPO python sshd_lint.py ``` ## 🚀 使用方法 分析默认的系统 SSH 配置: ``` python sshd_lint.py ``` 分析特定文件: ``` python sshd_lint.py /path/to/sshd_config ``` 按严重性过滤(仅限 HIGH 及以上): ``` python sshd_lint.py --severity high ``` 紧凑输出(隐藏解释信息 — 适用于快速扫描): ``` python sshd_lint.py --compact ``` 用于 pipeline 集成的 JSON 输出: ``` python sshd_lint.py --format json ``` JSON 输出是自包含的 — `exit_code` 和每个严重级别的 `summary` 都包含在顶层,因此使用者无需单独捕获 `$?`: ``` { "exit_code": 2, "summary": { "CRITICAL": 0, "HIGH": 1, "MEDIUM": 3, "LOW": 4, "INFO": 2 }, "findings": [ { "severity": "HIGH", "directive": "PasswordAuthentication", "value": "yes", "line": 42, "scope": "global", "message": "Password authentication is enabled.", "detail": "...", "references": ["CIS Benchmark for Linux", "Mozilla OpenSSH Guidelines"] } ] } ``` 使用 `jq` 过滤: ``` # 仅 CRITICAL 发现 python sshd_lint.py --format json | jq '.findings[] | select(.severity == "CRITICAL")' # 仅摘要 python sshd_lint.py --format json | jq '.summary' # 从 JSON 而非 $? 读取 exit code python sshd_lint.py --format json | jq '.exit_code' ``` 审计从远程服务器复制的配置,并从当前运行的系统解析 Include: ``` scp user@server:/etc/ssh/sshd_config /tmp/audit/sshd_config python sshd_lint.py /tmp/audit/sshd_config --base-dir /etc/ssh ``` ## 🧰 CLI 标志 | 标志 | 缩写 | 描述 | |-----------------------|-------|-------------| | `config` | — | sshd_config 的路径(默认:`/etc/ssh/sshd_config`) | | `--severity` | `-s` | 最低严重性:`critical`、`high`、`medium`、`low`、`info`(默认:`info`) | | `--format` | `-f` | 输出格式:`text` 或 `json`(默认:`text`) | | `--compact` | `-c` | 隐藏解释和参考以获得更清晰的输出 | | `--no-color` | — | 禁用 ANSI 颜色 | | `--openssh-version` | — | 目标 OpenSSH 版本(例如 `8.9`),用于进行感知版本的规则调整 | | `--base-dir` | — | 用于解析 `Include` 的基准目录。默认:与配置文件相同的目录 | | `--version` | `-v` | 显示版本并退出 | ## 🚦 退出码 专为 CI/CD pipeline 设计: | 代码 | 含义 | |------|---------| | `0` | 没有达到请求严重性阈值的发现 | | `1` | 存在发现,但没有 HIGH 或 CRITICAL 级别的问题 | | `2` | 至少有一个 HIGH 或 CRITICAL 级别的发现 — pipeline 应该失败 | 使用 `--format json` 时,退出码也会作为 `exit_code` 嵌入在 JSON 输出中,因此报告是完全自包含的,可供下游工具读取,而无需捕获 `$?`。 GitHub Actions 步骤示例: ``` - name: Lint SSH config run: python sshd_lint.py /etc/ssh/sshd_config --severity high --format json ``` ## 🧪 已评估的规则 ### 解析与文件处理 - 规则 00:Include 解析问题 — 无法读取的文件、匹配超过 500 个文件的 Include 通配符(直接拒绝)、匹配 0 个文件的 Include 通配符,以及无法解析为指令的行 ### 身份验证 - 规则 01:`PermitRootLogin` - 规则 02:`PasswordAuthentication` - 规则 03:`PermitEmptyPasswords` - 规则 04:`ChallengeResponseAuthentication` - 规则 05:`PubkeyAuthentication` - 规则 06:`HostbasedAuthentication` / `IgnoreRhosts` ### 访问控制 - 规则 10:`LoginGraceTime` - 规则 11:`MaxAuthTries` - 规则 12:`MaxSessions` - 规则 13:`MaxStartups` - 规则 14:`AllowUsers` / `AllowGroups` ### 转发与隧道 - 规则 20:`X11Forwarding` - 规则 21:`AllowTcpForwarding` - 规则 22:`AllowAgentForwarding` - 规则 23:`GatewayPorts` - 规则 24:`PermitTunnel` ### 日志与审计 - 规则 30:`LogLevel` - 规则 31:`PrintLastLog` ### 密码学 - 规则 40:弱或已弃用的 Ciphers - 规则 41:弱或已弃用的 MACs - 规则 42:弱 KexAlgorithms(密钥交换) - 规则 43:已弃用的 HostKeyAlgorithms - 规则 44:已弃用的 `PubkeyAcceptedAlgorithms` 规则 40-44 理解 OpenSSH 的 `+`/`-`/`^` 默认集合语法(例如 `Ciphers +arcfour` 会追加到编译时的默认值中,而不是替换它)—— 无论是完全替换列表还是仅仅追加到其中,弱算法都会被标记出来。 ### 其他 - 规则 50:`Banner` - 规则 51:`StrictModes` - 规则 52:`Port`(默认端口 22) - 规则 53:`ClientAliveInterval` / 空闲会话超时 - 规则 54:`UseDNS` - 规则 55:`Match` 块内的不安全指令(受作用域限制的风险) - 规则 56:重复的全局指令(被 sshd 遮蔽) - 规则 57:`PermitUserEnvironment` ## 🔍 sshd_lint 与同类工具的区别 | 工具 | 工作原理 | 需要 root 权限 / 运行中的系统 | |------|--------------|-----------------------------| | **Lynis** | 在系统上实时运行,审计许多方面 | 是 | | **ssh-audit** | 连接到正在运行的 SSH 服务器,测试其响应 | 是(需要网络访问) | | **sshd_lint** | 离线静态读取配置文件 | 否 | `sshd_lint` 专为离线审计、CI/CD pipeline 以及审查来自远程系统的配置而设计,无需访问正在运行的服务器。 ## ⚠️ 限制 - **仅限静态分析** — 不连接到正在运行的服务器或测试实际行为。 - **不评估 Match 块条件** — 条件字符串(例如 `User anoncvs`、`Address 10.0.0.0/8`)会被记录并报告,但 sshd_lint 无法确定它是否适用于给定的连接。 - **Include 解析需要文件系统访问权限** — 无法读取的文件和匹配超过 500 个文件的 Include 通配符会被报告为 CRITICAL;匹配 0 个文件的通配符被报告为 INFO(通常是良性的 — 例如一个空的 `sshd_config.d/` — 但值得看一眼)。 - **相对 `Include` 路径会根据 `--base-dir` / 配置文件自身的 目录进行解析,而不是真实 sshd 硬编码的 `/etc/ssh`。** 对于审计 正在运行的 `/etc/ssh/sshd_config` 这种常见情况,两者是相同的。当仅审计 主文件的副本(旁边没有其片段)时,请传入 `--base-dir /etc/ssh`,以按照 sshd 本身的方式解析 include。 - **感知版本的规则目前很少** — `--openssh-version` 标志仅调整了少量已知的默认值;在未来的版本中可能会添加更多特定于版本的规则。 - **编译时的默认值适用于 OpenSSH 8.x** — 在明显更旧或更新版本上的行为可能会有所不同。 ## 作者 * **capitan0n** - [capitan0n](https://github.com/capitan0n) ## 📜 许可证 MIT License
标签:DevSecOps, Python, SSH, 上游代理, 云安全监控, 关系图谱, 基线检查, 安全合规, 无后门, 网络代理, 逆向工具, 静态分析