移除SSH密钥入口并收紧密码认证边界

删除未注册的密钥 CLI、HTTP 处理和前端残留样式,保留 HTTPS 证书私钥路径与历史数据库兼容迁移;更新生产验收记录并记录后端、前端测试和构建结果。
This commit is contained in:
xuans 2026-10-02 09:18:55 +08:00
parent 1b9b6ecec4
commit 266eea723c
8 changed files with 39 additions and 330 deletions

View File

@ -1,29 +0,0 @@
package cli
import (
"errors"
"github.com/spf13/cobra"
)
func newSSHKeyCommand() *cobra.Command {
return &cobra.Command{
Use: "ssh-key",
Short: "SSH 密钥登录功能已停用",
RunE: func(cmd *cobra.Command, args []string) error {
return errors.New("SSH 密钥登录功能已永久停用,仅支持 SSH 密码认证")
},
}
}
func newSSHKeyListCommand() *cobra.Command {
return newSSHKeyCommand()
}
func newSSHKeyAddCommand() *cobra.Command {
return newSSHKeyCommand()
}
func newSSHKeyDeleteCommand() *cobra.Command {
return newSSHKeyCommand()
}

View File

@ -1,15 +0,0 @@
package httpapi
import "github.com/gin-gonic/gin"
func (s *Server) listSSHKeys(c *gin.Context) {
c.Status(404)
}
func (s *Server) createSSHKey(c *gin.Context) {
c.Status(404)
}
func (s *Server) deleteSSHKey(c *gin.Context) {
c.Status(404)
}

View File

@ -1809,3 +1809,16 @@ SQLite 新测试程序 SHA-256:4c81656fae63a2eebad999d58e6a79e63e6f441810e02ce
- [~] P0-10、三数据库清库后的 Web 流程、Smail 系统账户发信、验证码密码重置和最终清库首次验收仍未完成。
总体生产部署验收仍未放行,当前状态保持 **P0-03:完成,等待复审**。
## 2026年10月2日 SSH 登录密钥入口移除复核
本轮按安全要求收紧产品边界:SSH 登录只允许密码认证,不提供私钥、公钥、密钥生成、授权文件写入或密钥登录管理能力。HTTPS 证书部署所需的证书私钥路径仍保留,二者用途不同。
- [x] 删除未注册的 SSH 密钥 CLI 入口文件,正式 CLI 不再包含密钥管理命令。
- [x] 删除未注册的 SSH 密钥 HTTP 处理文件,生产路由继续不注册相关接口,`/api/v1/ssh-keys` 保持 404。
- [x] 删除未引用的前端密钥弹窗文件和遗留样式,生产页面不提供密钥管理入口、命令或授权文件提示。
- [x] 保留服务器主机指纹校验、SSH 密码字段和 HTTPS 证书私钥路径;没有把证书私钥误当作 SSH 登录凭据。
- [x] 后端 `go test ./...` 全部通过;前端 241 项测试全部通过;Vite 生产构建通过;`git diff --check` 通过。
- [~] 需要将最新候选包按正式安装流程部署到双机,并复核页面和 API 后再进入最终生产验收。
本轮不删除历史数据库迁移文件和禁用兼容测试;它们只用于既有数据库升级和安全回归,不会启用 SSH 密钥能力。总体生产验收仍未通过,既有证书 SAN、启动早期 502、Smail 系统账户发信和三数据库完整 Web 流程仍是门禁项。

View File

