From 4eae3e0f9a13f009b2329e82fa34e74043d21286 Mon Sep 17 00:00:00 2001 From: samwaf Date: Tue, 27 May 2025 10:39:44 +0800 Subject: [PATCH] feat:well-known file #323 --- global/config.go | 1 + wafenginecore/wafengine.go | 72 +++++++++++++++++++++----------------- waftask/task_config.go | 4 +++ 3 files changed, 45 insertions(+), 32 deletions(-) diff --git a/global/config.go b/global/config.go index 62f99ee..07707b3 100644 --- a/global/config.go +++ b/global/config.go @@ -18,6 +18,7 @@ var ( GCONFIG_RECORD_ENABLE_OWASP int64 = 0 //启动OWASP数据检测 GCONFIG_RECORD_ENABLE_HTTP_80 int64 = 0 //启动80端口服务(为自动申请证书使用 HTTP文件验证类型,DNS验证不需要) GCONFIG_RECORD_SSLOrder_EXPIRE_DAY int64 = 30 // 提前多少天进行自动申请 + GCONFIG_RECORD_SSLHTTP_CHECK int64 = 1 // ssl申请文件验证类型 是否校验原始路径HTTP响应代码 1 校验 0 不校验 GCONFIG_RECORD_CONNECT_TIME_OUT int64 = 30 // 连接超时 默认30s GCONFIG_RECORD_KEEPALIVE_TIME_OUT int64 = 30 // 保持活动超时 默认30s //GCONFIG_RECORD_PATCH_VERSION_CORE int64 = 20250106 // 核心数据库补丁日期 diff --git a/wafenginecore/wafengine.go b/wafenginecore/wafengine.go index 89ccdaf..e7e9309 100644 --- a/wafenginecore/wafengine.go +++ b/wafenginecore/wafengine.go @@ -451,7 +451,7 @@ func (waf *WafEngine) ServeHTTP(w http.ResponseWriter, r *http.Request) { } - if cacheConfig.IsEnableCache == 1 { + if cacheConfig.IsEnableCache == 1 && !strings.HasPrefix(weblogbean.URL, global.GSSL_HTTP_CHANGLE_PATH) { cacheResp := wafwebcache.LoadWebDataFormCache(w, r, hostTarget, cacheConfig, &weblogbean) if cacheResp != nil { // 将缓存的响应写回给客户端 @@ -854,7 +854,7 @@ func (waf *WafEngine) modifyResponse() func(*http.Response) error { if err != nil { zlog.Debug("解析cache json失败") } - if cacheConfig.IsEnableCache == 1 { + if cacheConfig.IsEnableCache == 1 && !strings.HasPrefix(weblogfrist.URL, global.GSSL_HTTP_CHANGLE_PATH) { wafwebcache.StoreWebDataCache(resp, waf.HostTarget[host], cacheConfig, weblogfrist) } @@ -863,40 +863,48 @@ func (waf *WafEngine) modifyResponse() func(*http.Response) error { datetimeNow := time.Now() weblogfrist.TimeSpent = datetimeNow.UnixNano()/1e6 - weblogfrist.UNIX_ADD_TIME - if strings.HasPrefix(weblogfrist.URL, global.GSSL_HTTP_CHANGLE_PATH) && (resp.StatusCode == 404 || resp.StatusCode == 301 || resp.StatusCode == 302) { - //如果远端HTTP01不存在挑战验证文件,那么我们映射到走本地再试一下 + // 根据配置决定是否检查HTTP响应代码并重定向到本地 + if strings.HasPrefix(weblogfrist.URL, global.GSSL_HTTP_CHANGLE_PATH) { + if global.GCONFIG_RECORD_SSLHTTP_CHECK == 0 || resp.StatusCode == 404 || resp.StatusCode == 301 || resp.StatusCode == 302 { + //如果远端HTTP01不存在挑战验证文件,那么我们映射到走本地再试一下 + //或者配置为不检查HTTP响应代码,直接走本地 - //Challenge /.well-known/acme-challenge/2NKiiETgQdPmmjlM88mH5uo6jM98PrgWwsDslaN8 - urls := strings.Split(weblogfrist.URL, "/") - if len(urls) == 4 { - challengeFile := urls[3] - //检测challengeFile是否合法 - if !utils.IsValidChallengeFile(challengeFile) { - return nil - } - //当前路径 data/vhost/domain code 变量下 - // 需要读取的文件路径 - filePath := utils.GetCurrentDir() + "/data/vhost/" + weblogfrist.HOST_CODE + "/.well-known/acme-challenge/" + challengeFile + //Challenge /.well-known/acme-challenge/2NKiiETgQdPmmjlM88mH5uo6jM98PrgWwsDslaN8 + urls := strings.Split(weblogfrist.URL, "/") + if len(urls) == 4 { + challengeFile := urls[3] + //检测challengeFile是否合法 + if !utils.IsValidChallengeFile(challengeFile) { + return nil + } + //当前路径 data/vhost/domain code 变量下 + // 需要读取的文件路径 + filePath := utils.GetCurrentDir() + "/data/vhost/" + weblogfrist.HOST_CODE + "/.well-known/acme-challenge/" + challengeFile - // 调用读取文件的函数 - content, err := utils.ReadFile(filePath) - if err != nil { - zlog.Error("Error reading file: %v", err.Error()) - } - if content != "" { - resp.StatusCode = http.StatusOK - resp.Status = http.StatusText(http.StatusOK) - resp.Body = io.NopCloser(bytes.NewBuffer([]byte(content))) - resp.ContentLength = int64(len(content)) - resp.Header.Set("Content-Length", strconv.FormatInt(int64(len(content)), 10)) + // 调用读取文件的函数 + content, err := utils.ReadFile(filePath) + if err != nil { + zlog.Error("Error reading file: %v", err.Error()) + } + if content != "" { + resp.StatusCode = http.StatusOK + resp.Status = http.StatusText(http.StatusOK) + resp.Body = io.NopCloser(bytes.NewBuffer([]byte(content))) + resp.ContentLength = int64(len(content)) + resp.Header.Set("Content-Length", strconv.FormatInt(int64(len(content)), 10)) - weblogfrist.ACTION = "放行" - weblogfrist.STATUS = resp.Status - weblogfrist.STATUS_CODE = resp.StatusCode - weblogfrist.TASK_FLAG = 1 - weblogfrist.BackendCheckCost = time.Now().UnixNano()/1e6 - backendCheckStart //响应数据处理时间 - global.GQEQUE_LOG_DB.Enqueue(weblogfrist) + weblogfrist.ACTION = "放行" + weblogfrist.STATUS = resp.Status + weblogfrist.STATUS_CODE = resp.StatusCode + weblogfrist.TASK_FLAG = 1 + weblogfrist.BackendCheckCost = time.Now().UnixNano()/1e6 - backendCheckStart //响应数据处理时间 + global.GQEQUE_LOG_DB.Enqueue(weblogfrist) + } } + } else if global.GCONFIG_RECORD_SSLHTTP_CHECK == 1 { + // 当配置为检查HTTP响应码且响应不是404/301/302时,记录警告信息 + zlog.Warn(fmt.Sprintf("ACME Challenge检测:域名 %s 的 URL %s 返回了非预期的状态码 %d,影响证书验证,可在系统配置里面将sslhttp_check设置成0", + weblogfrist.HOST, weblogfrist.URL, resp.StatusCode)) } } else { //记录响应Header信息 diff --git a/waftask/task_config.go b/waftask/task_config.go index b4db103..ca5f37e 100644 --- a/waftask/task_config.go +++ b/waftask/task_config.go @@ -85,6 +85,9 @@ func setConfigIntValue(name string, value int64, change int) { case "fake_spider_captcha": global.GCONFIG_RECORD_FAKE_SPIDER_CAPTCHA = value break + case "sslhttp_check": + global.GCONFIG_RECORD_SSLHTTP_CHECK = value + break default: zlog.Warn("Unknown config item:", name) } @@ -210,6 +213,7 @@ func TaskLoadSetting(initLoad bool) { updateConfigIntItem(initLoad, "ssl", "enable_http_80", global.GCONFIG_RECORD_ENABLE_HTTP_80, "启动80端口服务(为自动申请证书使用 HTTP文件验证类型需要,DNS验证不需要)", "int", "") updateConfigIntItem(initLoad, "ssl", "sslorder_expire_day", global.GCONFIG_RECORD_SSLOrder_EXPIRE_DAY, "自动续期检测小于多少天开始发起自动申请 默认30天", "int", "") + updateConfigIntItem(initLoad, "ssl", "sslhttp_check", global.GCONFIG_RECORD_SSLHTTP_CHECK, "证书文件验证方式是否要严控后端.well-known 响应代码必须是404 301 302 ,默认1控制 0 不控制", "int", "") updateConfigIntItem(initLoad, "network", "connect_time_out", global.GCONFIG_RECORD_CONNECT_TIME_OUT, "连接超时(默认30s)", "int", "") updateConfigIntItem(initLoad, "network", "keepalive_time_out", global.GCONFIG_RECORD_KEEPALIVE_TIME_OUT, "保持活动超时(默认30s)", "int", "")