diff --git a/api/entrance.go b/api/entrance.go index 4ada1e6..b03330c 100644 --- a/api/entrance.go +++ b/api/entrance.go @@ -44,6 +44,7 @@ type APIGroup struct { WafVpConfigApi WafFileApi WafSystemMonitorApi + WafCaServerInfoApi } var APIGroupAPP = new(APIGroup) @@ -102,4 +103,6 @@ var ( wafTunnelService = waf_service.WafTunnelServiceApp wafMonitorService = waf_service.WafSystemMonitorServiceApp + + wafCaServerInfoService = waf_service.WafCaServerInfoServiceApp ) diff --git a/api/waf_caserverinfo_api.go b/api/waf_caserverinfo_api.go new file mode 100644 index 0000000..9d2f7b1 --- /dev/null +++ b/api/waf_caserverinfo_api.go @@ -0,0 +1,95 @@ +package api + +import ( + "SamWaf/model/common/response" + "SamWaf/model/request" + "errors" + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +type WafCaServerInfoApi struct { +} + +func (w *WafCaServerInfoApi) AddApi(c *gin.Context) { + var req request.WafCaServerInfoAddReq + err := c.ShouldBindJSON(&req) + if err == nil { + + cnt := wafCaServerInfoService.CheckIsExistApi(req) + if cnt == 0 { + err = wafCaServerInfoService.AddApi(req) + if err == nil { + response.OkWithMessage("添加成功", c) + } else { + response.FailWithMessage("添加失败", c) + } + return + } else { + response.FailWithMessage("当前记录已经存在", c) + return + } + + } else { + response.FailWithMessage("解析失败", c) + } +} + +func (w *WafCaServerInfoApi) GetDetailApi(c *gin.Context) { + var req request.WafCaServerInfoDetailReq + err := c.ShouldBind(&req) + if err == nil { + bean := wafCaServerInfoService.GetDetailApi(req) + response.OkWithDetailed(bean, "获取成功", c) + } else { + response.FailWithMessage("解析失败", c) + } +} + +func (w *WafCaServerInfoApi) GetListApi(c *gin.Context) { + var req request.WafCaServerInfoSearchReq + err := c.ShouldBindJSON(&req) + if err == nil { + CaServerInfo, total, _ := wafCaServerInfoService.GetListApi(req) + response.OkWithDetailed(response.PageResult{ + List: CaServerInfo, + Total: total, + PageIndex: req.PageIndex, + PageSize: req.PageSize, + }, "获取成功", c) + } else { + response.FailWithMessage("解析失败", c) + } +} + +func (w *WafCaServerInfoApi) DelApi(c *gin.Context) { + var req request.WafCaServerInfoDelReq + err := c.ShouldBind(&req) + if err == nil { + err = wafCaServerInfoService.DelApi(req) + if err != nil && errors.Is(err, gorm.ErrRecordNotFound) { + response.FailWithMessage("请检测参数", c) + } else if err != nil { + response.FailWithMessage("发生错误", c) + } else { + response.OkWithMessage("删除成功", c) + } + } else { + response.FailWithMessage("解析失败", c) + } +} + +func (w *WafCaServerInfoApi) ModifyApi(c *gin.Context) { + var req request.WafCaServerInfoEditReq + err := c.ShouldBindJSON(&req) + if err == nil { + err = wafCaServerInfoService.ModifyApi(req) + if err != nil { + response.FailWithMessage("编辑发生错误"+err.Error(), c) + } else { + response.OkWithMessage("编辑成功", c) + } + } else { + response.FailWithMessage("解析失败", c) + } +} diff --git a/codetemplete/code_gen_test.go b/codetemplete/code_gen_test.go index c14e7c8..c603e8c 100644 --- a/codetemplete/code_gen_test.go +++ b/codetemplete/code_gen_test.go @@ -9,8 +9,7 @@ import ( func TestCodeGeneration(t *testing.T) { // 唯一校验定义字段信息的字符串 fieldDefs := []string{ - "PrivateGroupName:private_group_name:string", - "PrivateGroupBelongCloud:private_group_belong_cloud:string", + "CaServerName:ca_server_name:string", } // 构造 `uniFields` 列表 @@ -28,6 +27,6 @@ func TestCodeGeneration(t *testing.T) { } } - fields := GetStructFields(model.PrivateGroup{}) - CodeGeneration("PrivateGroup", fields, uniFields) + fields := GetStructFields(model.CaServerInfo{}) + CodeGeneration("CaServerInfo", fields, uniFields) } diff --git a/model/ca_server_info.go b/model/ca_server_info.go new file mode 100644 index 0000000..cb38aa8 --- /dev/null +++ b/model/ca_server_info.go @@ -0,0 +1,10 @@ +package model + +import "SamWaf/model/baseorm" + +type CaServerInfo struct { + baseorm.BaseOrm + CaServerName string `json:"ca_server_name"` //CA服务器名称 + CaServerAddress string `json:"ca_server_address"` //CA服务器地址 + Remarks string `json:"remarks"` //备注 +} diff --git a/model/request/waf_caserverinfo_req.go b/model/request/waf_caserverinfo_req.go new file mode 100644 index 0000000..da799b8 --- /dev/null +++ b/model/request/waf_caserverinfo_req.go @@ -0,0 +1,25 @@ +package request + +import "SamWaf/model/common/request" + +type WafCaServerInfoAddReq struct { + CaServerName string `json:"ca_server_name" form:"ca_server_name"` + CaServerAddress string `json:"ca_server_address" form:"ca_server_address"` + Remarks string `json:"remarks" form:"remarks"` +} +type WafCaServerInfoEditReq struct { + Id string `json:"id"` + + CaServerName string `json:"ca_server_name" form:"ca_server_name"` + CaServerAddress string `json:"ca_server_address" form:"ca_server_address"` + Remarks string `json:"remarks" form:"remarks"` +} +type WafCaServerInfoDetailReq struct { + Id string `json:"id" form:"id"` +} +type WafCaServerInfoDelReq struct { + Id string `json:"id" form:"id"` +} +type WafCaServerInfoSearchReq struct { + request.PageInfo +} diff --git a/router/entrance.go b/router/entrance.go index bc8f49c..3073d82 100644 --- a/router/entrance.go +++ b/router/entrance.go @@ -42,6 +42,7 @@ type ApiGroup struct { WafVpConfigRouter WafFileRouter WafSystemMonitorRouter + WafCaServerInfoRouter } type PublicApiGroup struct { LoginRouter diff --git a/router/waf_caserverinfo_router.go b/router/waf_caserverinfo_router.go new file mode 100644 index 0000000..c62f357 --- /dev/null +++ b/router/waf_caserverinfo_router.go @@ -0,0 +1,19 @@ +package router + +import ( + "SamWaf/api" + "github.com/gin-gonic/gin" +) + +type WafCaServerInfoRouter struct { +} + +func (receiver *WafCaServerInfoRouter) InitWafCaServerInfoRouter(group *gin.RouterGroup) { + api := api.APIGroupAPP.WafCaServerInfoApi + router := group.Group("") + router.POST("/samwaf/wafhost/caserverinfo/add", api.AddApi) + router.POST("/samwaf/wafhost/caserverinfo/list", api.GetListApi) + router.GET("/samwaf/wafhost/caserverinfo/detail", api.GetDetailApi) + router.POST("/samwaf/wafhost/caserverinfo/edit", api.ModifyApi) + router.GET("/samwaf/wafhost/caserverinfo/del", api.DelApi) +} diff --git a/service/waf_service/waf_caserverinfo_service.go b/service/waf_service/waf_caserverinfo_service.go new file mode 100644 index 0000000..5f324ff --- /dev/null +++ b/service/waf_service/waf_caserverinfo_service.go @@ -0,0 +1,133 @@ +package waf_service + +import ( + "SamWaf/common/uuid" + "SamWaf/customtype" + "SamWaf/global" + "SamWaf/model" + "SamWaf/model/baseorm" + "SamWaf/model/request" + "errors" + "time" +) + +type WafCaServerInfoService struct{} + +var WafCaServerInfoServiceApp = new(WafCaServerInfoService) + +func (receiver *WafCaServerInfoService) AddApi(req request.WafCaServerInfoAddReq) error { + var bean = &model.CaServerInfo{ + BaseOrm: baseorm.BaseOrm{ + Id: uuid.GenUUID(), + USER_CODE: global.GWAF_USER_CODE, + Tenant_ID: global.GWAF_TENANT_ID, + CREATE_TIME: customtype.JsonTime(time.Now()), + UPDATE_TIME: customtype.JsonTime(time.Now()), + }, + + CaServerName: req.CaServerName, + CaServerAddress: req.CaServerAddress, + Remarks: req.Remarks, + } + global.GWAF_LOCAL_DB.Create(bean) + return nil +} + +func (receiver *WafCaServerInfoService) CheckIsExistApi(req request.WafCaServerInfoAddReq) int { + var total int64 = 0 + /*where条件*/ + var whereField = "" + var whereValues []interface{} + //where字段 + whereField = "" + + if len(req.CaServerName) > 0 { + if len(whereField) > 0 { + whereField = whereField + " and " + } + whereField = whereField + " ca_server_name=? " + } + + //where字段赋值 + + if len(req.CaServerName) > 0 { + if len(whereField) > 0 { + whereValues = append(whereValues, req.CaServerName) + } + } + + global.GWAF_LOCAL_DB.Model(&model.CaServerInfo{}).Where(whereField, whereValues...).Count(&total) + return int(total) +} + +func (receiver *WafCaServerInfoService) ModifyApi(req request.WafCaServerInfoEditReq) error { + // 根据唯一字段生成查询条件(只有在UniFields不为空时才进行存在性检查) + + var total int64 = 0 + /*where条件*/ + var whereField = "" + var whereValues []interface{} + //where字段 + whereField = "" + + if len(req.CaServerName) > 0 { + if len(whereField) > 0 { + whereField = whereField + " and " + } + whereField = whereField + " ca_server_name=? " + } + + //where字段赋值 + + if len(req.CaServerName) > 0 { + whereValues = append(whereValues, req.CaServerName) + } + + global.GWAF_LOCAL_DB.Model(&model.CaServerInfo{}).Where(whereField, whereValues...).Count(&total) + // 查询是否已存在记录 + var bean model.CaServerInfo + global.GWAF_LOCAL_DB.Model(&model.CaServerInfo{}).Where(whereField, whereValues...).Limit(1).Find(&bean) + + if int(total) > 0 && bean.Id != "" && bean.Id != req.Id { + return errors.New("当前记录已经存在") + } + + beanMap := map[string]interface{}{ + + "CaServerName": req.CaServerName, + "CaServerAddress": req.CaServerAddress, + "Remarks": req.Remarks, + + "UPDATE_TIME": customtype.JsonTime(time.Now()), + } + err := global.GWAF_LOCAL_DB.Model(model.CaServerInfo{}).Where("id = ?", req.Id).Updates(beanMap).Error + + return err +} +func (receiver *WafCaServerInfoService) GetDetailApi(req request.WafCaServerInfoDetailReq) model.CaServerInfo { + var bean model.CaServerInfo + global.GWAF_LOCAL_DB.Where("id=?", req.Id).Find(&bean) + return bean +} +func (receiver *WafCaServerInfoService) GetDetailByIdApi(id string) model.CaServerInfo { + var bean model.CaServerInfo + global.GWAF_LOCAL_DB.Where("id=?", id).Find(&bean) + return bean +} +func (receiver *WafCaServerInfoService) GetListApi(req request.WafCaServerInfoSearchReq) ([]model.CaServerInfo, int64, error) { + var list []model.CaServerInfo + var total int64 = 0 + global.GWAF_LOCAL_DB.Model(&model.CaServerInfo{}).Limit(req.PageSize).Offset(req.PageSize * (req.PageIndex - 1)).Find(&list) + global.GWAF_LOCAL_DB.Model(&model.CaServerInfo{}).Count(&total) + + return list, total, nil +} +func (receiver *WafCaServerInfoService) DelApi(req request.WafCaServerInfoDelReq) error { + var bean model.CaServerInfo + err := global.GWAF_LOCAL_DB.Where("id = ?", req.Id).First(&bean).Error + if err != nil { + return err + } + err = global.GWAF_LOCAL_DB.Where("id = ?", req.Id).Delete(model.CaServerInfo{}).Error + return err +} diff --git a/service/waf_service/waf_sslorder.go b/service/waf_service/waf_sslorder.go index 3bfb811..c0c4445 100644 --- a/service/waf_service/waf_sslorder.go +++ b/service/waf_service/waf_sslorder.go @@ -171,3 +171,27 @@ func (receiver *WafSSLOrderService) GetLastedInfo(hostCode string) (model.SslOrd return bean, nil } + +// GetCAServerAddress 根据CA服务器名称获取地址 +func GetCAServerAddress(caServerName string) string { + // 如果没有指定CA服务器名称,默认使用letsencrypt + if caServerName == "" { + caServerName = "letsencrypt" + } + + // 从数据库查询CA服务器信息 + var caServer model.CaServerInfo + err := global.GWAF_LOCAL_DB.Where("ca_server_name = ?", caServerName).First(&caServer).Error + + // 如果查询失败或没有找到记录,返回默认的letsencrypt地址 + if err != nil { + return "https://acme-v02.api.letsencrypt.org/directory" + } + + // 如果找到记录但地址为空,也返回默认地址 + if caServer.CaServerAddress == "" { + return "https://acme-v02.api.letsencrypt.org/directory" + } + + return caServer.CaServerAddress +} diff --git a/utils/ssl/ssl.go b/utils/ssl/ssl.go index 42fcc28..02b7ad7 100644 --- a/utils/ssl/ssl.go +++ b/utils/ssl/ssl.go @@ -38,7 +38,7 @@ func (u *MyUser) GetPrivateKey() crypto.PrivateKey { return u.key } -func RegistrationSSL(order model.SslOrder, savePath string) (model.SslOrder, error) { +func RegistrationSSL(order model.SslOrder, savePath string, caServerAddress string) (model.SslOrder, error) { myUser := MyUser{ Email: order.ApplyEmail, } @@ -67,8 +67,7 @@ func RegistrationSSL(order model.SslOrder, savePath string) (model.SslOrder, err //order.ApplyKey = privateKey config := lego.NewConfig(&myUser) - // This CA URL is configured for a local dev instance of Boulder running in Docker in a VM. - config.CADirURL = lego.LEDirectoryProduction // 测试用 LEDirectoryStaging 正式用 LEDirectoryProduction + config.CADirURL = caServerAddress config.Certificate.KeyType = certcrypto.RSA2048 // A client facilitates communication with the CA server. @@ -137,7 +136,7 @@ func RegistrationSSL(order model.SslOrder, savePath string) (model.SslOrder, err return order, nil } -func ReNewSSL(order model.SslOrder, savePath string) (model.SslOrder, error) { +func ReNewSSL(order model.SslOrder, savePath string, caServerAddress string) (model.SslOrder, error) { myUser := MyUser{ Email: order.ApplyEmail, } @@ -165,9 +164,7 @@ func ReNewSSL(order model.SslOrder, savePath string) (model.SslOrder, error) { //order.ApplyKey = privateKey config := lego.NewConfig(&myUser) - - // This CA URL is configured for a local dev instance of Boulder running in Docker in a VM. - config.CADirURL = lego.LEDirectoryProduction // 测试用 LEDirectoryStaging 正式用 LEDirectoryProduction + config.CADirURL = caServerAddress config.Certificate.KeyType = certcrypto.RSA2048 // A client facilitates communication with the CA server. diff --git a/utils/ssl/ssl_test.go b/utils/ssl/ssl_test.go index 88916cb..5b2ef0f 100644 --- a/utils/ssl/ssl_test.go +++ b/utils/ssl/ssl_test.go @@ -24,5 +24,5 @@ func TestRegistrationSSL(t *testing.T) { ResultCSR: nil, Remarks: "", } - RegistrationSSL(order, "C:\\huawei\\goproject\\SamWaf\\data\\vhost\\ssl#samwaf#com") + RegistrationSSL(order, "C:\\huawei\\goproject\\SamWaf\\data\\vhost\\ssl#samwaf#com", "https://acme-v02.api.letsencrypt.org/directory") } diff --git a/wafdb/localdb.go b/wafdb/localdb.go index 8f651ad..3e5a588 100644 --- a/wafdb/localdb.go +++ b/wafdb/localdb.go @@ -158,6 +158,9 @@ func InitCoreDb(currentDir string) (bool, error) { //隧道 db.AutoMigrate(&model.Tunnel{}) + //CA服务器信息 + db.AutoMigrate(&model.CaServerInfo{}) + global.GWAF_LOCAL_DB.Callback().Query().Before("gorm:query").Register("tenant_plugin:before_query", before_query) global.GWAF_LOCAL_DB.Callback().Query().Before("gorm:update").Register("tenant_plugin:before_update", before_update) diff --git a/wafdb/patch_sql.go b/wafdb/patch_sql.go index 21c617b..f290103 100644 --- a/wafdb/patch_sql.go +++ b/wafdb/patch_sql.go @@ -203,6 +203,32 @@ func pathCoreSql(db *gorm.DB) { } else { zlog.Info("db", "hosts :static_site_json sensitive_paths update successfully") } + //20250827 初始化 letsencrypt CA 服务器记录 + var letsencryptCount int64 + db.Model(&model.CaServerInfo{}).Where("ca_server_name = ?", "letsencrypt").Count(&letsencryptCount) + + // 如果不存在 letsencrypt 记录,则创建 + if letsencryptCount == 0 { + letsencryptCA := model.CaServerInfo{ + BaseOrm: baseorm.BaseOrm{ + Id: uuid.GenUUID(), + USER_CODE: global.GWAF_USER_CODE, + Tenant_ID: global.GWAF_TENANT_ID, + CREATE_TIME: customtype.JsonTime(time.Now()), + UPDATE_TIME: customtype.JsonTime(time.Now()), + }, + CaServerName: "letsencrypt", + CaServerAddress: "https://acme-v02.api.letsencrypt.org/directory", + Remarks: "Let's Encrypt", + } + + err := db.Create(&letsencryptCA).Error + if err != nil { + zlog.Error("db", "init letsencrypt CA server fail", "error", err.Error()) + } else { + zlog.Info("db", "init letsencrypt CA server success") + } + } // 记录结束时间并计算耗时 duration := time.Since(startTime) zlog.Info("create core default value completely", "duration", duration.String()) diff --git a/wafenginecore/sslorder.go b/wafenginecore/sslorder.go index 044010d..b463087 100644 --- a/wafenginecore/sslorder.go +++ b/wafenginecore/sslorder.go @@ -41,7 +41,7 @@ func (waf *WafEngine) ApplySSLOrder(chanType int, bean model.SslOrder) { if filePathErr != nil { zlog.Error("ApplySSLOrder", filePathErr.Error()) } - updateSSLOrder, err := ssl.RegistrationSSL(bean, filePath) + updateSSLOrder, err := ssl.RegistrationSSL(bean, filePath, waf_service.GetCAServerAddress(bean.ApplyPlatform)) if err == nil { zlog.Info(fmt.Sprintf("%s 首次证书申请成功", bean.ApplyDomain)) @@ -75,7 +75,7 @@ func (waf *WafEngine) ApplySSLOrder(chanType int, bean model.SslOrder) { if filePathErr != nil { zlog.Error("ApplySSLOrder", filePathErr.Error()) } - updateSSLOrder, err := ssl.ReNewSSL(bean, filePath) + updateSSLOrder, err := ssl.ReNewSSL(bean, filePath, waf_service.GetCAServerAddress(bean.ApplyPlatform)) if err == nil { zlog.Info(fmt.Sprintf("%s 证书续期申请成功", bean.ApplyDomain)) diff --git a/wafmangeweb/localserver.go b/wafmangeweb/localserver.go index bc3adff..2c629c1 100644 --- a/wafmangeweb/localserver.go +++ b/wafmangeweb/localserver.go @@ -76,6 +76,7 @@ func (web *WafWebManager) initRouter(r *gin.Engine) { router.ApiGroupApp.InitWafVpConfigRouter(RouterGroup) router.ApiGroupApp.InitWafFileRouter(RouterGroup) router.ApiGroupApp.InitWafSystemMonitorRouter(RouterGroup) + router.ApiGroupApp.InitWafCaServerInfoRouter(RouterGroup) } if global.GWAF_RELEASE == "true" {