@ -1927,3 +1927,16 @@ P0-05 从 `[!]` 调整为 `[~]`,并未标记完成;P0-04 剩余门禁和全
- [ ] SQLite、MySQL、PostgreSQL 清库后的完整 Web 首次流程。
- [!] Smail 系统通知账户、真实发信、验证码、超级管理员重置密码和改密后重新登录闭环。
- [!] 第一台 `odacm.cn` 证书 SAN 不匹配及启动早期短暂 502 的生产结论。
## 2026年10月2日 SSH 登录密钥入口移除复核
本轮按安全要求收紧产品边界:SSH 登录只允许密码认证,不提供私钥、公钥、密钥生成、授权文件写入或密钥登录管理能力。HTTPS 证书部署所需的证书私钥路径仍保留,二者用途不同。
- [x] 删除未注册的 SSH 密钥 CLI 入口文件,正式 CLI 不再包含密钥管理命令。
- [x] 删除未注册的 SSH 密钥 HTTP 处理文件,生产路由继续不注册相关接口,`/api/v1/ssh-keys` 保持 404。
- [x] 删除未引用的前端密钥弹窗文件和遗留样式,生产页面不提供密钥管理入口、命令或授权文件提示。
- [x] 保留服务器主机指纹校验、SSH 密码字段和 HTTPS 证书私钥路径;没有把证书私钥误当作 SSH 登录凭据。
- [x] 后端 `go test ./...` 全部通过;前端 241 项测试全部通过;Vite 生产构建通过;`git diff --check` 通过。
- [~] 需要将最新候选包按正式安装流程部署到双机,并复核页面和 API 后再进入最终生产验收。
本轮不删除历史数据库迁移文件和禁用兼容测试;它们只用于既有数据库升级和安全回归,不会启用 SSH 密钥能力。总体生产验收仍未通过,既有证书 SAN、启动早期 502、Smail 系统账户发信和三数据库完整 Web 流程仍是门禁项。

View File

@ -2563,3 +2563,16 @@ P0-11 生产验收门禁
- [~] 启动早期 502 仍需按生产门禁继续评估:第二台本次安装后约第 2 秒探测出现 502,等待后恢复为严格 HTTPS 200;未开放 8443,也未绕过 Nginx。
当前总体状态仍为 **P0-03:完成,等待复审**,不能据此宣布生产整体验收通过。P0-10 负向恢复、三数据库清库后的完整 Web 流程、Smail 系统账户发信及密码重置闭环仍未完成。
## 2026年10月2日 SSH 登录密钥入口移除复核
本轮按安全要求收紧产品边界:SSH 登录只允许密码认证,不提供私钥、公钥、密钥生成、授权文件写入或密钥登录管理能力。HTTPS 证书部署所需的证书私钥路径仍保留,二者用途不同。
- [x] 删除未注册的 SSH 密钥 CLI 入口文件,正式 CLI 不再包含密钥管理命令。
- [x] 删除未注册的 SSH 密钥 HTTP 处理文件,生产路由继续不注册相关接口,`/api/v1/ssh-keys` 保持 404。
- [x] 删除未引用的前端密钥弹窗文件和遗留样式,生产页面不提供密钥管理入口、命令或授权文件提示。
- [x] 保留服务器主机指纹校验、SSH 密码字段和 HTTPS 证书私钥路径;没有把证书私钥误当作 SSH 登录凭据。
- [x] 后端 `go test ./...` 全部通过;前端 241 项测试全部通过;Vite 生产构建通过;`git diff --check` 通过。
- [~] 需要将最新候选包按正式安装流程部署到双机,并复核页面和 API 后再进入最终生产验收。
本轮不删除历史数据库迁移文件和禁用兼容测试;它们只用于既有数据库升级和安全回归,不会启用 SSH 密钥能力。总体生产验收仍未通过,既有证书 SAN、启动早期 502、Smail 系统账户发信和三数据库完整 Web 流程仍是门禁项。

View File

