From 4ad4840c4e100d217622780cfa2f336f59d94bd6 Mon Sep 17 00:00:00 2001 From: samwaf Date: Mon, 31 Aug 2026 16:02:30 +0800 Subject: [PATCH] fix:bind host to the ssl config id actually persisted --- .../waf_service/cross_engine_core_b_test.go | 18 +++++++++++++++- service/waf_service/waf_sslconfig.go | 21 ++++++++++++++----- wafenginecore/sslorder.go | 19 ++++++++++++----- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/service/waf_service/cross_engine_core_b_test.go b/service/waf_service/cross_engine_core_b_test.go index bdd0941..8a4f77d 100644 --- a/service/waf_service/cross_engine_core_b_test.go +++ b/service/waf_service/cross_engine_core_b_test.go @@ -185,11 +185,27 @@ func runCoreCRUDCasesB(t *testing.T, db *gorm.DB) { AutoLoadPath: 0, // 自动管理证书:关闭路径自动加载 } // 修复前:按值 Create + 零值默认列在此 panic(reflect.Value.SetInt unaddressable) - WafSslConfigServiceApp.CreateInner(cfg) + newId, createErr := WafSslConfigServiceApp.CreateInner(cfg) + fatalIf(t, createErr) + if newId != cfg.Id { + t.Fatalf("CreateInner 应返回落库的ID: 返回 %q 期望 %q", newId, cfg.Id) + } var got model.SslConfig firstBy(t, db, &got, "serial_no = ?", serial) if got.AutoLoadPath != 0 { t.Fatalf("CreateInner 后 auto_load_path 应为 0,实际 %d", got.AutoLoadPath) } + // 序列号已存在时不重复落库,必须返回库里那条已有记录的ID: + // 返回本次传入的新ID会让调用方把主机绑到一条不存在的证书夹上 + dup := cfg + dup.BaseOrm = newBase(uuid.GenUUID()) + dupId, dupErr := WafSslConfigServiceApp.CreateInner(dup) + fatalIf(t, dupErr) + if dupId != cfg.Id { + t.Fatalf("重复序列号应返回已有记录ID: 返回 %q 期望 %q", dupId, cfg.Id) + } + if n := countBy(t, db, &model.SslConfig{}, "id = ?", dup.Id); n != 0 { + t.Fatalf("重复序列号不应再落一条记录,实际 %d 条", n) + } }) } diff --git a/service/waf_service/waf_sslconfig.go b/service/waf_service/waf_sslconfig.go index 3552924..177c24b 100644 --- a/service/waf_service/waf_sslconfig.go +++ b/service/waf_service/waf_sslconfig.go @@ -153,12 +153,20 @@ func (receiver *WafSslConfigService) CreateNewIdInner(config model.SslConfig) { global.GWAF_LOCAL_DB.Create(&config) zlog.Info(fmt.Sprintf("%s 原来证书已备份", config.Domains)) } -func (receiver *WafSslConfigService) CreateInner(config model.SslConfig) { + +// CreateInner 新增证书夹条目,返回落库后可供主机绑定的证书夹ID。 +// 序列号已存在时不重复落库,返回的是库里那条已有记录的ID: +// 调用方拿这个ID去绑主机,才不会绑到一个库里并不存在的ID上。 +func (receiver *WafSslConfigService) CreateInner(config model.SslConfig) (string, error) { //检测如果证书编号已经存在不需在进行添加了 - err := global.GWAF_LOCAL_DB.First(&model.SslConfig{}, "serial_no = ?", config.SerialNo).Error - if err == nil && !errors.Is(err, gorm.ErrRecordNotFound) { + var exist model.SslConfig + err := global.GWAF_LOCAL_DB.First(&exist, "serial_no = ?", config.SerialNo).Error + if err == nil { zlog.Info(fmt.Sprintf("%s 证书已经存在不进行再次备份", config.Domains)) - return + return exist.Id, nil + } + if !errors.Is(err, gorm.ErrRecordNotFound) { + return "", err } if config.CertPath == "" { config.CertPath = filepath.Join(utils.GetCurrentDir(), "ssl", config.Id, "domain.crt") @@ -167,8 +175,11 @@ func (receiver *WafSslConfigService) CreateInner(config model.SslConfig) { config.KeyPath = filepath.Join(utils.GetCurrentDir(), "ssl", config.Id, "domain.key") } //必须传指针:按值传给 Create 会导致 GORM 回写字段默认值时对不可寻址反射值 SetInt 而 panic - global.GWAF_LOCAL_DB.Create(&config) + if err = global.GWAF_LOCAL_DB.Create(&config).Error; err != nil { + return "", err + } zlog.Info(fmt.Sprintf("%s 原来证书已备份", config.Domains)) + return config.Id, nil } func (receiver *WafSslConfigService) CheckIsExistApi(serialNo string) error { diff --git a/wafenginecore/sslorder.go b/wafenginecore/sslorder.go index cc1a74e..92b25f1 100644 --- a/wafenginecore/sslorder.go +++ b/wafenginecore/sslorder.go @@ -337,13 +337,22 @@ func (waf *WafEngine) processSSL(updateSSLOrder model.SslOrder, bean model.SslOr zlog.Info(fmt.Sprintf("%s 当前主机未配置证书新增一个证书文件夹", bean.ApplyDomain)) //该证书夹由 SamWaf 自动申请管理,关闭路径自动加载,避免凌晨3点被本地旧文件覆盖 newSslConfig.AutoLoadPath = 0 - //添加到证书夹内 - wafSslConfigService.CreateInner(newSslConfig) - //这里不触发证书导出:新建的证书夹条目导出路径必然为空(用户还没机会配置), - //而且 CreateInner 遇到重复序列号会直接返回不落库,此时按ID导出反而会误报"证书夹不存在"。 + //添加到证书夹内。绑定用的是 CreateInner 返回的ID: + //序列号已存在时它不会重复落库而是返回已有条目的ID,拿本地生成的ID去绑会绑到一条查不到的记录上, + //主机编辑页的证书夹下拉就只剩一串ID显示不出名称 + sslConfigId, createErr := wafSslConfigService.CreateInner(newSslConfig) + if createErr != nil { + zlog.Error(fmt.Sprintf("%s 证书夹保存失败 %v", bean.ApplyDomain, createErr.Error())) + } + //这里不触发证书导出:新建的证书夹条目导出路径必然为空(用户还没机会配置)。 //用户在证书夹页面配好导出路径后,保存那一下会立刻导出,之后每次续期也会导出。 //1.更新主机信息 2.发送主机通知 - err = wafHostService.UpdateSSLInfoAndBindId(string(updateSSLOrder.ResultCertificate), string(updateSSLOrder.ResultPrivateKey), bean.HostCode, newSslConfig.Id) + if sslConfigId == "" { + //证书夹没能落库:证书本身照常生效,但不写绑定ID,避免指向一条不存在的证书夹条目 + err = wafHostService.UpdateSSLInfo(string(updateSSLOrder.ResultCertificate), string(updateSSLOrder.ResultPrivateKey), bean.HostCode) + } else { + err = wafHostService.UpdateSSLInfoAndBindId(string(updateSSLOrder.ResultCertificate), string(updateSSLOrder.ResultPrivateKey), bean.HostCode, sslConfigId) + } if err == nil { hostBean.Keyfile = string(updateSSLOrder.ResultPrivateKey) hostBean.Certfile = string(updateSSLOrder.ResultCertificate)