|
@@ -2,6 +2,8 @@ package service
|
|
|
|
|
|
import (
|
|
|
"context"
|
|
|
+ "crypto/tls"
|
|
|
+ "crypto/x509"
|
|
|
"encoding/json"
|
|
|
"fmt"
|
|
|
v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
|
|
@@ -23,23 +25,26 @@ type WafFormatterService interface {
|
|
|
Require(ctx context.Context, req v1.GlobalRequire) (RequireResponse, error)
|
|
|
validateWafPortCount(ctx context.Context, hostId int) error
|
|
|
validateWafDomainCount(ctx context.Context, req v1.GlobalRequire) error
|
|
|
- ConvertToWildcardDomain(ctx context.Context,domain string) (string, error)
|
|
|
- AppendWafIp(ctx context.Context, req []string,returnSourceIp string) ([]v1.IpInfo, error)
|
|
|
+ ConvertToWildcardDomain(ctx context.Context, domain string) (string, error)
|
|
|
+ AppendWafIp(ctx context.Context, req []string, returnSourceIp string) ([]v1.IpInfo, error)
|
|
|
WashIps(ctx context.Context, req []string) ([]string, error)
|
|
|
- PublishIpWhitelistTask(ips []string, action string,returnSourceIp string, color string)
|
|
|
+ PublishIpWhitelistTask(ips []string, action string, returnSourceIp string, color string)
|
|
|
PublishDomainWhitelistTask(domain, ip, action string)
|
|
|
findIpDifferences(oldIps, newIps []string) ([]string, []string)
|
|
|
WashDeleteWafIp(ctx context.Context, backendList []string) ([]string, error)
|
|
|
- WashEditWafIp(ctx context.Context, newBackendList []string,oldBackendList []string) ([]string, []string, error)
|
|
|
+ WashEditWafIp(ctx context.Context, newBackendList []string, oldBackendList []string) ([]string, []string, error)
|
|
|
//cdn添加网站
|
|
|
AddOrigin(ctx context.Context, req v1.WebJson) (int64, error)
|
|
|
// 获取ip数量等于1的源站过白ip
|
|
|
WashDelIps(ctx context.Context, ips []string) ([]string, error)
|
|
|
// 判断域名是否是IDN,如果是,转换为 Punycode
|
|
|
- ConvertToPunycodeIfIDN(ctx context.Context,domain string) (isIDN bool, punycodeDomain string, err error)
|
|
|
+ ConvertToPunycodeIfIDN(ctx context.Context, domain string) (isIDN bool, punycodeDomain string, err error)
|
|
|
+ // 解析证书
|
|
|
+ ParseCert(ctx context.Context, httpsCert string, httpKey string) (serverName string, commonName []string, DNSNames []string, before int64, after int64, isSelfSigned bool, err error)
|
|
|
}
|
|
|
+
|
|
|
func NewWafFormatterService(
|
|
|
- service *Service,
|
|
|
+ service *Service,
|
|
|
globalRep repository.GlobalLimitRepository,
|
|
|
hostRep repository.HostRepository,
|
|
|
required RequiredService,
|
|
@@ -54,45 +59,45 @@ func NewWafFormatterService(
|
|
|
cdn CdnService,
|
|
|
) WafFormatterService {
|
|
|
return &wafFormatterService{
|
|
|
- Service: service,
|
|
|
- globalRep: globalRep,
|
|
|
- hostRep: hostRep,
|
|
|
- required: required,
|
|
|
- parser: parser,
|
|
|
- tcpforwardingRep: tcpforwardingRep,
|
|
|
- udpForWardingRep: udpForWardingRep,
|
|
|
- webForwardingRep: webForwardingRep,
|
|
|
- host : host,
|
|
|
- mq: mq,
|
|
|
- gatewayGroupRep: gatewayGroupRep,
|
|
|
+ Service: service,
|
|
|
+ globalRep: globalRep,
|
|
|
+ hostRep: hostRep,
|
|
|
+ required: required,
|
|
|
+ parser: parser,
|
|
|
+ tcpforwardingRep: tcpforwardingRep,
|
|
|
+ udpForWardingRep: udpForWardingRep,
|
|
|
+ webForwardingRep: webForwardingRep,
|
|
|
+ host: host,
|
|
|
+ mq: mq,
|
|
|
+ gatewayGroupRep: gatewayGroupRep,
|
|
|
gatewayGroupIpRep: gatewayGroupIpRep,
|
|
|
- cdn: cdn,
|
|
|
+ cdn: cdn,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
type wafFormatterService struct {
|
|
|
*Service
|
|
|
- globalRep repository.GlobalLimitRepository
|
|
|
- hostRep repository.HostRepository
|
|
|
- required RequiredService
|
|
|
- parser ParserService
|
|
|
- tcpforwardingRep repository.TcpforwardingRepository
|
|
|
- udpForWardingRep repository.UdpForWardingRepository
|
|
|
- webForwardingRep repository.WebForwardingRepository
|
|
|
- host HostService
|
|
|
- mq *rabbitmq.RabbitMQ
|
|
|
- gatewayGroupRep repository.GatewayGroupRepository
|
|
|
+ globalRep repository.GlobalLimitRepository
|
|
|
+ hostRep repository.HostRepository
|
|
|
+ required RequiredService
|
|
|
+ parser ParserService
|
|
|
+ tcpforwardingRep repository.TcpforwardingRepository
|
|
|
+ udpForWardingRep repository.UdpForWardingRepository
|
|
|
+ webForwardingRep repository.WebForwardingRepository
|
|
|
+ host HostService
|
|
|
+ mq *rabbitmq.RabbitMQ
|
|
|
+ gatewayGroupRep repository.GatewayGroupRepository
|
|
|
gatewayGroupIpRep repository.GateWayGroupIpRepository
|
|
|
- cdn CdnService
|
|
|
+ cdn CdnService
|
|
|
}
|
|
|
|
|
|
type RequireResponse struct {
|
|
|
model.GlobalLimit `json:"globalLimit" form:"globalLimit"`
|
|
|
- GatewayIps []string `json:"ips" form:"ips"`
|
|
|
- Tag string `json:"tag" form:"tag"`
|
|
|
+ GatewayIps []string `json:"ips" form:"ips"`
|
|
|
+ Tag string `json:"tag" form:"tag"`
|
|
|
}
|
|
|
|
|
|
-func (s *wafFormatterService) Require(ctx context.Context,req v1.GlobalRequire) (RequireResponse, error) {
|
|
|
+func (s *wafFormatterService) Require(ctx context.Context, req v1.GlobalRequire) (RequireResponse, error) {
|
|
|
var res RequireResponse
|
|
|
// 获取全局配置信息
|
|
|
globalLimit, err := s.globalRep.GetGlobalLimitByHostId(ctx, int64(req.HostId))
|
|
@@ -115,7 +120,6 @@ func (s *wafFormatterService) Require(ctx context.Context,req v1.GlobalRequire)
|
|
|
return res, nil
|
|
|
}
|
|
|
|
|
|
-
|
|
|
func (s *wafFormatterService) validateWafPortCount(ctx context.Context, hostId int) error {
|
|
|
congfig, err := s.host.GetGlobalLimitConfig(ctx, hostId)
|
|
|
if err != nil {
|
|
@@ -133,7 +137,7 @@ func (s *wafFormatterService) validateWafPortCount(ctx context.Context, hostId i
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- if int64(congfig.PortCount) > tcpCount + udpCount + webCount {
|
|
|
+ if int64(congfig.PortCount) > tcpCount+udpCount+webCount {
|
|
|
return nil
|
|
|
}
|
|
|
return fmt.Errorf("端口数量超出套餐限制,已配置%d个端口,套餐限制为%d个端口", tcpCount+udpCount+webCount, congfig.PortCount)
|
|
@@ -169,7 +173,7 @@ func (s *wafFormatterService) ConvertToWildcardDomain(ctx context.Context, domai
|
|
|
if err != nil {
|
|
|
s.logger.Error("无效的域名", zap.String("domain", domain), zap.Error(err))
|
|
|
// 如果域名无效(如 IP 地址、localhost),则返回错误。
|
|
|
- return "",nil
|
|
|
+ return "", nil
|
|
|
}
|
|
|
|
|
|
// 2. 比较原始域名和可注册域名。
|
|
@@ -184,15 +188,15 @@ func (s *wafFormatterService) ConvertToWildcardDomain(ctx context.Context, domai
|
|
|
return domain, nil
|
|
|
}
|
|
|
|
|
|
-func (s *wafFormatterService) AppendWafIp(ctx context.Context, req []string,returnSourceIp string) ([]v1.IpInfo, error) {
|
|
|
+func (s *wafFormatterService) AppendWafIp(ctx context.Context, req []string, returnSourceIp string) ([]v1.IpInfo, error) {
|
|
|
var ips []v1.IpInfo
|
|
|
for _, v := range req {
|
|
|
ips = append(ips, v1.IpInfo{
|
|
|
- FType: "0",
|
|
|
- FStartIp: v,
|
|
|
- FEndIp: v,
|
|
|
- FRemark: "宁波高防IP过白",
|
|
|
- FServerIp: returnSourceIp,
|
|
|
+ FType: "0",
|
|
|
+ FStartIp: v,
|
|
|
+ FEndIp: v,
|
|
|
+ FRemark: "宁波高防IP过白",
|
|
|
+ FServerIp: returnSourceIp,
|
|
|
})
|
|
|
}
|
|
|
return ips, nil
|
|
@@ -206,11 +210,11 @@ func (s *wafFormatterService) AppendWafIpByRemovePort(ctx context.Context, req [
|
|
|
return nil, err
|
|
|
}
|
|
|
ips = append(ips, v1.IpInfo{
|
|
|
- FType: "0",
|
|
|
- FStartIp: ip,
|
|
|
- FEndIp: ip,
|
|
|
- FRemark: "宁波高防IP过白",
|
|
|
- FServerIp: "",
|
|
|
+ FType: "0",
|
|
|
+ FStartIp: ip,
|
|
|
+ FEndIp: ip,
|
|
|
+ FRemark: "宁波高防IP过白",
|
|
|
+ FServerIp: "",
|
|
|
})
|
|
|
}
|
|
|
return ips, nil
|
|
@@ -220,7 +224,7 @@ func (s *wafFormatterService) AppendWafIpByRemovePort(ctx context.Context, req [
|
|
|
func (s *wafFormatterService) WashIps(ctx context.Context, req []string) ([]string, error) {
|
|
|
var res []string
|
|
|
for _, v := range req {
|
|
|
- res = append(res,v)
|
|
|
+ res = append(res, v)
|
|
|
}
|
|
|
return res, nil
|
|
|
}
|
|
@@ -273,26 +277,25 @@ func (s *wafFormatterService) PublishDomainWhitelistTask(domain, ip, action stri
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-func (s *wafFormatterService) PublishIpWhitelistTask(ips []string, action string, returnSourceIp string,color string) {
|
|
|
+func (s *wafFormatterService) PublishIpWhitelistTask(ips []string, action string, returnSourceIp string, color string) {
|
|
|
// Define message payload, including the action
|
|
|
type ipTaskPayload struct {
|
|
|
- Ips []string `json:"ips"`
|
|
|
- Action string `json:"action"`
|
|
|
- ReturnSourceIp string `json:"return_source_ip"`
|
|
|
- Color string `json:"color"`
|
|
|
+ Ips []string `json:"ips"`
|
|
|
+ Action string `json:"action"`
|
|
|
+ ReturnSourceIp string `json:"return_source_ip"`
|
|
|
+ Color string `json:"color"`
|
|
|
}
|
|
|
payload := ipTaskPayload{
|
|
|
- Ips: ips,
|
|
|
- Action: action,
|
|
|
+ Ips: ips,
|
|
|
+ Action: action,
|
|
|
ReturnSourceIp: returnSourceIp,
|
|
|
- Color: color,
|
|
|
+ Color: color,
|
|
|
}
|
|
|
|
|
|
// Serialize the message
|
|
|
msgBody, err := json.Marshal(payload)
|
|
|
if err != nil {
|
|
|
- s.logger.Error("序列化 IP 白名单任务消息失败", zap.Error(err), zap.Any("IPs", ips), zap.String("action", action),zap.String("color", color))
|
|
|
+ s.logger.Error("序列化 IP 白名单任务消息失败", zap.Error(err), zap.Any("IPs", ips), zap.String("action", action), zap.String("color", color))
|
|
|
return
|
|
|
}
|
|
|
|
|
@@ -316,13 +319,12 @@ func (s *wafFormatterService) PublishIpWhitelistTask(ips []string, action string
|
|
|
// Publish the message
|
|
|
err = s.mq.PublishWithCh(taskCfg.Exchange, routingKey, publishingMsg)
|
|
|
if err != nil {
|
|
|
- s.logger.Error("发布 IP 白名单任务到 MQ 失败", zap.Error(err), zap.String("action", action),zap.String("color", color))
|
|
|
+ s.logger.Error("发布 IP 白名单任务到 MQ 失败", zap.Error(err), zap.String("action", action), zap.String("color", color))
|
|
|
} else {
|
|
|
- s.logger.Info("成功将 IP 白名单任务发布到 MQ", zap.String("action", action),zap.String("color", color))
|
|
|
+ s.logger.Info("成功将 IP 白名单任务发布到 MQ", zap.String("action", action), zap.String("color", color))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
func (s *wafFormatterService) findIpDifferences(oldIps, newIps []string) ([]string, []string) {
|
|
|
// 使用 map 实现 set,用于快速查找
|
|
|
oldIpsSet := make(map[string]struct{}, len(oldIps))
|
|
@@ -366,7 +368,7 @@ func (s *wafFormatterService) WashDeleteWafIp(ctx context.Context, backendList [
|
|
|
return res, nil
|
|
|
}
|
|
|
|
|
|
-func (s *wafFormatterService) WashEditWafIp(ctx context.Context, newBackendList []string,oldBackendList []string) ([]string, []string,error) {
|
|
|
+func (s *wafFormatterService) WashEditWafIp(ctx context.Context, newBackendList []string, oldBackendList []string) ([]string, []string, error) {
|
|
|
var oldIps []string
|
|
|
var newIps []string
|
|
|
for _, v := range oldBackendList {
|
|
@@ -387,12 +389,9 @@ func (s *wafFormatterService) WashEditWafIp(ctx context.Context, newBackendList
|
|
|
}
|
|
|
addedIps, removedIps := s.findIpDifferences(oldIps, newIps)
|
|
|
|
|
|
-
|
|
|
-
|
|
|
- return addedIps, removedIps , nil
|
|
|
+ return addedIps, removedIps, nil
|
|
|
}
|
|
|
|
|
|
-
|
|
|
func (s *wafFormatterService) AddOrigin(ctx context.Context, req v1.WebJson) (int64, error) {
|
|
|
ip, port, err := net.SplitHostPort(req.BackendList)
|
|
|
if err != nil {
|
|
@@ -401,14 +400,14 @@ func (s *wafFormatterService) AddOrigin(ctx context.Context, req v1.WebJson) (in
|
|
|
addr := v1.Addr{
|
|
|
Protocol: req.ApiType,
|
|
|
Host: ip,
|
|
|
- Port: port,
|
|
|
+ Port: port,
|
|
|
}
|
|
|
id, err := s.cdn.CreateOrigin(ctx, v1.Origin{
|
|
|
- Addr: addr,
|
|
|
- Weight: 10,
|
|
|
- Description: req.Comment,
|
|
|
- Host: req.Host,
|
|
|
- IsOn: true,
|
|
|
+ Addr: addr,
|
|
|
+ Weight: 10,
|
|
|
+ Description: req.Comment,
|
|
|
+ Host: req.Host,
|
|
|
+ IsOn: true,
|
|
|
TlsSecurityVerifyMode: "auto",
|
|
|
})
|
|
|
if err != nil {
|
|
@@ -453,7 +452,6 @@ func (s *wafFormatterService) WashDelIps(ctx context.Context, ips []string) ([]s
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
-
|
|
|
// 2. 汇总所有计数结果
|
|
|
totalCountMap := make(map[string]int)
|
|
|
// 将多个 for 循环合并到一个函数中,可以显得更整洁(可选)
|
|
@@ -478,7 +476,7 @@ func (s *wafFormatterService) WashDelIps(ctx context.Context, ips []string) ([]s
|
|
|
}
|
|
|
|
|
|
// 判断域名是否为 中文域名,如果是,转换为 Punycode
|
|
|
-func (s *wafFormatterService) ConvertToPunycodeIfIDN(ctx context.Context,domain string) (isIDN bool, punycodeDomain string, err error) {
|
|
|
+func (s *wafFormatterService) ConvertToPunycodeIfIDN(ctx context.Context, domain string) (isIDN bool, punycodeDomain string, err error) {
|
|
|
// 使用 idna.ToASCII 将域名转换为 Punycode。
|
|
|
// 这个函数同时会根据 IDNA 规范验证域名的合法性。
|
|
|
punycodeDomain, err = idna.ToASCII(domain)
|
|
@@ -493,4 +491,44 @@ func (s *wafFormatterService) ConvertToPunycodeIfIDN(ctx context.Context,domain
|
|
|
isIDN = !strings.EqualFold(domain, punycodeDomain)
|
|
|
|
|
|
return isIDN, punycodeDomain, nil
|
|
|
-}
|
|
|
+}
|
|
|
+
|
|
|
+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) {
|
|
|
+ cert, err := tls.X509KeyPair([]byte(httpsCert), []byte(httpKey))
|
|
|
+ if err != nil {
|
|
|
+ return "", nil, nil, 0, 0, false, fmt.Errorf("无法从字符串加载密钥对: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(cert.Certificate) == 0 {
|
|
|
+ return "", nil, nil, 0, 0, false, fmt.Errorf("提供的证书数据中没有找到证书。")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析第一个证书(通常是叶子证书)
|
|
|
+ x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
|
|
|
+ if err != nil {
|
|
|
+ return "", nil, nil, 0, 0, false, fmt.Errorf("无法解析证书: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1. 获取 Common Name (通用名称)
|
|
|
+ // Common Name 位于 Subject 字段内. [1]
|
|
|
+ serverName = x509Cert.Subject.CommonName
|
|
|
+
|
|
|
+ // 2. 获取 DNS Names (备用主题名称中的DNS条目)
|
|
|
+ // DNS Names 直接是证书结构体的一个字段. [1]
|
|
|
+ DNSNames = x509Cert.DNSNames
|
|
|
+
|
|
|
+ // 检查证书是否为自签名
|
|
|
+ // 判断条件:颁发者(Issuer)和主题(Subject)相同,并且证书的签名可以由其自身的公钥验证
|
|
|
+ if err := x509Cert.CheckSignatureFrom(x509Cert); err == nil {
|
|
|
+ isSelfSigned = true
|
|
|
+ }
|
|
|
+
|
|
|
+ // 将CommonName放入一个切片,以匹配[]string的类型要求
|
|
|
+ var commonNames []string
|
|
|
+ if x509Cert.Subject.CommonName != "" {
|
|
|
+ commonNames = []string{x509Cert.Subject.CommonName}
|
|
|
+ }
|
|
|
+
|
|
|
+ return serverName, commonNames, DNSNames, x509Cert.NotBefore.Unix(), x509Cert.NotAfter.Unix(), isSelfSigned, nil
|
|
|
+
|
|
|
+}
|