@ -434,162 +434,10 @@ code {
color: #63748c !important;
}
.ssh-server-list {
display: grid;
gap: 14px;
}
.ssh-server-card {
display: grid;
gap: 14px;
padding: 18px;
border: 1px solid #dfe7f1;
border-radius: 16px;
background: linear-gradient(135deg, #ffffff, #f8fbff);
}
.ssh-server-card-heading,
.ssh-orphaned-heading,
.ssh-server-command {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.ssh-server-meta {
display: flex;
flex-wrap: wrap;
gap: 8px 18px;
color: #718198;
font-size: 12px;
}
.ssh-server-domain-bindings {
display: grid;
gap: 8px;
padding: 12px;
border: 1px solid #e4ebf4;
border-radius: 12px;
background: #fff;
}
.ssh-server-domain-list {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.ssh-server-details {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
}
.ssh-server-detail {
display: grid;
gap: 5px;
min-width: 0;
padding: 12px;
border: 1px solid #e4ebf4;
border-radius: 12px;
background: #fff;
color: #718198;
font-size: 12px;
line-height: 1.5;
}
.ssh-server-detail strong {
color: #30435f;
font-size: 13px;
}
.detail-label {
color: #8a98ab;
font-size: 11px;
letter-spacing: 0.04em;
}
.ssh-server-detail code,
.ssh-server-command code {
overflow-wrap: anywhere;
color: #345f94;
font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
font-size: 12px;
}
.inline-error {
margin: 0;
}
.ssh-server-command {
padding: 10px 12px;
border: 1px solid #d8e5f4;
border-radius: 12px;
background: #f3f8ff;
}
.ssh-server-command > div {
display: grid;
gap: 5px;
min-width: 0;
}
.ssh-orphaned-keys {
display: grid;
gap: 10px;
margin-top: 18px;
padding-top: 16px;
border-top: 1px solid #e7edf5;
}
.ssh-orphaned-heading > div {
display: grid;
gap: 3px;
}
.ssh-orphaned-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.ssh-orphaned-item {
display: inline-flex !important;
align-items: center;
gap: 8px;
padding: 7px 10px;
border: 1px solid #e1e8f1;
border-radius: 9px;
background: #fbfcfe;
color: #6f7f94;
font-size: 12px;
}
@media (max-width: 900px) {
.ssh-server-details {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 640px) {
.ssh-server-card-heading,
.ssh-orphaned-heading,
.ssh-server-command {
align-items: stretch;
flex-direction: column;
}
.ssh-server-details {
grid-template-columns: minmax(0, 1fr);
}
.ssh-server-command .inline-button {
align-self: flex-start;
}
}
.modal-backdrop {
position: fixed;
inset: 0;

View File

@ -1,131 +0,0 @@
.modal-backdrop.ssh-key-backdrop {
padding: 16px;
}
.ssh-key-modal {
box-sizing: border-box;
width: min(100%, 1180px);
max-height: calc(100dvh - 32px);
overflow: auto;
padding: 18px 24px;
}
.ssh-key-modal .modal-heading {
margin-bottom: 10px;
padding-bottom: 10px;
}
.ssh-key-modal form {
grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
gap: 18px;
}
.ssh-key-modal .ssh-key-guide,
.ssh-key-modal .ssh-key-form-fields {
min-width: 0;
padding: 12px;
}
.ssh-key-modal .ssh-key-guide-heading {
margin-bottom: 8px;
}
.ssh-key-modal .ssh-key-guide-heading > div:first-child,
.ssh-key-modal .ssh-key-steps article > div,
.ssh-key-modal .field > span,
.ssh-key-modal .field-label-row > span {
display: flex;
align-items: center;
gap: 6px;
}
.ssh-key-modal .ssh-key-steps {
gap: 6px;
}
.ssh-key-modal .ssh-key-steps article {
gap: 5px;
padding: 7px 5px;
}
.ssh-key-modal .ssh-key-steps strong {
font-size: 12px;
}
.ssh-key-modal .ssh-key-platforms {
margin: 8px 0;
padding: 3px;
}
.ssh-key-modal .platform-tab {
padding: 6px 8px;
}
.ssh-key-modal .ssh-key-path-line,
.ssh-key-modal .ssh-key-path-summary {
gap: 5px;
padding: 6px 8px;
margin: 6px 0;
}
.ssh-key-modal .ssh-key-path-summary {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.ssh-key-modal .ssh-key-path-summary > div:last-child {
grid-column: auto;
}
.ssh-key-modal .ssh-command-card {
margin-top: 6px;
padding: 7px 8px;
}
.ssh-key-modal .ssh-command-heading {
margin-bottom: 4px;
}
.ssh-key-modal .ssh-command-card > code {
font-size: 11px;
line-height: 1.4;
}
.ssh-key-modal .field {
margin-bottom: 10px;
gap: 5px;
}
.ssh-key-modal .field input,
.ssh-key-modal .field textarea {
min-height: 36px;
padding: 8px 10px;
}
.ssh-key-modal .ssh-key-form-fields .field textarea {
min-height: 130px;
}
.ssh-key-modal .ssh-key-form-fields .modal-actions {
margin-top: 10px;
padding-top: 10px;
}
@media (max-width: 900px) {
.ssh-key-modal form {
grid-template-columns: minmax(0, 1fr);
}
.ssh-key-modal .ssh-key-form-fields {
grid-column: 1;
}
}
@media (max-width: 640px) {
.ssh-key-modal {
padding: 16px;
}
.ssh-key-modal .ssh-key-path-summary {
grid-template-columns: minmax(0, 1fr);
}
}

View File

@ -1,3 +0,0 @@
<template>
<span v-if="false" aria-hidden="true"></span>
</template>