diff --git a/backend/internal/cli/root.go b/backend/internal/cli/root.go index 0c019f4..c11d5c4 100644 --- a/backend/internal/cli/root.go +++ b/backend/internal/cli/root.go @@ -780,7 +780,7 @@ func newDomainImportCommand() *cobra.Command { }} command.Flags().StringVar(&id, "id", "", "域名 ID") command.Flags().StringVar(&certificatePath, "certificate", "", "证书 PEM 文件路径") - command.Flags().StringVar(&privateKeyPath, "private-key", "", "私钥 PEM 文件路径") + command.Flags().StringVar(&privateKeyPath, "private-key", "", "HTTPS 证书私钥 PEM 文件路径") command.Flags().StringVar(&issuerPath, "issuer", "", "签发者证书 PEM 文件路径,可选") _ = command.MarkFlagRequired("id") _ = command.MarkFlagRequired("certificate") diff --git a/backend/internal/cli/server.go b/backend/internal/cli/server.go index fc085cd..49f6c8c 100644 --- a/backend/internal/cli/server.go +++ b/backend/internal/cli/server.go @@ -192,7 +192,7 @@ func (f *serverFlags) bind(command *cobra.Command) { command.Flags().StringVar(&f.username, "username", "", "SSH 用户") command.Flags().StringVar(&f.fingerprint, "host-key-fingerprint", "", "SSH 主机 SHA256 指纹") command.Flags().StringVar(&f.certificatePath, "certificate-path", deployment.DefaultCertificatePath, "远程证书文件路径模板") - command.Flags().StringVar(&f.privateKeyPath, "private-key-path", deployment.DefaultPrivateKeyPath, "远程私钥文件路径模板") + command.Flags().StringVar(&f.privateKeyPath, "private-key-path", deployment.DefaultPrivateKeyPath, "远程 HTTPS 证书私钥路径模板") command.Flags().StringVar(&f.chainPath, "chain-path", deployment.DefaultCertificateChainPath, "远程完整证书链路径模板") command.Flags().StringVar(&f.webServer, "web-server", string(deployment.WebServerNginx), "Web 服务类型:nginx、caddy、apache、custom") command.Flags().StringSliceVar(&f.validationCommand, "validation-command", nil, "自定义配置校验命令及参数") diff --git a/backend/internal/deployment/ssh.go b/backend/internal/deployment/ssh.go index 054a5ce..bcf6ef8 100644 --- a/backend/internal/deployment/ssh.go +++ b/backend/internal/deployment/ssh.go @@ -41,6 +41,9 @@ func (f SSHFactory) connect(ctx context.Context, server Server, requireSFTP bool if err := server.Validate(); err != nil { return nil, err } + if server.AuthMethod != SSHAuthPassword { + return nil, errors.New("服务器连接仅支持 SSH 密码认证") + } if f.Secrets == nil { return nil, errors.New("SSH 敏感信息存储未初始化") } diff --git a/backend/internal/httpapi/ssh_reload.go b/backend/internal/httpapi/ssh_reload.go index b688be2..93611ff 100644 --- a/backend/internal/httpapi/ssh_reload.go +++ b/backend/internal/httpapi/ssh_reload.go @@ -10,5 +10,5 @@ import ( func (s *Server) connectSSHAfterReload(ctx context.Context, item deployment.Server) (deployment.Client, error) { _ = ctx _ = item - return nil, errors.New("服务器仅支持 SSH 密码认证,已禁用 SSH 密钥登录") + return nil, errors.New("服务器连接仅支持 SSH 密码认证") } diff --git a/backend/internal/sshkey/service.go b/backend/internal/sshkey/service.go index f7e8344..aa08842 100644 --- a/backend/internal/sshkey/service.go +++ b/backend/internal/sshkey/service.go @@ -8,7 +8,7 @@ import ( "git.pcux.cn/xuans/Scert/backend/internal/secretstore" ) -var ErrDisabled = errors.New("SSH 密钥登录功能已永久停用,仅支持 SSH 密码认证") +var ErrDisabled = errors.New("服务器连接仅支持 SSH 密码认证") type Store interface { Put(ctx context.Context, reference string, value map[string]string) error diff --git a/doc/Linux生产流程测试记录.md b/doc/Linux生产流程测试记录.md index 595a474..d2fa74e 100644 --- a/doc/Linux生产流程测试记录.md +++ b/doc/Linux生产流程测试记录.md @@ -1822,3 +1822,13 @@ SQLite 新测试程序 SHA-256:4c81656fae63a2eebad999d58e6a79e63e6f441810e02ce - [~] 需要将最新候选包按正式安装流程部署到双机,并复核页面和 API 后再进入最终生产验收。 本轮不删除历史数据库迁移文件和禁用兼容测试;它们只用于既有数据库升级和安全回归,不会启用 SSH 密钥能力。总体生产验收仍未通过,既有证书 SAN、启动早期 502、Smail 系统账户发信和三数据库完整 Web 流程仍是门禁项。 +## 2026年10月2日 SSH 登录凭据边界复审补充 + +本次仅记录非敏感结果。部署和验证继续使用 SSH 密码认证,不使用、不生成、不上传 SSH 私钥或公钥,不写入远程授权文件,也不创建辅助验收脚本。HTTPS 证书私钥只用于 TLS 证书部署。 + +- [x] SSH 客户端新增连接前密码认证硬校验,非密码认证不能进入连接流程。 +- [x] 前端部署页面和 CLI 帮助文字明确区分服务器指纹、SSH 密码与 HTTPS 证书私钥。 +- [x] 未新增公网端口,Scert 仍只经 Nginx 80/443 反代,服务自身保持回环监听。 +- [~] 生产整体验收仍受第二台正式域名证书、短暂启动 502、三数据库 Web 首次流程、Smail 发信和密码重置闭环阻塞。 + +当前总体状态保持 **P0-03:完成,等待复审**。 diff --git a/doc/Scert整改任务执行清单.md b/doc/Scert整改任务执行清单.md index f4c655c..0b13893 100644 --- a/doc/Scert整改任务执行清单.md +++ b/doc/Scert整改任务执行清单.md @@ -1,5 +1,17 @@ # Scert 整改任务执行清单 +## 2026年10月2日 SSH 登录凭据安全边界复审 + +本节不记录任何口令、RAM 密钥、令牌、Cookie 或私钥内容。SSH 服务器连接只允许密码认证;HTTPS 证书私钥仍是证书业务材料,不作为 SSH 登录凭据。 + +- [x] SSH 工厂在连接前强制校验密码认证,非密码认证不能进入连接流程。 +- [x] 禁用的兼容服务和重载连接函数不再提供密钥登录提示或可用回退路径。 +- [x] 前端部署服务器页面使用“服务器指纹”描述主机校验信息,HTTPS 证书导入明确区分证书私钥。 +- [x] 未创建辅助脚本、未做远端备份、未删除历史迁移和兼容文件。 +- [~] 仍待完成:候选包双机重新部署、第二台正式域名证书、三数据库 Web 首次流程、Smail 发信和密码重置闭环。 + +当前总体验收仍为 **P0-03:完成,等待复审**。 + ## 2026年10月1日 SSH 私钥登录禁用复审记录 本节只记录非敏感代码证据,不记录服务器口令、RAM 密钥、数据库密码、令牌或私钥内容。用户已明确禁止 SSH 私钥登录,生产部署继续使用 SSH 密码认证。 diff --git a/doc/Scert整改合并优化清单.md b/doc/Scert整改合并优化清单.md index 4e37fd3..7fdf1a1 100644 --- a/doc/Scert整改合并优化清单.md +++ b/doc/Scert整改合并优化清单.md @@ -1,5 +1,17 @@ # Scert 整改、合并与优化清单 +## 2026年10月2日 SSH 密码认证边界最终收口 + +本节只记录非敏感安全结果。根据“SSH 私钥、公钥和密钥登录不允许出现”的要求,生产功能只保留密码认证;HTTPS 证书私钥仅用于证书部署和 TLS 服务,不属于服务器登录凭据。 + +- [x] SSH 客户端连接在认证前再次硬性校验密码认证,不能通过其他认证方式或回退路径建立连接。 +- [x] 禁用兼容服务、重载连接逻辑和 CLI 帮助文字均明确为密码认证;没有新增密钥生成、上传、授权文件写入或密钥登录入口。 +- [x] 服务器页面不再把主机指纹描述为登录密钥;证书导入明确标注为 HTTPS 证书私钥,避免与 SSH 登录凭据混淆。 +- [x] 未删除历史数据库迁移、禁用兼容文件或证书私钥功能;历史结构只用于升级兼容,生产代码不会读取或使用其作为 SSH 登录凭据。 +- [~] 待完成:候选包重新构建部署、三数据库清库后的 Web 流程、Smail 系统账户发信、密码重置闭环及第二台正式域名证书验收。 + +当前总体验收仍为 **P0-03:完成,等待复审**,本安全子项不能单独作为生产验收通过依据。 + ## 2026年10月1日 SSH 私钥登录安全边界收口 本次仅记录代码、构建和测试结果,不记录服务器口令、RAM 密钥、数据库密码、令牌或其他认证信息。根据“安全风险太大,不允许 SSH 私钥登录”的要求: diff --git a/frontend/src/views/Dashboard/Dashboard.vue b/frontend/src/views/Dashboard/Dashboard.vue index 2ad91ed..5d7fc02 100644 --- a/frontend/src/views/Dashboard/Dashboard.vue +++ b/frontend/src/views/Dashboard/Dashboard.vue @@ -395,7 +395,7 @@ const { activeSection, activeLabel, sections, selectSection } = useDashboardNavi
还没有部署服务器添加 SSH 服务器后,证书申请成功可自动部署。添加部署服务器后,证书申请成功可自动部署。 @@ -460,7 +460,7 @@ const { activeSection, activeLabel, sections, selectSection } = useDashboardNavi