Files
SafeLine/management/webserver/main.go
T
Binbim b8788c239e
MCP Docker / build-and-push (push) Has been cancelled
feat: SafeLine CE 9.3.9 with pro features
- 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
2026-07-04 07:59:04 +08:00

226 lines
6.9 KiB
Go

package main
import (
"flag"
"fmt"
"net/http"
"os"
"strconv"
"strings"
"time"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"chaitin.cn/patronus/safeline-2/management/webserver/api"
"chaitin.cn/patronus/safeline-2/management/webserver/cmd"
"chaitin.cn/patronus/safeline-2/management/webserver/middleware"
"chaitin.cn/patronus/safeline-2/management/webserver/model"
"chaitin.cn/patronus/safeline-2/management/webserver/pkg/config"
"chaitin.cn/patronus/safeline-2/management/webserver/pkg/constants"
"chaitin.cn/patronus/safeline-2/management/webserver/pkg/cron"
"chaitin.cn/patronus/safeline-2/management/webserver/pkg/database"
"chaitin.cn/patronus/safeline-2/management/webserver/pkg/fvm"
"chaitin.cn/patronus/safeline-2/management/webserver/pkg/log"
"chaitin.cn/patronus/safeline-2/management/webserver/rpc"
)
var (
logger = log.GetLogger("main")
version = "undefined"
githash = "undefined"
buildstamp = "undefined"
goVersion = "undefined"
)
func init() {
// do something that do not raise error
}
func main() {
log.SetLogFormatter()
fs := flag.NewFlagSet("webserver", flag.ExitOnError)
showVersion := fs.Bool("v", false, "show version")
cfgFile := fs.String("c", constants.ConfigFilePath, "config file path")
genCerts := fs.Bool("gen_certs", false, "generate certs")
showFSL := fs.Bool("show_fsl", false, "show full selectors")
push_fsl := fs.Bool("push_fsl", false, "compile and push fsl")
fakeLogs := fs.Bool("fake_logs", false, "fake logs")
resetUsername := fs.String("reset_user", "", "reset user")
if err := fs.Parse(os.Args[1:]); err != nil {
logger.Fatalln("Failed to parse args: ", err)
}
if *showVersion {
i, _ := strconv.Atoi(buildstamp)
t := time.Unix(int64(i), 0).Format("2006-01-02 15:04:05")
fmt.Println("Version: ", version)
fmt.Println("Githash: ", githash)
fmt.Println("Build: ", t)
fmt.Println("Go version: ", goVersion)
return
}
constants.Version = version
// init configs
if err := config.InitConfigs(*cfgFile); err != nil {
logger.Fatalln("Failed to init configs: ", err)
}
if err := log.InitLogger(); err != nil {
logger.Fatalln("Failed to init db: ", err)
}
if *genCerts {
if err := cmd.GenCerts(); err != nil {
logger.Fatalln("Failed to generate certs: ", err)
}
return
}
logger.Info("Init database")
if err := database.InitDB(); err != nil {
logger.Fatalln("Failed to init db: ", err)
}
if *showFSL {
if fullFSL, err := cmd.ShowFSL(); err != nil {
logger.Fatalln("Failed to generate fsl: ", err)
} else {
logger.Info(strings.ReplaceAll(strings.ReplaceAll(fullFSL, ";", ";\n"), "CREATE", "\nCREATE"))
}
return
}
logger.Info("Init models")
if err := model.InitModels(); err != nil {
logger.Fatalln("Failed to init models: ", err)
}
if len(*resetUsername) > 0 {
logger.Infoln("reset user:", *resetUsername)
cmd.ResetUser(*resetUsername)
logger.Infoln("success!")
return
}
if *fakeLogs {
logger.Infoln("faking logs...")
cmd.FakeLogs()
logger.Infoln("success!")
return
}
if *push_fsl {
logger.Infoln("push fsl...")
if err := cmd.PushFSL(); err != nil {
logger.Fatalln("Failed to generate fsl: ", err)
}
logger.Infoln("success!")
return
}
logger.Info("Init FVM bytecode")
fvm.InitFVMBytecode()
if err := cron.StartCron(); err != nil {
logger.Fatalln("Failed to start cron: ", err)
}
if err := rpc.StartGRPCSever(); err != nil {
logger.Fatalln("Failed to start gRPC server: ", err)
}
gin.SetMode(gin.ReleaseMode)
r := gin.Default()
var option model.Options
database.GetDB().Where(&model.Options{Key: constants.SecretKey}).First(&option)
logger.Debugf("Secret: %s", option.Value)
store := cookie.NewStore([]byte(option.Value))
r.Use(sessions.Sessions("session", store))
publicRouters := r.Group("/api")
publicRouters.POST(api.Login, api.PostLogin)
publicRouters.POST(api.Logout, api.PostLogout)
publicRouters.POST(api.Behaviour, api.PostBehaviour)
publicRouters.POST(api.FalsePositives, api.PostFalsePositives)
publicRouters.GET(api.OTPUrl, api.GetOTPUrl)
publicRouters.GET(api.Version, api.GetVersion)
publicRouters.GET(api.UpgradeTips, api.GetUpgradeTips)
// test use
publicRouters.GET("/Ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
limitedRouters := r.Group("/api")
noAuth, existed := os.LookupEnv("NO_AUTH")
if existed && len(noAuth) >= 0 {
logger.Warn("No auth")
} else {
limitedRouters.Use(middleware.AuthRequired)
}
readOnly, existed := os.LookupEnv("READ_ONLY")
if existed && len(readOnly) >= 0 {
logger.Warn("Read only")
limitedRouters.Use(middleware.ReadOnly)
}
limitedRouters.GET(api.User, api.GetUser)
limitedRouters.GET(api.CurrentUser, api.GetCurrentUser)
// Admin-only user management
adminRouters := limitedRouters.Group("")
adminRouters.Use(middleware.RequireRole("admin"))
adminRouters.GET(api.Users, api.GetUsers)
adminRouters.POST(api.Users, api.PostUser)
adminRouters.PUT(api.Users+"/:id", api.PutUser)
adminRouters.DELETE(api.Users+"/:id", api.DeleteUser)
adminRouters.POST(api.Users+"/:id/reset-password", api.PostResetPassword)
// Operator and above (read + policy management)
operatorRouters := limitedRouters.Group("")
operatorRouters.Use(middleware.RequireRole("admin", "operator"))
operatorRouters.GET(api.DetectLogList, api.GetDetectLogList)
operatorRouters.GET(api.DetectLogDetail, api.GetDetectLogDetail)
operatorRouters.POST(api.Website, api.PostWebsite)
operatorRouters.PUT(api.Website, api.PutWebsite)
operatorRouters.DELETE(api.Website, api.DeleteWebsite)
operatorRouters.GET(api.Website, api.GetWebsite)
operatorRouters.POST(api.UploadSSLCert, api.PostUploadSSLCert)
operatorRouters.POST(api.SSLCert, api.PostSSLCert)
operatorRouters.POST(api.PolicyRule, api.PostPolicyRule)
operatorRouters.PUT(api.PolicyRule, api.PutPolicyRule)
operatorRouters.DELETE(api.PolicyRule, api.DeletePolicyRule)
operatorRouters.GET(api.PolicyRule, api.GetPolicyRule)
operatorRouters.PUT(api.SwitchPolicyRule, api.PutSwitchPolicyRule)
// All authenticated users can access dashboard and view policies
limitedRouters.GET(api.DashboardCounts, api.GetDashboardCounts)
limitedRouters.GET(api.DashboardSites, api.GetDashboardSites)
limitedRouters.GET(api.DashboardQps, api.GetDashboardQps)
limitedRouters.GET(api.DashboardRequests, api.GetDashboardRequests)
limitedRouters.GET(api.DashboardIntercepts, api.GetDashboardIntercepts)
limitedRouters.GET(api.PolicyGroupGlobal, api.GetPolicyGroupGlobal)
limitedRouters.PUT(api.PolicyGroupGlobal, api.PutPolicyGroupGlobal)
limitedRouters.POST(api.PolicyGroupPreset, api.PostPolicyGroupPreset)
limitedRouters.GET(api.SrcIPConfig, api.GetSrcIPConfig)
limitedRouters.PUT(api.SrcIPConfig, api.PutSrcIPConfig)
logger.Info("Staring...")
if err := r.Run(config.GlobalConfig.Server.ListenAddr); err != nil {
logger.Fatalln("Error occurred when running web server: ", err)
}
}