mirror of
https://github.com/Rayzggz/server_torii.git
synced 2025-06-22 23:11:30 +08:00
Compare commits
5 Commits
feature/ex
...
feature/do
Author | SHA1 | Date | |
---|---|---|---|
8b7d8fe91c | |||
d80b49045b | |||
c72117e95e | |||
c69c5b5cd7 | |||
4a9afa8784 |
49
.github/workflows/release-tag-version.yml
vendored
Normal file
49
.github/workflows/release-tag-version.yml
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
name: release-tag-version
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
packages: write # to publish to ghcr.io
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: git fetch --unshallow --quiet --tags --force
|
||||||
|
- uses: docker/setup-qemu-action@v3
|
||||||
|
- uses: docker/setup-buildx-action@v3
|
||||||
|
- uses: docker/metadata-action@v5
|
||||||
|
id: meta
|
||||||
|
with:
|
||||||
|
images: |-
|
||||||
|
ghcr.io/rayzggz/server_torii
|
||||||
|
# this will generate tags in the following format:
|
||||||
|
# latest
|
||||||
|
# 1
|
||||||
|
# 1.2
|
||||||
|
# 1.2.3
|
||||||
|
tags: |
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
- name: Login to GHCR using PAT
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: build rootful docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
32
Dockerfile
Normal file
32
Dockerfile
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
ARG ALPINE_VERSION=3.21
|
||||||
|
ARG GO_VERSION=1.23.5
|
||||||
|
ARG AUTHOR=Rayzggz
|
||||||
|
ARG SERVER_NAME=server_torii
|
||||||
|
|
||||||
|
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS builder
|
||||||
|
|
||||||
|
ARG ALPINE_VERSION
|
||||||
|
ARG GO_VERSION
|
||||||
|
ARG SERVER_NAME
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
TARGETOS=${TARGETOS:-linux}; \
|
||||||
|
TARGETARCH=${TARGETARCH:-amd64}; \
|
||||||
|
echo "Building for TARGETOS=${TARGETOS} TARGETARCH=${TARGETARCH}"; \
|
||||||
|
go mod tidy; \
|
||||||
|
CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="-s -w" -o /app/${SERVER_NAME}
|
||||||
|
|
||||||
|
FROM alpine:${ALPINE_VERSION} AS final
|
||||||
|
|
||||||
|
ARG SERVER_NAME
|
||||||
|
|
||||||
|
COPY --from=builder /app/${SERVER_NAME} /app/${SERVER_NAME}
|
||||||
|
|
||||||
|
EXPOSE 25555
|
||||||
|
ENTRYPOINT ["/app/server_torii"]
|
4
config_example/rules/CAPTCHA.yml
Normal file
4
config_example/rules/CAPTCHA.yml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
secret_key: "0378b0f84c4310279918d71a5647ba5d"
|
||||||
|
captcha_validate_time: 600
|
||||||
|
captcha_challenge_session_timeout: 120
|
||||||
|
hcaptcha_secret: ""
|
4
config_example/rules/HTTPFlood.yml
Normal file
4
config_example/rules/HTTPFlood.yml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
HTTPFloodSpeedLimit:
|
||||||
|
- "150/10s"
|
||||||
|
HTTPFloodSameURILimit:
|
||||||
|
- "50/10s"
|
@ -1,22 +0,0 @@
|
|||||||
CAPTCHA:
|
|
||||||
secret_key: "0378b0f84c4310279918d71a5647ba5d"
|
|
||||||
captcha_validate_time: 600
|
|
||||||
captcha_challenge_session_timeout: 120
|
|
||||||
hcaptcha_secret: ""
|
|
||||||
HTTPFlood:
|
|
||||||
HTTPFloodSpeedLimit:
|
|
||||||
- "150/10s"
|
|
||||||
HTTPFloodSameURILimit:
|
|
||||||
- "50/10s"
|
|
||||||
VerifyBot:
|
|
||||||
verify_google_bot: true
|
|
||||||
verify_bing_bot: true
|
|
||||||
verify_baidu_bot: true
|
|
||||||
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
|
|
6
config_example/rules/VerifyBot.yml
Normal file
6
config_example/rules/VerifyBot.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
verify_google_bot: true
|
||||||
|
verify_bing_bot: true
|
||||||
|
verify_baidu_bot: true
|
||||||
|
verify_yandex_bot: true
|
||||||
|
verify_sogou_bot: true
|
||||||
|
verify_apple_bot: true
|
@ -1,103 +0,0 @@
|
|||||||
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))
|
|
||||||
}
|
|
@ -2,7 +2,6 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
@ -28,20 +27,6 @@ type MainConfig struct {
|
|||||||
|
|
||||||
// LoadMainConfig Read the configuration file and return the configuration object
|
// LoadMainConfig Read the configuration file and return the configuration object
|
||||||
func LoadMainConfig(basePath string) (*MainConfig, error) {
|
func LoadMainConfig(basePath string) (*MainConfig, error) {
|
||||||
|
|
||||||
defaultCfg := MainConfig{
|
|
||||||
Port: "25555",
|
|
||||||
WebPath: "/torii",
|
|
||||||
RulePath: "/www/server_torii/config/rules",
|
|
||||||
ErrorPage: "/www/server_torii/config/error_page",
|
|
||||||
LogPath: "/www/server_torii/log/",
|
|
||||||
NodeName: "Server Torii",
|
|
||||||
ConnectingHostHeaders: []string{"Torii-Real-Host"},
|
|
||||||
ConnectingIPHeaders: []string{"Torii-Real-IP"},
|
|
||||||
ConnectingURIHeaders: []string{"Torii-Original-URI"},
|
|
||||||
ConnectingCaptchaStatusHeaders: []string{"Torii-Captcha-Status"},
|
|
||||||
}
|
|
||||||
|
|
||||||
exePath, err := os.Executable()
|
exePath, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -53,12 +38,12 @@ func LoadMainConfig(basePath string) (*MainConfig, error) {
|
|||||||
|
|
||||||
data, err := os.ReadFile(configPath)
|
data, err := os.ReadFile(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &defaultCfg, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var cfg MainConfig
|
var cfg MainConfig
|
||||||
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
||||||
return &defaultCfg, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &cfg, nil
|
return &cfg, nil
|
||||||
@ -66,115 +51,95 @@ func LoadMainConfig(basePath string) (*MainConfig, error) {
|
|||||||
|
|
||||||
// RuleSet stores all rules
|
// RuleSet stores all rules
|
||||||
type RuleSet struct {
|
type RuleSet struct {
|
||||||
IPAllowTrie *dataType.TrieNode
|
IPAllowTrie *dataType.TrieNode
|
||||||
IPBlockTrie *dataType.TrieNode
|
IPBlockTrie *dataType.TrieNode
|
||||||
URLAllowList *dataType.URLRuleList
|
URLAllowList *dataType.URLRuleList
|
||||||
URLBlockList *dataType.URLRuleList
|
URLBlockList *dataType.URLRuleList
|
||||||
CAPTCHARule *dataType.CaptchaRule
|
CAPTCHARule *dataType.CaptchaRule
|
||||||
VerifyBotRule *dataType.VerifyBotRule
|
VerifyBotRule *dataType.VerifyBotRule
|
||||||
HTTPFloodRule *dataType.HTTPFloodRule
|
HTTPFloodRule *dataType.HTTPFloodRule
|
||||||
ExternalMigrationRule *dataType.ExternalMigrationRule
|
|
||||||
}
|
|
||||||
|
|
||||||
// ruleSetWrapper
|
|
||||||
type ruleSetWrapper struct {
|
|
||||||
CAPTCHARule *dataType.CaptchaRule `yaml:"CAPTCHA"`
|
|
||||||
VerifyBotRule *dataType.VerifyBotRule `yaml:"VerifyBot"`
|
|
||||||
HTTPFloodRule httpFloodRuleWrapper `yaml:"HTTPFlood"`
|
|
||||||
ExternalMigrationRule *dataType.ExternalMigrationRule `yaml:"ExternalMigration"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type httpFloodRuleWrapper struct {
|
|
||||||
HTTPFloodSpeedLimit []string `yaml:"HTTPFloodSpeedLimit"`
|
|
||||||
HTTPFloodSameURILimit []string `yaml:"HTTPFloodSameURILimit"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadRules Load all rules from the specified path
|
// LoadRules Load all rules from the specified path
|
||||||
func LoadRules(rulePath string) (*RuleSet, error) {
|
func LoadRules(rulePath string) (*RuleSet, error) {
|
||||||
rs := RuleSet{
|
rs := RuleSet{
|
||||||
IPAllowTrie: &dataType.TrieNode{},
|
IPAllowTrie: &dataType.TrieNode{},
|
||||||
IPBlockTrie: &dataType.TrieNode{},
|
IPBlockTrie: &dataType.TrieNode{},
|
||||||
URLAllowList: &dataType.URLRuleList{},
|
URLAllowList: &dataType.URLRuleList{},
|
||||||
URLBlockList: &dataType.URLRuleList{},
|
URLBlockList: &dataType.URLRuleList{},
|
||||||
CAPTCHARule: &dataType.CaptchaRule{},
|
CAPTCHARule: &dataType.CaptchaRule{},
|
||||||
VerifyBotRule: &dataType.VerifyBotRule{},
|
VerifyBotRule: &dataType.VerifyBotRule{},
|
||||||
HTTPFloodRule: &dataType.HTTPFloodRule{},
|
HTTPFloodRule: &dataType.HTTPFloodRule{},
|
||||||
ExternalMigrationRule: &dataType.ExternalMigrationRule{},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load IP Allow List
|
// Load IP Allow List
|
||||||
ipAllowFile := filepath.Join(rulePath, "/IP_AllowList.conf")
|
ipAllowFile := rulePath + "/IP_AllowList.conf"
|
||||||
if err := loadIPRules(ipAllowFile, rs.IPAllowTrie); err != nil {
|
if err := loadIPRules(ipAllowFile, rs.IPAllowTrie); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load IP Block List
|
// Load IP Block List
|
||||||
ipBlockFile := filepath.Join(rulePath, "/IP_BlockList.conf")
|
ipBlockFile := rulePath + "/IP_BlockList.conf"
|
||||||
if err := loadIPRules(ipBlockFile, rs.IPBlockTrie); err != nil {
|
if err := loadIPRules(ipBlockFile, rs.IPBlockTrie); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load URL Allow List
|
// Load URL Allow List
|
||||||
urlAllowFile := filepath.Join(rulePath, "/URL_AllowList.conf")
|
urlAllowFile := rulePath + "/URL_AllowList.conf"
|
||||||
if err := loadURLRules(urlAllowFile, rs.URLAllowList); err != nil {
|
if err := loadURLRules(urlAllowFile, rs.URLAllowList); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load URL Block List
|
// Load URL Block List
|
||||||
urlBlockFile := filepath.Join(rulePath, "/URL_BlockList.conf")
|
urlBlockFile := rulePath + "/URL_BlockList.conf"
|
||||||
if err := loadURLRules(urlBlockFile, rs.URLBlockList); err != nil {
|
if err := loadURLRules(urlBlockFile, rs.URLBlockList); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
YAMLFile := filepath.Join(rulePath, "Server.yml")
|
// Load CAPTCHA Rule
|
||||||
set, err := loadServerRules(YAMLFile, rs)
|
captchaFile := rulePath + "/CAPTCHA.yml"
|
||||||
if err != nil {
|
if err := loadCAPTCHARule(captchaFile, rs.CAPTCHARule); err != nil {
|
||||||
return set, err
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Verify Bot Rule
|
||||||
|
verifyBotFile := rulePath + "/VerifyBot.yml"
|
||||||
|
if err := loadVerifyBotRule(verifyBotFile, rs.VerifyBotRule); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load HTTP Flood Rule
|
||||||
|
httpFloodFile := rulePath + "/HTTPFlood.yml"
|
||||||
|
if err := loadHTTPFloodRule(httpFloodFile, rs.HTTPFloodRule); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &rs, nil
|
return &rs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadServerRules(YAMLFile string, rs RuleSet) (*RuleSet, error) {
|
func loadCAPTCHARule(file string, rule *dataType.CaptchaRule) error {
|
||||||
yamlData, err := os.ReadFile(YAMLFile)
|
data, err := os.ReadFile(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
return err
|
||||||
return nil, fmt.Errorf("[ERROR] rules file %s does not exist: %w", YAMLFile, err)
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("[ERROR] failed to read rules file %s: %w", YAMLFile, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var wrapper ruleSetWrapper
|
if err := yaml.Unmarshal(data, &rule); err != nil {
|
||||||
if err := yaml.Unmarshal(yamlData, &wrapper); err != nil {
|
return err
|
||||||
return nil, fmt.Errorf("[ERROR] failed to parse rules file %s: %w", YAMLFile, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*rs.CAPTCHARule = *wrapper.CAPTCHARule
|
return nil
|
||||||
*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)
|
|
||||||
|
|
||||||
for _, s := range wrapper.HTTPFloodRule.HTTPFloodSpeedLimit {
|
func loadVerifyBotRule(file string, rule *dataType.VerifyBotRule) error {
|
||||||
limit, seconds, err := utils.ParseRate(s)
|
data, err := os.ReadFile(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
|
||||||
rs.HTTPFloodRule.HTTPFloodSpeedLimit[seconds] = limit
|
|
||||||
}
|
}
|
||||||
|
if err := yaml.Unmarshal(data, &rule); err != nil {
|
||||||
for _, s := range wrapper.HTTPFloodRule.HTTPFloodSameURILimit {
|
return err
|
||||||
limit, seconds, err := utils.ParseRate(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rs.HTTPFloodRule.HTTPFloodSameURILimit[seconds] = limit
|
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadIPRules read the IP rule file and insert the rules into the trie
|
// loadIPRules read the IP rule file and insert the rules into the trie
|
||||||
@ -252,3 +217,43 @@ func loadURLRules(filePath string, list *dataType.URLRuleList) error {
|
|||||||
|
|
||||||
return scanner.Err()
|
return scanner.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadHTTPFloodRule(file string, rule *dataType.HTTPFloodRule) error {
|
||||||
|
data, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpFloodRuleYAML struct {
|
||||||
|
HTTPFloodSpeedLimit []string `yaml:"HTTPFloodSpeedLimit"`
|
||||||
|
HTTPFloodSameURILimit []string `yaml:"HTTPFloodSameURILimit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var ymlRule httpFloodRuleYAML
|
||||||
|
err = yaml.Unmarshal(data, &ymlRule)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rule.HTTPFloodSpeedLimit = make(map[int64]int64)
|
||||||
|
rule.HTTPFloodSameURILimit = make(map[int64]int64)
|
||||||
|
|
||||||
|
for _, s := range ymlRule.HTTPFloodSpeedLimit {
|
||||||
|
limit, seconds, err := utils.ParseRate(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rule.HTTPFloodSpeedLimit[seconds] = limit
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range ymlRule.HTTPFloodSameURILimit {
|
||||||
|
limit, seconds, err := utils.ParseRate(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rule.HTTPFloodSameURILimit[seconds] = limit
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -31,13 +31,6 @@ type HTTPFloodRule struct {
|
|||||||
HTTPFloodSameURILimit map[int64]int64
|
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 {
|
type SharedMemory struct {
|
||||||
HTTPFloodSpeedLimitCounter *Counter
|
HTTPFloodSpeedLimitCounter *Counter
|
||||||
HTTPFloodSameURILimitCounter *Counter
|
HTTPFloodSameURILimitCounter *Counter
|
||||||
|
@ -25,7 +25,6 @@ func CheckMain(w http.ResponseWriter, userRequestData dataType.UserRequest, rule
|
|||||||
checkFuncs = append(checkFuncs, check.URLBlockList)
|
checkFuncs = append(checkFuncs, check.URLBlockList)
|
||||||
checkFuncs = append(checkFuncs, check.VerifyBot)
|
checkFuncs = append(checkFuncs, check.VerifyBot)
|
||||||
checkFuncs = append(checkFuncs, check.HTTPFlood)
|
checkFuncs = append(checkFuncs, check.HTTPFlood)
|
||||||
checkFuncs = append(checkFuncs, check.ExternalMigration)
|
|
||||||
checkFuncs = append(checkFuncs, check.Captcha)
|
checkFuncs = append(checkFuncs, check.Captcha)
|
||||||
|
|
||||||
for _, checkFunc := range checkFuncs {
|
for _, checkFunc := range checkFuncs {
|
||||||
@ -107,11 +106,6 @@ func CheckMain(w http.ResponseWriter, userRequestData dataType.UserRequest, rule
|
|||||||
return
|
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 {
|
} else {
|
||||||
//should never happen
|
//should never happen
|
||||||
utils.LogError(userRequestData, fmt.Sprintf("Error access in wrong state: %v", decision), "CheckMain")
|
utils.LogError(userRequestData, fmt.Sprintf("Error access in wrong state: %v", decision), "CheckMain")
|
||||||
|
@ -2,7 +2,6 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/hmac"
|
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"server_torii/internal/action"
|
"server_torii/internal/action"
|
||||||
@ -10,7 +9,6 @@ import (
|
|||||||
"server_torii/internal/config"
|
"server_torii/internal/config"
|
||||||
"server_torii/internal/dataType"
|
"server_torii/internal/dataType"
|
||||||
"server_torii/internal/utils"
|
"server_torii/internal/utils"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,8 +20,6 @@ func CheckTorii(w http.ResponseWriter, r *http.Request, reqData dataType.UserReq
|
|||||||
check.CheckCaptcha(r, reqData, ruleSet, decision)
|
check.CheckCaptcha(r, reqData, ruleSet, decision)
|
||||||
} else if reqData.Uri == cfg.WebPath+"/health_check" {
|
} else if reqData.Uri == cfg.WebPath+"/health_check" {
|
||||||
decision.SetResponse(action.Done, []byte("200"), []byte("ok"))
|
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.HTTPCode, []byte("200")) == 0 {
|
||||||
if bytes.Compare(decision.ResponseData, []byte("ok")) == 0 {
|
if bytes.Compare(decision.ResponseData, []byte("ok")) == 0 {
|
||||||
@ -92,44 +88,3 @@ 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))
|
|
||||||
}
|
|
||||||
|
2
main.go
2
main.go
@ -27,7 +27,7 @@ func main() {
|
|||||||
// Load MainConfig
|
// Load MainConfig
|
||||||
cfg, err := config.LoadMainConfig(basePath)
|
cfg, err := config.LoadMainConfig(basePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[ERROR] Load config failed: %v. Using default config.", err)
|
log.Fatalf("Load config failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load rules
|
// Load rules
|
||||||
|
Reference in New Issue
Block a user