9 Commits

Author SHA1 Message Date
f32fb3836b feat: External Migration 2025-06-18 18:20:07 -04:00
ae94e5a7ce feat: External Migration 2025-06-17 19:09:14 -04:00
fff4327007 revert: Let Current Waiting room become External Migration 2025-06-17 18:25:24 -04:00
6abbb3a323 feat: waiting room 2025-06-15 17:10:11 -04:00
50380ebc5c feat: waiting room 2025-06-14 21:11:50 -04:00
aa1e760a79 feat: waiting room check api 2025-06-09 18:48:48 -04:00
fcb08478d2 feat: use checker to manager key and session 2025-06-03 12:21:28 -04:00
c203cdf684 feat: Waiting room checker 2025-06-02 18:51:05 -04:00
42e9d6502d feat: config and checker of waiting room 2025-05-29 21:15:34 -04:00
6 changed files with 190 additions and 18 deletions

View File

@ -15,3 +15,8 @@ VerifyBot:
verify_yandex_bot: true
verify_sogou_bot: true
verify_apple_bot: true
ExternalMigration:
enabled: false
redirect_url: "https://example.com/migration"
secret_key: "0378b0f84c4310279918d71a5647ba5d"
session_timeout: 1800

View File

@ -0,0 +1,103 @@
package check
import (
"crypto/hmac"
"crypto/sha512"
"fmt"
"server_torii/internal/action"
"server_torii/internal/config"
"server_torii/internal/dataType"
"server_torii/internal/utils"
"strconv"
"strings"
"time"
)
func ExternalMigration(reqData dataType.UserRequest, ruleSet *config.RuleSet, decision *action.Decision, sharedMem *dataType.SharedMemory) {
if !ruleSet.ExternalMigrationRule.Enabled {
decision.Set(action.Continue)
return
}
if !verifyExternalMigrationClearanceCookie(reqData, *ruleSet) {
decision.SetResponse(action.Done, []byte("EXTERNAL"), genExternalMigrationSessionID(reqData, *ruleSet))
return
}
decision.Set(action.Continue)
}
func GenExternalMigrationClearance(reqData dataType.UserRequest, ruleSet config.RuleSet) []byte {
timeNow := time.Now().Unix()
mac := hmac.New(sha512.New, []byte(ruleSet.ExternalMigrationRule.SecretKey))
mac.Write([]byte(fmt.Sprintf("%d%s%sEXTERNAL-CLEARANCE", timeNow, reqData.Host, utils.GetClearanceUserAgent(reqData.UserAgent))))
return []byte(fmt.Sprintf("%s:%s", fmt.Sprintf("%d", time.Now().Unix()), fmt.Sprintf("%x", mac.Sum(nil))))
}
func verifyExternalMigrationClearanceCookie(reqData dataType.UserRequest, ruleSet config.RuleSet) bool {
if reqData.ToriiClearance == "" {
return false
}
parts := strings.Split(reqData.ToriiClearance, ":")
if len(parts) != 2 {
return false
}
timestamp := parts[0]
expectedHash := parts[1]
timeNow := time.Now().Unix()
parsedTimestamp, err := strconv.ParseInt(timestamp, 10, 64)
if err != nil {
utils.LogError(reqData, "", fmt.Sprintf("Error parsing timestamp: %v", err))
return false
}
if timeNow-parsedTimestamp > ruleSet.ExternalMigrationRule.SessionTimeout {
return false
}
mac := hmac.New(sha512.New, []byte(ruleSet.ExternalMigrationRule.SecretKey))
mac.Write([]byte(fmt.Sprintf("%d%s%sEXTERNAL-CLEARANCE", parsedTimestamp, reqData.Host, utils.GetClearanceUserAgent(reqData.UserAgent))))
computedHash := fmt.Sprintf("%x", mac.Sum(nil))
return hmac.Equal([]byte(computedHash), []byte(expectedHash))
}
func genExternalMigrationSessionID(reqData dataType.UserRequest, ruleSet config.RuleSet) []byte {
timeNow := time.Now().Unix()
mac := hmac.New(sha512.New, []byte(ruleSet.ExternalMigrationRule.SecretKey))
mac.Write([]byte(fmt.Sprintf("%d%s%sEXTERNAL-SESSION", timeNow, reqData.Host, utils.GetClearanceUserAgent(reqData.UserAgent))))
return []byte(fmt.Sprintf("%s:%s", fmt.Sprintf("%d", time.Now().Unix()), fmt.Sprintf("%x", mac.Sum(nil))))
}
func VerifyExternalMigrationSessionIDCookie(reqData dataType.UserRequest, ruleSet config.RuleSet) bool {
if reqData.ToriiSessionID == "" {
return false
}
parts := strings.Split(reqData.ToriiSessionID, ":")
if len(parts) != 2 {
return false
}
timestamp := parts[0]
expectedHash := parts[1]
parsedTimestamp, err := strconv.ParseInt(timestamp, 10, 64)
if err != nil {
utils.LogError(reqData, "", fmt.Sprintf("Error parsing timestamp: %v", err))
return false
}
mac := hmac.New(sha512.New, []byte(ruleSet.ExternalMigrationRule.SecretKey))
mac.Write([]byte(fmt.Sprintf("%d%s%sEXTERNAL-SESSION", parsedTimestamp, reqData.Host, utils.GetClearanceUserAgent(reqData.UserAgent))))
computedHash := fmt.Sprintf("%x", mac.Sum(nil))
return hmac.Equal([]byte(computedHash), []byte(expectedHash))
}
func CalculateExternalMigrationHMAC(sessionID, timestampStr, secretKey string) string {
mac := hmac.New(sha512.New, []byte(secretKey))
mac.Write([]byte(fmt.Sprintf("%s%s", sessionID, timestampStr)))
return fmt.Sprintf("%x", mac.Sum(nil))
}

