Skip to content

Commit ac59176

Browse files
authored
Merge pull request #648 from samwafgo/feat_zerossl
feat:add zerossl
2 parents 2bad986 + fd662be commit ac59176

7 files changed

Lines changed: 256 additions & 11 deletions

File tree

api/waf_sslorder.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
package api
22

33
import (
4+
"SamWaf/common/zlog"
45
"SamWaf/enums"
56
"SamWaf/global"
67
"SamWaf/model"
78
"SamWaf/model/common/response"
89
"SamWaf/model/request"
910
"SamWaf/model/spec"
1011
"SamWaf/utils"
12+
"encoding/json"
1113
"errors"
14+
"fmt"
15+
"io/ioutil"
16+
"net/http"
17+
"net/url"
1218
"strings"
19+
"time"
1320

1421
"github.com/gin-gonic/gin"
1522
"gorm.io/gorm"
@@ -43,6 +50,21 @@ func (w *WafSslOrderApi) AddApi(c *gin.Context) {
4350
response.FailWithMessage("IP证书只支持HTTP文件验证方式(http01),不支持DNS验证方式", c)
4451
return
4552
}
53+
if req.ApplyPlatform == "zerossl" {
54+
55+
if global.GCONFIG_ZEROSSL_EAB_KID == "" || global.GCONFIG_ZEROSSL_EAB_HMAC_KEY == "" {
56+
if global.GCONFIG_ZEROSSL_ACCESS_KEY == "" {
57+
response.FailWithMessage("请配置zerossl访问key,在系统配置中 zerossl_access_key 中配置", c)
58+
return
59+
}
60+
// 调用 ZeroSSL API 获取 EAB 凭证
61+
err := w.fetchAndUpdateZeroSSLEABCredentials()
62+
if err != nil {
63+
response.FailWithMessage(fmt.Sprintf("获取ZeroSSL EAB凭证失败: %s", err.Error()), c)
64+
return
65+
}
66+
}
67+
}
4668
addResult, err := wafSslOrderService.AddApi(req)
4769
if err == nil {
4870
w.NotifyWaf(enums.ChanSslOrderSubmitted, addResult)
@@ -171,3 +193,125 @@ func (w *WafSslOrderApi) check80Port(hosts model.Hosts) bool {
171193
}
172194
return false
173195
}
196+
197+
// fetchAndUpdateZeroSSLEABCredentials 调用 ZeroSSL API 获取 EAB 凭证并更新配置
198+
func (w *WafSslOrderApi) fetchAndUpdateZeroSSLEABCredentials() error {
199+
// 构建请求 URL
200+
apiURL := "https://api.zerossl.com/acme/eab-credentials"
201+
u, err := url.Parse(apiURL)
202+
if err != nil {
203+
return fmt.Errorf("解析URL失败: %w", err)
204+
}
205+
206+
// 添加查询参数(GET 请求)
207+
q := u.Query()
208+
q.Set("access_key", global.GCONFIG_ZEROSSL_ACCESS_KEY)
209+
u.RawQuery = q.Encode()
210+
211+
// 创建 HTTP 客户端
212+
client := &http.Client{
213+
Timeout: 30 * time.Second,
214+
Transport: &http.Transport{
215+
TLSHandshakeTimeout: 10 * time.Second,
216+
ResponseHeaderTimeout: 10 * time.Second,
217+
},
218+
}
219+
220+
// 发送 POST 请求(URL 中包含查询参数)
221+
zlog.Info("调用 ZeroSSL API 获取 EAB 凭证", "url", u.String())
222+
req, err := http.NewRequest("POST", u.String(), nil)
223+
if err != nil {
224+
return fmt.Errorf("创建请求失败: %w", err)
225+
}
226+
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
227+
228+
resp, err := client.Do(req)
229+
if err != nil {
230+
return fmt.Errorf("HTTP请求失败: %w", err)
231+
}
232+
defer resp.Body.Close()
233+
234+
// 读取响应体
235+
body, err := ioutil.ReadAll(resp.Body)
236+
if err != nil {
237+
return fmt.Errorf("读取响应失败: %w", err)
238+
}
239+
240+
// 检查 HTTP 状态码
241+
if resp.StatusCode != http.StatusOK {
242+
return fmt.Errorf("API请求失败,状态码: %d, 响应: %s", resp.StatusCode, string(body))
243+
}
244+
245+
// 解析 JSON 响应
246+
var apiResponse struct {
247+
Success bool `json:"success"`
248+
EabKid string `json:"eab_kid"`
249+
EabHmacKey string `json:"eab_hmac_key"`
250+
Error *struct {
251+
Code int `json:"code"`
252+
Type string `json:"type"`
253+
Message string `json:"message"`
254+
} `json:"error"`
255+
}
256+
257+
if err := json.Unmarshal(body, &apiResponse); err != nil {
258+
return fmt.Errorf("解析JSON响应失败: %w, 原始响应: %s", err, string(body))
259+
}
260+
261+
// 检查 API 响应是否成功
262+
if !apiResponse.Success {
263+
errorMsg := string(body)
264+
if apiResponse.Error != nil {
265+
errorMsg = fmt.Sprintf("错误代码: %d, 类型: %s, 消息: %s",
266+
apiResponse.Error.Code, apiResponse.Error.Type, apiResponse.Error.Message)
267+
}
268+
return fmt.Errorf("API返回失败: %s", errorMsg)
269+
}
270+
271+
// 验证返回的凭证是否有效
272+
if apiResponse.EabKid == "" || apiResponse.EabHmacKey == "" {
273+
return fmt.Errorf("API返回的凭证为空, 响应: %s", string(body))
274+
}
275+
276+
// 更新全局变量
277+
global.GCONFIG_ZEROSSL_EAB_KID = apiResponse.EabKid
278+
global.GCONFIG_ZEROSSL_EAB_HMAC_KEY = apiResponse.EabHmacKey
279+
280+
// 更新数据库配置
281+
// 更新 zerossl_eab_kid
282+
eabKidConfig := wafSystemConfigService.GetDetailByItemApi(request.WafSystemConfigDetailByItemReq{Item: "zerossl_eab_kid"})
283+
if eabKidConfig.Id != "" {
284+
err = wafSystemConfigService.ModifyApi(request.WafSystemConfigEditReq{
285+
Id: eabKidConfig.Id,
286+
Item: eabKidConfig.Item,
287+
ItemClass: eabKidConfig.ItemClass,
288+
Value: apiResponse.EabKid,
289+
Remarks: eabKidConfig.Remarks,
290+
ItemType: eabKidConfig.ItemType,
291+
Options: eabKidConfig.Options,
292+
})
293+
if err != nil {
294+
zlog.Warn("更新 zerossl_eab_kid 配置失败", "error", err.Error())
295+
}
296+
}
297+
298+
// 更新 zerossl_eab_hmac_key
299+
eabHmacKeyConfig := wafSystemConfigService.GetDetailByItemApi(request.WafSystemConfigDetailByItemReq{Item: "zerossl_eab_hmac_key"})
300+
if eabHmacKeyConfig.Id != "" {
301+
err = wafSystemConfigService.ModifyApi(request.WafSystemConfigEditReq{
302+
Id: eabHmacKeyConfig.Id,
303+
Item: eabHmacKeyConfig.Item,
304+
ItemClass: eabHmacKeyConfig.ItemClass,
305+
Value: apiResponse.EabHmacKey,
306+
Remarks: eabHmacKeyConfig.Remarks,
307+
ItemType: eabHmacKeyConfig.ItemType,
308+
Options: eabHmacKeyConfig.Options,
309+
})
310+
if err != nil {
311+
zlog.Warn("更新 zerossl_eab_hmac_key 配置失败", "error", err.Error())
312+
}
313+
}
314+
315+
zlog.Info("ZeroSSL EAB 凭证获取并更新成功")
316+
return nil
317+
}

global/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,9 @@ var (
6565

6666
GCONFIG_CHECK_BETA_VERSION int64 = 1 //是否检测beta版本更新 1启用 0禁用(默认检测最新版本)
6767

68+
// ZeroSSL 相关配置
69+
GCONFIG_ZEROSSL_ACCESS_KEY string = "" // zerossl访问key
70+
GCONFIG_ZEROSSL_EAB_KID string = "" // zerossl eab_kid
71+
GCONFIG_ZEROSSL_EAB_HMAC_KEY string = "" // zerossl eab_hmac_key
72+
6873
)

utils/ssl/ssl.go

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func (u *MyUser) GetPrivateKey() crypto.PrivateKey {
4444
return u.key
4545
}
4646

47-
func RegistrationSSL(order model.SslOrder, savePath string, caServerAddress string) (model.SslOrder, error) {
47+
func RegistrationSSL(order model.SslOrder, savePath string, caServerAddress string, applyPlatform string, eab_kid string, eab_hmac_key string) (model.SslOrder, error) {
4848
isIpSSL := utils.IsIP(order.ApplyDomain)
4949
myUser := MyUser{
5050
Email: order.ApplyEmail,
@@ -109,9 +109,24 @@ func RegistrationSSL(order model.SslOrder, savePath string, caServerAddress stri
109109
return order, err
110110
}
111111
// New users will need to register
112-
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
113-
if err != nil {
114-
return order, err
112+
var reg *registration.Resource
113+
if applyPlatform == "zerossl" {
114+
// ZeroSSL 需要使用 EAB (External Account Binding) 方式注册
115+
eabOptions := registration.RegisterEABOptions{
116+
TermsOfServiceAgreed: true,
117+
Kid: eab_kid,
118+
HmacEncoded: eab_hmac_key,
119+
}
120+
reg, err = client.Registration.RegisterWithExternalAccountBinding(eabOptions)
121+
if err != nil {
122+
return order, err
123+
}
124+
} else {
125+
// 其他平台使用原来的注册方式
126+
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
127+
if err != nil {
128+
return order, err
129+
}
115130
}
116131
myUser.Registration = reg
117132

@@ -195,7 +210,7 @@ func RegistrationSSL(order model.SslOrder, savePath string, caServerAddress stri
195210
return order, nil
196211
}
197212

198-
func ReNewSSL(order model.SslOrder, savePath string, caServerAddress string) (model.SslOrder, error) {
213+
func ReNewSSL(order model.SslOrder, savePath string, caServerAddress string, applyPlatform string, eab_kid string, eab_hmac_key string) (model.SslOrder, error) {
199214
// 判断是否是IP证书
200215
isIpSSL := utils.IsIP(order.ApplyDomain)
201216

@@ -262,9 +277,24 @@ func ReNewSSL(order model.SslOrder, savePath string, caServerAddress string) (mo
262277
return order, err
263278
}
264279
// New users will need to register
265-
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
266-
if err != nil {
267-
return order, err
280+
var reg *registration.Resource
281+
if applyPlatform == "zerossl" {
282+
// ZeroSSL 需要使用 EAB (External Account Binding) 方式注册
283+
eabOptions := registration.RegisterEABOptions{
284+
TermsOfServiceAgreed: true,
285+
Kid: eab_kid,
286+
HmacEncoded: eab_hmac_key,
287+
}
288+
reg, err = client.Registration.RegisterWithExternalAccountBinding(eabOptions)
289+
if err != nil {
290+
return order, err
291+
}
292+
} else {
293+
// 其他平台使用原来的注册方式
294+
reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
295+
if err != nil {
296+
return order, err
297+
}
268298
}
269299
myUser.Registration = reg
270300

utils/ssl/ssl_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@ func TestRegistrationSSL(t *testing.T) {
2424
ResultCSR: nil,
2525
Remarks: "",
2626
}
27-
RegistrationSSL(order, "C:\\huawei\\goproject\\SamWaf\\data\\vhost\\ssl#samwaf#com", "https://acme-v02.api.letsencrypt.org/directory")
27+
RegistrationSSL(order, "C:\\huawei\\goproject\\SamWaf\\data\\vhost\\ssl#samwaf#com", "https://acme-v02.api.letsencrypt.org/directory", "letsencrypt", "", "")
2828
}

wafdb/migrations_core.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package wafdb
22

33
import (
4+
"SamWaf/common/uuid"
45
"SamWaf/common/zlog"
6+
"SamWaf/customtype"
57
"SamWaf/global"
68
"SamWaf/model"
9+
"SamWaf/model/baseorm"
710
"fmt"
811
"time"
912

@@ -404,6 +407,49 @@ func RunCoreDBMigrations(db *gorm.DB) error {
404407
return nil
405408
},
406409
},
410+
// 迁移11: 初始化 ZeroSSL CA 服务器记录
411+
{
412+
ID: "202601100001_init_zerossl_ca_server",
413+
Migrate: func(tx *gorm.DB) error {
414+
zlog.Info("迁移 202601100001: 初始化 ZeroSSL CA 服务器记录")
415+
416+
// 检查是否已存在 ZeroSSL 记录(通过名称或地址检查)
417+
var zerosslCount int64
418+
tx.Model(&model.CaServerInfo{}).Where("ca_server_name = ? OR ca_server_address = ?", "ZeroSSL", "https://acme.zerossl.com/v2/DV90").Count(&zerosslCount)
419+
420+
// 如果已存在,跳过创建
421+
if zerosslCount > 0 {
422+
zlog.Info("ZeroSSL CA 服务器记录已存在,跳过创建")
423+
return nil
424+
}
425+
426+
// 创建 ZeroSSL CA 服务器记录
427+
zerosslCA := model.CaServerInfo{
428+
BaseOrm: baseorm.BaseOrm{
429+
Id: uuid.GenUUID(),
430+
USER_CODE: global.GWAF_USER_CODE,
431+
Tenant_ID: global.GWAF_TENANT_ID,
432+
CREATE_TIME: customtype.JsonTime(time.Now()),
433+
UPDATE_TIME: customtype.JsonTime(time.Now()),
434+
},
435+
CaServerName: "zerossl",
436+
CaServerAddress: "https://acme.zerossl.com/v2/DV90",
437+
Remarks: "ZeroSSL",
438+
}
439+
440+
if err := tx.Create(&zerosslCA).Error; err != nil {
441+
return fmt.Errorf("创建 ZeroSSL CA 服务器记录失败: %w", err)
442+
}
443+
444+
zlog.Info("ZeroSSL CA 服务器记录创建成功")
445+
return nil
446+
},
447+
Rollback: func(tx *gorm.DB) error {
448+
zlog.Info("回滚 202601100001: 删除 ZeroSSL CA 服务器记录")
449+
// 只删除我们创建的记录(通过名称和地址匹配)
450+
return tx.Where("ca_server_name = ? AND ca_server_address = ?", "ZeroSSL", "https://acme.zerossl.com/v2/DV90").Delete(&model.CaServerInfo{}).Error
451+
},
452+
},
407453
})
408454

409455
// 执行迁移

wafenginecore/sslorder.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ func (waf *WafEngine) ApplySSLOrder(chanType int, bean model.SslOrder) {
3434
//查询环境变量信息
3535
wafssl.LoadDnsProviderEnvInfo(privateGroupName, privateGroupBelongCloud)
3636
}
37+
eab_kid := ""
38+
eab_hmac_key := ""
39+
if bean.ApplyPlatform == "zerossl" {
40+
eab_kid = global.GCONFIG_ZEROSSL_EAB_KID
41+
eab_hmac_key = global.GCONFIG_ZEROSSL_EAB_HMAC_KEY
42+
}
3743
if chanType == enums.ChanSslOrderSubmitted {
3844
//发起申请
3945
zlog.Info(fmt.Sprintf("%s 正在进行首次证书申请", bean.ApplyDomain))
@@ -42,7 +48,7 @@ func (waf *WafEngine) ApplySSLOrder(chanType int, bean model.SslOrder) {
4248
if filePathErr != nil {
4349
zlog.Error("ApplySSLOrder", filePathErr.Error())
4450
}
45-
updateSSLOrder, err := ssl.RegistrationSSL(bean, filePath, waf_service.GetCAServerAddress(bean.ApplyPlatform))
51+
updateSSLOrder, err := ssl.RegistrationSSL(bean, filePath, waf_service.GetCAServerAddress(bean.ApplyPlatform), bean.ApplyPlatform, eab_kid, eab_hmac_key)
4652
if err == nil {
4753
zlog.Info(fmt.Sprintf("%s 首次证书申请成功", bean.ApplyDomain))
4854

@@ -157,7 +163,7 @@ func (waf *WafEngine) ApplySSLOrder(chanType int, bean model.SslOrder) {
157163
if filePathErr != nil {
158164
zlog.Error("ApplySSLOrder", filePathErr.Error())
159165
}
160-
updateSSLOrder, err := ssl.ReNewSSL(bean, filePath, waf_service.GetCAServerAddress(bean.ApplyPlatform))
166+
updateSSLOrder, err := ssl.ReNewSSL(bean, filePath, waf_service.GetCAServerAddress(bean.ApplyPlatform), bean.ApplyPlatform, eab_kid, eab_hmac_key)
161167
if err == nil {
162168
zlog.Info(fmt.Sprintf("%s 证书续期申请成功", bean.ApplyDomain))
163169

waftask/task_config.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,15 @@ func setConfigStringValue(name string, value string, change int) {
185185
// 重新加载状态码配置
186186
wafipban.GetIPFailureManager().ReloadStatusCodes()
187187
break
188+
case "zerossl_access_key":
189+
global.GCONFIG_ZEROSSL_ACCESS_KEY = value
190+
break
191+
case "zerossl_eab_kid":
192+
global.GCONFIG_ZEROSSL_EAB_KID = value
193+
break
194+
case "zerossl_eab_hmac_key":
195+
global.GCONFIG_ZEROSSL_EAB_HMAC_KEY = value
196+
break
188197
default:
189198
zlog.Warn("Unknown config item:", name)
190199
}
@@ -316,4 +325,9 @@ func TaskLoadSetting(initLoad bool) {
316325

317326
// 版本更新相关配置
318327
updateConfigIntItem(initLoad, "system", "check_beta_version", global.GCONFIG_CHECK_BETA_VERSION, "是否检测beta版本更新(1启用 0禁用)", "options", "0|禁用,1|启用", configMap)
328+
329+
// ZeroSSL 相关配置
330+
updateConfigStringItem(initLoad, "ssl", "zerossl_access_key", global.GCONFIG_ZEROSSL_ACCESS_KEY, "zerossl访问key", "string", "", configMap)
331+
updateConfigStringItem(initLoad, "ssl", "zerossl_eab_kid", global.GCONFIG_ZEROSSL_EAB_KID, "zerossl eab_kid", "string", "", configMap)
332+
updateConfigStringItem(initLoad, "ssl", "zerossl_eab_hmac_key", global.GCONFIG_ZEROSSL_EAB_HMAC_KEY, "zerossl eab_hmac_key", "string", "", configMap)
319333
}

0 commit comments

Comments
 (0)