Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09dcf0d712 | ||
|
|
60aebd9306 | ||
|
|
04191d04d3 | ||
|
|
b80a5c525f | ||
|
|
265c1e5312 | ||
|
|
2723f705b6 | ||
|
|
b4cddd6341 | ||
|
|
5636a81d48 | ||
|
|
d8059960de | ||
|
|
17af4064af | ||
|
|
15f37d2c93 | ||
|
|
6dc3aa8cb7 | ||
|
|
900cccf2f1 | ||
|
|
1fec88dfc6 | ||
|
|
7da9363336 | ||
|
|
d82e633bba | ||
|
|
b363bbaafd | ||
|
|
92a20e3c9a | ||
|
|
5742dfb263 | ||
|
|
0ae63511d5 | ||
|
|
aa60092c20 | ||
|
|
54fc265d24 | ||
|
|
a5ac900784 | ||
|
|
4053f1da32 | ||
|
|
0374ccd8a8 | ||
|
|
1d46c446cf | ||
|
|
54b66805f9 | ||
|
|
f7afcbde92 |
@@ -473,6 +473,7 @@ func (this *FileListDB) initTables(times int) error {
|
|||||||
{
|
{
|
||||||
// expiredAt - 过期时间,用来判断有无过期
|
// expiredAt - 过期时间,用来判断有无过期
|
||||||
// staleAt - 过时缓存最大时间,用来清理缓存
|
// staleAt - 过时缓存最大时间,用来清理缓存
|
||||||
|
// 不对 hash 增加 unique 参数,是尽可能避免产生 malformed 错误
|
||||||
_, err := this.writeDB.Exec(`CREATE TABLE IF NOT EXISTS "` + this.itemsTableName + `" (
|
_, err := this.writeDB.Exec(`CREATE TABLE IF NOT EXISTS "` + this.itemsTableName + `" (
|
||||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
"hash" varchar(32),
|
"hash" varchar(32),
|
||||||
@@ -498,7 +499,7 @@ ON "` + this.itemsTableName + `" (
|
|||||||
"staleAt" ASC
|
"staleAt" ASC
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "hash"
|
CREATE INDEX IF NOT EXISTS "hash"
|
||||||
ON "` + this.itemsTableName + `" (
|
ON "` + this.itemsTableName + `" (
|
||||||
"hash" ASC
|
"hash" ASC
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/iwind/TeaGo/rands"
|
"github.com/iwind/TeaGo/rands"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
"golang.org/x/text/message"
|
"golang.org/x/text/message"
|
||||||
"math"
|
"math"
|
||||||
@@ -58,6 +59,7 @@ const (
|
|||||||
HotItemLifeSeconds int64 = 3600 // 热点数据生命周期
|
HotItemLifeSeconds int64 = 3600 // 热点数据生命周期
|
||||||
FileToMemoryMaxSize = 32 * sizes.M // 可以从文件写入到内存的最大文件尺寸
|
FileToMemoryMaxSize = 32 * sizes.M // 可以从文件写入到内存的最大文件尺寸
|
||||||
FileTmpSuffix = ".tmp"
|
FileTmpSuffix = ".tmp"
|
||||||
|
MinDiskSpace = 5 << 30 // 当前磁盘最小剩余空间
|
||||||
)
|
)
|
||||||
|
|
||||||
var sharedWritingFileKeyMap = map[string]zero.Zero{} // key => bool
|
var sharedWritingFileKeyMap = map[string]zero.Zero{} // key => bool
|
||||||
@@ -90,6 +92,8 @@ type FileStorage struct {
|
|||||||
ignoreKeys *setutils.FixedSet
|
ignoreKeys *setutils.FixedSet
|
||||||
|
|
||||||
openFileCache *OpenFileCache
|
openFileCache *OpenFileCache
|
||||||
|
|
||||||
|
diskIsFull bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFileStorage(policy *serverconfigs.HTTPCachePolicy) *FileStorage {
|
func NewFileStorage(policy *serverconfigs.HTTPCachePolicy) *FileStorage {
|
||||||
@@ -287,6 +291,9 @@ func (this *FileStorage) Init() error {
|
|||||||
// open file cache
|
// open file cache
|
||||||
this.initOpenFileCache()
|
this.initOpenFileCache()
|
||||||
|
|
||||||
|
// 检查磁盘空间
|
||||||
|
this.checkDiskSpace()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,6 +404,11 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, siz
|
|||||||
return nil, ErrWritingUnavailable
|
return nil, ErrWritingUnavailable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 当前磁盘可用容量是否严重不足
|
||||||
|
if this.diskIsFull {
|
||||||
|
return nil, NewCapacityError("the disk is full")
|
||||||
|
}
|
||||||
|
|
||||||
// 是否已忽略
|
// 是否已忽略
|
||||||
if this.ignoreKeys.Has(key) {
|
if this.ignoreKeys.Has(key) {
|
||||||
return nil, ErrEntityTooLarge
|
return nil, ErrEntityTooLarge
|
||||||
@@ -938,18 +950,25 @@ func (this *FileStorage) initList() error {
|
|||||||
|
|
||||||
// 清理任务
|
// 清理任务
|
||||||
func (this *FileStorage) purgeLoop() {
|
func (this *FileStorage) purgeLoop() {
|
||||||
|
// 检查磁盘剩余空间
|
||||||
|
this.checkDiskSpace()
|
||||||
|
|
||||||
// 计算是否应该开启LFU清理
|
// 计算是否应该开启LFU清理
|
||||||
var capacityBytes = this.policy.CapacityBytes()
|
var capacityBytes = this.policy.CapacityBytes()
|
||||||
var startLFU = false
|
var startLFU = false
|
||||||
var usedPercent = float32(this.TotalDiskSize()*100) / float32(capacityBytes)
|
|
||||||
var lfuFreePercent = this.policy.PersistenceLFUFreePercent
|
var lfuFreePercent = this.policy.PersistenceLFUFreePercent
|
||||||
if lfuFreePercent <= 0 {
|
if lfuFreePercent <= 0 {
|
||||||
lfuFreePercent = 5
|
lfuFreePercent = 5
|
||||||
}
|
}
|
||||||
if capacityBytes > 0 {
|
if this.diskIsFull {
|
||||||
if lfuFreePercent < 100 {
|
startLFU = true
|
||||||
if usedPercent >= 100-lfuFreePercent {
|
} else {
|
||||||
startLFU = true
|
var usedPercent = float32(this.TotalDiskSize()*100) / float32(capacityBytes)
|
||||||
|
if capacityBytes > 0 {
|
||||||
|
if lfuFreePercent < 100 {
|
||||||
|
if usedPercent >= 100-lfuFreePercent {
|
||||||
|
startLFU = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1327,3 +1346,15 @@ func (this *FileStorage) runMemoryStorageSafety(f func(memoryStorage *MemoryStor
|
|||||||
f(memoryStorage)
|
f(memoryStorage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查磁盘剩余空间
|
||||||
|
func (this *FileStorage) checkDiskSpace() {
|
||||||
|
if this.options != nil && len(this.options.Dir) > 0 {
|
||||||
|
var stat unix.Statfs_t
|
||||||
|
err := unix.Statfs(this.options.Dir, &stat)
|
||||||
|
if err == nil {
|
||||||
|
var availableBytes = stat.Bavail * uint64(stat.Bsize)
|
||||||
|
this.diskIsFull = availableBytes < MinDiskSpace
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "0.5.3"
|
Version = "0.5.6"
|
||||||
|
|
||||||
ProductName = "Edge Node"
|
ProductName = "Edge Node"
|
||||||
ProcessName = "edge-node"
|
ProcessName = "edge-node"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -74,10 +75,16 @@ func (this *IPTablesAction) runAction(action string, listType IPListType, item *
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *IPTablesAction) runActionSingleIP(action string, listType IPListType, item *pb.IPItem) error {
|
func (this *IPTablesAction) runActionSingleIP(action string, listType IPListType, item *pb.IPItem) error {
|
||||||
|
// 暂时不支持ipv6
|
||||||
|
// TODO 将来支持ipv6
|
||||||
|
if utils.IsIPv6(item.IpFrom) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if item.Type == "all" {
|
if item.Type == "all" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
path := this.config.Path
|
var path = this.config.Path
|
||||||
var err error
|
var err error
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
path, err = exec.LookPath("iptables")
|
path, err = exec.LookPath("iptables")
|
||||||
@@ -88,6 +95,7 @@ func (this *IPTablesAction) runActionSingleIP(action string, listType IPListType
|
|||||||
this.iptablesNotFound = true
|
this.iptablesNotFound = true
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
this.config.Path = path
|
||||||
}
|
}
|
||||||
iptablesAction := ""
|
iptablesAction := ""
|
||||||
switch action {
|
switch action {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package iplibrary
|
package iplibrary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
@@ -141,6 +142,12 @@ func (this *IPListManager) init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *IPListManager) loop() error {
|
func (this *IPListManager) loop() error {
|
||||||
|
// 是否同步IP名单
|
||||||
|
nodeConfig, _ := nodeconfigs.SharedNodeConfig()
|
||||||
|
if nodeConfig != nil && !nodeConfig.EnableIPLists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
hasNext, err := this.fetch()
|
hasNext, err := this.fetch()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1603,9 +1603,25 @@ func (this *HTTPRequest) fixRequestHeader(header http.Header) {
|
|||||||
header.Del(k)
|
header.Del(k)
|
||||||
k = strings.ReplaceAll(k, "-Websocket-", "-WebSocket-")
|
k = strings.ReplaceAll(k, "-Websocket-", "-WebSocket-")
|
||||||
header[k] = v
|
header[k] = v
|
||||||
} else if k == "Www-Authenticate" {
|
} else if strings.HasPrefix(k, "Sec-Ch") {
|
||||||
header.Del(k)
|
header.Del(k)
|
||||||
header["WWW-Authenticate"] = v
|
k = strings.ReplaceAll(k, "Sec-Ch-Ua", "Sec-CH-UA")
|
||||||
|
header[k] = v
|
||||||
|
} else {
|
||||||
|
switch k {
|
||||||
|
case "Www-Authenticate":
|
||||||
|
header.Del(k)
|
||||||
|
header["WWW-Authenticate"] = v
|
||||||
|
case "A-Im":
|
||||||
|
header.Del(k)
|
||||||
|
header["A-IM"] = v
|
||||||
|
case "Content-Md5":
|
||||||
|
header.Del(k)
|
||||||
|
header["Content-MD5"] = v
|
||||||
|
case "Sec-Gpc":
|
||||||
|
header.Del(k)
|
||||||
|
header["Content-GPC"] = v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ import (
|
|||||||
|
|
||||||
// 读取缓存
|
// 读取缓存
|
||||||
func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
||||||
|
// 需要动态Upgrade的不缓存
|
||||||
|
if len(this.RawReq.Header.Get("Upgrade")) > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.cacheCanTryStale = false
|
this.cacheCanTryStale = false
|
||||||
|
|
||||||
var cachePolicy = this.ReqServer.HTTPCachePolicy
|
var cachePolicy = this.ReqServer.HTTPCachePolicy
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -13,7 +17,7 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
if this.web.MergeSlashes {
|
if this.web.MergeSlashes {
|
||||||
urlPath = utils.CleanPath(urlPath)
|
urlPath = utils.CleanPath(urlPath)
|
||||||
}
|
}
|
||||||
fullURL := this.requestScheme() + "://" + this.ReqHost + urlPath
|
var fullURL = this.requestScheme() + "://" + this.ReqHost + urlPath
|
||||||
for _, u := range this.web.HostRedirects {
|
for _, u := range this.web.HostRedirects {
|
||||||
if !u.IsOn {
|
if !u.IsOn {
|
||||||
continue
|
continue
|
||||||
@@ -21,11 +25,50 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
if !u.MatchRequest(this.Format) {
|
if !u.MatchRequest(this.Format) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if u.MatchPrefix { // 匹配前缀
|
if len(u.Type) == 0 || u.Type == serverconfigs.HTTPHostRedirectTypeURL {
|
||||||
if strings.HasPrefix(fullURL, u.BeforeURL) {
|
if u.MatchPrefix { // 匹配前缀
|
||||||
afterURL := u.AfterURL
|
if strings.HasPrefix(fullURL, u.BeforeURL) {
|
||||||
if u.KeepRequestURI {
|
afterURL := u.AfterURL
|
||||||
afterURL += this.RawReq.URL.RequestURI()
|
if u.KeepRequestURI {
|
||||||
|
afterURL += this.RawReq.URL.RequestURI()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 前后是否一致
|
||||||
|
if fullURL == afterURL {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Status <= 0 {
|
||||||
|
u.Status = http.StatusTemporaryRedirect
|
||||||
|
}
|
||||||
|
this.processResponseHeaders(this.writer.Header(), u.Status)
|
||||||
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else if u.MatchRegexp { // 正则匹配
|
||||||
|
var reg = u.BeforeURLRegexp()
|
||||||
|
if reg == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var matches = reg.FindStringSubmatch(fullURL)
|
||||||
|
if len(matches) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var afterURL = u.AfterURL
|
||||||
|
for i, match := range matches {
|
||||||
|
afterURL = strings.ReplaceAll(afterURL, "${"+strconv.Itoa(i)+"}", match)
|
||||||
|
}
|
||||||
|
|
||||||
|
var subNames = reg.SubexpNames()
|
||||||
|
if len(subNames) > 0 {
|
||||||
|
for _, subName := range subNames {
|
||||||
|
if len(subName) > 0 {
|
||||||
|
index := reg.SubexpIndex(subName)
|
||||||
|
if index > -1 {
|
||||||
|
afterURL = strings.ReplaceAll(afterURL, "${"+subName+"}", matches[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 前后是否一致
|
// 前后是否一致
|
||||||
@@ -33,69 +76,6 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.Status <= 0 {
|
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusTemporaryRedirect)
|
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, http.StatusTemporaryRedirect)
|
|
||||||
} else {
|
|
||||||
this.processResponseHeaders(this.writer.Header(), u.Status)
|
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} else if u.MatchRegexp { // 正则匹配
|
|
||||||
reg := u.BeforeURLRegexp()
|
|
||||||
if reg == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
matches := reg.FindStringSubmatch(fullURL)
|
|
||||||
if len(matches) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
afterURL := u.AfterURL
|
|
||||||
for i, match := range matches {
|
|
||||||
afterURL = strings.ReplaceAll(afterURL, "${"+strconv.Itoa(i)+"}", match)
|
|
||||||
}
|
|
||||||
|
|
||||||
subNames := reg.SubexpNames()
|
|
||||||
if len(subNames) > 0 {
|
|
||||||
for _, subName := range subNames {
|
|
||||||
if len(subName) > 0 {
|
|
||||||
index := reg.SubexpIndex(subName)
|
|
||||||
if index > -1 {
|
|
||||||
afterURL = strings.ReplaceAll(afterURL, "${"+subName+"}", matches[index])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 前后是否一致
|
|
||||||
if fullURL == afterURL {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.KeepArgs {
|
|
||||||
var qIndex = strings.Index(this.uri, "?")
|
|
||||||
if qIndex >= 0 {
|
|
||||||
afterURL += this.uri[qIndex:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Status <= 0 {
|
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusTemporaryRedirect)
|
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, http.StatusTemporaryRedirect)
|
|
||||||
} else {
|
|
||||||
this.processResponseHeaders(this.writer.Header(), u.Status)
|
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
} else { // 精准匹配
|
|
||||||
if fullURL == u.RealBeforeURL() {
|
|
||||||
// 前后是否一致
|
|
||||||
if fullURL == u.AfterURL {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var afterURL = u.AfterURL
|
|
||||||
if u.KeepArgs {
|
if u.KeepArgs {
|
||||||
var qIndex = strings.Index(this.uri, "?")
|
var qIndex = strings.Index(this.uri, "?")
|
||||||
if qIndex >= 0 {
|
if qIndex >= 0 {
|
||||||
@@ -104,12 +84,110 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if u.Status <= 0 {
|
if u.Status <= 0 {
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusTemporaryRedirect)
|
u.Status = http.StatusTemporaryRedirect
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, http.StatusTemporaryRedirect)
|
}
|
||||||
} else {
|
this.processResponseHeaders(this.writer.Header(), u.Status)
|
||||||
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
||||||
|
return true
|
||||||
|
} else { // 精准匹配
|
||||||
|
if fullURL == u.RealBeforeURL() {
|
||||||
|
// 前后是否一致
|
||||||
|
if fullURL == u.AfterURL {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var afterURL = u.AfterURL
|
||||||
|
if u.KeepArgs {
|
||||||
|
var qIndex = strings.Index(this.uri, "?")
|
||||||
|
if qIndex >= 0 {
|
||||||
|
afterURL += this.uri[qIndex:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Status <= 0 {
|
||||||
|
u.Status = http.StatusTemporaryRedirect
|
||||||
|
}
|
||||||
this.processResponseHeaders(this.writer.Header(), u.Status)
|
this.processResponseHeaders(this.writer.Header(), u.Status)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else if u.Type == serverconfigs.HTTPHostRedirectTypeDomain {
|
||||||
|
if len(u.DomainAfter) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果跳转前后域名一致,则终止
|
||||||
|
if u.DomainAfter == this.ReqHost {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var scheme = u.DomainAfterScheme
|
||||||
|
if len(scheme) == 0 {
|
||||||
|
scheme = this.requestScheme()
|
||||||
|
}
|
||||||
|
if u.DomainsAll || configutils.MatchDomains(u.DomainsBefore, this.ReqHost) {
|
||||||
|
var afterURL = scheme + "://" + u.DomainAfter + urlPath
|
||||||
|
if fullURL == afterURL {
|
||||||
|
// 终止匹配
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if u.Status <= 0 {
|
||||||
|
u.Status = http.StatusTemporaryRedirect
|
||||||
|
}
|
||||||
|
this.processResponseHeaders(this.writer.Header(), u.Status)
|
||||||
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else if u.Type == serverconfigs.HTTPHostRedirectTypePort {
|
||||||
|
if u.PortAfter <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var scheme = u.PortAfterScheme
|
||||||
|
if len(scheme) == 0 {
|
||||||
|
scheme = this.requestScheme()
|
||||||
|
}
|
||||||
|
|
||||||
|
reqHost, reqPort, _ := net.SplitHostPort(this.ReqHost)
|
||||||
|
if len(reqHost) == 0 {
|
||||||
|
reqHost = this.ReqHost
|
||||||
|
}
|
||||||
|
if len(reqPort) == 0 {
|
||||||
|
switch this.requestScheme() {
|
||||||
|
case "http":
|
||||||
|
reqPort = "80"
|
||||||
|
case "https":
|
||||||
|
reqPort = "443"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果跳转前后端口一致,则终止
|
||||||
|
if reqPort == types.String(u.PortAfter) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var containsPort = false
|
||||||
|
if u.PortsAll {
|
||||||
|
containsPort = true
|
||||||
|
} else {
|
||||||
|
containsPort = u.ContainsPort(types.Int(reqPort))
|
||||||
|
}
|
||||||
|
if containsPort {
|
||||||
|
var newReqHost = reqHost
|
||||||
|
if !((scheme == "http" && u.PortAfter == 80) || (scheme == "https" && u.PortAfter == 443)) {
|
||||||
|
newReqHost += ":" + types.String(u.PortAfter)
|
||||||
|
}
|
||||||
|
var afterURL = scheme + "://" + newReqHost + urlPath
|
||||||
|
if fullURL == afterURL {
|
||||||
|
// 终止匹配
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if u.Status <= 0 {
|
||||||
|
u.Status = http.StatusTemporaryRedirect
|
||||||
|
}
|
||||||
|
this.processResponseHeaders(this.writer.Header(), u.Status)
|
||||||
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,27 +51,46 @@ func (this *HTTPRequest) log() {
|
|||||||
addr = addr[:index]
|
addr = addr[:index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var serverGlobalConfig = this.nodeConfig.GlobalServerConfig
|
||||||
|
|
||||||
// 请求Cookie
|
// 请求Cookie
|
||||||
var cookies = map[string]string{}
|
var cookies = map[string]string{}
|
||||||
if ref == nil || ref.ContainsField(serverconfigs.HTTPAccessLogFieldCookie) {
|
var enableCookies = false
|
||||||
for _, cookie := range this.RawReq.Cookies() {
|
if serverGlobalConfig == nil || serverGlobalConfig.HTTPAccessLog.EnableCookies {
|
||||||
cookies[cookie.Name] = cookie.Value
|
enableCookies = true
|
||||||
|
if ref == nil || ref.ContainsField(serverconfigs.HTTPAccessLogFieldCookie) {
|
||||||
|
for _, cookie := range this.RawReq.Cookies() {
|
||||||
|
cookies[cookie.Name] = cookie.Value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 请求Header
|
// 请求Header
|
||||||
var pbReqHeader = map[string]*pb.Strings{}
|
var pbReqHeader = map[string]*pb.Strings{}
|
||||||
if ref == nil || ref.ContainsField(serverconfigs.HTTPAccessLogFieldHeader) {
|
if serverGlobalConfig == nil || serverGlobalConfig.HTTPAccessLog.EnableRequestHeaders {
|
||||||
for k, v := range this.RawReq.Header {
|
if ref == nil || ref.ContainsField(serverconfigs.HTTPAccessLogFieldHeader) {
|
||||||
pbReqHeader[k] = &pb.Strings{Values: v}
|
// 是否只记录通用Header
|
||||||
|
var commonHeadersOnly = serverGlobalConfig != nil && serverGlobalConfig.HTTPAccessLog.CommonRequestHeadersOnly
|
||||||
|
|
||||||
|
for k, v := range this.RawReq.Header {
|
||||||
|
if commonHeadersOnly && !serverconfigs.IsCommonRequestHeader(k) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !enableCookies && k == "Cookie" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pbReqHeader[k] = &pb.Strings{Values: v}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 响应Header
|
// 响应Header
|
||||||
var pbResHeader = map[string]*pb.Strings{}
|
var pbResHeader = map[string]*pb.Strings{}
|
||||||
if ref == nil || ref.ContainsField(serverconfigs.HTTPAccessLogFieldSentHeader) {
|
if serverGlobalConfig == nil || serverGlobalConfig.HTTPAccessLog.EnableResponseHeaders {
|
||||||
for k, v := range this.writer.Header() {
|
if ref == nil || ref.ContainsField(serverconfigs.HTTPAccessLogFieldSentHeader) {
|
||||||
pbResHeader[k] = &pb.Strings{Values: v}
|
for k, v := range this.writer.Header() {
|
||||||
|
pbResHeader[k] = &pb.Strings{Values: v}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
|
|||||||
}
|
}
|
||||||
|
|
||||||
goNext, hasRequestBody, ruleGroup, ruleSet, err := w.MatchRequest(this, this.writer)
|
goNext, hasRequestBody, ruleGroup, ruleSet, err := w.MatchRequest(this, this.writer)
|
||||||
if forceLog && logRequestBody && hasRequestBody {
|
if forceLog && logRequestBody && hasRequestBody && ruleSet != nil && ruleSet.HasAttackActions() {
|
||||||
this.wafHasRequestBody = true
|
this.wafHasRequestBody = true
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -294,7 +294,7 @@ func (this *HTTPRequest) checkWAFResponse(firewallPolicy *firewallconfigs.HTTPFi
|
|||||||
}
|
}
|
||||||
|
|
||||||
goNext, hasRequestBody, ruleGroup, ruleSet, err := w.MatchResponse(this, resp, this.writer)
|
goNext, hasRequestBody, ruleGroup, ruleSet, err := w.MatchResponse(this, resp, this.writer)
|
||||||
if forceLog && logRequestBody && hasRequestBody {
|
if forceLog && logRequestBody && hasRequestBody && ruleSet != nil && ruleSet.HasAttackActions() {
|
||||||
this.wafHasRequestBody = true
|
this.wafHasRequestBody = true
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package nodes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"io"
|
"io"
|
||||||
@@ -9,8 +10,36 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// WebsocketResponseReader Websocket响应Reader
|
||||||
|
type WebsocketResponseReader struct {
|
||||||
|
rawReader io.Reader
|
||||||
|
buf []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWebsocketResponseReader(rawReader io.Reader) *WebsocketResponseReader {
|
||||||
|
return &WebsocketResponseReader{
|
||||||
|
rawReader: rawReader,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *WebsocketResponseReader) Read(p []byte) (n int, err error) {
|
||||||
|
n, err = this.rawReader.Read(p)
|
||||||
|
if n > 0 {
|
||||||
|
if len(this.buf) == 0 {
|
||||||
|
this.buf = make([]byte, n)
|
||||||
|
copy(this.buf, p[:n])
|
||||||
|
} else {
|
||||||
|
this.buf = append(this.buf, p[:n]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 处理Websocket请求
|
// 处理Websocket请求
|
||||||
func (this *HTTPRequest) doWebsocket(requestHost string, isLastRetry bool) (shouldRetry bool) {
|
func (this *HTTPRequest) doWebsocket(requestHost string, isLastRetry bool) (shouldRetry bool) {
|
||||||
|
// 设置不缓存
|
||||||
|
this.web.Cache = nil
|
||||||
|
|
||||||
if this.web.WebsocketRef == nil || !this.web.WebsocketRef.IsOn || this.web.Websocket == nil || !this.web.Websocket.IsOn {
|
if this.web.WebsocketRef == nil || !this.web.WebsocketRef.IsOn || this.web.Websocket == nil || !this.web.Websocket.IsOn {
|
||||||
this.writer.WriteHeader(http.StatusForbidden)
|
this.writer.WriteHeader(http.StatusForbidden)
|
||||||
this.addError(errors.New("websocket have not been enabled yet"))
|
this.addError(errors.New("websocket have not been enabled yet"))
|
||||||
@@ -84,14 +113,20 @@ func (this *HTTPRequest) doWebsocket(requestHost string, isLastRetry bool) (shou
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
// 读取第一个响应
|
// 读取第一个响应
|
||||||
resp, err := http.ReadResponse(bufio.NewReader(originConn), this.RawReq)
|
var respReader = NewWebsocketResponseReader(originConn)
|
||||||
if err != nil {
|
resp, err := http.ReadResponse(bufio.NewReader(respReader), this.RawReq)
|
||||||
|
if err != nil || resp == nil {
|
||||||
|
if resp != nil && resp.Body != nil {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
_ = clientConn.Close()
|
_ = clientConn.Close()
|
||||||
_ = originConn.Close()
|
_ = originConn.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.processResponseHeaders(resp.Header, resp.StatusCode)
|
this.processResponseHeaders(resp.Header, resp.StatusCode)
|
||||||
|
this.writer.statusCode = resp.StatusCode
|
||||||
|
|
||||||
// 将响应写回客户端
|
// 将响应写回客户端
|
||||||
err = resp.Write(clientConn)
|
err = resp.Write(clientConn)
|
||||||
@@ -105,6 +140,25 @@ func (this *HTTPRequest) doWebsocket(requestHost string, isLastRetry bool) (shou
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 剩余已经从源站读取的内容
|
||||||
|
var headerBytes = respReader.buf
|
||||||
|
var headerIndex = bytes.Index(headerBytes, []byte{'\r', '\n', '\r', '\n'}) // CRLF
|
||||||
|
if headerIndex > 0 {
|
||||||
|
var leftBytes = headerBytes[headerIndex+4:]
|
||||||
|
if len(leftBytes) > 0 {
|
||||||
|
_, err = clientConn.Write(leftBytes)
|
||||||
|
if err != nil {
|
||||||
|
if resp.Body != nil {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = clientConn.Close()
|
||||||
|
_ = originConn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if resp.Body != nil {
|
if resp.Body != nil {
|
||||||
_ = resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ func (this *BaseListener) findNamedServer(name string) (serverConfig *serverconf
|
|||||||
|
|
||||||
// 严格查找域名
|
// 严格查找域名
|
||||||
func (this *BaseListener) findNamedServerMatched(name string) (serverConfig *serverconfigs.ServerConfig, serverName string) {
|
func (this *BaseListener) findNamedServerMatched(name string) (serverConfig *serverconfigs.ServerConfig, serverName string) {
|
||||||
group := this.Group
|
var group = this.Group
|
||||||
if group == nil {
|
if group == nil {
|
||||||
return nil, ""
|
return nil, ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func (this *HTTPListener) Serve() error {
|
|||||||
Handler: this,
|
Handler: this,
|
||||||
ReadTimeout: 1 * time.Hour, // TODO 改成可以配置
|
ReadTimeout: 1 * time.Hour, // TODO 改成可以配置
|
||||||
ReadHeaderTimeout: 3 * time.Second, // TODO 改成可以配置
|
ReadHeaderTimeout: 3 * time.Second, // TODO 改成可以配置
|
||||||
WriteTimeout: 1 * time.Hour, // TODO 改成可以配置
|
WriteTimeout: 2 * time.Hour, // TODO 改成可以配置
|
||||||
IdleTimeout: 75 * time.Second, // TODO 改成可以配置
|
IdleTimeout: 75 * time.Second, // TODO 改成可以配置
|
||||||
ConnState: func(conn net.Conn, state http.ConnState) {
|
ConnState: func(conn net.Conn, state http.ConnState) {
|
||||||
switch state {
|
switch state {
|
||||||
@@ -175,6 +175,15 @@ func (this *HTTPListener) ServeHTTP(rawWriter http.ResponseWriter, rawReq *http.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查用户
|
||||||
|
if server != nil && server.UserId > 0 {
|
||||||
|
if !SharedUserManager.CheckUserServersIsEnabled(server.UserId) {
|
||||||
|
rawWriter.WriteHeader(http.StatusNotFound)
|
||||||
|
_, _ = rawWriter.Write([]byte("The site owner is unavailable."))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 包装新请求对象
|
// 包装新请求对象
|
||||||
var req = &HTTPRequest{
|
var req = &HTTPRequest{
|
||||||
RawReq: rawReq,
|
RawReq: rawReq,
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ func (this *TCPListener) handleConn(conn net.Conn) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 是否已达到流量限制
|
// 是否已达到流量限制
|
||||||
if this.reachedTrafficLimit() {
|
if this.reachedTrafficLimit() || (server.UserId > 0 && !SharedUserManager.CheckUserServersIsEnabled(server.UserId)) {
|
||||||
// 关闭连接
|
// 关闭连接
|
||||||
tcpConn, ok := conn.(LingerConn)
|
tcpConn, ok := conn.(LingerConn)
|
||||||
if ok {
|
if ok {
|
||||||
|
|||||||
@@ -170,6 +170,11 @@ func (this *UDPListener) servePacketListener(listener UDPPacketListener) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查用户状态
|
||||||
|
if firstServer.UserId > 0 && !SharedUserManager.CheckUserServersIsEnabled(firstServer.UserId) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
n, cm, clientAddr, err := listener.ReadFrom(buffer)
|
n, cm, clientAddr, err := listener.ReadFrom(buffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if this.isClosed {
|
if this.isClosed {
|
||||||
|
|||||||
@@ -205,9 +205,7 @@ func (this *Node) Start() {
|
|||||||
|
|
||||||
// 统计
|
// 统计
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
stats.SharedTrafficStatManager.Start(func() *nodeconfigs.NodeConfig {
|
stats.SharedTrafficStatManager.Start()
|
||||||
return sharedNodeConfig
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
stats.SharedHTTPRequestStatManager.Start()
|
stats.SharedHTTPRequestStatManager.Start()
|
||||||
@@ -430,6 +428,22 @@ func (this *Node) execTask(rpcClient *rpc.RPCClient, nodeCtx context.Context, ta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case "userServersStateChanged":
|
||||||
|
if task.UserId > 0 {
|
||||||
|
resp, err := rpcClient.UserRPC.CheckUserServersState(nodeCtx, &pb.CheckUserServersStateRequest{UserId: task.UserId})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedUserManager.UpdateUserServersIsEnabled(task.UserId, resp.IsEnabled)
|
||||||
|
|
||||||
|
if resp.IsEnabled {
|
||||||
|
err = this.syncUserServersConfig(task.UserId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
remotelogs.Error("NODE", "task '"+types.String(task.Id)+"', type '"+task.Type+"' has not been handled")
|
remotelogs.Error("NODE", "task '"+types.String(task.Id)+"', type '"+task.Type+"' has not been handled")
|
||||||
}
|
}
|
||||||
@@ -615,6 +629,36 @@ func (this *Node) syncServerConfig(serverId int64) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 同步某个用户下的所有服务配置
|
||||||
|
func (this *Node) syncUserServersConfig(userId int64) error {
|
||||||
|
rpcClient, err := rpc.SharedRPC()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
serverConfigsResp, err := rpcClient.ServerRPC.ComposeAllUserServersConfig(rpcClient.Context(), &pb.ComposeAllUserServersConfigRequest{
|
||||||
|
UserId: userId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(serverConfigsResp.ServersConfigJSON) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var serverConfigs = []*serverconfigs.ServerConfig{}
|
||||||
|
err = json.Unmarshal(serverConfigsResp.ServersConfigJSON, &serverConfigs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
this.locker.Lock()
|
||||||
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
|
for _, config := range serverConfigs {
|
||||||
|
this.updatingServerMap[config.Id] = config
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// 启动同步计时器
|
// 启动同步计时器
|
||||||
func (this *Node) startSyncTimer() {
|
func (this *Node) startSyncTimer() {
|
||||||
// TODO 这个时间间隔可以自行设置
|
// TODO 这个时间间隔可以自行设置
|
||||||
@@ -759,6 +803,7 @@ func (this *Node) listenSock() error {
|
|||||||
|
|
||||||
// 退出主进程
|
// 退出主进程
|
||||||
events.Notify(events.EventQuit)
|
events.Notify(events.EventQuit)
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
utils.Exit()
|
utils.Exit()
|
||||||
case "quit":
|
case "quit":
|
||||||
_ = cmd.ReplyOk()
|
_ = cmd.ReplyOk()
|
||||||
|
|||||||
@@ -195,8 +195,8 @@ func (this *NodeStatusExecutor) updateDisk(status *nodeconfigs.NodeStatus) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 当前TeaWeb所在的fs
|
// 当前TeaWeb所在的fs
|
||||||
rootFS := ""
|
var rootFS = ""
|
||||||
rootTotal := uint64(0)
|
var rootTotal = uint64(0)
|
||||||
if lists.ContainsString([]string{"darwin", "linux", "freebsd"}, runtime.GOOS) {
|
if lists.ContainsString([]string{"darwin", "linux", "freebsd"}, runtime.GOOS) {
|
||||||
for _, p := range partitions {
|
for _, p := range partitions {
|
||||||
if p.Mountpoint == "/" {
|
if p.Mountpoint == "/" {
|
||||||
@@ -210,9 +210,9 @@ func (this *NodeStatusExecutor) updateDisk(status *nodeconfigs.NodeStatus) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
total := rootTotal
|
var total = rootTotal
|
||||||
totalUsage := uint64(0)
|
var totalUsage = uint64(0)
|
||||||
maxUsage := float64(0)
|
var maxUsage = float64(0)
|
||||||
for _, partition := range partitions {
|
for _, partition := range partitions {
|
||||||
if runtime.GOOS != "windows" && !strings.Contains(partition.Device, "/") && !strings.Contains(partition.Device, "\\") {
|
if runtime.GOOS != "windows" && !strings.Contains(partition.Device, "/") && !strings.Contains(partition.Device, "\\") {
|
||||||
continue
|
continue
|
||||||
@@ -252,7 +252,7 @@ func (this *NodeStatusExecutor) updateDisk(status *nodeconfigs.NodeStatus) {
|
|||||||
// 缓存空间
|
// 缓存空间
|
||||||
func (this *NodeStatusExecutor) updateCacheSpace(status *nodeconfigs.NodeStatus) {
|
func (this *NodeStatusExecutor) updateCacheSpace(status *nodeconfigs.NodeStatus) {
|
||||||
var result = []maps.Map{}
|
var result = []maps.Map{}
|
||||||
cachePaths := caches.SharedManager.FindAllCachePaths()
|
var cachePaths = caches.SharedManager.FindAllCachePaths()
|
||||||
for _, path := range cachePaths {
|
for _, path := range cachePaths {
|
||||||
var stat unix.Statfs_t
|
var stat unix.Statfs_t
|
||||||
err := unix.Statfs(path, &stat)
|
err := unix.Statfs(path, &stat)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
//go:build !windows
|
//go:build !windows
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
|
|||||||
49
internal/nodes/user_manager.go
Normal file
49
internal/nodes/user_manager.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var SharedUserManager = NewUserManager()
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ServersEnabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserManager struct {
|
||||||
|
userMap map[int64]*User // id => *User
|
||||||
|
|
||||||
|
locker sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserManager() *UserManager {
|
||||||
|
return &UserManager{
|
||||||
|
userMap: map[int64]*User{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UserManager) UpdateUserServersIsEnabled(userId int64, isEnabled bool) {
|
||||||
|
this.locker.Lock()
|
||||||
|
u, ok := this.userMap[userId]
|
||||||
|
if ok {
|
||||||
|
u.ServersEnabled = isEnabled
|
||||||
|
} else {
|
||||||
|
u = &User{ServersEnabled: isEnabled}
|
||||||
|
this.userMap[userId] = u
|
||||||
|
}
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UserManager) CheckUserServersIsEnabled(userId int64) (isEnabled bool) {
|
||||||
|
this.locker.RLock()
|
||||||
|
u, ok := this.userMap[userId]
|
||||||
|
if ok {
|
||||||
|
isEnabled = u.ServersEnabled
|
||||||
|
} else {
|
||||||
|
isEnabled = true
|
||||||
|
}
|
||||||
|
this.locker.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -49,6 +49,7 @@ type RPCClient struct {
|
|||||||
FirewallRPC pb.FirewallServiceClient
|
FirewallRPC pb.FirewallServiceClient
|
||||||
SSLCertRPC pb.SSLCertServiceClient
|
SSLCertRPC pb.SSLCertServiceClient
|
||||||
ScriptRPC pb.ScriptServiceClient
|
ScriptRPC pb.ScriptServiceClient
|
||||||
|
UserRPC pb.UserServiceClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
|
func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
|
||||||
@@ -81,6 +82,7 @@ func NewRPCClient(apiConfig *configs.APIConfig) (*RPCClient, error) {
|
|||||||
client.FirewallRPC = pb.NewFirewallServiceClient(client)
|
client.FirewallRPC = pb.NewFirewallServiceClient(client)
|
||||||
client.SSLCertRPC = pb.NewSSLCertServiceClient(client)
|
client.SSLCertRPC = pb.NewSSLCertServiceClient(client)
|
||||||
client.ScriptRPC = pb.NewScriptServiceClient(client)
|
client.ScriptRPC = pb.NewScriptServiceClient(client)
|
||||||
|
client.UserRPC = pb.NewUserServiceClient(client)
|
||||||
|
|
||||||
err := client.init()
|
err := client.init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package stats
|
package stats
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
@@ -42,6 +43,8 @@ type BandwidthStat struct {
|
|||||||
type BandwidthStatManager struct {
|
type BandwidthStatManager struct {
|
||||||
m map[string]*BandwidthStat // key => *BandwidthStat
|
m map[string]*BandwidthStat // key => *BandwidthStat
|
||||||
|
|
||||||
|
pbStats []*pb.ServerBandwidthStat
|
||||||
|
|
||||||
lastTime string // 上一次执行的时间
|
lastTime string // 上一次执行的时间
|
||||||
|
|
||||||
ticker *time.Ticker
|
ticker *time.Ticker
|
||||||
@@ -65,6 +68,12 @@ func (this *BandwidthStatManager) Start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *BandwidthStatManager) Loop() error {
|
func (this *BandwidthStatManager) Loop() error {
|
||||||
|
var regionId int64
|
||||||
|
nodeConfig, _ := nodeconfigs.SharedNodeConfig()
|
||||||
|
if nodeConfig != nil {
|
||||||
|
regionId = nodeConfig.RegionId
|
||||||
|
}
|
||||||
|
|
||||||
var now = time.Now()
|
var now = time.Now()
|
||||||
var day = timeutil.Format("Ymd", now)
|
var day = timeutil.Format("Ymd", now)
|
||||||
var currentTime = timeutil.FormatTime("Hi", now.Unix()/300*300)
|
var currentTime = timeutil.FormatTime("Hi", now.Unix()/300*300)
|
||||||
@@ -76,16 +85,29 @@ func (this *BandwidthStatManager) Loop() error {
|
|||||||
|
|
||||||
var pbStats = []*pb.ServerBandwidthStat{}
|
var pbStats = []*pb.ServerBandwidthStat{}
|
||||||
|
|
||||||
|
// 历史未提交记录
|
||||||
|
if len(this.pbStats) > 0 {
|
||||||
|
var expiredTime = timeutil.FormatTime("Hi", time.Now().Unix()-1200) // 只保留20分钟
|
||||||
|
|
||||||
|
for _, stat := range this.pbStats {
|
||||||
|
if stat.TimeAt > expiredTime {
|
||||||
|
pbStats = append(pbStats, stat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.pbStats = nil
|
||||||
|
}
|
||||||
|
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
for key, stat := range this.m {
|
for key, stat := range this.m {
|
||||||
if stat.Day < day || stat.TimeAt < currentTime {
|
if stat.Day < day || stat.TimeAt < currentTime {
|
||||||
pbStats = append(pbStats, &pb.ServerBandwidthStat{
|
pbStats = append(pbStats, &pb.ServerBandwidthStat{
|
||||||
Id: 0,
|
Id: 0,
|
||||||
UserId: stat.UserId,
|
UserId: stat.UserId,
|
||||||
ServerId: stat.ServerId,
|
ServerId: stat.ServerId,
|
||||||
Day: stat.Day,
|
Day: stat.Day,
|
||||||
TimeAt: stat.TimeAt,
|
TimeAt: stat.TimeAt,
|
||||||
Bytes: stat.MaxBytes / bandwidthTimestampDelim,
|
Bytes: stat.MaxBytes / bandwidthTimestampDelim,
|
||||||
|
NodeRegionId: regionId,
|
||||||
})
|
})
|
||||||
delete(this.m, key)
|
delete(this.m, key)
|
||||||
}
|
}
|
||||||
@@ -100,6 +122,8 @@ func (this *BandwidthStatManager) Loop() error {
|
|||||||
}
|
}
|
||||||
_, err = rpcClient.ServerBandwidthStatRPC.UploadServerBandwidthStats(rpcClient.Context(), &pb.UploadServerBandwidthStatsRequest{ServerBandwidthStats: pbStats})
|
_, err = rpcClient.ServerBandwidthStatRPC.UploadServerBandwidthStats(rpcClient.Context(), &pb.UploadServerBandwidthStatsRequest{ServerBandwidthStats: pbStats})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
this.pbStats = pbStats
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,19 +31,33 @@ type TrafficItem struct {
|
|||||||
CheckingTrafficLimit bool
|
CheckingTrafficLimit bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *TrafficItem) Add(anotherItem *TrafficItem) {
|
||||||
|
this.Bytes += anotherItem.Bytes
|
||||||
|
this.CachedBytes += anotherItem.CachedBytes
|
||||||
|
this.CountRequests += anotherItem.CountRequests
|
||||||
|
this.CountCachedRequests += anotherItem.CountCachedRequests
|
||||||
|
this.CountAttackRequests += anotherItem.CountAttackRequests
|
||||||
|
this.AttackBytes += anotherItem.AttackBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
const trafficStatsMaxLife = 1200 // 最大只保存20分钟内的数据
|
||||||
|
|
||||||
// TrafficStatManager 区域流量统计
|
// TrafficStatManager 区域流量统计
|
||||||
type TrafficStatManager struct {
|
type TrafficStatManager struct {
|
||||||
itemMap map[string]*TrafficItem // [timestamp serverId] => *TrafficItem
|
itemMap map[string]*TrafficItem // [timestamp serverId] => *TrafficItem
|
||||||
domainsMap map[string]*TrafficItem // timestamp @ serverId @ domain => *TrafficItem
|
domainsMap map[string]*TrafficItem // timestamp @ serverId @ domain => *TrafficItem
|
||||||
locker sync.Mutex
|
|
||||||
configFunc func() *nodeconfigs.NodeConfig
|
pbItems []*pb.ServerDailyStat
|
||||||
|
pbDomainItems []*pb.UploadServerDailyStatsRequest_DomainStat
|
||||||
|
|
||||||
|
locker sync.Mutex
|
||||||
|
|
||||||
totalRequests int64
|
totalRequests int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTrafficStatManager 获取新对象
|
// NewTrafficStatManager 获取新对象
|
||||||
func NewTrafficStatManager() *TrafficStatManager {
|
func NewTrafficStatManager() *TrafficStatManager {
|
||||||
manager := &TrafficStatManager{
|
var manager = &TrafficStatManager{
|
||||||
itemMap: map[string]*TrafficItem{},
|
itemMap: map[string]*TrafficItem{},
|
||||||
domainsMap: map[string]*TrafficItem{},
|
domainsMap: map[string]*TrafficItem{},
|
||||||
}
|
}
|
||||||
@@ -52,9 +66,7 @@ func NewTrafficStatManager() *TrafficStatManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start 启动自动任务
|
// Start 启动自动任务
|
||||||
func (this *TrafficStatManager) Start(configFunc func() *nodeconfigs.NodeConfig) {
|
func (this *TrafficStatManager) Start() {
|
||||||
this.configFunc = configFunc
|
|
||||||
|
|
||||||
// 上传请求总数
|
// 上传请求总数
|
||||||
var monitorTicker = time.NewTicker(1 * time.Minute)
|
var monitorTicker = time.NewTicker(1 * time.Minute)
|
||||||
events.OnKey(events.EventQuit, this, func() {
|
events.OnKey(events.EventQuit, this, func() {
|
||||||
@@ -70,7 +82,7 @@ func (this *TrafficStatManager) Start(configFunc func() *nodeconfigs.NodeConfig)
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 上传统计数据
|
// 上传统计数据
|
||||||
duration := 5 * time.Minute
|
var duration = 5 * time.Minute
|
||||||
if Tea.IsTesting() {
|
if Tea.IsTesting() {
|
||||||
// 测试环境缩短上传时间,方便我们调试
|
// 测试环境缩短上传时间,方便我们调试
|
||||||
duration = 30 * time.Second
|
duration = 30 * time.Second
|
||||||
@@ -143,9 +155,10 @@ func (this *TrafficStatManager) Add(serverId int64, domain string, bytes int64,
|
|||||||
|
|
||||||
// Upload 上传流量
|
// Upload 上传流量
|
||||||
func (this *TrafficStatManager) Upload() error {
|
func (this *TrafficStatManager) Upload() error {
|
||||||
var config = this.configFunc()
|
var regionId int64
|
||||||
if config == nil {
|
nodeConfig, _ := nodeconfigs.SharedNodeConfig()
|
||||||
return nil
|
if nodeConfig != nil {
|
||||||
|
regionId = nodeConfig.RegionId
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := rpc.SharedRPC()
|
client, err := rpc.SharedRPC()
|
||||||
@@ -154,10 +167,14 @@ func (this *TrafficStatManager) Upload() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
|
|
||||||
var itemMap = this.itemMap
|
var itemMap = this.itemMap
|
||||||
var domainMap = this.domainsMap
|
var domainMap = this.domainsMap
|
||||||
|
|
||||||
|
// reset
|
||||||
this.itemMap = map[string]*TrafficItem{}
|
this.itemMap = map[string]*TrafficItem{}
|
||||||
this.domainsMap = map[string]*TrafficItem{}
|
this.domainsMap = map[string]*TrafficItem{}
|
||||||
|
|
||||||
this.locker.Unlock()
|
this.locker.Unlock()
|
||||||
|
|
||||||
// 服务统计
|
// 服务统计
|
||||||
@@ -174,7 +191,7 @@ func (this *TrafficStatManager) Upload() error {
|
|||||||
|
|
||||||
pbServerStats = append(pbServerStats, &pb.ServerDailyStat{
|
pbServerStats = append(pbServerStats, &pb.ServerDailyStat{
|
||||||
ServerId: serverId,
|
ServerId: serverId,
|
||||||
RegionId: config.RegionId,
|
NodeRegionId: regionId,
|
||||||
Bytes: item.Bytes,
|
Bytes: item.Bytes,
|
||||||
CachedBytes: item.CachedBytes,
|
CachedBytes: item.CachedBytes,
|
||||||
CountRequests: item.CountRequests,
|
CountRequests: item.CountRequests,
|
||||||
@@ -186,9 +203,6 @@ func (this *TrafficStatManager) Upload() error {
|
|||||||
CreatedAt: timestamp,
|
CreatedAt: timestamp,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if len(pbServerStats) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 域名统计
|
// 域名统计
|
||||||
var pbDomainStats = []*pb.UploadServerDailyStatsRequest_DomainStat{}
|
var pbDomainStats = []*pb.UploadServerDailyStatsRequest_DomainStat{}
|
||||||
@@ -210,9 +224,40 @@ func (this *TrafficStatManager) Upload() error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 历史未提交记录
|
||||||
|
if len(this.pbItems) > 0 || len(this.pbDomainItems) > 0 {
|
||||||
|
var expiredAt = time.Now().Unix() - 1200 // 只保留20分钟
|
||||||
|
|
||||||
|
for _, item := range this.pbItems {
|
||||||
|
if item.CreatedAt > expiredAt {
|
||||||
|
pbServerStats = append(pbServerStats, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.pbItems = nil
|
||||||
|
|
||||||
|
for _, item := range this.pbDomainItems {
|
||||||
|
if item.CreatedAt > expiredAt {
|
||||||
|
pbDomainStats = append(pbDomainStats, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.pbDomainItems = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pbServerStats) == 0 && len(pbDomainStats) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
_, err = client.ServerDailyStatRPC.UploadServerDailyStats(client.Context(), &pb.UploadServerDailyStatsRequest{
|
_, err = client.ServerDailyStatRPC.UploadServerDailyStats(client.Context(), &pb.UploadServerDailyStatsRequest{
|
||||||
Stats: pbServerStats,
|
Stats: pbServerStats,
|
||||||
DomainStats: pbDomainStats,
|
DomainStats: pbDomainStats,
|
||||||
})
|
})
|
||||||
return err
|
if err != nil {
|
||||||
|
// 加回历史记录
|
||||||
|
this.pbItems = pbServerStats
|
||||||
|
this.pbDomainItems = pbDomainStats
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ClockManager struct {
|
type ClockManager struct {
|
||||||
|
lastFailAt int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClockManager() *ClockManager {
|
func NewClockManager() *ClockManager {
|
||||||
@@ -51,7 +52,13 @@ func (this *ClockManager) Start() {
|
|||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
err := this.Sync()
|
err := this.Sync()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Warn("CLOCK", "sync clock failed: "+err.Error())
|
var currentTimestamp = time.Now().Unix()
|
||||||
|
|
||||||
|
// 每天只提醒一次错误
|
||||||
|
if currentTimestamp-this.lastFailAt > 86400 {
|
||||||
|
remotelogs.Warn("CLOCK", "sync clock failed: "+err.Error())
|
||||||
|
this.lastFailAt = currentTimestamp
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,7 +125,7 @@ func (this *ClockManager) syncNtpdate(ntpdate string, server string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 参考自:https://medium.com/learning-the-go-programming-language/lets-make-an-ntp-client-in-go-287c4b9a969f
|
// ReadServer 参考自:https://medium.com/learning-the-go-programming-language/lets-make-an-ntp-client-in-go-287c4b9a969f
|
||||||
func (this *ClockManager) ReadServer(server string) (time.Time, error) {
|
func (this *ClockManager) ReadServer(server string) (time.Time, error) {
|
||||||
conn, err := net.Dial("udp", server+":123")
|
conn, err := net.Dial("udp", server+":123")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ func NewDB(rawDB *sql.DB) *DB {
|
|||||||
rawDB: rawDB,
|
rawDB: rawDB,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
events.OnKey(events.EventQuit, fmt.Sprintf("db_%p", db), func() {
|
||||||
|
_ = rawDB.Close()
|
||||||
|
})
|
||||||
events.OnKey(events.EventTerminated, fmt.Sprintf("db_%p", db), func() {
|
events.OnKey(events.EventTerminated, fmt.Sprintf("db_%p", db), func() {
|
||||||
_ = rawDB.Close()
|
_ = rawDB.Close()
|
||||||
})
|
})
|
||||||
|
|||||||
28
internal/utils/readers/reader_print.go
Normal file
28
internal/utils/readers/reader_print.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package readers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PrintReader struct {
|
||||||
|
rawReader io.Reader
|
||||||
|
tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPrintReader(rawReader io.Reader, tag string) io.Reader {
|
||||||
|
return &PrintReader{
|
||||||
|
rawReader: rawReader,
|
||||||
|
tag: tag,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *PrintReader) Read(p []byte) (n int, err error) {
|
||||||
|
n, err = this.rawReader.Read(p)
|
||||||
|
if n > 0 {
|
||||||
|
log.Println("[" + this.tag + "]" + string(p[:n]))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -114,7 +114,8 @@ func (this *ServiceManager) installSystemdService(systemd, exePath string, args
|
|||||||
var shortName = teaconst.SystemdServiceName
|
var shortName = teaconst.SystemdServiceName
|
||||||
var longName = "GoEdge Node" // TODO 将来可以修改
|
var longName = "GoEdge Node" // TODO 将来可以修改
|
||||||
|
|
||||||
var desc = `# Provides: ` + shortName + `
|
var desc = `### BEGIN INIT INFO
|
||||||
|
# Provides: ` + shortName + `
|
||||||
# Required-Start: $all
|
# Required-Start: $all
|
||||||
# Required-Stop:
|
# Required-Stop:
|
||||||
# Default-Start: 2 3 4 5
|
# Default-Start: 2 3 4 5
|
||||||
|
|||||||
28
internal/utils/writers/writer_print.go
Normal file
28
internal/utils/writers/writer_print.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package writers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PrintWriter struct {
|
||||||
|
rawWriter io.Writer
|
||||||
|
tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPrintWriter(rawWriter io.Writer, tag string) io.Writer {
|
||||||
|
return &PrintWriter{
|
||||||
|
rawWriter: rawWriter,
|
||||||
|
tag: tag,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *PrintWriter) Write(p []byte) (n int, err error) {
|
||||||
|
n, err = this.rawWriter.Write(p)
|
||||||
|
if n > 0 {
|
||||||
|
log.Println("[" + this.tag + "]" + string(p[:n]))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Checkpoint struct {
|
type Checkpoint struct {
|
||||||
|
priority int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Checkpoint) Init() {
|
func (this *Checkpoint) Init() {
|
||||||
@@ -36,6 +37,14 @@ func (this *Checkpoint) Stop() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *Checkpoint) SetPriority(priority int) {
|
||||||
|
this.priority = priority
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Checkpoint) Priority() int {
|
||||||
|
return this.priority
|
||||||
|
}
|
||||||
|
|
||||||
func (this *Checkpoint) RequestBodyIsEmpty(req requests.Request) bool {
|
func (this *Checkpoint) RequestBodyIsEmpty(req requests.Request) bool {
|
||||||
if req.WAFRaw().ContentLength == 0 {
|
if req.WAFRaw().ContentLength == 0 {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ type CheckpointDefinition struct {
|
|||||||
Prefix string
|
Prefix string
|
||||||
HasParams bool // has sub params
|
HasParams bool // has sub params
|
||||||
Instance CheckpointInterface
|
Instance CheckpointInterface
|
||||||
|
Priority int
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,4 +33,10 @@ type CheckpointInterface interface {
|
|||||||
|
|
||||||
// Stop stop
|
// Stop stop
|
||||||
Stop()
|
Stop()
|
||||||
|
|
||||||
|
// SetPriority set priority
|
||||||
|
SetPriority(priority int)
|
||||||
|
|
||||||
|
// get priority
|
||||||
|
Priority() int
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,19 +41,45 @@ func (this *RequestRefererBlockCheckpoint) RequestValue(req requests.Request, pa
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var domains = options.GetSlice("allowDomains")
|
// allow domains
|
||||||
var domainStrings = []string{}
|
var allowDomains = options.GetSlice("allowDomains")
|
||||||
for _, domain := range domains {
|
var allowDomainStrings = []string{}
|
||||||
domainStrings = append(domainStrings, types.String(domain))
|
for _, domain := range allowDomains {
|
||||||
|
allowDomainStrings = append(allowDomainStrings, types.String(domain))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(domainStrings) == 0 {
|
// deny domains
|
||||||
|
var denyDomains = options.GetSlice("denyDomains")
|
||||||
|
var denyDomainStrings = []string{}
|
||||||
|
for _, domain := range denyDomains {
|
||||||
|
denyDomainStrings = append(denyDomainStrings, types.String(domain))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(allowDomainStrings) == 0 {
|
||||||
|
if len(denyDomainStrings) > 0 {
|
||||||
|
if configutils.MatchDomains(denyDomainStrings, host) {
|
||||||
|
value = 0
|
||||||
|
} else {
|
||||||
|
value = 1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
value = 0
|
value = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if configutils.MatchDomains(domainStrings, host) {
|
if configutils.MatchDomains(allowDomainStrings, host) {
|
||||||
|
if len(denyDomainStrings) > 0 {
|
||||||
|
if configutils.MatchDomains(denyDomainStrings, host) {
|
||||||
|
value = 0
|
||||||
|
} else {
|
||||||
|
value = 1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
value = 1
|
value = 1
|
||||||
|
return
|
||||||
} else {
|
} else {
|
||||||
value = 0
|
value = 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "通用Header比如Cache-Control、Accept之类的长度限制,防止缓冲区溢出攻击",
|
Description: "通用Header比如Cache-Control、Accept之类的长度限制,防止缓冲区溢出攻击",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestGeneralHeaderLengthCheckpoint),
|
Instance: new(RequestGeneralHeaderLengthCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "客户端地址(IP)",
|
Name: "客户端地址(IP)",
|
||||||
@@ -15,6 +16,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "试图通过分析X-Forwarded-For等Header获取的客户端地址,比如192.168.1.100",
|
Description: "试图通过分析X-Forwarded-For等Header获取的客户端地址,比如192.168.1.100",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestRemoteAddrCheckpoint),
|
Instance: new(RequestRemoteAddrCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "客户端源地址(IP)",
|
Name: "客户端源地址(IP)",
|
||||||
@@ -22,6 +24,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "直接连接的客户端地址,比如192.168.1.100",
|
Description: "直接连接的客户端地址,比如192.168.1.100",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestRawRemoteAddrCheckpoint),
|
Instance: new(RequestRawRemoteAddrCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "客户端端口",
|
Name: "客户端端口",
|
||||||
@@ -29,6 +32,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "直接连接的客户端地址端口",
|
Description: "直接连接的客户端地址端口",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestRemotePortCheckpoint),
|
Instance: new(RequestRemotePortCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "客户端用户名",
|
Name: "客户端用户名",
|
||||||
@@ -36,6 +40,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "通过BasicAuth登录的客户端用户名",
|
Description: "通过BasicAuth登录的客户端用户名",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestRemoteUserCheckpoint),
|
Instance: new(RequestRemoteUserCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "请求URI",
|
Name: "请求URI",
|
||||||
@@ -43,6 +48,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "包含URL参数的请求URI,类似于 /hello/world?lang=go",
|
Description: "包含URL参数的请求URI,类似于 /hello/world?lang=go",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestURICheckpoint),
|
Instance: new(RequestURICheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "请求路径",
|
Name: "请求路径",
|
||||||
@@ -50,6 +56,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "不包含URL参数的请求路径,类似于 /hello/world",
|
Description: "不包含URL参数的请求路径,类似于 /hello/world",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestPathCheckpoint),
|
Instance: new(RequestPathCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "请求URL",
|
Name: "请求URL",
|
||||||
@@ -57,6 +64,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "完整的请求URL,包含协议、域名、请求路径、参数等,类似于 https://example.com/hello?name=lily",
|
Description: "完整的请求URL,包含协议、域名、请求路径、参数等,类似于 https://example.com/hello?name=lily",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestURLCheckpoint),
|
Instance: new(RequestURLCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "请求内容长度",
|
Name: "请求内容长度",
|
||||||
@@ -64,6 +72,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "请求Header中的Content-Length",
|
Description: "请求Header中的Content-Length",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestLengthCheckpoint),
|
Instance: new(RequestLengthCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "请求体内容",
|
Name: "请求体内容",
|
||||||
@@ -71,6 +80,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "通常在POST或者PUT等操作时会附带请求体,最大限制32M",
|
Description: "通常在POST或者PUT等操作时会附带请求体,最大限制32M",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestBodyCheckpoint),
|
Instance: new(RequestBodyCheckpoint),
|
||||||
|
Priority: 5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "请求URI和请求体组合",
|
Name: "请求URI和请求体组合",
|
||||||
@@ -78,6 +88,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "${requestURI}和${requestBody}组合",
|
Description: "${requestURI}和${requestBody}组合",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestAllCheckpoint),
|
Instance: new(RequestAllCheckpoint),
|
||||||
|
Priority: 5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "请求表单参数",
|
Name: "请求表单参数",
|
||||||
@@ -85,6 +96,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "获取POST或者其他方法发送的表单参数,最大请求体限制32M",
|
Description: "获取POST或者其他方法发送的表单参数,最大请求体限制32M",
|
||||||
HasParams: true,
|
HasParams: true,
|
||||||
Instance: new(RequestFormArgCheckpoint),
|
Instance: new(RequestFormArgCheckpoint),
|
||||||
|
Priority: 5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "上传文件",
|
Name: "上传文件",
|
||||||
@@ -92,6 +104,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "获取POST上传的文件信息,最大请求体限制32M",
|
Description: "获取POST上传的文件信息,最大请求体限制32M",
|
||||||
HasParams: true,
|
HasParams: true,
|
||||||
Instance: new(RequestUploadCheckpoint),
|
Instance: new(RequestUploadCheckpoint),
|
||||||
|
Priority: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "请求JSON参数",
|
Name: "请求JSON参数",
|
||||||
@@ -99,6 +112,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "获取POST或者其他方法发送的JSON,最大请求体限制32M,使用点(.)符号表示多级数据",
|
Description: "获取POST或者其他方法发送的JSON,最大请求体限制32M,使用点(.)符号表示多级数据",
|
||||||
HasParams: true,
|
HasParams: true,
|
||||||
Instance: new(RequestJSONArgCheckpoint),
|
Instance: new(RequestJSONArgCheckpoint),
|
||||||
|
Priority: 5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "请求方法",
|
Name: "请求方法",
|
||||||
@@ -106,6 +120,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "比如GET、POST",
|
Description: "比如GET、POST",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestMethodCheckpoint),
|
Instance: new(RequestMethodCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "请求协议",
|
Name: "请求协议",
|
||||||
@@ -113,6 +128,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "比如http或https",
|
Description: "比如http或https",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestSchemeCheckpoint),
|
Instance: new(RequestSchemeCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "HTTP协议版本",
|
Name: "HTTP协议版本",
|
||||||
@@ -120,6 +136,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "比如HTTP/1.1",
|
Description: "比如HTTP/1.1",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestProtoCheckpoint),
|
Instance: new(RequestProtoCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "主机名",
|
Name: "主机名",
|
||||||
@@ -127,6 +144,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "比如teaos.cn",
|
Description: "比如teaos.cn",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestHostCheckpoint),
|
Instance: new(RequestHostCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "请求来源URL",
|
Name: "请求来源URL",
|
||||||
@@ -134,6 +152,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "请求Header中的Referer值",
|
Description: "请求Header中的Referer值",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestRefererCheckpoint),
|
Instance: new(RequestRefererCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "客户端信息",
|
Name: "客户端信息",
|
||||||
@@ -141,6 +160,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "比如Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103",
|
Description: "比如Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestUserAgentCheckpoint),
|
Instance: new(RequestUserAgentCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "内容类型",
|
Name: "内容类型",
|
||||||
@@ -148,6 +168,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "请求Header的Content-Type",
|
Description: "请求Header的Content-Type",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestContentTypeCheckpoint),
|
Instance: new(RequestContentTypeCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "所有cookie组合字符串",
|
Name: "所有cookie组合字符串",
|
||||||
@@ -155,6 +176,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "比如sid=IxZVPFhE&city=beijing&uid=18237",
|
Description: "比如sid=IxZVPFhE&city=beijing&uid=18237",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestCookiesCheckpoint),
|
Instance: new(RequestCookiesCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "单个cookie值",
|
Name: "单个cookie值",
|
||||||
@@ -162,6 +184,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "单个cookie值",
|
Description: "单个cookie值",
|
||||||
HasParams: true,
|
HasParams: true,
|
||||||
Instance: new(RequestCookieCheckpoint),
|
Instance: new(RequestCookieCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "所有URL参数组合",
|
Name: "所有URL参数组合",
|
||||||
@@ -169,6 +192,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "比如name=lu&age=20",
|
Description: "比如name=lu&age=20",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestArgsCheckpoint),
|
Instance: new(RequestArgsCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "单个URL参数值",
|
Name: "单个URL参数值",
|
||||||
@@ -176,6 +200,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "单个URL参数值",
|
Description: "单个URL参数值",
|
||||||
HasParams: true,
|
HasParams: true,
|
||||||
Instance: new(RequestArgCheckpoint),
|
Instance: new(RequestArgCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "所有Header信息",
|
Name: "所有Header信息",
|
||||||
@@ -183,6 +208,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "使用\\n隔开的Header信息字符串",
|
Description: "使用\\n隔开的Header信息字符串",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestHeadersCheckpoint),
|
Instance: new(RequestHeadersCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "单个Header值",
|
Name: "单个Header值",
|
||||||
@@ -190,6 +216,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "单个Header值",
|
Description: "单个Header值",
|
||||||
HasParams: true,
|
HasParams: true,
|
||||||
Instance: new(RequestHeaderCheckpoint),
|
Instance: new(RequestHeaderCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "国家/地区名称",
|
Name: "国家/地区名称",
|
||||||
@@ -197,6 +224,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "国家/地区名称",
|
Description: "国家/地区名称",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestGeoCountryNameCheckpoint),
|
Instance: new(RequestGeoCountryNameCheckpoint),
|
||||||
|
Priority: 90,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "省份名称",
|
Name: "省份名称",
|
||||||
@@ -204,6 +232,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "中国省份名称",
|
Description: "中国省份名称",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestGeoProvinceNameCheckpoint),
|
Instance: new(RequestGeoProvinceNameCheckpoint),
|
||||||
|
Priority: 90,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "城市名称",
|
Name: "城市名称",
|
||||||
@@ -211,6 +240,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "中国城市名称",
|
Description: "中国城市名称",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestGeoCityNameCheckpoint),
|
Instance: new(RequestGeoCityNameCheckpoint),
|
||||||
|
Priority: 90,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "ISP名称",
|
Name: "ISP名称",
|
||||||
@@ -218,6 +248,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "ISP名称",
|
Description: "ISP名称",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(RequestISPNameCheckpoint),
|
Instance: new(RequestISPNameCheckpoint),
|
||||||
|
Priority: 90,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "CC统计(旧)",
|
Name: "CC统计(旧)",
|
||||||
@@ -225,6 +256,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "统计某段时间段内的请求信息",
|
Description: "统计某段时间段内的请求信息",
|
||||||
HasParams: true,
|
HasParams: true,
|
||||||
Instance: new(CCCheckpoint),
|
Instance: new(CCCheckpoint),
|
||||||
|
Priority: 10,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "CC统计(新)",
|
Name: "CC统计(新)",
|
||||||
@@ -232,6 +264,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "统计某段时间段内的请求信息",
|
Description: "统计某段时间段内的请求信息",
|
||||||
HasParams: true,
|
HasParams: true,
|
||||||
Instance: new(CC2Checkpoint),
|
Instance: new(CC2Checkpoint),
|
||||||
|
Priority: 10,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "防盗链",
|
Name: "防盗链",
|
||||||
@@ -239,6 +272,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "阻止一些域名访问引用本站资源",
|
Description: "阻止一些域名访问引用本站资源",
|
||||||
HasParams: true,
|
HasParams: true,
|
||||||
Instance: new(RequestRefererBlockCheckpoint),
|
Instance: new(RequestRefererBlockCheckpoint),
|
||||||
|
Priority: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "通用响应Header长度限制",
|
Name: "通用响应Header长度限制",
|
||||||
@@ -246,6 +280,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "通用Header比如Cache-Control、Accept之类的长度限制,防止缓冲区溢出攻击",
|
Description: "通用Header比如Cache-Control、Accept之类的长度限制,防止缓冲区溢出攻击",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(ResponseGeneralHeaderLengthCheckpoint),
|
Instance: new(ResponseGeneralHeaderLengthCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "响应状态码",
|
Name: "响应状态码",
|
||||||
@@ -253,6 +288,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "响应状态码,比如200、404、500",
|
Description: "响应状态码,比如200、404、500",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(ResponseStatusCheckpoint),
|
Instance: new(ResponseStatusCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "响应Header",
|
Name: "响应Header",
|
||||||
@@ -260,6 +296,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "响应Header值",
|
Description: "响应Header值",
|
||||||
HasParams: true,
|
HasParams: true,
|
||||||
Instance: new(ResponseHeaderCheckpoint),
|
Instance: new(ResponseHeaderCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "响应内容",
|
Name: "响应内容",
|
||||||
@@ -267,6 +304,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "响应内容字符串",
|
Description: "响应内容字符串",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(ResponseBodyCheckpoint),
|
Instance: new(ResponseBodyCheckpoint),
|
||||||
|
Priority: 5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "响应内容长度",
|
Name: "响应内容长度",
|
||||||
@@ -274,6 +312,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
Description: "响应内容长度,通过响应的Header Content-Length获取",
|
Description: "响应内容长度,通过响应的Header Content-Length获取",
|
||||||
HasParams: false,
|
HasParams: false,
|
||||||
Instance: new(ResponseBytesSentCheckpoint),
|
Instance: new(ResponseBytesSentCheckpoint),
|
||||||
|
Priority: 100,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,6 +320,7 @@ var AllCheckpoints = []*CheckpointDefinition{
|
|||||||
func FindCheckpoint(prefix string) CheckpointInterface {
|
func FindCheckpoint(prefix string) CheckpointInterface {
|
||||||
for _, def := range AllCheckpoints {
|
for _, def := range AllCheckpoints {
|
||||||
if def.Prefix == prefix {
|
if def.Prefix == prefix {
|
||||||
|
def.Instance.SetPriority(def.Priority)
|
||||||
return def.Instance
|
return def.Instance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ type Rule struct {
|
|||||||
Value string `yaml:"value" json:"value"` // compared value
|
Value string `yaml:"value" json:"value"` // compared value
|
||||||
IsCaseInsensitive bool `yaml:"isCaseInsensitive" json:"isCaseInsensitive"`
|
IsCaseInsensitive bool `yaml:"isCaseInsensitive" json:"isCaseInsensitive"`
|
||||||
CheckpointOptions map[string]interface{} `yaml:"checkpointOptions" json:"checkpointOptions"`
|
CheckpointOptions map[string]interface{} `yaml:"checkpointOptions" json:"checkpointOptions"`
|
||||||
|
Priority int `yaml:"priority" json:"priority"`
|
||||||
|
|
||||||
checkpointFinder func(prefix string) checkpoints.CheckpointInterface
|
checkpointFinder func(prefix string) checkpoints.CheckpointInterface
|
||||||
|
|
||||||
@@ -132,9 +133,9 @@ func (this *Rule) Init() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if singleParamRegexp.MatchString(this.Param) {
|
if singleParamRegexp.MatchString(this.Param) {
|
||||||
param := this.Param[2 : len(this.Param)-1]
|
var param = this.Param[2 : len(this.Param)-1]
|
||||||
pieces := strings.SplitN(param, ".", 2)
|
var pieces = strings.SplitN(param, ".", 2)
|
||||||
prefix := pieces[0]
|
var prefix = pieces[0]
|
||||||
if len(pieces) == 1 {
|
if len(pieces) == 1 {
|
||||||
this.singleParam = ""
|
this.singleParam = ""
|
||||||
} else {
|
} else {
|
||||||
@@ -142,18 +143,20 @@ func (this *Rule) Init() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if this.checkpointFinder != nil {
|
if this.checkpointFinder != nil {
|
||||||
checkpoint := this.checkpointFinder(prefix)
|
var checkpoint = this.checkpointFinder(prefix)
|
||||||
if checkpoint == nil {
|
if checkpoint == nil {
|
||||||
return errors.New("no check point '" + prefix + "' found")
|
return errors.New("no check point '" + prefix + "' found")
|
||||||
}
|
}
|
||||||
this.singleCheckpoint = checkpoint
|
this.singleCheckpoint = checkpoint
|
||||||
|
this.Priority = checkpoint.Priority()
|
||||||
} else {
|
} else {
|
||||||
checkpoint := checkpoints.FindCheckpoint(prefix)
|
var checkpoint = checkpoints.FindCheckpoint(prefix)
|
||||||
if checkpoint == nil {
|
if checkpoint == nil {
|
||||||
return errors.New("no check point '" + prefix + "' found")
|
return errors.New("no check point '" + prefix + "' found")
|
||||||
}
|
}
|
||||||
checkpoint.Init()
|
checkpoint.Init()
|
||||||
this.singleCheckpoint = checkpoint
|
this.singleCheckpoint = checkpoint
|
||||||
|
this.Priority = checkpoint.Priority()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -162,22 +165,24 @@ func (this *Rule) Init() error {
|
|||||||
this.multipleCheckpoints = map[string]checkpoints.CheckpointInterface{}
|
this.multipleCheckpoints = map[string]checkpoints.CheckpointInterface{}
|
||||||
var err error = nil
|
var err error = nil
|
||||||
configutils.ParseVariables(this.Param, func(varName string) (value string) {
|
configutils.ParseVariables(this.Param, func(varName string) (value string) {
|
||||||
pieces := strings.SplitN(varName, ".", 2)
|
var pieces = strings.SplitN(varName, ".", 2)
|
||||||
prefix := pieces[0]
|
var prefix = pieces[0]
|
||||||
if this.checkpointFinder != nil {
|
if this.checkpointFinder != nil {
|
||||||
checkpoint := this.checkpointFinder(prefix)
|
var checkpoint = this.checkpointFinder(prefix)
|
||||||
if checkpoint == nil {
|
if checkpoint == nil {
|
||||||
err = errors.New("no check point '" + prefix + "' found")
|
err = errors.New("no check point '" + prefix + "' found")
|
||||||
} else {
|
} else {
|
||||||
this.multipleCheckpoints[prefix] = checkpoint
|
this.multipleCheckpoints[prefix] = checkpoint
|
||||||
|
this.Priority = checkpoint.Priority()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
checkpoint := checkpoints.FindCheckpoint(prefix)
|
var checkpoint = checkpoints.FindCheckpoint(prefix)
|
||||||
if checkpoint == nil {
|
if checkpoint == nil {
|
||||||
err = errors.New("no check point '" + prefix + "' found")
|
err = errors.New("no check point '" + prefix + "' found")
|
||||||
} else {
|
} else {
|
||||||
checkpoint.Init()
|
checkpoint.Init()
|
||||||
this.multipleCheckpoints[prefix] = checkpoint
|
this.multipleCheckpoints[prefix] = checkpoint
|
||||||
|
this.Priority = checkpoint.Priority()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -52,6 +52,11 @@ func (this *RuleSet) Init(waf *WAF) error {
|
|||||||
return errors.New("init rule '" + rule.Param + " " + rule.Operator + " " + types.String(rule.Value) + "' failed: " + err.Error())
|
return errors.New("init rule '" + rule.Param + " " + rule.Operator + " " + types.String(rule.Value) + "' failed: " + err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sort by priority
|
||||||
|
sort.Slice(this.Rules, func(i, j int) bool {
|
||||||
|
return this.Rules[i].Priority > this.Rules[j].Priority
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// action codes
|
// action codes
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ func (this *WAF) Init() (resultErrors []error) {
|
|||||||
for _, def := range checkpoints.AllCheckpoints {
|
for _, def := range checkpoints.AllCheckpoints {
|
||||||
instance := reflect.New(reflect.Indirect(reflect.ValueOf(def.Instance)).Type()).Interface().(checkpoints.CheckpointInterface)
|
instance := reflect.New(reflect.Indirect(reflect.ValueOf(def.Instance)).Type()).Interface().(checkpoints.CheckpointInterface)
|
||||||
instance.Init()
|
instance.Init()
|
||||||
|
instance.SetPriority(def.Priority)
|
||||||
this.checkpointsMap[def.Prefix] = instance
|
this.checkpointsMap[def.Prefix] = instance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user