mirror of
https://github.com/TeaOSLab/EdgeNode.git
synced 2025-11-12 06:10:25 +08:00
支持任意域名通过CNAME访问服务(开启选项后)
This commit is contained in:
1
go.mod
1
go.mod
@@ -22,6 +22,7 @@ require (
|
|||||||
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
|
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
|
||||||
github.com/lionsoul2014/ip2region v2.2.0-release+incompatible
|
github.com/lionsoul2014/ip2region v2.2.0-release+incompatible
|
||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||||
|
github.com/miekg/dns v1.1.43
|
||||||
github.com/mssola/user_agent v0.5.2
|
github.com/mssola/user_agent v0.5.2
|
||||||
github.com/pires/go-proxyproto v0.6.1
|
github.com/pires/go-proxyproto v0.6.1
|
||||||
github.com/shirou/gopsutil v3.21.5+incompatible
|
github.com/shirou/gopsutil v3.21.5+incompatible
|
||||||
|
|||||||
3
go.sum
3
go.sum
@@ -96,6 +96,8 @@ github.com/lionsoul2014/ip2region v2.2.0-release+incompatible h1:1qp9iks+69h7IGL
|
|||||||
github.com/lionsoul2014/ip2region v2.2.0-release+incompatible/go.mod h1:+ZBN7PBoh5gG6/y0ZQ85vJDBe21WnfbRrQQwTfliJJI=
|
github.com/lionsoul2014/ip2region v2.2.0-release+incompatible/go.mod h1:+ZBN7PBoh5gG6/y0ZQ85vJDBe21WnfbRrQQwTfliJJI=
|
||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
|
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||||
|
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/mssola/user_agent v0.5.2 h1:CZkTUahjL1+OcZ5zv3kZr8QiJ8jy2H08vZIEkBeRbxo=
|
github.com/mssola/user_agent v0.5.2 h1:CZkTUahjL1+OcZ5zv3kZr8QiJ8jy2H08vZIEkBeRbxo=
|
||||||
@@ -163,6 +165,7 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
|
|||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
|||||||
@@ -21,19 +21,19 @@ type BaseListener struct {
|
|||||||
countActiveConnections int64 // 当前活跃的连接数
|
countActiveConnections int64 // 当前活跃的连接数
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// Init 初始化
|
||||||
func (this *BaseListener) Init() {
|
func (this *BaseListener) Init() {
|
||||||
this.namedServers = map[string]*NamedServer{}
|
this.namedServers = map[string]*NamedServer{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除既有配置
|
// Reset 清除既有配置
|
||||||
func (this *BaseListener) Reset() {
|
func (this *BaseListener) Reset() {
|
||||||
this.namedServersLocker.Lock()
|
this.namedServersLocker.Lock()
|
||||||
this.namedServers = map[string]*NamedServer{}
|
this.namedServers = map[string]*NamedServer{}
|
||||||
this.namedServersLocker.Unlock()
|
this.namedServersLocker.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前活跃连接数
|
// CountActiveListeners 获取当前活跃连接数
|
||||||
func (this *BaseListener) CountActiveListeners() int {
|
func (this *BaseListener) CountActiveListeners() int {
|
||||||
return types.Int(this.countActiveConnections)
|
return types.Int(this.countActiveConnections)
|
||||||
}
|
}
|
||||||
@@ -253,3 +253,38 @@ func (this *BaseListener) findNamedServerMatched(name string) (serverConfig *ser
|
|||||||
|
|
||||||
return nil, name
|
return nil, name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 使用CNAME来查找服务
|
||||||
|
// TODO 防止单IP随机生成域名攻击
|
||||||
|
func (this *BaseListener) findServerWithCname(domain string) *serverconfigs.ServerConfig {
|
||||||
|
if !sharedNodeConfig.SupportCNAME {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var realName = sharedCNAMEManager.Lookup(domain)
|
||||||
|
if len(realName) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
this.serversLocker.Lock()
|
||||||
|
defer this.serversLocker.Unlock()
|
||||||
|
|
||||||
|
group := this.Group
|
||||||
|
if group == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
currentServers := group.Servers
|
||||||
|
countServers := len(currentServers)
|
||||||
|
if countServers == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, server := range currentServers {
|
||||||
|
if server.SupportCNAME && lists.ContainsString(server.AliasServerNames, realName) {
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ func (this *HTTPListener) handleHTTP(rawWriter http.ResponseWriter, rawReq *http
|
|||||||
}
|
}
|
||||||
|
|
||||||
server, serverName := this.findNamedServer(domain)
|
server, serverName := this.findNamedServer(domain)
|
||||||
|
if server == nil {
|
||||||
|
server = this.findServerWithCname(domain)
|
||||||
if server == nil {
|
if server == nil {
|
||||||
// 严格匹配域名模式下,我们拒绝用户访问
|
// 严格匹配域名模式下,我们拒绝用户访问
|
||||||
if sharedNodeConfig.GlobalConfig != nil && sharedNodeConfig.GlobalConfig.HTTPAll.MatchDomainStrictly {
|
if sharedNodeConfig.GlobalConfig != nil && sharedNodeConfig.GlobalConfig.HTTPAll.MatchDomainStrictly {
|
||||||
@@ -184,6 +186,9 @@ func (this *HTTPListener) handleHTTP(rawWriter http.ResponseWriter, rawReq *http
|
|||||||
|
|
||||||
http.Error(rawWriter, "404 page not found: '"+rawReq.URL.String()+"'", http.StatusNotFound)
|
http.Error(rawWriter, "404 page not found: '"+rawReq.URL.String()+"'", http.StatusNotFound)
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
serverName = domain
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 包装新请求对象
|
// 包装新请求对象
|
||||||
|
|||||||
48
internal/nodes/server_cname_manager.go
Normal file
48
internal/nodes/server_cname_manager.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sharedCNAMEManager = NewServerCNAMEManager()
|
||||||
|
|
||||||
|
// ServerCNAMEManager 服务CNAME管理
|
||||||
|
// TODO 需要自动更新缓存里的记录
|
||||||
|
type ServerCNAMEManager struct {
|
||||||
|
ttlCache *ttlcache.Cache
|
||||||
|
|
||||||
|
locker sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerCNAMEManager() *ServerCNAMEManager {
|
||||||
|
return &ServerCNAMEManager{
|
||||||
|
ttlCache: ttlcache.NewCache(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ServerCNAMEManager) Lookup(domain string) string {
|
||||||
|
if len(domain) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var item = this.ttlCache.Read(domain)
|
||||||
|
if item != nil {
|
||||||
|
return types.String(item.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
cname, _ := utils.LookupCNAME(domain)
|
||||||
|
if len(cname) > 0 {
|
||||||
|
cname = strings.TrimSuffix(cname, ".")
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ttlCache.Write(domain, cname, time.Now().Unix()+600)
|
||||||
|
|
||||||
|
return cname
|
||||||
|
}
|
||||||
19
internal/nodes/server_cname_manager_test.go
Normal file
19
internal/nodes/server_cname_manager_test.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServerCNameManager_Lookup(t *testing.T) {
|
||||||
|
var cnameManager = NewServerCNAMEManager()
|
||||||
|
t.Log(cnameManager.Lookup("www.yun4s.cn"))
|
||||||
|
|
||||||
|
var before = time.Now()
|
||||||
|
defer func() {
|
||||||
|
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||||
|
}()
|
||||||
|
t.Log(cnameManager.Lookup("www.yun4s.cn"))
|
||||||
|
}
|
||||||
35
internal/utils/lookup.go
Normal file
35
internal/utils/lookup.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LookupCNAME 获取CNAME
|
||||||
|
func LookupCNAME(host string) (string, error) {
|
||||||
|
config, err := dns.ClientConfigFromFile("/etc/resolv.conf")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := new(dns.Client)
|
||||||
|
m := new(dns.Msg)
|
||||||
|
|
||||||
|
m.SetQuestion(host+".", dns.TypeCNAME)
|
||||||
|
m.RecursionDesired = true
|
||||||
|
|
||||||
|
var lastErr error
|
||||||
|
for _, serverAddr := range config.Servers {
|
||||||
|
r, _, err := c.Exchange(m, configutils.QuoteIP(serverAddr)+":"+config.Port)
|
||||||
|
if err != nil {
|
||||||
|
lastErr = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(r.Answer) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Answer[0].(*dns.CNAME).Target, nil
|
||||||
|
}
|
||||||
|
return "", lastErr
|
||||||
|
}
|
||||||
9
internal/utils/lookup_test.go
Normal file
9
internal/utils/lookup_test.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestLookupCNAME(t *testing.T) {
|
||||||
|
t.Log(LookupCNAME("www.yun4s.cn"))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user