View File

@ -66,20 +66,22 @@ func LoadMainConfig(basePath string) (*MainConfig, error) {
// RuleSet stores all rules
type RuleSet struct {
IPAllowTrie *dataType.TrieNode
IPBlockTrie *dataType.TrieNode
URLAllowList *dataType.URLRuleList
URLBlockList *dataType.URLRuleList
CAPTCHARule *dataType.CaptchaRule
VerifyBotRule *dataType.VerifyBotRule
HTTPFloodRule *dataType.HTTPFloodRule
IPAllowTrie *dataType.TrieNode
IPBlockTrie *dataType.TrieNode
URLAllowList *dataType.URLRuleList
URLBlockList *dataType.URLRuleList
CAPTCHARule *dataType.CaptchaRule
VerifyBotRule *dataType.VerifyBotRule
HTTPFloodRule *dataType.HTTPFloodRule
ExternalMigrationRule *dataType.ExternalMigrationRule
}
// ruleSetWrapper
type ruleSetWrapper struct {
CAPTCHARule *dataType.CaptchaRule `yaml:"CAPTCHA"`
VerifyBotRule *dataType.VerifyBotRule `yaml:"VerifyBot"`
HTTPFloodRule httpFloodRuleWrapper `yaml:"HTTPFlood"`
CAPTCHARule *dataType.CaptchaRule `yaml:"CAPTCHA"`
VerifyBotRule *dataType.VerifyBotRule `yaml:"VerifyBot"`
HTTPFloodRule httpFloodRuleWrapper `yaml:"HTTPFlood"`
ExternalMigrationRule *dataType.ExternalMigrationRule `yaml:"ExternalMigration"`
}
type httpFloodRuleWrapper struct {
@ -90,13 +92,14 @@ type httpFloodRuleWrapper struct {
// LoadRules Load all rules from the specified path
func LoadRules(rulePath string) (*RuleSet, error) {
rs := RuleSet{
IPAllowTrie: &dataType.TrieNode{},
IPBlockTrie: &dataType.TrieNode{},
URLAllowList: &dataType.URLRuleList{},
URLBlockList: &dataType.URLRuleList{},
CAPTCHARule: &dataType.CaptchaRule{},
VerifyBotRule: &dataType.VerifyBotRule{},
HTTPFloodRule: &dataType.HTTPFloodRule{},
IPAllowTrie: &dataType.TrieNode{},
IPBlockTrie: &dataType.TrieNode{},
URLAllowList: &dataType.URLRuleList{},
URLBlockList: &dataType.URLRuleList{},
CAPTCHARule: &dataType.CaptchaRule{},
VerifyBotRule: &dataType.VerifyBotRule{},
HTTPFloodRule: &dataType.HTTPFloodRule{},
ExternalMigrationRule: &dataType.ExternalMigrationRule{},
}
// Load IP Allow List
@ -149,6 +152,9 @@ func loadServerRules(YAMLFile string, rs RuleSet) (*RuleSet, error) {
*rs.CAPTCHARule = *wrapper.CAPTCHARule
*rs.VerifyBotRule = *wrapper.VerifyBotRule
if wrapper.ExternalMigrationRule != nil {
*rs.ExternalMigrationRule = *wrapper.ExternalMigrationRule
}
rs.HTTPFloodRule.HTTPFloodSpeedLimit = make(map[int64]int64)
rs.HTTPFloodRule.HTTPFloodSameURILimit = make(map[int64]int64)

View File

@ -31,6 +31,13 @@ type HTTPFloodRule struct {
HTTPFloodSameURILimit map[int64]int64
}
type ExternalMigrationRule struct {
Enabled bool `yaml:"enabled"`
RedirectUrl string `yaml:"redirect_url"`
SecretKey string `yaml:"secret_key"`
SessionTimeout int64 `yaml:"session_timeout"`
}
type SharedMemory struct {
HTTPFloodSpeedLimitCounter *Counter
HTTPFloodSameURILimitCounter *Counter

View File

@ -25,6 +25,7 @@ func CheckMain(w http.ResponseWriter, userRequestData dataType.UserRequest, rule
checkFuncs = append(checkFuncs, check.URLBlockList)
checkFuncs = append(checkFuncs, check.VerifyBot)
checkFuncs = append(checkFuncs, check.HTTPFlood)
checkFuncs = append(checkFuncs, check.ExternalMigration)
checkFuncs = append(checkFuncs, check.Captcha)
for _, checkFunc := range checkFuncs {
@ -106,6 +107,11 @@ func CheckMain(w http.ResponseWriter, userRequestData dataType.UserRequest, rule
return
}
} else if bytes.Compare(decision.HTTPCode, []byte("EXTERNAL")) == 0 {
w.Header().Set("Set-Cookie", "__torii_sessionid="+string(decision.ResponseData)+"; Path=/; Max-Age=86400; Priority=High; HttpOnly; SameSite=Lax")
w.Header().Set("Location", ruleSet.ExternalMigrationRule.RedirectUrl+"?domain="+userRequestData.Host+"&session_id="+string(decision.ResponseData)+"&original_uri="+userRequestData.Uri)
w.WriteHeader(http.StatusFound)
return
} else {
//should never happen
utils.LogError(userRequestData, fmt.Sprintf("Error access in wrong state: %v", decision), "CheckMain")

View File

@ -2,6 +2,7 @@ package server
import (
"bytes"
"crypto/hmac"
"html/template"
"net/http"
"server_torii/internal/action"
@ -9,6 +10,7 @@ import (
"server_torii/internal/config"
"server_torii/internal/dataType"
"server_torii/internal/utils"
"strconv"
"time"
)
@ -20,6 +22,8 @@ func CheckTorii(w http.ResponseWriter, r *http.Request, reqData dataType.UserReq
check.CheckCaptcha(r, reqData, ruleSet, decision)
} else if reqData.Uri == cfg.WebPath+"/health_check" {
decision.SetResponse(action.Done, []byte("200"), []byte("ok"))
} else if reqData.Uri == cfg.WebPath+"/external_migration" {
handleExternalMigration(w, r, reqData, ruleSet, decision)
}
if bytes.Compare(decision.HTTPCode, []byte("200")) == 0 {
if bytes.Compare(decision.ResponseData, []byte("ok")) == 0 {
@ -88,3 +92,44 @@ func CheckTorii(w http.ResponseWriter, r *http.Request, reqData dataType.UserReq
}
}
}
func handleExternalMigration(w http.ResponseWriter, r *http.Request, data dataType.UserRequest, set *config.RuleSet, decision *action.Decision) {
if !set.ExternalMigrationRule.Enabled {
decision.SetResponse(action.Done, []byte("200"), []byte("bad"))
return
}
originalURI := r.URL.Query().Get("original_uri")
timestampStr := r.URL.Query().Get("timestamp")
hmacParam := r.URL.Query().Get("hmac")
if timestampStr == "" || hmacParam == "" {
decision.SetResponse(action.Done, []byte("200"), []byte("bad"))
return
}
timestamp, err := strconv.ParseInt(timestampStr, 10, 64)
if err != nil {
decision.SetResponse(action.Done, []byte("200"), []byte("bad"))
return
}
currentTime := time.Now().Unix()
if currentTime-timestamp > set.ExternalMigrationRule.SessionTimeout {
decision.SetResponse(action.Done, []byte("200"), []byte("bad"))
return
}
if !check.VerifyExternalMigrationSessionIDCookie(data, *set) {
decision.SetResponse(action.Done, []byte("200"), []byte("badSession"))
return
}
expectedHMAC := check.CalculateExternalMigrationHMAC(data.ToriiSessionID, timestampStr, set.ExternalMigrationRule.SecretKey)
if !hmac.Equal([]byte(expectedHMAC), []byte(hmacParam)) {
decision.SetResponse(action.Done, []byte("200"), []byte("bad"))
return
}
decision.SetResponse(action.Continue, []byte("302"), []byte(originalURI))
}