wafformatter.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. package service
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "crypto/x509"
  6. "encoding/json"
  7. "fmt"
  8. v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
  9. "github.com/go-nunu/nunu-layout-advanced/internal/model"
  10. "github.com/go-nunu/nunu-layout-advanced/internal/repository"
  11. "github.com/go-nunu/nunu-layout-advanced/pkg/rabbitmq"
  12. amqp "github.com/rabbitmq/amqp091-go"
  13. "go.uber.org/zap"
  14. "golang.org/x/net/idna"
  15. "golang.org/x/net/publicsuffix"
  16. "golang.org/x/sync/errgroup"
  17. "net"
  18. "slices"
  19. "strconv"
  20. "strings"
  21. )
  22. type WafFormatterService interface {
  23. Require(ctx context.Context, req v1.GlobalRequire) (RequireResponse, error)
  24. validateWafPortCount(ctx context.Context, hostId int) error
  25. validateWafDomainCount(ctx context.Context, req v1.GlobalRequire) error
  26. ConvertToWildcardDomain(ctx context.Context, domain string) (string, error)
  27. AppendWafIp(ctx context.Context, req []string, returnSourceIp string) ([]v1.IpInfo, error)
  28. WashIps(ctx context.Context, req []string) ([]string, error)
  29. PublishIpWhitelistTask(ips []string, action string, returnSourceIp string, color string)
  30. PublishDomainWhitelistTask(domain, ip, action string)
  31. findIpDifferences(oldIps, newIps []string) ([]string, []string)
  32. WashDeleteWafIp(ctx context.Context, backendList []string) ([]string, error)
  33. WashEditWafIp(ctx context.Context, newBackendList []string, oldBackendList []string) ([]string, []string, error)
  34. //cdn添加网站
  35. AddOrigin(ctx context.Context, req v1.WebJson) (int64, error)
  36. // 获取ip数量等于1的源站过白ip
  37. WashDelIps(ctx context.Context, ips []string) ([]string, error)
  38. // 判断域名是否是IDN,如果是,转换为 Punycode
  39. ConvertToPunycodeIfIDN(ctx context.Context, domain string) (isIDN bool, punycodeDomain string, err error)
  40. // 解析证书
  41. ParseCert(ctx context.Context, httpsCert string, httpKey string) (serverName string, commonName []string, DNSNames []string, before int64, after int64, isSelfSigned bool, err error)
  42. AddSSLPolicy(ctx context.Context, req v1.SSL) (sslPolicyId int64, sslCertId int64, err error)
  43. EditSSL(ctx context.Context, req v1.SSL) error
  44. // 验证端口重复
  45. VerifyPort(ctx context.Context,protocol string, port string,hostId int64,domain string) error
  46. }
  47. func NewWafFormatterService(
  48. service *Service,
  49. globalRep repository.GlobalLimitRepository,
  50. hostRep repository.HostRepository,
  51. required RequiredService,
  52. parser ParserService,
  53. tcpforwardingRep repository.TcpforwardingRepository,
  54. udpForWardingRep repository.UdpForWardingRepository,
  55. webForwardingRep repository.WebForwardingRepository,
  56. mq *rabbitmq.RabbitMQ,
  57. host HostService,
  58. gatewayGroupRep repository.GatewayGroupRepository,
  59. gatewayGroupIpRep repository.GateWayGroupIpRepository,
  60. cdn CdnService,
  61. ) WafFormatterService {
  62. return &wafFormatterService{
  63. Service: service,
  64. globalRep: globalRep,
  65. hostRep: hostRep,
  66. required: required,
  67. parser: parser,
  68. tcpforwardingRep: tcpforwardingRep,
  69. udpForWardingRep: udpForWardingRep,
  70. webForwardingRep: webForwardingRep,
  71. host: host,
  72. mq: mq,
  73. gatewayGroupRep: gatewayGroupRep,
  74. gatewayGroupIpRep: gatewayGroupIpRep,
  75. cdn: cdn,
  76. }
  77. }
  78. type wafFormatterService struct {
  79. *Service
  80. globalRep repository.GlobalLimitRepository
  81. hostRep repository.HostRepository
  82. required RequiredService
  83. parser ParserService
  84. tcpforwardingRep repository.TcpforwardingRepository
  85. udpForWardingRep repository.UdpForWardingRepository
  86. webForwardingRep repository.WebForwardingRepository
  87. host HostService
  88. mq *rabbitmq.RabbitMQ
  89. gatewayGroupRep repository.GatewayGroupRepository
  90. gatewayGroupIpRep repository.GateWayGroupIpRepository
  91. cdn CdnService
  92. }
  93. type RequireResponse struct {
  94. model.GlobalLimit `json:"globalLimit" form:"globalLimit"`
  95. GatewayIps []string `json:"ips" form:"ips"`
  96. Tag string `json:"tag" form:"tag"`
  97. SslPolicyId int64 `json:"sslPolicyId" form:"sslPolicyId"`
  98. }
  99. func (s *wafFormatterService) Require(ctx context.Context, req v1.GlobalRequire) (RequireResponse, error) {
  100. var res RequireResponse
  101. // 获取全局配置信息
  102. globalLimit, err := s.globalRep.GetGlobalLimitByHostId(ctx, int64(req.HostId))
  103. if err != nil {
  104. return RequireResponse{}, err
  105. }
  106. if globalLimit != nil {
  107. res.GlobalLimit = *globalLimit
  108. }
  109. // 获取主机名
  110. domain, err := s.hostRep.GetDomainById(ctx, req.HostId)
  111. if err != nil {
  112. return RequireResponse{}, err
  113. }
  114. res.Tag = strconv.Itoa(req.Uid) + "_" + strconv.Itoa(req.HostId) + "_" + domain + "_" + req.Comment
  115. res.GatewayIps, err = s.gatewayGroupIpRep.GetGateWayGroupAllIpByGatewayGroupId(ctx, res.GatewayGroupId)
  116. if err != nil {
  117. return RequireResponse{}, err
  118. }
  119. return res, nil
  120. }
  121. func (s *wafFormatterService) validateWafPortCount(ctx context.Context, hostId int) error {
  122. congfig, err := s.host.GetGlobalLimitConfig(ctx, hostId)
  123. if err != nil {
  124. return err
  125. }
  126. tcpCount, err := s.tcpforwardingRep.GetTcpForwardingPortCountByHostId(ctx, hostId)
  127. if err != nil {
  128. return err
  129. }
  130. udpCount, err := s.udpForWardingRep.GetUdpForwardingPortCountByHostId(ctx, hostId)
  131. if err != nil {
  132. return err
  133. }
  134. webCount, err := s.webForwardingRep.GetWebForwardingPortCountByHostId(ctx, hostId)
  135. if err != nil {
  136. return err
  137. }
  138. if int64(congfig.PortCount) > tcpCount+udpCount+webCount {
  139. return nil
  140. }
  141. return fmt.Errorf("端口数量超出套餐限制,已配置%d个端口,套餐限制为%d个端口", tcpCount+udpCount+webCount, congfig.PortCount)
  142. }
  143. func (s *wafFormatterService) validateWafDomainCount(ctx context.Context, req v1.GlobalRequire) error {
  144. congfig, err := s.host.GetGlobalLimitConfig(ctx, req.HostId)
  145. if err != nil {
  146. return err
  147. }
  148. domainCount, domainSlice, err := s.webForwardingRep.GetWebForwardingDomainCountByHostId(ctx, req.HostId)
  149. if err != nil {
  150. return err
  151. }
  152. if req.Domain != "" {
  153. if !slices.Contains(domainSlice, req.Domain) {
  154. domainCount += 1
  155. if domainCount > int64(congfig.DomainCount) {
  156. return fmt.Errorf("域名数量已达到上限,已配置%d个域名,套餐限制为%d个域名", domainCount, congfig.DomainCount)
  157. }
  158. }
  159. }
  160. return nil
  161. }
  162. func (s *wafFormatterService) ConvertToWildcardDomain(ctx context.Context, domain string) (string, error) {
  163. // 1. 使用 EffectiveTLDPlusOne 获取可注册域名部分。
  164. // 例如,对于 "www.google.com",这将返回 "google.com"。
  165. // 对于 "a.b.c.tokyo.jp",这将返回 "c.tokyo.jp"。
  166. if domain == "" {
  167. return "", nil
  168. }
  169. registrableDomain, err := publicsuffix.EffectiveTLDPlusOne(domain)
  170. if err != nil {
  171. s.logger.Error("无效的域名", zap.String("domain", domain), zap.Error(err))
  172. // 如果域名无效(如 IP 地址、localhost),则返回错误。
  173. return "", nil
  174. }
  175. // 2. 比较原始域名和可注册域名。
  176. // 如果它们不相等,说明原始域名包含子域名。
  177. if domain != registrableDomain {
  178. // 3. 如果存在子域名,则用 "*." 加上可注册域名来构造通配符域名。
  179. return registrableDomain, nil
  180. }
  181. // 4. 如果原始域名和可注册域名相同(例如,输入就是 "google.com"),
  182. // 则说明没有子域名可替换,直接返回原始域名。
  183. return domain, nil
  184. }
  185. func (s *wafFormatterService) AppendWafIp(ctx context.Context, req []string, returnSourceIp string) ([]v1.IpInfo, error) {
  186. var ips []v1.IpInfo
  187. for _, v := range req {
  188. ips = append(ips, v1.IpInfo{
  189. FType: "0",
  190. FStartIp: v,
  191. FEndIp: v,
  192. FRemark: "宁波高防IP过白",
  193. FServerIp: returnSourceIp,
  194. })
  195. }
  196. return ips, nil
  197. }
  198. func (s *wafFormatterService) AppendWafIpByRemovePort(ctx context.Context, req []string) ([]v1.IpInfo, error) {
  199. var ips []v1.IpInfo
  200. for _, v := range req {
  201. ip, _, err := net.SplitHostPort(v)
  202. if err != nil {
  203. return nil, err
  204. }
  205. ips = append(ips, v1.IpInfo{
  206. FType: "0",
  207. FStartIp: ip,
  208. FEndIp: ip,
  209. FRemark: "宁波高防IP过白",
  210. FServerIp: "",
  211. })
  212. }
  213. return ips, nil
  214. }
  215. func (s *wafFormatterService) WashIps(ctx context.Context, req []string) ([]string, error) {
  216. var res []string
  217. for _, v := range req {
  218. res = append(res, v)
  219. }
  220. return res, nil
  221. }
  222. // publishDomainWhitelistTask is a helper function to publish domain whitelist tasks to RabbitMQ.
  223. // It can handle different actions like "add" or "del".
  224. func (s *wafFormatterService) PublishDomainWhitelistTask(domain, ip, action string) {
  225. // Define message payload, including the action
  226. type domainTaskPayload struct {
  227. Domain string `json:"domain"`
  228. Ip string `json:"ip"`
  229. Action string `json:"action"`
  230. }
  231. payload := domainTaskPayload{
  232. Domain: domain,
  233. Ip: ip,
  234. Action: action,
  235. }
  236. // Serialize the message
  237. msgBody, err := json.Marshal(payload)
  238. if err != nil {
  239. s.logger.Error("Failed to serialize domain whitelist task message", zap.Error(err), zap.String("domain", domain), zap.String("ip", ip), zap.String("action", action))
  240. return
  241. }
  242. // Get task configuration
  243. taskCfg, ok := s.mq.GetTaskConfig("domain_whitelist")
  244. if !ok {
  245. s.logger.Error("Failed to get 'domain_whitelist' task configuration")
  246. return
  247. }
  248. // Construct the routing key dynamically based on the action
  249. routingKey := fmt.Sprintf("whitelist.domain.%s", action)
  250. // Construct the amqp.Publishing message
  251. publishingMsg := amqp.Publishing{
  252. ContentType: "application/json",
  253. Body: msgBody,
  254. DeliveryMode: amqp.Persistent, // Persistent message
  255. }
  256. // Publish the message
  257. err = s.mq.PublishWithCh(taskCfg.Exchange, routingKey, publishingMsg)
  258. if err != nil {
  259. s.logger.Error("发布 域名 白名单任务到 MQ 失败", zap.Error(err), zap.String("domain", domain), zap.String("action", action))
  260. } else {
  261. s.logger.Info("成功将 域名 白名单任务发布到 MQ", zap.String("domain", domain), zap.String("action", action))
  262. }
  263. }
  264. func (s *wafFormatterService) PublishIpWhitelistTask(ips []string, action string, returnSourceIp string, color string) {
  265. // Define message payload, including the action
  266. type ipTaskPayload struct {
  267. Ips []string `json:"ips"`
  268. Action string `json:"action"`
  269. ReturnSourceIp string `json:"return_source_ip"`
  270. Color string `json:"color"`
  271. }
  272. payload := ipTaskPayload{
  273. Ips: ips,
  274. Action: action,
  275. ReturnSourceIp: returnSourceIp,
  276. Color: color,
  277. }
  278. // Serialize the message
  279. msgBody, err := json.Marshal(payload)
  280. if err != nil {
  281. s.logger.Error("序列化 IP 白名单任务消息失败", zap.Error(err), zap.Any("IPs", ips), zap.String("action", action), zap.String("color", color))
  282. return
  283. }
  284. // Get task configuration
  285. taskCfg, ok := s.mq.GetTaskConfig("ip_white")
  286. if !ok {
  287. s.logger.Error("无法获取“ip_white”任务配置")
  288. return
  289. }
  290. // Construct the routing key dynamically based on the action
  291. routingKey := fmt.Sprintf("task.ip_white.%s", action)
  292. // Construct the amqp.Publishing message
  293. publishingMsg := amqp.Publishing{
  294. ContentType: "application/json",
  295. Body: msgBody,
  296. DeliveryMode: amqp.Persistent, // Persistent message
  297. }
  298. // Publish the message
  299. err = s.mq.PublishWithCh(taskCfg.Exchange, routingKey, publishingMsg)
  300. if err != nil {
  301. s.logger.Error("发布 IP 白名单任务到 MQ 失败", zap.Error(err), zap.String("action", action), zap.String("color", color))
  302. } else {
  303. s.logger.Info("成功将 IP 白名单任务发布到 MQ", zap.String("action", action), zap.String("color", color))
  304. }
  305. }
  306. func (s *wafFormatterService) findIpDifferences(oldIps, newIps []string) ([]string, []string) {
  307. // 使用 map 实现 set,用于快速查找
  308. oldIpsSet := make(map[string]struct{}, len(oldIps))
  309. for _, ip := range oldIps {
  310. oldIpsSet[ip] = struct{}{}
  311. }
  312. newIpsSet := make(map[string]struct{}, len(newIps))
  313. for _, ip := range newIps {
  314. newIpsSet[ip] = struct{}{}
  315. }
  316. var addedIps []string
  317. // 查找新增的 IP:存在于 newIpsSet 但不存在于 oldIpsSet
  318. for ip := range newIpsSet {
  319. if _, found := oldIpsSet[ip]; !found {
  320. addedIps = append(addedIps, ip)
  321. }
  322. }
  323. var removedIps []string
  324. // 查找移除的 IP:存在于 oldIpsSet 但不存在于 newIpsSet
  325. for ip := range oldIpsSet {
  326. if _, found := newIpsSet[ip]; !found {
  327. removedIps = append(removedIps, ip)
  328. }
  329. }
  330. return addedIps, removedIps
  331. }
  332. func (s *wafFormatterService) WashDeleteWafIp(ctx context.Context, backendList []string) ([]string, error) {
  333. var res []string
  334. for _, v := range backendList {
  335. ip, _, err := net.SplitHostPort(v)
  336. if err != nil {
  337. return nil, err
  338. }
  339. res = append(res, ip)
  340. }
  341. return res, nil
  342. }
  343. func (s *wafFormatterService) WashEditWafIp(ctx context.Context, newBackendList []string, oldBackendList []string) ([]string, []string, error) {
  344. var oldIps []string
  345. var newIps []string
  346. for _, v := range oldBackendList {
  347. ip, _, err := net.SplitHostPort(v)
  348. if err != nil {
  349. return nil, nil, err
  350. }
  351. oldIps = append(oldIps, ip)
  352. }
  353. if newBackendList != nil {
  354. for _, v := range newBackendList {
  355. ip, _, err := net.SplitHostPort(v)
  356. if err != nil {
  357. return nil, nil, err
  358. }
  359. newIps = append(newIps, ip)
  360. }
  361. }
  362. addedIps, removedIps := s.findIpDifferences(oldIps, newIps)
  363. return addedIps, removedIps, nil
  364. }
  365. func (s *wafFormatterService) AddOrigin(ctx context.Context, req v1.WebJson) (int64, error) {
  366. ip, port, err := net.SplitHostPort(req.BackendList)
  367. if err != nil {
  368. return 0, fmt.Errorf("无效的后端地址: %s", err)
  369. }
  370. addr := v1.Addr{
  371. Protocol: req.ApiType,
  372. Host: ip,
  373. Port: port,
  374. }
  375. id, err := s.cdn.CreateOrigin(ctx, v1.Origin{
  376. Addr: addr,
  377. Weight: 10,
  378. Description: req.Comment,
  379. Host: req.Host,
  380. IsOn: true,
  381. TlsSecurityVerifyMode: "auto",
  382. })
  383. if err != nil {
  384. return 0, err
  385. }
  386. return id, nil
  387. }
  388. // 获取ip数量等于1的源站过白ip
  389. func (s *wafFormatterService) WashDelIps(ctx context.Context, ips []string) ([]string, error) {
  390. var udpIpCounts, tcpIpCounts, webIpCounts []v1.IpCountResult
  391. g, gCtx := errgroup.WithContext(ctx)
  392. // 1. 查询 IP 的数量
  393. g.Go(func() error {
  394. var err error
  395. udpIpCounts, err = s.udpForWardingRep.GetIpCountByIp(gCtx, ips)
  396. if err != nil {
  397. return fmt.Errorf("in udp repository: %w", err)
  398. }
  399. return nil
  400. })
  401. g.Go(func() error {
  402. var err error
  403. tcpIpCounts, err = s.tcpforwardingRep.GetIpCountByIp(gCtx, ips)
  404. if err != nil {
  405. return fmt.Errorf("in tcp repository: %w", err)
  406. }
  407. return nil
  408. })
  409. g.Go(func() error {
  410. var err error
  411. webIpCounts, err = s.webForwardingRep.GetIpCountByIp(gCtx, ips)
  412. if err != nil {
  413. return fmt.Errorf("in web repository: %w", err)
  414. }
  415. return nil
  416. })
  417. if err := g.Wait(); err != nil {
  418. return nil, err
  419. }
  420. // 2. 汇总所有计数结果
  421. totalCountMap := make(map[string]int)
  422. // 将多个 for 循环合并到一个函数中,可以显得更整洁(可选)
  423. accumulateCounts := func(counts []v1.IpCountResult) {
  424. for _, result := range counts {
  425. totalCountMap[result.Ip] += result.Count
  426. }
  427. }
  428. accumulateCounts(udpIpCounts)
  429. accumulateCounts(tcpIpCounts)
  430. accumulateCounts(webIpCounts)
  431. // 3. 筛选出总引用数小于 2 的 IP
  432. var ipsToDelist []string
  433. for _, ip := range ips {
  434. if totalCountMap[ip] < 2 {
  435. ipsToDelist = append(ipsToDelist, ip)
  436. }
  437. }
  438. return ipsToDelist, nil
  439. }
  440. // 判断域名是否为 中文域名,如果是,转换为 Punycode
  441. func (s *wafFormatterService) ConvertToPunycodeIfIDN(ctx context.Context, domain string) (isIDN bool, punycodeDomain string, err error) {
  442. // 使用 idna.ToASCII 将域名转换为 Punycode。
  443. // 这个函数同时会根据 IDNA 规范验证域名的合法性。
  444. punycodeDomain, err = idna.ToASCII(domain)
  445. if err != nil {
  446. // 如果转换出错,说明域名格式不符合 IDNA 标准。
  447. return false, "", fmt.Errorf("域名 '%s' 格式无效: %v", domain, err)
  448. }
  449. // 判断是否为 IDN 的关键:
  450. // 比较转换后的 Punycode 域名和原始域名(忽略大小写)。
  451. // 如果不相等,说明原始域名包含非 ASCII 字符,即为 IDN。
  452. isIDN = !strings.EqualFold(domain, punycodeDomain)
  453. return isIDN, punycodeDomain, nil
  454. }
  455. func (s *wafFormatterService) ParseCert(ctx context.Context, httpsCert string, httpKey string) (serverName string, commonName []string, DNSNames []string, before int64, after int64, isSelfSigned bool, err error) {
  456. cert, err := tls.X509KeyPair([]byte(httpsCert), []byte(httpKey))
  457. if err != nil {
  458. return "", nil, nil, 0, 0, false, fmt.Errorf("无法从字符串加载密钥对: %v", err)
  459. }
  460. if len(cert.Certificate) == 0 {
  461. return "", nil, nil, 0, 0, false, fmt.Errorf("提供的证书数据中没有找到证书。")
  462. }
  463. // 解析第一个证书(通常是叶子证书)
  464. x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
  465. if err != nil {
  466. return "", nil, nil, 0, 0, false, fmt.Errorf("无法解析证书: %v", err)
  467. }
  468. // 1. 获取 Common Name (通用名称)
  469. // Common Name 位于 Subject 字段内. [1]
  470. serverName = x509Cert.Subject.CommonName
  471. // 2. 获取 DNS Names (备用主题名称中的DNS条目)
  472. // DNS Names 直接是证书结构体的一个字段. [1]
  473. DNSNames = x509Cert.DNSNames
  474. // 检查证书是否为自签名
  475. // 判断条件:颁发者(Issuer)和主题(Subject)相同,并且证书的签名可以由其自身的公钥验证
  476. if err := x509Cert.CheckSignatureFrom(x509Cert); err == nil {
  477. isSelfSigned = true
  478. }
  479. // 将CommonName放入一个切片,以匹配[]string的类型要求
  480. var commonNames []string
  481. if x509Cert.Subject.CommonName != "" {
  482. commonNames = []string{x509Cert.Subject.CommonName}
  483. }
  484. return serverName, commonNames, DNSNames, x509Cert.NotBefore.Unix(), x509Cert.NotAfter.Unix(), isSelfSigned, nil
  485. }
  486. // HandleSSLPolicy 负责处理SSL证书的完整生命周期:解析、上传到CDN并创建或更新SSL策略。
  487. // 它封装了与CDN服务交互的复杂性,并返回一个可用的SSL策略ID。
  488. func (s *wafFormatterService) AddSSLPolicy(ctx context.Context, req v1.SSL) (sslPolicyId int64, sslCertId int64, err error) {
  489. // 1. 解析证书文件,提取元数据
  490. serverName, commonNames, DNSNames, before, after, isSelfSigned, err := s.ParseCert(ctx, req.CertData, req.KeyData)
  491. if err != nil {
  492. return 0, 0, fmt.Errorf("解析证书失败: %w", err)
  493. }
  494. // 2. 将证书添加到CDN提供商
  495. // 这是获取可以在策略中引用的 `sslCertId` 的前提
  496. newSslCertId, err := s.cdn.AddSSLCert(ctx, v1.SSlCert{
  497. IsOn: true,
  498. UserId: int64(req.CdnUserId),
  499. Name: req.Domain, // 使用域名作为证书名称
  500. ServerName: serverName,
  501. Description: req.Description,
  502. CertData: []byte(req.CertData),
  503. KeyData: []byte(req.KeyData),
  504. TimeBeginAt: before,
  505. TimeEndAt: after,
  506. DnsNames: DNSNames,
  507. CommonNames: commonNames,
  508. IsSelfSigned: isSelfSigned,
  509. })
  510. if err != nil {
  511. return 0, 0, fmt.Errorf("添加SSL证书到CDN失败: %w", err)
  512. }
  513. // 3. 基于获取到的证书ID,创建SSL策略
  514. if newSslCertId != 0 {
  515. // 构造策略中引用的证书列表
  516. type sslCerts struct {
  517. IsOn bool `json:"isOn" form:"isOn"`
  518. CertId int64 `json:"certId" form:"certId"`
  519. }
  520. var sslCertsSlice []sslCerts
  521. sslCertsSlice = append(sslCertsSlice, sslCerts{
  522. IsOn: true,
  523. CertId: newSslCertId,
  524. })
  525. sslCertsJson, err := json.Marshal(sslCertsSlice)
  526. if err != nil {
  527. return 0, 0, fmt.Errorf("序列化SSL证书引用失败: %w", err)
  528. }
  529. // 调用CDN服务创建策略
  530. newSslPolicyId, err := s.cdn.AddSSLPolicy(ctx, v1.AddSSLPolicy{
  531. Http2Enabled: true,
  532. SslCertsJSON: sslCertsJson,
  533. MinVersion: "TLS 1.1", // 可根据安全要求调整
  534. })
  535. if err != nil {
  536. // 如果策略创建失败,需要考虑回滚或记录错误,这里直接返回错误
  537. return 0, 0, fmt.Errorf("通过CDN添加SSL策略失败: %w", err)
  538. }
  539. return newSslPolicyId, newSslCertId, nil
  540. }
  541. return 0, 0, fmt.Errorf("未能创建有效的SSL证书ID,无法继续创建策略")
  542. }
  543. func (s *wafFormatterService) EditSSL(ctx context.Context, req v1.SSL) error {
  544. oldData, err := s.webForwardingRep.GetWebForwarding(ctx, req.WebId)
  545. if err != nil {
  546. return err
  547. }
  548. if oldData.HttpsKey != req.KeyData || oldData.HttpsCert != req.CertData {
  549. serverName, commonNames, DNSNames, before, after, isSelfSigned, err := s.ParseCert(ctx, req.CertData, req.KeyData)
  550. if err != nil {
  551. return fmt.Errorf("解析证书失败: %w", err)
  552. }
  553. sslCert, err := s.webForwardingRep.GetSslCertId(ctx, oldData.SslCertId)
  554. if err != nil {
  555. return fmt.Errorf("获取SSL证书失败: %w", err)
  556. }
  557. for _, v := range sslCert {
  558. err = s.cdn.EditSSLCert(ctx, v1.SSlCert{
  559. SslCertId: v.CertId,
  560. IsOn: v.IsOn,
  561. UserId: int64(req.CdnUserId),
  562. Name: req.Domain, // 使用域名作为证书名称
  563. ServerName: serverName,
  564. Description: req.Description,
  565. CertData: []byte(req.CertData),
  566. KeyData: []byte(req.KeyData),
  567. TimeBeginAt: before,
  568. TimeEndAt: after,
  569. DnsNames: DNSNames,
  570. CommonNames: commonNames,
  571. IsSelfSigned: isSelfSigned,
  572. })
  573. if err != nil {
  574. return fmt.Errorf("更新SSL证书失败: %w", err)
  575. }
  576. }
  577. return nil
  578. }
  579. return nil
  580. }
  581. // 验证端口重复
  582. func (s *wafFormatterService) VerifyPort(ctx context.Context,protocol string, port string,hostId int64,domain string) error {
  583. errPortInUse := fmt.Errorf("端口 %s 已经被使用,无法添加", port)
  584. switch protocol {
  585. case "http", "https":
  586. domains, err := s.webForwardingRep.GetDomainByHostIdPort(ctx, hostId, port)
  587. if err != nil {
  588. return err
  589. }
  590. tcpCount, err := s.tcpforwardingRep.GetPortCount(ctx, hostId, port)
  591. if err != nil {
  592. return err
  593. }
  594. if tcpCount > 0 {
  595. return errPortInUse
  596. }
  597. for _, v := range domains {
  598. if v == "" {
  599. return errPortInUse
  600. }
  601. if net.ParseIP(v) != nil {
  602. return errPortInUse
  603. }
  604. }
  605. if net.ParseIP(domain) != nil || domain == "" {
  606. if len(domains) > 0 {
  607. return errPortInUse
  608. }
  609. }
  610. return nil
  611. case "tcp":
  612. count, err := s.tcpforwardingRep.GetPortCount(ctx, hostId, port)
  613. if err != nil {
  614. return err
  615. }
  616. webCount, err := s.webForwardingRep.GetDomainByHostIdPort(ctx, hostId, port)
  617. if err != nil {
  618. return err
  619. }
  620. if count + int64(len(webCount)) > 0 {
  621. return errPortInUse
  622. }
  623. return nil
  624. case "udp":
  625. count, err := s.udpForWardingRep.GetPortCount(ctx, hostId, port)
  626. if err != nil {
  627. return err
  628. }
  629. if count > 0 {
  630. return errPortInUse
  631. }
  632. return nil
  633. default:
  634. return fmt.Errorf("不支持的协议类型:%s", protocol)
  635. }
  636. }