收口超级管理员重置安全边界

阻断公网重置入口并同步初始化与 CLI 提示,保留本机运维重置路径的后续验证。
This commit is contained in:
xuans 2026-10-04 13:23:08 +08:00
parent 6f702cdc36
commit 55c500c1a8
4 changed files with 6 additions and 11 deletions

View File

@ -233,7 +233,7 @@ func newInitCommandWithDependencies(firewallManager firewall.Manager, issue init
fmt.Fprintln(cmd.OutOrStdout(), "当前使用 ACME 测试环境,证书不受浏览器信任,不能作为正式生产证书。") fmt.Fprintln(cmd.OutOrStdout(), "当前使用 ACME 测试环境,证书不受浏览器信任,不能作为正式生产证书。")
} }
fmt.Fprintf(cmd.OutOrStdout(), "本机防火墙:%s\n", firewallResult.Message) fmt.Fprintf(cmd.OutOrStdout(), "本机防火墙:%s\n", firewallResult.Message)
fmt.Fprintf(cmd.OutOrStdout(), "请确认云安全组已放行主服务端口 %d。Scert-admin 默认关闭;需要重置密码时启动后访问:%s\n", cfg.Server.Port, cfg.ResetURL()) fmt.Fprintf(cmd.OutOrStdout(), "请确认云安全组已放行主服务端口 %d。超级管理员密码重置不开放公网入口,请由运维人员在部署主机本机受保护终端处理。\n", cfg.Server.Port)
return nil return nil
}} }}
command.Flags().StringVar(&adminUsername, "admin-username", "", "超级管理员用户名") command.Flags().StringVar(&adminUsername, "admin-username", "", "超级管理员用户名")
@ -672,7 +672,7 @@ func newStatusCommand() *cobra.Command {
} }
func commandStatus(cfg config.Config) map[string]any { func commandStatus(cfg config.Config) map[string]any {
return map[string]any{"service": "scert", "version": buildinfo.Version, "config_version": cfg.Version, "setup_ready": cfg.SetupReady, "https_mode": cfg.Server.HTTPSMode, "database_driver": cfg.Database.Driver, "server_port": cfg.Server.Port, "reset_url": cfg.ResetURL(), "web_enabled": cfg.Web.Enabled, "web_hostname": cfg.Web.Hostname, "web_public_ip": cfg.Web.PublicIP, "dns_verification_state": cfg.DNS.VerificationState} return map[string]any{"service": "scert", "version": buildinfo.Version, "config_version": cfg.Version, "setup_ready": cfg.SetupReady, "https_mode": cfg.Server.HTTPSMode, "database_driver": cfg.Database.Driver, "server_port": cfg.Server.Port, "web_enabled": cfg.Web.Enabled, "web_hostname": cfg.Web.Hostname, "web_public_ip": cfg.Web.PublicIP, "dns_verification_state": cfg.DNS.VerificationState}
} }
func newServeCommand() *cobra.Command { func newServeCommand() *cobra.Command {

View File

@ -99,7 +99,7 @@ func TestPublicStatusDoesNotExposeInternalConfiguration(t *testing.T) {
if err := json.Unmarshal(response.Body.Bytes(), &payload); err != nil { if err := json.Unmarshal(response.Body.Bytes(), &payload); err != nil {
t.Fatalf("解析公开状态响应失败:%v", err) t.Fatalf("解析公开状态响应失败:%v", err)
} }
for _, key := range []string{"database_driver", "https_mode", "reset_url"} { for _, key := range []string{"database_driver", "https_mode"} {
if _, exists := payload[key]; exists { if _, exists := payload[key]; exists {
t.Fatalf("公开状态接口不应返回内部字段 %s:%s", key, response.Body.String()) t.Fatalf("公开状态接口不应返回内部字段 %s:%s", key, response.Body.String())
} }

View File

@ -121,7 +121,6 @@ func (s *Server) setupStatus(c *gin.Context) {
"phase": phase, "phase": phase,
"blockers": blockers, "blockers": blockers,
"server_port": cfg.Server.Port, "server_port": cfg.Server.Port,
"reset_url": cfg.ResetURL(),
"https_mode": cfg.Server.HTTPSMode, "https_mode": cfg.Server.HTTPSMode,
"proxy_https_port": cfg.Server.ProxyHTTPSPort, "proxy_https_port": cfg.Server.ProxyHTTPSPort,
"web_enabled": cfg.Web.Enabled, "web_enabled": cfg.Web.Enabled,
@ -521,12 +520,11 @@ func (s *Server) applySetup(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"initialized": true, "initialized": true,
"message": fmt.Sprintf( "message": fmt.Sprintf(
"初始化配置已保存,DNS API 已完成在线验证且凭据已加密保存。%s。请确认云安全组已放行主服务端口 %d;Scert-admin 默认关闭,启动后通过 %s 使用临时重置入口。管理后台证书申请成功。", "初始化配置已保存,DNS API 已完成在线验证且凭据已加密保存。%s。请确认云安全组已放行主服务端口 %d;超级管理员密码重置不开放公网入口,请由运维人员在部署主机本机受保护终端处理。管理后台证书申请成功。",
firewallResult.Message, updated.Server.Port, updated.ResetURL(), firewallResult.Message, updated.Server.Port,
), ),
"status": gin.H{ "status": gin.H{
"server_port": updated.Server.Port, "server_port": updated.Server.Port,
"reset_url": updated.ResetURL(),
"web_enabled": updated.Web.Enabled, "web_enabled": updated.Web.Enabled,
"https_mode": updated.Server.HTTPSMode, "https_mode": updated.Server.HTTPSMode,
"firewall": firewallResult, "firewall": firewallResult,

View File

@ -202,7 +202,7 @@ func TestSetupStatusHidesConfigurationAfterInitialization(t *testing.T) {
if payload["initialized"] != true { if payload["initialized"] != true {
t.Fatalf("初始化状态不正确:%s", response.Body.String()) t.Fatalf("初始化状态不正确:%s", response.Body.String())
} }
for _, key := range []string{"server_port", "reset_url", "https_mode", "web_hostname", "web_public_ip", "certificate_root", "dns_provider", "dns_verification_state"} { for _, key := range []string{"server_port", "https_mode", "web_hostname", "web_public_ip", "certificate_root", "dns_provider", "dns_verification_state"} {
if _, exists := payload[key]; exists { if _, exists := payload[key]; exists {
t.Fatalf("初始化完成后不应公开内部字段 %s:%s", key, response.Body.String()) t.Fatalf("初始化完成后不应公开内部字段 %s:%s", key, response.Body.String())
} }
@ -358,9 +358,6 @@ func TestSetupApplyPersistsConfigurationWithoutPlainPassword(t *testing.T) {
if !strings.Contains(response.Body.String(), `"firewall"`) || !strings.Contains(response.Body.String(), `"created":true`) { if !strings.Contains(response.Body.String(), `"firewall"`) || !strings.Contains(response.Body.String(), `"created":true`) {
t.Fatalf("初始化响应应返回本机防火墙结果:%s", response.Body.String()) t.Fatalf("初始化响应应返回本机防火墙结果:%s", response.Body.String())
} }
if !strings.Contains(response.Body.String(), `"reset_url":"https://scert.example.test:8443/reset"`) {
t.Fatalf("初始化响应应返回根据 Web 地址生成的密码重置路径:%s", response.Body.String())
}
data, err := os.ReadFile(configPath) data, err := os.ReadFile(configPath)
if err != nil { if err != nil {