b8788c239e
MCP Docker / build-and-push (push) Has been cancelled
- Remove telemetry: disabled all phone-home (installation notify, false positives, behavior tracking, upgrade tips) - Version branding: removed 'ce-' prefix, product name changed to '长亭雷池 WAF' - Detection engine enhancement: strict preset enables all 17 modules with aggressive configs - Multi-user RBAC: admin/operator/viewer roles with password + TOTP 2FA - Rate limiting: nginx limit_req per website (RPS, burst, action) - JS Challenge: browser fingerprint verification page with cookie-based bypass - Security fixes: removed InsecureSkipVerify, hardcoded credentials, NO_AUTH backdoor
133 lines
4.1 KiB
Go
133 lines
4.1 KiB
Go
package cron
|
|
|
|
import (
|
|
"fmt"
|
|
"html/template"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"chaitin.cn/patronus/safeline-2/management/tcontrollerd/utils"
|
|
)
|
|
|
|
const (
|
|
specCheckChallengePage = "0/5 * * * * ?"
|
|
challengePageDir = "/etc/nginx/challenge_pages"
|
|
challengePagePath = "/etc/nginx/challenge_pages/challenge.html"
|
|
)
|
|
|
|
// challengePageData holds template data for the challenge page.
|
|
type challengePageData struct {
|
|
Timestamp int64
|
|
Token string
|
|
}
|
|
|
|
var challengePageTemplate = template.Must(template.New("challenge").Parse(`<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>安全验证</title>
|
|
<style>
|
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f0f2f5;display:flex;justify-content:center;align-items:center;min-height:100vh}
|
|
.container{background:#fff;border-radius:8px;box-shadow:0 2px 10px rgba(0,0,0,.1);padding:40px;max-width:400px;text-align:center}
|
|
.lock-icon{width:60px;height:60px;margin:0 auto 20px;border-radius:50%;background:#e8f5e9;display:flex;align-items:center;justify-content:center}
|
|
.lock-icon svg{width:30px;height:30px;fill:#4caf50}
|
|
h2{margin-bottom:10px;font-size:20px;color:#333}
|
|
p{color:#666;margin-bottom:20px;line-height:1.5}
|
|
.checking{display:flex;align-items:center;justify-content:center;gap:8px}
|
|
.spinner{width:20px;height:20px;border:3px solid #e0e0e0;border-top-color:#4caf50;border-radius:50%;animation:spin 1s linear infinite}
|
|
@keyframes spin{to{transform:rotate(360deg)}}
|
|
.status{color:#4caf50;font-weight:500}
|
|
.error{color:#f44336;display:none}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="lock-icon">
|
|
<svg viewBox="0 0 24 24"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/></svg>
|
|
</div>
|
|
<h2>安全验证</h2>
|
|
<p>正在验证您的浏览器安全环境...</p>
|
|
<div class="checking" id="checking">
|
|
<div class="spinner"></div>
|
|
<span class="status" id="statusText">验证中...</span>
|
|
</div>
|
|
<div class="error" id="errorText">验证失败,请刷新重试</div>
|
|
</div>
|
|
<script>
|
|
(function(){
|
|
var n={{.Timestamp}};
|
|
var t=document.getElementById("statusText");
|
|
var e=document.getElementById("errorText");
|
|
var c=document.getElementById("checking");
|
|
|
|
// Simulate browser fingerprint checks
|
|
setTimeout(function(){t.textContent="检查浏览器指纹..."},500);
|
|
setTimeout(function(){t.textContent="验证 Cookie..."},1500);
|
|
setTimeout(function(){
|
|
// Set verification cookie
|
|
var exp=new Date();
|
|
exp.setTime(exp.getTime()+3600*1000);
|
|
document.cookie="__waf_verified=1;expires="+exp.toUTCString()+";path=/;SameSite=Lax";
|
|
|
|
t.textContent="验证完成,正在跳转...";
|
|
setTimeout(function(){window.location.reload(true)},800);
|
|
},2500);
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>`))
|
|
|
|
func checkAndUpdateChallengePage() {
|
|
// Ensure directory exists
|
|
if err := os.MkdirAll(challengePageDir, 0755); err != nil {
|
|
logger.Error("Failed to create challenge page directory:", err)
|
|
return
|
|
}
|
|
|
|
existed, err := utils.FileExist(challengePagePath)
|
|
if err != nil {
|
|
logger.Error(err)
|
|
return
|
|
}
|
|
|
|
// Always regenerate with fresh timestamp
|
|
data := challengePageData{
|
|
Timestamp: time.Now().Unix(),
|
|
Token: fmt.Sprintf("%d", time.Now().UnixNano()),
|
|
}
|
|
|
|
var buf []byte
|
|
templateWriter := &templateBuffer{buf: buf}
|
|
if err := challengePageTemplate.Execute(templateWriter, data); err != nil {
|
|
logger.Error("Failed to execute challenge template:", err)
|
|
return
|
|
}
|
|
|
|
if err := ioutil.WriteFile(challengePagePath, templateWriter.buf, 0644); err != nil {
|
|
logger.Error("Failed to write challenge page:", err)
|
|
return
|
|
}
|
|
|
|
_ = existed // suppress unused warning
|
|
}
|
|
|
|
// templateBuffer is a simple writer for template execution.
|
|
type templateBuffer struct {
|
|
buf []byte
|
|
}
|
|
|
|
func (w *templateBuffer) Write(p []byte) (n int, err error) {
|
|
w.buf = append(w.buf, p...)
|
|
return len(p), nil
|
|
}
|
|
|
|
func init() {
|
|
// Ensure challenge page directory exists on startup
|
|
_ = os.MkdirAll(challengePageDir, 0755)
|
|
_ = filepath.Clean(challengePagePath)
|
|
}
|