mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-08 07:41:10 +08:00
【修复】长期持有tcp连接未关闭
【新增】支持通过webhook调用自己的服务解析dns记录 【新增】支持通过webhook推送证书和密钥 【新增】导入导出工作流、通知、证书、api授权数据 【新增】支持自定义插件目录
This commit is contained in:
@@ -4,7 +4,9 @@ import (
|
||||
"ALLinSSL/backend/internal/access"
|
||||
"ALLinSSL/backend/internal/cert"
|
||||
"ALLinSSL/backend/internal/cert/apply/lego/jdcloud"
|
||||
"ALLinSSL/backend/internal/cert/apply/lego/webhook"
|
||||
"ALLinSSL/backend/public"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
azcorecloud "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
|
||||
@@ -202,6 +204,10 @@ func GetDNSProvider(providerName string, creds map[string]string, httpClient *ht
|
||||
config.SecretKey = creds["secret_key"]
|
||||
config.PropagationTimeout = maxWait
|
||||
return constellix.NewDNSProviderConfig(config)
|
||||
case "webhook":
|
||||
config := webhook.NewConfig(creds)
|
||||
config.PropagationTimeout = maxWait
|
||||
return webhook.NewDNSProviderConfig(config)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的 DNS Provider: %s", providerName)
|
||||
@@ -495,7 +501,9 @@ func Apply(cfg map[string]any, logger *public.Logger) (map[string]any, error) {
|
||||
}
|
||||
httpClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyURL(proxyURL),
|
||||
Proxy: http.ProxyURL(proxyURL),
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
DisableKeepAlives: true,
|
||||
},
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
77
backend/internal/cert/apply/lego/webhook/lego.go
Normal file
77
backend/internal/cert/apply/lego/webhook/lego.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"ALLinSSL/backend/public"
|
||||
"fmt"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"time"
|
||||
)
|
||||
|
||||
var configData string
|
||||
|
||||
type Config struct {
|
||||
WebhookConfig *public.WebhookConfig
|
||||
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
config *Config
|
||||
}
|
||||
|
||||
func NewConfig(WebhookConfigStr map[string]string) *Config {
|
||||
fmt.Println(WebhookConfigStr)
|
||||
|
||||
WebhookConfig := &public.WebhookConfig{
|
||||
Url: WebhookConfigStr["url"],
|
||||
Data: WebhookConfigStr["data"],
|
||||
Method: WebhookConfigStr["method"],
|
||||
Headers: WebhookConfigStr["headers"],
|
||||
IgnoreSSL: WebhookConfigStr["ignore_ssl"] == "true",
|
||||
}
|
||||
fmt.Println(WebhookConfig.Url)
|
||||
|
||||
return &Config{
|
||||
WebhookConfig: WebhookConfig,
|
||||
TTL: 600,
|
||||
PropagationTimeout: dns01.DefaultPropagationTimeout,
|
||||
PollingInterval: dns01.DefaultPollingInterval,
|
||||
HTTPTimeout: 30 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, fmt.Errorf("配置不能为空")
|
||||
}
|
||||
return &DNSProvider{config: config}, nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fmt.Println(d.config.WebhookConfig.Url)
|
||||
configData = d.config.WebhookConfig.Data
|
||||
return d.send(domain, token, keyAuth, "present")
|
||||
}
|
||||
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
d.config.WebhookConfig.Data = configData
|
||||
return d.send(domain, token, keyAuth, "cleanup")
|
||||
}
|
||||
|
||||
func (d *DNSProvider) send(domain, token, keyAuth, action string) error {
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
data, err := public.ReplaceJSONPlaceholders(d.config.WebhookConfig.Data, map[string]interface{}{"domain": info.EffectiveFQDN, "token": token, "keyAuth": info.Value, "action": action})
|
||||
if err != nil {
|
||||
return fmt.Errorf("替换JSON占位符失败: %w", err)
|
||||
}
|
||||
d.config.WebhookConfig.Data = data
|
||||
return d.config.WebhookConfig.Send()
|
||||
}
|
||||
@@ -91,7 +91,8 @@ func Request1panel(data *map[string]any, method, providerID, requestUrl string)
|
||||
ignoreSsl = true
|
||||
}
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: ignoreSsl},
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: ignoreSsl},
|
||||
DisableKeepAlives: true,
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: tr}
|
||||
@@ -269,7 +270,7 @@ func OnePanelSiteList(providerID string) ([]response.AccessSiteList, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取网站列表失败 %v", err)
|
||||
}
|
||||
|
||||
|
||||
var result []response.AccessSiteList
|
||||
sites, ok := siteList["data"].(map[string]any)["items"].([]any)
|
||||
if !ok {
|
||||
|
||||
@@ -65,7 +65,8 @@ func RequestBt(data *url.Values, method, providerID, requestUrl string) (map[str
|
||||
ignoreSsl = true
|
||||
}
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: ignoreSsl},
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: ignoreSsl},
|
||||
DisableKeepAlives: true,
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: tr}
|
||||
|
||||
@@ -65,7 +65,8 @@ func RequestBtWaf(data *map[string]any, method, providerID, requestUrl string) (
|
||||
ignoreSsl = true
|
||||
}
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: ignoreSsl},
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: ignoreSsl},
|
||||
DisableKeepAlives: true,
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: tr}
|
||||
@@ -207,4 +208,4 @@ func BtWafAPITest(providerID string) error {
|
||||
return fmt.Errorf("测试请求失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"ALLinSSL/backend/internal/cert/deploy/doge"
|
||||
"ALLinSSL/backend/internal/cert/deploy/lecdn"
|
||||
"ALLinSSL/backend/internal/cert/deploy/plugin"
|
||||
"ALLinSSL/backend/internal/cert/deploy/webhook"
|
||||
"ALLinSSL/backend/public"
|
||||
"fmt"
|
||||
)
|
||||
@@ -106,6 +107,9 @@ func Deploy(cfg map[string]any, logger *public.Logger) error {
|
||||
case "plugin":
|
||||
logger.Debug("使用插件部署...")
|
||||
return plugin.Deploy(cfg, logger)
|
||||
case "webhook":
|
||||
logger.Debug("通过Webhook推送证书...")
|
||||
return webhook.Deploy(cfg)
|
||||
default:
|
||||
return fmt.Errorf("不支持的部署: %s", providerName)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,8 @@ func requestLecdn(url, method, token string, params map[string]any, ignoreSsl bo
|
||||
|
||||
client := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: ignoreSsl},
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: ignoreSsl},
|
||||
DisableKeepAlives: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,8 @@ func RequestSafeLineWaf(data *map[string]any, method, providerID, requestUrl str
|
||||
ignoreSsl = true
|
||||
}
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: ignoreSsl},
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: ignoreSsl},
|
||||
DisableKeepAlives: true,
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: tr}
|
||||
@@ -211,4 +212,4 @@ func SafeLineAPITest(providerID string) error {
|
||||
return fmt.Errorf("测试请求失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
57
backend/internal/cert/deploy/webhook/deploy.go
Normal file
57
backend/internal/cert/deploy/webhook/deploy.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"ALLinSSL/backend/internal/access"
|
||||
"ALLinSSL/backend/public"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func Deploy(cfg map[string]any) error {
|
||||
cert, ok := cfg["certificate"].(map[string]any)
|
||||
if !ok {
|
||||
return fmt.Errorf("证书不存在")
|
||||
}
|
||||
var providerID string
|
||||
switch v := cfg["provider_id"].(type) {
|
||||
case float64:
|
||||
providerID = strconv.Itoa(int(v))
|
||||
case string:
|
||||
providerID = v
|
||||
default:
|
||||
return fmt.Errorf("参数错误:provider_id")
|
||||
}
|
||||
//
|
||||
providerData, err := access.GetAccess(providerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
providerConfigStr, ok := providerData["config"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("api配置错误")
|
||||
}
|
||||
// 解析 JSON 配置
|
||||
var providerConfig public.WebhookConfig
|
||||
err = json.Unmarshal([]byte(providerConfigStr), &providerConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certStr, ok := cert["cert"].(string)
|
||||
if !ok || certStr == "" {
|
||||
return fmt.Errorf("cert is required and must be a string")
|
||||
}
|
||||
keyStr, ok := cert["key"].(string)
|
||||
if !ok || keyStr == "" {
|
||||
return fmt.Errorf("key is required and must be a string")
|
||||
}
|
||||
|
||||
data, err := public.ReplaceJSONPlaceholders(providerConfig.Data, map[string]interface{}{"key": keyStr, "cert": certStr})
|
||||
if err != nil {
|
||||
return fmt.Errorf("替换JSON占位符失败: %w", err)
|
||||
}
|
||||
providerConfig.Data = data
|
||||
|
||||
return providerConfig.Send()
|
||||
}
|
||||
@@ -69,8 +69,8 @@ func Check(certs []*x509.Certificate, host string, advanceDay int) (result *Cert
|
||||
}
|
||||
|
||||
result.CommonName = leafCert.Subject.CommonName
|
||||
result.NotBefore = leafCert.NotBefore.Format("2006-01-02 15:04:05")
|
||||
result.NotAfter = leafCert.NotAfter.Format("2006-01-02 15:04:05")
|
||||
result.NotBefore = leafCert.NotBefore.In(time.Local).Format("2006-01-02 15:04:05")
|
||||
result.NotAfter = leafCert.NotAfter.In(time.Local).Format("2006-01-02 15:04:05")
|
||||
result.DaysLeft = int(leafCert.NotAfter.Sub(time.Now()).Hours() / 24)
|
||||
result.SANs = strings.Join(leafCert.DNSNames, ",")
|
||||
result.SignatureAlgo = leafCert.SignatureAlgorithm.String()
|
||||
@@ -141,8 +141,9 @@ func CheckHttps(target string, advanceDay int) (result *CertInfo, err error) {
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
DisableKeepAlives: true,
|
||||
},
|
||||
//Timeout: 5 * time.Second,
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"fmt"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -184,7 +183,7 @@ func NotifyWebHook(params map[string]any) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析配置失败: %v", err)
|
||||
}
|
||||
config.Data, err = ReplaceJSONPlaceholders(config.Data, params)
|
||||
config.Data, err = public.ReplaceJSONPlaceholders(config.Data, params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("替换JSON占位符失败: %w", err)
|
||||
}
|
||||
@@ -197,16 +196,3 @@ func NotifyWebHook(params map[string]any) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReplaceJSONPlaceholders(jsonStr string, vars map[string]any) (string, error) {
|
||||
re := regexp.MustCompile(`__([a-zA-Z0-9_]+)__`)
|
||||
result := re.ReplaceAllStringFunc(jsonStr, func(match string) string {
|
||||
key := re.FindStringSubmatch(match)[1]
|
||||
if val, ok := vars[key]; ok {
|
||||
return fmt.Sprintf("%v", val) // 将 any 类型转换为字符串
|
||||
}
|
||||
return match // 未匹配到变量则保留原样
|
||||
})
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"ALLinSSL/backend/public"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -75,7 +76,7 @@ func NotifyWorkWx(params map[string]any) error {
|
||||
}
|
||||
`
|
||||
}
|
||||
msg, err := ReplaceJSONPlaceholders(config["data"], params)
|
||||
msg, err := public.ReplaceJSONPlaceholders(config["data"], params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("替换JSON占位符失败: %v", err)
|
||||
}
|
||||
|
||||
@@ -16,13 +16,14 @@ import (
|
||||
)
|
||||
|
||||
type Setting struct {
|
||||
Timeout int `json:"timeout" form:"timeout"`
|
||||
Secure string `json:"secure" form:"secure"`
|
||||
Https string `json:"https" form:"https"`
|
||||
Key string `json:"key" form:"key"`
|
||||
Cert string `json:"cert" form:"cert"`
|
||||
Username string `json:"username" form:"username"`
|
||||
Password string `json:"password" form:"password"`
|
||||
Timeout int `json:"timeout" form:"timeout"`
|
||||
Secure string `json:"secure" form:"secure"`
|
||||
Https string `json:"https" form:"https"`
|
||||
Key string `json:"key" form:"key"`
|
||||
Cert string `json:"cert" form:"cert"`
|
||||
Username string `json:"username" form:"username"`
|
||||
Password string `json:"password" form:"password"`
|
||||
PluginPath string `json:"plugin_path" form:"plugin_path"`
|
||||
}
|
||||
|
||||
func Get() (Setting, error) {
|
||||
@@ -57,6 +58,7 @@ func Get() (Setting, error) {
|
||||
}
|
||||
username := data[0]["username"].(string)
|
||||
setting.Username = username
|
||||
setting.PluginPath = public.GetSettingIgnoreError("plugin_dir")
|
||||
return setting, nil
|
||||
}
|
||||
|
||||
@@ -108,6 +110,9 @@ func Save(setting *Setting) error {
|
||||
public.TimeOut = setting.Timeout
|
||||
restart = true
|
||||
}
|
||||
if setting.PluginPath != "" && setting.PluginPath != public.GetSettingIgnoreError("plugin_dir") {
|
||||
public.UpdateSetting("plugin_dir", setting.PluginPath)
|
||||
}
|
||||
if setting.Https != "" {
|
||||
if setting.Https == "1" {
|
||||
if setting.Key == "" || setting.Cert == "" {
|
||||
@@ -192,13 +197,16 @@ func GetVersion() (map[string]string, error) {
|
||||
update := "0"
|
||||
newVersionObj, err := http.Get("https://download.allinssl.com/version.json")
|
||||
if err != nil {
|
||||
return map[string]string{
|
||||
"version": version,
|
||||
"new_version": version,
|
||||
"update": update,
|
||||
"log": "",
|
||||
"date": "",
|
||||
}, nil
|
||||
newVersionObj, err = http.Get("https://node1.allinssl.com/version.json")
|
||||
if err != nil {
|
||||
return map[string]string{
|
||||
"version": version,
|
||||
"new_version": version,
|
||||
"update": update,
|
||||
"log": "",
|
||||
"date": "",
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
defer newVersionObj.Body.Close()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user