mirror of
https://github.com/TeaOSLab/EdgeNode.git
synced 2025-11-03 15:00:26 +08:00
阶段性提交
This commit is contained in:
@@ -10,6 +10,8 @@ type APIConfig struct {
|
||||
RPC struct {
|
||||
Endpoints []string `yaml:"endpoints"`
|
||||
} `yaml:"rpc"`
|
||||
NodeId string `yaml:"nodeId"`
|
||||
Secret string `yaml:"secret"`
|
||||
}
|
||||
|
||||
func LoadAPIConfig() (*APIConfig, error) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package configs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs"
|
||||
"github.com/go-yaml/yaml"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"io/ioutil"
|
||||
@@ -9,10 +10,13 @@ import (
|
||||
var sharedNodeConfig *NodeConfig = nil
|
||||
|
||||
type NodeConfig struct {
|
||||
Id string `yaml:"id"`
|
||||
Servers []*ServerConfig `yaml:"servers"`
|
||||
Id string `yaml:"id" json:"id"`
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
Servers []*serverconfigs.ServerConfig `yaml:"servers" json:"servers"`
|
||||
Version int `yaml:"version" json:"version"`
|
||||
}
|
||||
|
||||
// 取得当前节点配置单例
|
||||
func SharedNodeConfig() (*NodeConfig, error) {
|
||||
sharedLocker.Lock()
|
||||
defer sharedLocker.Unlock()
|
||||
@@ -36,9 +40,19 @@ func SharedNodeConfig() (*NodeConfig, error) {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// 刷新当前节点配置
|
||||
func ReloadNodeConfig() error {
|
||||
sharedLocker.Lock()
|
||||
sharedNodeConfig = nil
|
||||
sharedLocker.Unlock()
|
||||
|
||||
_, err := SharedNodeConfig()
|
||||
return err
|
||||
}
|
||||
|
||||
// 根据网络地址和协议分组
|
||||
func (this *NodeConfig) AvailableGroups() []*ServerGroup {
|
||||
groupMapping := map[string]*ServerGroup{} // protocol://addr => Server Group
|
||||
func (this *NodeConfig) AvailableGroups() []*serverconfigs.ServerGroup {
|
||||
groupMapping := map[string]*serverconfigs.ServerGroup{} // protocol://addr => Server Group
|
||||
for _, server := range this.Servers {
|
||||
if !server.IsOn {
|
||||
continue
|
||||
@@ -48,15 +62,39 @@ func (this *NodeConfig) AvailableGroups() []*ServerGroup {
|
||||
if ok {
|
||||
group.Add(server)
|
||||
} else {
|
||||
group = NewServerGroup(addr)
|
||||
group = serverconfigs.NewServerGroup(addr)
|
||||
group.Add(server)
|
||||
}
|
||||
groupMapping[addr] = group
|
||||
}
|
||||
}
|
||||
result := []*ServerGroup{}
|
||||
result := []*serverconfigs.ServerGroup{}
|
||||
for _, group := range groupMapping {
|
||||
result = append(result, group)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (this *NodeConfig) Init() error {
|
||||
for _, server := range this.Servers {
|
||||
err := server.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 写入到文件
|
||||
func (this *NodeConfig) Save() error {
|
||||
sharedLocker.Lock()
|
||||
defer sharedLocker.Unlock()
|
||||
|
||||
data, err := yaml.Marshal(this)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(Tea.ConfigFile("node.yaml"), data, 0777)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package configs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"testing"
|
||||
@@ -27,18 +28,37 @@ func TestSharedNodeConfig(t *testing.T) {
|
||||
|
||||
func TestNodeConfig_Groups(t *testing.T) {
|
||||
config := &NodeConfig{}
|
||||
config.Servers = []*ServerConfig{
|
||||
config.Servers = []*serverconfigs.ServerConfig{
|
||||
{
|
||||
IsOn: true,
|
||||
HTTP: &HTTPProtocolConfig{
|
||||
IsOn: true,
|
||||
Listen: []string{"127.0.0.1:1234", ":8080"},
|
||||
HTTP: &serverconfigs.HTTPProtocolConfig{
|
||||
BaseProtocol: serverconfigs.BaseProtocol{
|
||||
IsOn: true,
|
||||
Listen: []*serverconfigs.NetworkAddressConfig{
|
||||
{
|
||||
Protocol: serverconfigs.ProtocolHTTP,
|
||||
Host: "127.0.0.1",
|
||||
PortRange: "1234",
|
||||
},
|
||||
{
|
||||
Protocol: serverconfigs.ProtocolHTTP,
|
||||
PortRange: "8080",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
HTTP: &HTTPProtocolConfig{
|
||||
IsOn: true,
|
||||
Listen: []string{":8080"},
|
||||
HTTP: &serverconfigs.HTTPProtocolConfig{
|
||||
BaseProtocol: serverconfigs.BaseProtocol{
|
||||
IsOn: true,
|
||||
Listen: []*serverconfigs.NetworkAddressConfig{
|
||||
{
|
||||
Protocol: serverconfigs.ProtocolHTTP,
|
||||
PortRange: "8080",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
package configs
|
||||
|
||||
type OriginServerConfig struct {
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package configs
|
||||
|
||||
type HTTPProtocolConfig struct {
|
||||
IsOn bool `yaml:"isOn"` // 是否开启
|
||||
IPVersion IPVersion `yaml:"ipVersion"` // 4, 6
|
||||
Listen []string `yaml:"listen" json:"listen"` // 监听地址
|
||||
}
|
||||
|
||||
func (this *HTTPProtocolConfig) Addresses() []string {
|
||||
result := []string{}
|
||||
for _, listen := range this.Listen {
|
||||
switch this.IPVersion {
|
||||
case IPv4:
|
||||
result = append(result, ProtocolHTTP4+"://"+listen)
|
||||
case IPv6:
|
||||
result = append(result, ProtocolHTTP6+"://"+listen)
|
||||
default:
|
||||
result = append(result, ProtocolHTTP+"://"+listen)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package configs
|
||||
|
||||
type HTTPSProtocolConfig struct {
|
||||
IsOn bool `yaml:"isOn"` // 是否开启
|
||||
IPVersion string `yaml:"ipVersion"` // 4, 6
|
||||
Listen []string `yaml:"listen" json:"listen"` // 监听地址
|
||||
}
|
||||
|
||||
func (this *HTTPSProtocolConfig) Addresses() []string {
|
||||
result := []string{}
|
||||
for _, listen := range this.Listen {
|
||||
switch this.IPVersion {
|
||||
case IPv4:
|
||||
result = append(result, ProtocolHTTPS4+"://"+listen)
|
||||
case IPv6:
|
||||
result = append(result, ProtocolHTTPS6+"://"+listen)
|
||||
default:
|
||||
result = append(result, ProtocolHTTPS+"://"+listen)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package configs
|
||||
|
||||
type TCPProtocolConfig struct {
|
||||
IsOn bool `yaml:"isOn"` // 是否开启
|
||||
IPVersion IPVersion `yaml:"ipVersion"` // 4, 6
|
||||
Listen []string `yaml:"listen" json:"listen"` // 监听地址
|
||||
}
|
||||
|
||||
func (this *TCPProtocolConfig) Addresses() []string {
|
||||
result := []string{}
|
||||
for _, listen := range this.Listen {
|
||||
switch this.IPVersion {
|
||||
case IPv4:
|
||||
result = append(result, ProtocolTCP4+"://"+listen)
|
||||
case IPv6:
|
||||
result = append(result, ProtocolTCP6+"://"+listen)
|
||||
default:
|
||||
result = append(result, ProtocolTCP+"://"+listen)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package configs
|
||||
|
||||
type TLSProtocolConfig struct {
|
||||
IsOn bool `yaml:"isOn"` // 是否开启
|
||||
IPVersion string `yaml:"ipVersion"` // 4, 6
|
||||
Listen []string `yaml:"listen" json:"listen"` // 监听地址
|
||||
}
|
||||
|
||||
func (this *TLSProtocolConfig) Addresses() []string {
|
||||
result := []string{}
|
||||
for _, listen := range this.Listen {
|
||||
switch this.IPVersion {
|
||||
case IPv4:
|
||||
result = append(result, ProtocolTLS4+"://"+listen)
|
||||
case IPv6:
|
||||
result = append(result, ProtocolTLS6+"://"+listen)
|
||||
default:
|
||||
result = append(result, ProtocolTLS+"://"+listen)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package configs
|
||||
|
||||
type UDPProtocolConfig struct {
|
||||
IsOn bool `yaml:"isOn"` // 是否开启
|
||||
Listen []string `yaml:"listen" json:"listen"` // 监听地址
|
||||
}
|
||||
|
||||
func (this *UDPProtocolConfig) Addresses() []string {
|
||||
result := []string{}
|
||||
for _, listen := range this.Listen {
|
||||
result = append(result, ProtocolUDP+"://"+listen)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package configs
|
||||
|
||||
type UnixProtocolConfig struct {
|
||||
IsOn bool `yaml:"isOn"` // 是否开启
|
||||
Listen []string `yaml:"listen" json:"listen"` // 监听地址
|
||||
}
|
||||
|
||||
func (this *UnixProtocolConfig) Addresses() []string {
|
||||
result := []string{}
|
||||
for _, listen := range this.Listen {
|
||||
result = append(result, ProtocolUnix+":"+listen)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package configs
|
||||
|
||||
type ServerConfig struct {
|
||||
Id string `yaml:"id"` // ID
|
||||
IsOn bool `yaml:"isOn"` // 是否开启
|
||||
Components []*ComponentConfig `yaml:"components"` // 组件
|
||||
Filters []*FilterConfig `yaml:"filters"` // 过滤器
|
||||
Name string `yaml:"name"` // 名称
|
||||
Description string `yaml:"description"` // 描述
|
||||
ServerNames []string `yaml:"serverNames"` // 域名
|
||||
|
||||
// 协议
|
||||
HTTP *HTTPProtocolConfig `yaml:"http"` // HTTP配置
|
||||
HTTPS *HTTPSProtocolConfig `yaml:"https"` // HTTPS配置
|
||||
TCP *TCPProtocolConfig `yaml:"tcp"` // TCP配置
|
||||
TLS *TLSProtocolConfig `yaml:"tls"` // TLS配置
|
||||
Unix *UnixProtocolConfig `yaml:"unix"` // Unix配置
|
||||
UDP *UDPProtocolConfig `yaml:"udp"` // UDP配置
|
||||
|
||||
// Web配置
|
||||
Web *WebConfig `yaml:"web"`
|
||||
}
|
||||
|
||||
func NewServerConfig() *ServerConfig {
|
||||
return &ServerConfig{}
|
||||
}
|
||||
|
||||
func (this *ServerConfig) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *ServerConfig) FullAddresses() []string {
|
||||
result := []Protocol{}
|
||||
if this.HTTP != nil && this.HTTP.IsOn {
|
||||
result = append(result, this.HTTP.Addresses()...)
|
||||
}
|
||||
if this.HTTPS != nil && this.HTTPS.IsOn {
|
||||
result = append(result, this.HTTPS.Addresses()...)
|
||||
}
|
||||
if this.TCP != nil && this.TCP.IsOn {
|
||||
result = append(result, this.TCP.Addresses()...)
|
||||
}
|
||||
if this.TLS != nil && this.TLS.IsOn {
|
||||
result = append(result, this.TLS.Addresses()...)
|
||||
}
|
||||
if this.Unix != nil && this.Unix.IsOn {
|
||||
result = append(result, this.Unix.Addresses()...)
|
||||
}
|
||||
if this.UDP != nil && this.UDP.IsOn {
|
||||
result = append(result, this.UDP.Addresses()...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package configs
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestServerConfig_Protocols(t *testing.T) {
|
||||
{
|
||||
server := NewServerConfig()
|
||||
t.Log(server.FullAddresses())
|
||||
}
|
||||
|
||||
{
|
||||
server := NewServerConfig()
|
||||
server.HTTP = &HTTPProtocolConfig{IsOn: true, Listen: []string{"127.0.0.1:1234"}}
|
||||
server.HTTPS = &HTTPSProtocolConfig{IsOn: true, Listen: []string{"127.0.0.1:1234"}}
|
||||
server.TCP = &TCPProtocolConfig{IsOn: true, Listen: []string{"127.0.0.1:1234"}}
|
||||
server.TLS = &TLSProtocolConfig{IsOn: true, Listen: []string{"127.0.0.1:1234"}}
|
||||
server.Unix = &UnixProtocolConfig{IsOn: true, Listen: []string{"127.0.0.1:1234"}}
|
||||
server.UDP = &UDPProtocolConfig{IsOn: true, Listen: []string{"127.0.0.1:1234"}}
|
||||
t.Log(server.FullAddresses())
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package configs
|
||||
|
||||
import "strings"
|
||||
|
||||
type ServerGroup struct {
|
||||
fullAddr string
|
||||
Servers []*ServerConfig
|
||||
}
|
||||
|
||||
func NewServerGroup(fullAddr string) *ServerGroup {
|
||||
return &ServerGroup{fullAddr: fullAddr}
|
||||
}
|
||||
|
||||
// 添加服务
|
||||
func (this *ServerGroup) Add(server *ServerConfig) {
|
||||
this.Servers = append(this.Servers, server)
|
||||
}
|
||||
|
||||
// 获取完整的地址
|
||||
func (this *ServerGroup) FullAddr() string {
|
||||
return this.fullAddr
|
||||
}
|
||||
|
||||
// 获取当前分组的协议
|
||||
func (this *ServerGroup) Protocol() Protocol {
|
||||
for _, p := range AllProtocols() {
|
||||
if strings.HasPrefix(this.fullAddr, p+":") {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return ProtocolHTTP
|
||||
}
|
||||
|
||||
// 获取当前分组的地址
|
||||
func (this *ServerGroup) Addr() string {
|
||||
protocol := this.Protocol()
|
||||
if protocol == ProtocolUnix {
|
||||
return strings.TrimPrefix(this.fullAddr, protocol+":")
|
||||
}
|
||||
return strings.TrimPrefix(this.fullAddr, protocol+"://")
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package configs
|
||||
package serverconfigs
|
||||
|
||||
type ComponentConfig struct {
|
||||
}
|
||||
20
internal/configs/serverconfigs/configutils/copy.go
Normal file
20
internal/configs/serverconfigs/configutils/copy.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package configutils
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// 拷贝同类型struct指针对象中的字段
|
||||
func CopyStructObject(destPtr, sourcePtr interface{}) {
|
||||
value := reflect.ValueOf(destPtr)
|
||||
value2 := reflect.ValueOf(sourcePtr)
|
||||
|
||||
countFields := value2.Elem().NumField()
|
||||
for i := 0; i < countFields; i++ {
|
||||
v := value2.Elem().Field(i)
|
||||
if !v.IsValid() || !v.CanSet() {
|
||||
continue
|
||||
}
|
||||
value.Elem().Field(i).Set(v)
|
||||
}
|
||||
}
|
||||
28
internal/configs/serverconfigs/configutils/copy_test.go
Normal file
28
internal/configs/serverconfigs/configutils/copy_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package configutils
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCopyStructObject(t *testing.T) {
|
||||
type Book struct {
|
||||
Name string
|
||||
Price int
|
||||
Year int
|
||||
Author string
|
||||
press string
|
||||
}
|
||||
|
||||
book1 := &Book{
|
||||
Name: "Hello Golang",
|
||||
Price: 100,
|
||||
Year: 2020,
|
||||
Author: "Liu",
|
||||
press: "Beijing",
|
||||
}
|
||||
book2 := new(Book)
|
||||
CopyStructObject(book2, book1)
|
||||
logs.PrintAsJSON(book2, t)
|
||||
logs.PrintAsJSON(book1, t)
|
||||
}
|
||||
59
internal/configs/serverconfigs/configutils/domain.go
Normal file
59
internal/configs/serverconfigs/configutils/domain.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package configutils
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/utils/string"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 从一组规则中匹配域名
|
||||
// 支持的格式:example.com, www.example.com, .example.com, *.example.com, ~(\d+).example.com
|
||||
// 更多参考:http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name
|
||||
func MatchDomains(patterns []string, domain string) (isMatched bool) {
|
||||
if len(patterns) == 0 {
|
||||
return
|
||||
}
|
||||
for _, pattern := range patterns {
|
||||
if matchDomain(pattern, domain) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 匹配单个域名规则
|
||||
func matchDomain(pattern string, domain string) (isMatched bool) {
|
||||
if len(pattern) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// 正则表达式
|
||||
if pattern[0] == '~' {
|
||||
reg, err := stringutil.RegexpCompile(strings.TrimSpace(pattern[1:]))
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return false
|
||||
}
|
||||
return reg.MatchString(domain)
|
||||
}
|
||||
|
||||
if pattern[0] == '.' {
|
||||
return strings.HasSuffix(domain, pattern)
|
||||
}
|
||||
|
||||
// 其他匹配
|
||||
patternPieces := strings.Split(pattern, ".")
|
||||
domainPieces := strings.Split(domain, ".")
|
||||
if len(patternPieces) != len(domainPieces) {
|
||||
return
|
||||
}
|
||||
isMatched = true
|
||||
for index, patternPiece := range patternPieces {
|
||||
if patternPiece == "" || patternPiece == "*" || patternPiece == domainPieces[index] {
|
||||
continue
|
||||
}
|
||||
isMatched = false
|
||||
break
|
||||
}
|
||||
return isMatched
|
||||
}
|
||||
79
internal/configs/serverconfigs/configutils/domain_test.go
Normal file
79
internal/configs/serverconfigs/configutils/domain_test.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package configutils
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMatchDomain(t *testing.T) {
|
||||
a := assert.NewAssertion(t)
|
||||
{
|
||||
ok := MatchDomains([]string{}, "example.com")
|
||||
a.IsFalse(ok)
|
||||
}
|
||||
|
||||
{
|
||||
ok := MatchDomains([]string{"example.com"}, "example.com")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
|
||||
{
|
||||
ok := MatchDomains([]string{"www.example.com"}, "example.com")
|
||||
a.IsFalse(ok)
|
||||
}
|
||||
|
||||
{
|
||||
ok := MatchDomains([]string{".example.com"}, "www.example.com")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
|
||||
{
|
||||
ok := MatchDomains([]string{".example.com"}, "a.www.example.com")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
|
||||
{
|
||||
ok := MatchDomains([]string{".example.com"}, "a.www.example123.com")
|
||||
a.IsFalse(ok)
|
||||
}
|
||||
|
||||
{
|
||||
ok := MatchDomains([]string{"*.example.com"}, "www.example.com")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
|
||||
{
|
||||
ok := MatchDomains([]string{"*.*.com"}, "www.example.com")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
|
||||
{
|
||||
ok := MatchDomains([]string{"www.*.com"}, "www.example.com")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
|
||||
{
|
||||
ok := MatchDomains([]string{"gallery.*.com"}, "www.example.com")
|
||||
a.IsFalse(ok)
|
||||
}
|
||||
|
||||
{
|
||||
ok := MatchDomains([]string{"~\\w+.example.com"}, "www.example.com")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
|
||||
{
|
||||
ok := MatchDomains([]string{"~\\w+.example.com"}, "a.www.example.com")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
|
||||
{
|
||||
ok := MatchDomains([]string{"~^\\d+.example.com$"}, "www.example.com")
|
||||
a.IsFalse(ok)
|
||||
}
|
||||
|
||||
{
|
||||
ok := MatchDomains([]string{"~^\\d+.example.com$"}, "123.example.com")
|
||||
a.IsTrue(ok)
|
||||
}
|
||||
}
|
||||
11
internal/configs/serverconfigs/configutils/log.go
Normal file
11
internal/configs/serverconfigs/configutils/log.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package configutils
|
||||
|
||||
import "github.com/iwind/TeaGo/logs"
|
||||
|
||||
// 记录错误
|
||||
func LogError(arg ...interface{}) {
|
||||
if len(arg) == 0 {
|
||||
return
|
||||
}
|
||||
logs.Println(arg...)
|
||||
}
|
||||
25
internal/configs/serverconfigs/configutils/match.go
Normal file
25
internal/configs/serverconfigs/configutils/match.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package configutils
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var whitespaceReg = regexp.MustCompile(`\s+`)
|
||||
|
||||
// 关键词匹配
|
||||
func MatchKeyword(source, keyword string) bool {
|
||||
if len(keyword) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
pieces := whitespaceReg.Split(keyword, -1)
|
||||
source = strings.ToLower(source)
|
||||
for _, piece := range pieces {
|
||||
if strings.Index(source, strings.ToLower(piece)) > -1 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
13
internal/configs/serverconfigs/configutils/match_test.go
Normal file
13
internal/configs/serverconfigs/configutils/match_test.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package configutils
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMatchKeyword(t *testing.T) {
|
||||
a := assert.NewAssertion(t)
|
||||
a.IsTrue(MatchKeyword("a b c", "a"))
|
||||
a.IsFalse(MatchKeyword("a b c", ""))
|
||||
a.IsTrue(MatchKeyword("abc", "BC"))
|
||||
}
|
||||
14
internal/configs/serverconfigs/configutils/yaml.go
Normal file
14
internal/configs/serverconfigs/configutils/yaml.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package configutils
|
||||
|
||||
import (
|
||||
"github.com/go-yaml/yaml"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func UnmarshalYamlFile(file string, ptr interface{}) error {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return yaml.Unmarshal(data, ptr)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package configs
|
||||
package serverconfigs
|
||||
|
||||
type FilterConfig struct {
|
||||
}
|
||||
43
internal/configs/serverconfigs/global_config.go
Normal file
43
internal/configs/serverconfigs/global_config.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package serverconfigs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/configutils"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/shared"
|
||||
)
|
||||
|
||||
var globalConfig *GlobalConfig = nil
|
||||
var globalConfigFile = "global.yaml"
|
||||
|
||||
// 全局设置
|
||||
type GlobalConfig struct {
|
||||
HTTPAll struct {
|
||||
MatchDomainStrictly bool `yaml:"matchDomainStrictly"`
|
||||
} `yaml:"httpAll"`
|
||||
HTTP struct{} `yaml:"http"`
|
||||
HTTPS struct{} `yaml:"https"`
|
||||
TCPAll struct{} `yaml:"tcpAll"`
|
||||
TCP struct{} `yaml:"tcp"`
|
||||
TLS struct{} `yaml:"tls"`
|
||||
Unix struct{} `yaml:"unix"`
|
||||
UDP struct{} `yaml:"udp"`
|
||||
}
|
||||
|
||||
func SharedGlobalConfig() *GlobalConfig {
|
||||
shared.Locker.Lock()
|
||||
defer shared.Locker.Unlock()
|
||||
|
||||
if globalConfig != nil {
|
||||
return globalConfig
|
||||
}
|
||||
|
||||
err := configutils.UnmarshalYamlFile(globalConfigFile, globalConfig)
|
||||
if err != nil {
|
||||
configutils.LogError("[SharedGlobalConfig]" + err.Error())
|
||||
globalConfig = &GlobalConfig{}
|
||||
}
|
||||
return globalConfig
|
||||
}
|
||||
|
||||
func (this *GlobalConfig) Init() error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package configs
|
||||
package serverconfigs
|
||||
|
||||
type IPVersion = string
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package configs
|
||||
package serverconfigs
|
||||
|
||||
type LocationConfig struct {
|
||||
}
|
||||
70
internal/configs/serverconfigs/network_address_config.go
Normal file
70
internal/configs/serverconfigs/network_address_config.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package serverconfigs
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var regexpSinglePort = regexp.MustCompile(`^\d+$`)
|
||||
|
||||
// 网络地址配置
|
||||
type NetworkAddressConfig struct {
|
||||
Protocol string `yaml:"protocol" json:"protocol"` // 协议,http、tcp、tcp4、tcp6、unix、udp等
|
||||
Host string `yaml:"host" json:"host"` // 主机地址或主机名
|
||||
PortRange string `yaml:"portRange" json:"portRange"` // 端口范围,支持 8080、8080-8090、8080:8090
|
||||
|
||||
minPort int
|
||||
maxPort int
|
||||
}
|
||||
|
||||
func (this *NetworkAddressConfig) Init() error {
|
||||
// 8080
|
||||
if regexpSinglePort.MatchString(this.PortRange) {
|
||||
this.minPort = types.Int(this.PortRange)
|
||||
this.maxPort = this.minPort
|
||||
return nil
|
||||
}
|
||||
|
||||
// 8080:8090
|
||||
if strings.Contains(this.PortRange, ":") {
|
||||
pieces := strings.SplitN(this.PortRange, ":", 2)
|
||||
minPort := types.Int(pieces[0])
|
||||
maxPort := types.Int(pieces[1])
|
||||
if minPort > maxPort {
|
||||
minPort, maxPort = maxPort, minPort
|
||||
}
|
||||
this.minPort = minPort
|
||||
this.maxPort = maxPort
|
||||
return nil
|
||||
}
|
||||
|
||||
// 8080-8090
|
||||
if strings.Contains(this.PortRange, "-") {
|
||||
pieces := strings.SplitN(this.PortRange, "-", 2)
|
||||
minPort := types.Int(pieces[0])
|
||||
maxPort := types.Int(pieces[1])
|
||||
if minPort > maxPort {
|
||||
minPort, maxPort = maxPort, minPort
|
||||
}
|
||||
this.minPort = minPort
|
||||
this.maxPort = maxPort
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *NetworkAddressConfig) FullAddresses() []string {
|
||||
if this.Protocol == ProtocolUnix {
|
||||
return []string{this.Protocol + ":" + this.Host}
|
||||
}
|
||||
|
||||
result := []string{}
|
||||
for i := this.minPort; i <= this.maxPort; i++ {
|
||||
host := this.Host
|
||||
result = append(result, this.Protocol+"://"+host+":"+strconv.Itoa(i))
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package serverconfigs
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestNetworkAddressConfig_FullAddresses(t *testing.T) {
|
||||
{
|
||||
addr := &NetworkAddressConfig{
|
||||
Protocol: "http",
|
||||
Host: "127.0.0.1",
|
||||
PortRange: "8080",
|
||||
}
|
||||
err := addr.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(addr.FullAddresses())
|
||||
}
|
||||
|
||||
{
|
||||
addr := &NetworkAddressConfig{
|
||||
Protocol: "http",
|
||||
Host: "127.0.0.1",
|
||||
PortRange: "8080:8090",
|
||||
}
|
||||
err := addr.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(addr.FullAddresses())
|
||||
}
|
||||
|
||||
{
|
||||
addr := &NetworkAddressConfig{
|
||||
Protocol: "http",
|
||||
Host: "127.0.0.1",
|
||||
PortRange: "8080-8090",
|
||||
}
|
||||
err := addr.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(addr.FullAddresses())
|
||||
}
|
||||
|
||||
{
|
||||
addr := &NetworkAddressConfig{
|
||||
Protocol: "http",
|
||||
Host: "127.0.0.1",
|
||||
PortRange: "8080-8070",
|
||||
}
|
||||
err := addr.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(addr.FullAddresses())
|
||||
}
|
||||
}
|
||||
10
internal/configs/serverconfigs/origin_server_config.go
Normal file
10
internal/configs/serverconfigs/origin_server_config.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package serverconfigs
|
||||
|
||||
// 源站服务配置
|
||||
type OriginServerConfig struct {
|
||||
Id string `yaml:"id" json:"id"` // ID
|
||||
IsOn bool `yaml:"isOn" json:"isOn"` // 是否启用
|
||||
Name string `yaml:"name" json:"name"` // 名称 TODO
|
||||
Addr *NetworkAddressConfig `yaml:"addr" json:"addr"` // 地址
|
||||
Description string `yaml:"description" json:"description"` // 描述 TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package serverconfigs
|
||||
|
||||
// TODO 需要实现
|
||||
type OriginServerGroupConfig struct {
|
||||
Origins []*OriginServerConfig `yaml:"origins" json:"origins"` // 源站列表
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package configs
|
||||
package serverconfigs
|
||||
|
||||
type Protocol = string
|
||||
|
||||
32
internal/configs/serverconfigs/protocol_base.go
Normal file
32
internal/configs/serverconfigs/protocol_base.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package serverconfigs
|
||||
|
||||
// 协议基础数据结构
|
||||
type BaseProtocol struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"` // 是否开启
|
||||
Listen []*NetworkAddressConfig `yaml:"listen" json:"listen"` // 绑定的网络地址
|
||||
}
|
||||
|
||||
// 初始化
|
||||
func (this *BaseProtocol) InitBase() error {
|
||||
for _, addr := range this.Listen {
|
||||
err := addr.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取完整的地址列表
|
||||
func (this *BaseProtocol) FullAddresses() []string {
|
||||
result := []string{}
|
||||
for _, addr := range this.Listen {
|
||||
result = append(result, addr.FullAddresses()...)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// 添加地址
|
||||
func (this *BaseProtocol) AddListen(addr ...*NetworkAddressConfig) {
|
||||
this.Listen = append(this.Listen, addr...)
|
||||
}
|
||||
14
internal/configs/serverconfigs/protocol_http_config.go
Normal file
14
internal/configs/serverconfigs/protocol_http_config.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package serverconfigs
|
||||
|
||||
type HTTPProtocolConfig struct {
|
||||
BaseProtocol `yaml:",inline"`
|
||||
}
|
||||
|
||||
func (this *HTTPProtocolConfig) Init() error {
|
||||
err := this.InitBase()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
24
internal/configs/serverconfigs/protocol_https_config.go
Normal file
24
internal/configs/serverconfigs/protocol_https_config.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package serverconfigs
|
||||
|
||||
import "github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/sslconfigs"
|
||||
|
||||
// TLS Version
|
||||
type TLSVersion = string
|
||||
|
||||
// Cipher Suites
|
||||
type TLSCipherSuite = string
|
||||
|
||||
type HTTPSProtocolConfig struct {
|
||||
BaseProtocol `yaml:",inline"`
|
||||
|
||||
SSL *sslconfigs.SSLConfig `yaml:"ssl"`
|
||||
}
|
||||
|
||||
func (this *HTTPSProtocolConfig) Init() error {
|
||||
err := this.InitBase()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
14
internal/configs/serverconfigs/protocol_tcp_config.go
Normal file
14
internal/configs/serverconfigs/protocol_tcp_config.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package serverconfigs
|
||||
|
||||
type TCPProtocolConfig struct {
|
||||
BaseProtocol `yaml:",inline"`
|
||||
}
|
||||
|
||||
func (this *TCPProtocolConfig) Init() error {
|
||||
err := this.InitBase()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
18
internal/configs/serverconfigs/protocol_tls_config.go
Normal file
18
internal/configs/serverconfigs/protocol_tls_config.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package serverconfigs
|
||||
|
||||
import "github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/sslconfigs"
|
||||
|
||||
type TLSProtocolConfig struct {
|
||||
BaseProtocol `yaml:",inline"`
|
||||
|
||||
SSL *sslconfigs.SSLConfig `yaml:"ssl"`
|
||||
}
|
||||
|
||||
func (this *TLSProtocolConfig) Init() error {
|
||||
err := this.InitBase()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
14
internal/configs/serverconfigs/protocol_udp_config.go
Normal file
14
internal/configs/serverconfigs/protocol_udp_config.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package serverconfigs
|
||||
|
||||
type UDPProtocolConfig struct {
|
||||
BaseProtocol `yaml:",inline"`
|
||||
}
|
||||
|
||||
func (this *UDPProtocolConfig) Init() error {
|
||||
err := this.InitBase()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
14
internal/configs/serverconfigs/protocol_unix_config.go
Normal file
14
internal/configs/serverconfigs/protocol_unix_config.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package serverconfigs
|
||||
|
||||
type UnixProtocolConfig struct {
|
||||
BaseProtocol `yaml:",inline"`
|
||||
}
|
||||
|
||||
func (this *UnixProtocolConfig) Init() error {
|
||||
err := this.InitBase()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
6
internal/configs/serverconfigs/reverse_proxy_config.go
Normal file
6
internal/configs/serverconfigs/reverse_proxy_config.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package serverconfigs
|
||||
|
||||
type ReverseProxyConfig struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"` // 是否启用
|
||||
Origins []*OriginServerConfig `yaml:"origins" json:"origins"` // 源站列表
|
||||
}
|
||||
178
internal/configs/serverconfigs/server_config.go
Normal file
178
internal/configs/serverconfigs/server_config.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package serverconfigs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/sslconfigs"
|
||||
)
|
||||
|
||||
type ServerConfig struct {
|
||||
Id string `yaml:"id" json:"id"` // ID
|
||||
IsOn bool `yaml:"isOn" json:"isOn"` // 是否开启
|
||||
Components []*ComponentConfig `yaml:"components" json:"components"` // 组件
|
||||
Filters []*FilterConfig `yaml:"filters" json:"filters"` // 过滤器
|
||||
Name string `yaml:"name" json:"name"` // 名称
|
||||
Description string `yaml:"description" json:"description"` // 描述
|
||||
ServerNames []*ServerNameConfig `yaml:"serverNames" json:"serverNames"` // 域名
|
||||
|
||||
// 前端协议
|
||||
HTTP *HTTPProtocolConfig `yaml:"http" json:"http"` // HTTP配置
|
||||
HTTPS *HTTPSProtocolConfig `yaml:"https" json:"https"` // HTTPS配置
|
||||
TCP *TCPProtocolConfig `yaml:"tcp" json:"tcp"` // TCP配置
|
||||
TLS *TLSProtocolConfig `yaml:"tls" json:"tls"` // TLS配置
|
||||
Unix *UnixProtocolConfig `yaml:"unix" json:"unix"` // Unix配置
|
||||
UDP *UDPProtocolConfig `yaml:"udp" json:"udp"` // UDP配置
|
||||
|
||||
// Web配置
|
||||
Web *WebConfig `yaml:"web" json:"web"`
|
||||
|
||||
// 反向代理配置
|
||||
ReverseProxy *ReverseProxyConfig `yaml:"reverseProxy" json:"reverseProxy"`
|
||||
}
|
||||
|
||||
func NewServerConfig() *ServerConfig {
|
||||
return &ServerConfig{}
|
||||
}
|
||||
|
||||
func (this *ServerConfig) Init() error {
|
||||
if this.HTTP != nil {
|
||||
err := this.HTTP.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if this.HTTPS != nil {
|
||||
err := this.HTTPS.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if this.TCP != nil {
|
||||
err := this.TCP.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if this.TLS != nil {
|
||||
err := this.TLS.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if this.Unix != nil {
|
||||
err := this.Unix.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if this.UDP != nil {
|
||||
err := this.UDP.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *ServerConfig) FullAddresses() []string {
|
||||
result := []Protocol{}
|
||||
if this.HTTP != nil && this.HTTP.IsOn {
|
||||
result = append(result, this.HTTP.FullAddresses()...)
|
||||
}
|
||||
if this.HTTPS != nil && this.HTTPS.IsOn {
|
||||
result = append(result, this.HTTPS.FullAddresses()...)
|
||||
}
|
||||
if this.TCP != nil && this.TCP.IsOn {
|
||||
result = append(result, this.TCP.FullAddresses()...)
|
||||
}
|
||||
if this.TLS != nil && this.TLS.IsOn {
|
||||
result = append(result, this.TLS.FullAddresses()...)
|
||||
}
|
||||
if this.Unix != nil && this.Unix.IsOn {
|
||||
result = append(result, this.Unix.FullAddresses()...)
|
||||
}
|
||||
if this.UDP != nil && this.UDP.IsOn {
|
||||
result = append(result, this.UDP.FullAddresses()...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (this *ServerConfig) Listen() []*NetworkAddressConfig {
|
||||
result := []*NetworkAddressConfig{}
|
||||
if this.HTTP != nil {
|
||||
result = append(result, this.HTTP.Listen...)
|
||||
}
|
||||
if this.HTTPS != nil {
|
||||
result = append(result, this.HTTPS.Listen...)
|
||||
}
|
||||
if this.TCP != nil {
|
||||
result = append(result, this.TCP.Listen...)
|
||||
}
|
||||
if this.TLS != nil {
|
||||
result = append(result, this.TLS.Listen...)
|
||||
}
|
||||
if this.Unix != nil {
|
||||
result = append(result, this.Unix.Listen...)
|
||||
}
|
||||
if this.UDP != nil {
|
||||
result = append(result, this.UDP.Listen...)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (this *ServerConfig) AsJSON() ([]byte, error) {
|
||||
return json.Marshal(this)
|
||||
}
|
||||
|
||||
func (this *ServerConfig) IsHTTP() bool {
|
||||
return this.HTTP != nil || this.HTTPS != nil
|
||||
}
|
||||
|
||||
func (this *ServerConfig) IsTCP() bool {
|
||||
return this.TCP != nil || this.TLS != nil
|
||||
}
|
||||
|
||||
func (this *ServerConfig) IsUnix() bool {
|
||||
return this.Unix != nil
|
||||
}
|
||||
|
||||
func (this *ServerConfig) IsUDP() bool {
|
||||
return this.UDP != nil
|
||||
}
|
||||
|
||||
// 判断是否和域名匹配
|
||||
func (this *ServerConfig) MatchName(name string) bool {
|
||||
for _, serverName := range this.ServerNames {
|
||||
if serverName.Match(name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 判断是否严格匹配
|
||||
func (this *ServerConfig) MatchNameStrictly(name string) bool {
|
||||
for _, serverName := range this.ServerNames {
|
||||
if serverName.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SSL信息
|
||||
func (this *ServerConfig) SSLConfig() *sslconfigs.SSLConfig {
|
||||
if this.HTTPS != nil {
|
||||
return this.HTTPS.SSL
|
||||
}
|
||||
if this.TLS != nil {
|
||||
return this.TLS.SSL
|
||||
}
|
||||
return nil
|
||||
}
|
||||
74
internal/configs/serverconfigs/server_config_test.go
Normal file
74
internal/configs/serverconfigs/server_config_test.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package serverconfigs
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestServerConfig_Protocols(t *testing.T) {
|
||||
{
|
||||
server := NewServerConfig()
|
||||
t.Log(server.FullAddresses())
|
||||
}
|
||||
|
||||
{
|
||||
server := NewServerConfig()
|
||||
server.HTTP = &HTTPProtocolConfig{BaseProtocol: BaseProtocol{
|
||||
IsOn: true,
|
||||
Listen: []*NetworkAddressConfig{
|
||||
{
|
||||
Protocol: ProtocolHTTP,
|
||||
PortRange: "1234",
|
||||
},
|
||||
},
|
||||
}}
|
||||
server.HTTPS = &HTTPSProtocolConfig{BaseProtocol: BaseProtocol{
|
||||
IsOn: true,
|
||||
Listen: []*NetworkAddressConfig{
|
||||
{
|
||||
Protocol: ProtocolUnix,
|
||||
Host: "/hello.sock",
|
||||
PortRange: "1235",
|
||||
},
|
||||
},
|
||||
}}
|
||||
server.TCP = &TCPProtocolConfig{BaseProtocol: BaseProtocol{
|
||||
IsOn: true,
|
||||
Listen: []*NetworkAddressConfig{
|
||||
{
|
||||
Protocol: ProtocolHTTPS,
|
||||
PortRange: "1236",
|
||||
},
|
||||
},
|
||||
}}
|
||||
server.TLS = &TLSProtocolConfig{BaseProtocol: BaseProtocol{
|
||||
IsOn: true,
|
||||
Listen: []*NetworkAddressConfig{
|
||||
{
|
||||
Protocol: ProtocolTCP,
|
||||
PortRange: "1234",
|
||||
},
|
||||
},
|
||||
}}
|
||||
server.Unix = &UnixProtocolConfig{BaseProtocol: BaseProtocol{
|
||||
IsOn: true,
|
||||
Listen: []*NetworkAddressConfig{
|
||||
{
|
||||
Protocol: ProtocolTLS,
|
||||
PortRange: "1234",
|
||||
},
|
||||
},
|
||||
}}
|
||||
server.UDP = &UDPProtocolConfig{BaseProtocol: BaseProtocol{
|
||||
IsOn: true,
|
||||
Listen: []*NetworkAddressConfig{
|
||||
{
|
||||
Protocol: ProtocolUDP,
|
||||
PortRange: "1234",
|
||||
},
|
||||
},
|
||||
}}
|
||||
err := server.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(server.FullAddresses())
|
||||
}
|
||||
}
|
||||
85
internal/configs/serverconfigs/server_group.go
Normal file
85
internal/configs/serverconfigs/server_group.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package serverconfigs
|
||||
|
||||
import "strings"
|
||||
|
||||
type ServerGroup struct {
|
||||
fullAddr string
|
||||
Servers []*ServerConfig
|
||||
}
|
||||
|
||||
func NewServerGroup(fullAddr string) *ServerGroup {
|
||||
return &ServerGroup{fullAddr: fullAddr}
|
||||
}
|
||||
|
||||
// 添加服务
|
||||
func (this *ServerGroup) Add(server *ServerConfig) {
|
||||
this.Servers = append(this.Servers, server)
|
||||
}
|
||||
|
||||
// 获取完整的地址
|
||||
func (this *ServerGroup) FullAddr() string {
|
||||
return this.fullAddr
|
||||
}
|
||||
|
||||
// 获取当前分组的协议
|
||||
func (this *ServerGroup) Protocol() Protocol {
|
||||
for _, p := range AllProtocols() {
|
||||
if strings.HasPrefix(this.fullAddr, p+":") {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return ProtocolHTTP
|
||||
}
|
||||
|
||||
// 获取当前分组的地址
|
||||
func (this *ServerGroup) Addr() string {
|
||||
protocol := this.Protocol()
|
||||
if protocol == ProtocolUnix {
|
||||
return strings.TrimPrefix(this.fullAddr, protocol+":")
|
||||
}
|
||||
return strings.TrimPrefix(this.fullAddr, protocol+"://")
|
||||
}
|
||||
|
||||
// 判断当前分组是否为HTTP
|
||||
func (this *ServerGroup) IsHTTP() bool {
|
||||
p := this.Protocol()
|
||||
return p == ProtocolHTTP || p == ProtocolHTTP4 || p == ProtocolHTTP6
|
||||
}
|
||||
|
||||
// 判断当前分组是否为HTTPS
|
||||
func (this *ServerGroup) IsHTTPS() bool {
|
||||
p := this.Protocol()
|
||||
return p == ProtocolHTTPS || p == ProtocolHTTPS4 || p == ProtocolHTTPS6
|
||||
}
|
||||
|
||||
// 判断当前分组是否为TCP
|
||||
func (this *ServerGroup) IsTCP() bool {
|
||||
p := this.Protocol()
|
||||
return p == ProtocolTCP || p == ProtocolTCP4 || p == ProtocolTCP6
|
||||
}
|
||||
|
||||
// 判断当前分组是否为TLS
|
||||
func (this *ServerGroup) IsTLS() bool {
|
||||
p := this.Protocol()
|
||||
return p == ProtocolTLS || p == ProtocolTLS4 || p == ProtocolTLS6
|
||||
}
|
||||
|
||||
// 判断当前分组是否为Unix
|
||||
func (this *ServerGroup) IsUnix() bool {
|
||||
p := this.Protocol()
|
||||
return p == ProtocolUnix
|
||||
}
|
||||
|
||||
// 判断当前分组是否为UDP
|
||||
func (this *ServerGroup) IsUDP() bool {
|
||||
p := this.Protocol()
|
||||
return p == ProtocolUDP
|
||||
}
|
||||
|
||||
// 获取第一个Server
|
||||
func (this *ServerGroup) FirstServer() *ServerConfig {
|
||||
if len(this.Servers) > 0 {
|
||||
return this.Servers[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package configs
|
||||
package serverconfigs
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
23
internal/configs/serverconfigs/server_name_config.go
Normal file
23
internal/configs/serverconfigs/server_name_config.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package serverconfigs
|
||||
|
||||
import "github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/configutils"
|
||||
|
||||
type ServerNameType = string
|
||||
|
||||
const (
|
||||
ServerNameTypeFull = "full" // 完整的域名,包含通配符等
|
||||
ServerNameTypePrefix = "prefix" // 前缀
|
||||
ServerNameTypeSuffix = "suffix" // 后缀
|
||||
ServerNameTypeMatch = "match" // 正则匹配
|
||||
)
|
||||
|
||||
// 主机名(域名)配置
|
||||
type ServerNameConfig struct {
|
||||
Name string `yaml:"name" json:"name"` // 名称
|
||||
Type string `yaml:"type" json:"type"` // 类型
|
||||
}
|
||||
|
||||
// 判断主机名是否匹配
|
||||
func (this *ServerNameConfig) Match(name string) bool {
|
||||
return configutils.MatchDomains([]string{this.Name}, name)
|
||||
}
|
||||
21
internal/configs/serverconfigs/shared/locker.go
Normal file
21
internal/configs/serverconfigs/shared/locker.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var Locker = new(FileLocker)
|
||||
|
||||
// global file modify locker
|
||||
type FileLocker struct {
|
||||
locker sync.Mutex
|
||||
}
|
||||
|
||||
// lock
|
||||
func (this *FileLocker) Lock() {
|
||||
this.locker.Lock()
|
||||
}
|
||||
|
||||
func (this *FileLocker) Unlock() {
|
||||
this.locker.Unlock()
|
||||
}
|
||||
207
internal/configs/serverconfigs/sslconfigs/ssl.go
Normal file
207
internal/configs/serverconfigs/sslconfigs/ssl.go
Normal file
@@ -0,0 +1,207 @@
|
||||
package sslconfigs
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TLS Version
|
||||
type TLSVersion = string
|
||||
|
||||
// Cipher Suites
|
||||
type TLSCipherSuite = string
|
||||
|
||||
// SSL配置
|
||||
type SSLConfig struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"` // 是否开启
|
||||
|
||||
Certs []*SSLCertConfig `yaml:"certs" json:"certs"`
|
||||
ClientAuthType SSLClientAuthType `yaml:"clientAuthType" json:"clientAuthType"` // 客户端认证类型
|
||||
ClientCACertIds []string `yaml:"clientCACertIds" json:"clientCACertIds"` // 客户端认证CA
|
||||
|
||||
Listen []string `yaml:"listen" json:"listen"` // 网络地址
|
||||
MinVersion TLSVersion `yaml:"minVersion" json:"minVersion"` // 支持的最小版本
|
||||
CipherSuites []TLSCipherSuite `yaml:"cipherSuites" json:"cipherSuites"` // 加密算法套件
|
||||
|
||||
HSTS *HSTSConfig `yaml:"hsts2" json:"hsts"` // HSTS配置,yaml之所以使用hsts2,是因为要和以前的版本分开
|
||||
HTTP2Disabled bool `yaml:"http2Disabled" json:"http2Disabled"` // 是否禁用HTTP2
|
||||
|
||||
nameMapping map[string]*tls.Certificate // dnsName => cert
|
||||
|
||||
minVersion uint16
|
||||
cipherSuites []uint16
|
||||
|
||||
clientCAPool *x509.CertPool
|
||||
}
|
||||
|
||||
// 获取新对象
|
||||
func NewSSLConfig() *SSLConfig {
|
||||
return &SSLConfig{}
|
||||
}
|
||||
|
||||
// 校验配置
|
||||
func (this *SSLConfig) Init() error {
|
||||
if !this.IsOn {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(this.Certs) == 0 {
|
||||
return errors.New("no certificates in https config")
|
||||
}
|
||||
|
||||
for _, cert := range this.Certs {
|
||||
err := cert.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if this.Listen == nil {
|
||||
this.Listen = []string{}
|
||||
} else {
|
||||
for index, addr := range this.Listen {
|
||||
_, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
this.Listen[index] = strings.TrimSuffix(addr, ":") + ":443"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// min version
|
||||
this.convertMinVersion()
|
||||
|
||||
// cipher suite categories
|
||||
this.initCipherSuites()
|
||||
|
||||
// hsts
|
||||
if this.HSTS != nil {
|
||||
err := this.HSTS.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// CA证书
|
||||
if len(this.ClientCACertIds) > 0 && this.ClientAuthType != SSLClientAuthTypeNoClientCert {
|
||||
this.clientCAPool = x509.NewCertPool()
|
||||
list := SharedSSLCertList()
|
||||
for _, certId := range this.ClientCACertIds {
|
||||
cert := list.FindCert(certId)
|
||||
if cert == nil {
|
||||
continue
|
||||
}
|
||||
if !cert.On {
|
||||
continue
|
||||
}
|
||||
data, err := ioutil.ReadFile(cert.FullCertPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
this.clientCAPool.AppendCertsFromPEM(data)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 取得最小版本
|
||||
func (this *SSLConfig) TLSMinVersion() uint16 {
|
||||
return this.minVersion
|
||||
}
|
||||
|
||||
// 套件
|
||||
func (this *SSLConfig) TLSCipherSuites() []uint16 {
|
||||
return this.cipherSuites
|
||||
}
|
||||
|
||||
// 校验是否匹配某个域名
|
||||
func (this *SSLConfig) MatchDomain(domain string) (cert *tls.Certificate, ok bool) {
|
||||
for _, cert := range this.Certs {
|
||||
if cert.MatchDomain(domain) {
|
||||
return cert.CertObject(), true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// 取得第一个证书
|
||||
func (this *SSLConfig) FirstCert() *tls.Certificate {
|
||||
for _, cert := range this.Certs {
|
||||
return cert.CertObject()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 是否包含某个证书或密钥路径
|
||||
func (this *SSLConfig) ContainsFile(file string) bool {
|
||||
for _, cert := range this.Certs {
|
||||
if cert.CertFile == file || cert.KeyFile == file {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 删除证书文件
|
||||
func (this *SSLConfig) DeleteFiles() error {
|
||||
var resultErr error = nil
|
||||
|
||||
for _, cert := range this.Certs {
|
||||
err := cert.DeleteFiles()
|
||||
if err != nil {
|
||||
resultErr = err
|
||||
}
|
||||
}
|
||||
|
||||
return resultErr
|
||||
}
|
||||
|
||||
// 查找单个证书配置
|
||||
func (this *SSLConfig) FindCert(certId string) *SSLCertConfig {
|
||||
for _, cert := range this.Certs {
|
||||
if cert.Id == certId {
|
||||
return cert
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 添加证书
|
||||
func (this *SSLConfig) AddCert(cert *SSLCertConfig) {
|
||||
this.Certs = append(this.Certs, cert)
|
||||
}
|
||||
|
||||
// CA证书Pool,用于TLS对客户端进行认证
|
||||
func (this *SSLConfig) CAPool() *x509.CertPool {
|
||||
return this.clientCAPool
|
||||
}
|
||||
|
||||
// 分解所有监听地址
|
||||
func (this *SSLConfig) ParseListenAddresses() []string {
|
||||
result := []string{}
|
||||
var reg = regexp.MustCompile(`\[\s*(\d+)\s*[,:-]\s*(\d+)\s*]$`)
|
||||
for _, addr := range this.Listen {
|
||||
match := reg.FindStringSubmatch(addr)
|
||||
if len(match) == 0 {
|
||||
result = append(result, addr)
|
||||
} else {
|
||||
min := types.Int(match[1])
|
||||
max := types.Int(match[2])
|
||||
if min > max {
|
||||
min, max = max, min
|
||||
}
|
||||
for i := min; i <= max; i++ {
|
||||
newAddr := reg.ReplaceAllString(addr, ":"+strconv.Itoa(i))
|
||||
result = append(result, newAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
75
internal/configs/serverconfigs/sslconfigs/ssl_auth.go
Normal file
75
internal/configs/serverconfigs/sslconfigs/ssl_auth.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package sslconfigs
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
// 认证类型
|
||||
type SSLClientAuthType = int
|
||||
|
||||
const (
|
||||
SSLClientAuthTypeNoClientCert SSLClientAuthType = 0
|
||||
SSLClientAuthTypeRequestClientCert SSLClientAuthType = 1
|
||||
SSLClientAuthTypeRequireAnyClientCert SSLClientAuthType = 2
|
||||
SSLClientAuthTypeVerifyClientCertIfGiven SSLClientAuthType = 3
|
||||
SSLClientAuthTypeRequireAndVerifyClientCert SSLClientAuthType = 4
|
||||
)
|
||||
|
||||
// 所有的客户端认证类型
|
||||
func AllSSLClientAuthTypes() []maps.Map {
|
||||
return []maps.Map{
|
||||
{
|
||||
"name": "不需要客户端证书",
|
||||
"type": SSLClientAuthTypeNoClientCert,
|
||||
"requireCA": false,
|
||||
},
|
||||
{
|
||||
"name": "请求客户端证书",
|
||||
"type": SSLClientAuthTypeRequestClientCert,
|
||||
"requireCA": true,
|
||||
},
|
||||
{
|
||||
"name": "需要客户端证书,但不校验",
|
||||
"type": SSLClientAuthTypeRequireAnyClientCert,
|
||||
"requireCA": true,
|
||||
},
|
||||
{
|
||||
"name": "有客户端证书的时候才校验",
|
||||
"type": SSLClientAuthTypeVerifyClientCertIfGiven,
|
||||
"requireCA": true,
|
||||
},
|
||||
{
|
||||
"name": "校验客户端提供的证书",
|
||||
"type": SSLClientAuthTypeRequireAndVerifyClientCert,
|
||||
"requireCA": true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// 查找单个认证方式的名称
|
||||
func FindSSLClientAuthTypeName(authType SSLClientAuthType) string {
|
||||
for _, m := range AllSSLClientAuthTypes() {
|
||||
if m.GetInt("type") == authType {
|
||||
return m.GetString("name")
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// 认证类型和tls包内类型的映射
|
||||
func GoSSLClientAuthType(authType SSLClientAuthType) tls.ClientAuthType {
|
||||
switch authType {
|
||||
case SSLClientAuthTypeNoClientCert:
|
||||
return tls.NoClientCert
|
||||
case SSLClientAuthTypeRequestClientCert:
|
||||
return tls.RequestClientCert
|
||||
case SSLClientAuthTypeRequireAnyClientCert:
|
||||
return tls.RequireAnyClientCert
|
||||
case SSLClientAuthTypeVerifyClientCertIfGiven:
|
||||
return tls.VerifyClientCertIfGiven
|
||||
case SSLClientAuthTypeRequireAndVerifyClientCert:
|
||||
return tls.RequireAndVerifyClientCert
|
||||
}
|
||||
return tls.NoClientCert
|
||||
}
|
||||
271
internal/configs/serverconfigs/sslconfigs/ssl_cert.go
Normal file
271
internal/configs/serverconfigs/sslconfigs/ssl_cert.go
Normal file
@@ -0,0 +1,271 @@
|
||||
package sslconfigs
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/configutils"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/files"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/utils/string"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SSL证书
|
||||
type SSLCertConfig struct {
|
||||
Id string `yaml:"id" json:"id"`
|
||||
On bool `yaml:"on" json:"on"`
|
||||
Description string `yaml:"description" json:"description"` // 说明
|
||||
CertFile string `yaml:"certFile" json:"certFile"`
|
||||
KeyFile string `yaml:"keyFile" json:"keyFile"`
|
||||
IsLocal bool `yaml:"isLocal" json:"isLocal"` // 是否为本地文件
|
||||
TaskId string `yaml:"taskId" json:"taskId"` // 生成证书任务ID
|
||||
IsShared bool `yaml:"isShared" json:"isShared"` // 是否为公用组件
|
||||
ServerName string `yaml:"serverName" json:"serverName"` // 证书使用的主机名,在请求TLS服务器时需要
|
||||
IsCA bool `yaml:"isCA" json:"isCA"` // 是否为CA证书
|
||||
|
||||
dnsNames []string
|
||||
cert *tls.Certificate
|
||||
timeBefore time.Time
|
||||
timeAfter time.Time
|
||||
issuer pkix.Name
|
||||
}
|
||||
|
||||
// 获取新的SSL证书
|
||||
func NewSSLCertConfig(certFile string, keyFile string) *SSLCertConfig {
|
||||
return &SSLCertConfig{
|
||||
On: true,
|
||||
Id: stringutil.Rand(16),
|
||||
CertFile: certFile,
|
||||
KeyFile: keyFile,
|
||||
}
|
||||
}
|
||||
|
||||
// 校验
|
||||
func (this *SSLCertConfig) Init() error {
|
||||
if this.IsShared {
|
||||
shared := this.FindShared()
|
||||
if shared == nil {
|
||||
return errors.New("the shared cert has been deleted")
|
||||
}
|
||||
|
||||
// 拷贝之前需要保留的
|
||||
serverName := this.ServerName
|
||||
|
||||
// copy
|
||||
configutils.CopyStructObject(this, shared)
|
||||
this.ServerName = serverName
|
||||
}
|
||||
|
||||
this.dnsNames = []string{}
|
||||
|
||||
if len(this.CertFile) == 0 {
|
||||
return errors.New("cert file should not be empty")
|
||||
}
|
||||
|
||||
// 分析证书
|
||||
if this.IsCA { // CA证书
|
||||
data, err := ioutil.ReadFile(this.FullCertPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
index := -1
|
||||
this.cert = &tls.Certificate{
|
||||
Certificate: [][]byte{},
|
||||
}
|
||||
for {
|
||||
index++
|
||||
|
||||
block, rest := pem.Decode(data)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
if len(rest) == 0 {
|
||||
break
|
||||
}
|
||||
this.cert.Certificate = append(this.cert.Certificate, block.Bytes)
|
||||
data = rest
|
||||
c, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c == nil {
|
||||
return errors.New("no available certificates in file")
|
||||
}
|
||||
|
||||
dnsNames := c.DNSNames
|
||||
if len(dnsNames) > 0 {
|
||||
for _, dnsName := range dnsNames {
|
||||
if !lists.ContainsString(this.dnsNames, dnsName) {
|
||||
this.dnsNames = append(this.dnsNames, dnsName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if index == 0 {
|
||||
this.timeBefore = c.NotBefore
|
||||
this.timeAfter = c.NotAfter
|
||||
this.issuer = c.Issuer
|
||||
}
|
||||
}
|
||||
} else { // 证书+私钥
|
||||
if len(this.KeyFile) == 0 {
|
||||
return errors.New("key file should not be empty")
|
||||
}
|
||||
cert, err := tls.LoadX509KeyPair(this.FullCertPath(), this.FullKeyPath())
|
||||
if err != nil {
|
||||
return errors.New("load certificate '" + this.CertFile + "', '" + this.KeyFile + "' failed:" + err.Error())
|
||||
}
|
||||
|
||||
for index, data := range cert.Certificate {
|
||||
c, err := x509.ParseCertificate(data)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
dnsNames := c.DNSNames
|
||||
if len(dnsNames) > 0 {
|
||||
for _, dnsName := range dnsNames {
|
||||
if !lists.ContainsString(this.dnsNames, dnsName) {
|
||||
this.dnsNames = append(this.dnsNames, dnsName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if index == 0 {
|
||||
this.timeBefore = c.NotBefore
|
||||
this.timeAfter = c.NotAfter
|
||||
this.issuer = c.Issuer
|
||||
}
|
||||
}
|
||||
|
||||
this.cert = &cert
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 查找共享的证书
|
||||
func (this *SSLCertConfig) FindShared() *SSLCertConfig {
|
||||
if !this.IsShared {
|
||||
return nil
|
||||
}
|
||||
return SharedSSLCertList().FindCert(this.Id)
|
||||
}
|
||||
|
||||
// 证书文件路径
|
||||
func (this *SSLCertConfig) FullCertPath() string {
|
||||
if len(this.CertFile) == 0 {
|
||||
return ""
|
||||
}
|
||||
if !strings.ContainsAny(this.CertFile, "/\\") {
|
||||
return Tea.ConfigFile(this.CertFile)
|
||||
}
|
||||
return this.CertFile
|
||||
}
|
||||
|
||||
// 密钥文件路径
|
||||
func (this *SSLCertConfig) FullKeyPath() string {
|
||||
if len(this.KeyFile) == 0 {
|
||||
return ""
|
||||
}
|
||||
if !strings.ContainsAny(this.KeyFile, "/\\") {
|
||||
return Tea.ConfigFile(this.KeyFile)
|
||||
}
|
||||
return this.KeyFile
|
||||
}
|
||||
|
||||
// 校验是否匹配某个域名
|
||||
func (this *SSLCertConfig) MatchDomain(domain string) bool {
|
||||
if len(this.dnsNames) == 0 {
|
||||
return false
|
||||
}
|
||||
return configutils.MatchDomains(this.dnsNames, domain)
|
||||
}
|
||||
|
||||
// 证书中的域名
|
||||
func (this *SSLCertConfig) DNSNames() []string {
|
||||
return this.dnsNames
|
||||
}
|
||||
|
||||
// 获取证书对象
|
||||
func (this *SSLCertConfig) CertObject() *tls.Certificate {
|
||||
return this.cert
|
||||
}
|
||||
|
||||
// 开始时间
|
||||
func (this *SSLCertConfig) TimeBefore() time.Time {
|
||||
return this.timeBefore
|
||||
}
|
||||
|
||||
// 结束时间
|
||||
func (this *SSLCertConfig) TimeAfter() time.Time {
|
||||
return this.timeAfter
|
||||
}
|
||||
|
||||
// 发行信息
|
||||
func (this *SSLCertConfig) Issuer() pkix.Name {
|
||||
return this.issuer
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
func (this *SSLCertConfig) DeleteFiles() error {
|
||||
if this.IsLocal {
|
||||
return nil
|
||||
}
|
||||
|
||||
var resultErr error = nil
|
||||
if len(this.CertFile) > 0 && !strings.ContainsAny(this.CertFile, "/\\") {
|
||||
err := files.NewFile(this.FullCertPath()).Delete()
|
||||
if err != nil {
|
||||
resultErr = err
|
||||
}
|
||||
}
|
||||
|
||||
if len(this.KeyFile) > 0 && !strings.ContainsAny(this.KeyFile, "/\\") {
|
||||
err := files.NewFile(this.FullKeyPath()).Delete()
|
||||
if err != nil {
|
||||
resultErr = err
|
||||
}
|
||||
}
|
||||
return resultErr
|
||||
}
|
||||
|
||||
// 读取证书文件
|
||||
func (this *SSLCertConfig) ReadCert() ([]byte, error) {
|
||||
if len(this.CertFile) == 0 {
|
||||
return nil, errors.New("cert file should not be empty")
|
||||
}
|
||||
|
||||
if this.IsLocal {
|
||||
return ioutil.ReadFile(this.CertFile)
|
||||
}
|
||||
|
||||
return ioutil.ReadFile(Tea.ConfigFile(this.CertFile))
|
||||
}
|
||||
|
||||
// 读取密钥文件
|
||||
func (this *SSLCertConfig) ReadKey() ([]byte, error) {
|
||||
if len(this.KeyFile) == 0 {
|
||||
return nil, errors.New("key file should not be empty")
|
||||
}
|
||||
|
||||
if this.IsLocal {
|
||||
return ioutil.ReadFile(this.KeyFile)
|
||||
}
|
||||
|
||||
return ioutil.ReadFile(Tea.ConfigFile(this.KeyFile))
|
||||
}
|
||||
|
||||
// 匹配关键词
|
||||
func (this *SSLCertConfig) MatchKeyword(keyword string) (matched bool, name string, tags []string) {
|
||||
if configutils.MatchKeyword(this.Description, keyword) {
|
||||
matched = true
|
||||
name = this.Description
|
||||
}
|
||||
return
|
||||
}
|
||||
86
internal/configs/serverconfigs/sslconfigs/ssl_cert_list.go
Normal file
86
internal/configs/serverconfigs/sslconfigs/ssl_cert_list.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package sslconfigs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/shared"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"gopkg.in/yaml.v3"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
const (
|
||||
sslCertListFilename = "ssl.certs.conf"
|
||||
)
|
||||
|
||||
// 获取证书列表实例
|
||||
// 一定会返回不为nil的值
|
||||
func SharedSSLCertList() *SSLCertList {
|
||||
data, err := ioutil.ReadFile(Tea.ConfigFile(sslCertListFilename))
|
||||
if err != nil {
|
||||
return NewSSLCertList()
|
||||
}
|
||||
|
||||
list := &SSLCertList{}
|
||||
err = yaml.Unmarshal(data, list)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return NewSSLCertList()
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
// 公共的SSL证书列表
|
||||
type SSLCertList struct {
|
||||
Certs []*SSLCertConfig `yaml:"certs" json:"certs"` // 证书
|
||||
}
|
||||
|
||||
// 获取新对象
|
||||
func NewSSLCertList() *SSLCertList {
|
||||
return &SSLCertList{
|
||||
Certs: []*SSLCertConfig{},
|
||||
}
|
||||
}
|
||||
|
||||
// 添加证书
|
||||
func (this *SSLCertList) AddCert(cert *SSLCertConfig) {
|
||||
this.Certs = append(this.Certs, cert)
|
||||
}
|
||||
|
||||
// 删除证书
|
||||
func (this *SSLCertList) RemoveCert(certId string) {
|
||||
result := []*SSLCertConfig{}
|
||||
for _, cert := range this.Certs {
|
||||
if cert.Id == certId {
|
||||
continue
|
||||
}
|
||||
result = append(result, cert)
|
||||
}
|
||||
this.Certs = result
|
||||
}
|
||||
|
||||
// 查找证书
|
||||
func (this *SSLCertList) FindCert(certId string) *SSLCertConfig {
|
||||
if len(certId) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, cert := range this.Certs {
|
||||
if cert.Id == certId {
|
||||
return cert
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 保存
|
||||
func (this *SSLCertList) Save() error {
|
||||
shared.Locker.Lock()
|
||||
defer shared.Locker.Unlock()
|
||||
|
||||
data, err := yaml.Marshal(this)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(Tea.ConfigFile(sslCertListFilename), data, 0777)
|
||||
}
|
||||
124
internal/configs/serverconfigs/sslconfigs/ssl_go_1.11.go
Normal file
124
internal/configs/serverconfigs/sslconfigs/ssl_go_1.11.go
Normal file
@@ -0,0 +1,124 @@
|
||||
// +build !go1.12
|
||||
|
||||
package sslconfigs
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
var AllTlsVersions = []TLSVersion{"SSL 3.0", "TLS 1.0", "TLS 1.1", "TLS 1.2"}
|
||||
|
||||
var AllTLSCipherSuites = []TLSCipherSuite{
|
||||
"TLS_RSA_WITH_RC4_128_SHA",
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||
}
|
||||
|
||||
var TLSModernCipherSuites = []string{
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
}
|
||||
|
||||
var TLSIntermediateCipherSuites = []string{
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
}
|
||||
|
||||
func (this *SSLConfig) convertMinVersion() {
|
||||
switch this.MinVersion {
|
||||
case "SSL 3.0":
|
||||
this.minVersion = tls.VersionSSL30
|
||||
case "TLS 1.0":
|
||||
this.minVersion = tls.VersionTLS10
|
||||
case "TLS 1.1":
|
||||
this.minVersion = tls.VersionTLS11
|
||||
case "TLS 1.2":
|
||||
this.minVersion = tls.VersionTLS12
|
||||
default:
|
||||
this.minVersion = tls.VersionTLS10
|
||||
}
|
||||
}
|
||||
|
||||
func (this *SSLConfig) initCipherSuites() {
|
||||
// cipher suites
|
||||
suites := []uint16{}
|
||||
for _, suite := range this.CipherSuites {
|
||||
switch suite {
|
||||
case "TLS_RSA_WITH_RC4_128_SHA":
|
||||
suites = append(suites, tls.TLS_RSA_WITH_RC4_128_SHA)
|
||||
case "TLS_RSA_WITH_3DES_EDE_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA)
|
||||
case "TLS_RSA_WITH_AES_128_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_RSA_WITH_AES_128_CBC_SHA)
|
||||
case "TLS_RSA_WITH_AES_256_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_RSA_WITH_AES_256_CBC_SHA)
|
||||
case "TLS_RSA_WITH_AES_128_CBC_SHA256":
|
||||
suites = append(suites, tls.TLS_RSA_WITH_AES_128_CBC_SHA256)
|
||||
case "TLS_RSA_WITH_AES_128_GCM_SHA256":
|
||||
suites = append(suites, tls.TLS_RSA_WITH_AES_128_GCM_SHA256)
|
||||
case "TLS_RSA_WITH_AES_256_GCM_SHA384":
|
||||
suites = append(suites, tls.TLS_RSA_WITH_AES_256_GCM_SHA384)
|
||||
case "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA":
|
||||
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA)
|
||||
case "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
|
||||
case "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
|
||||
case "TLS_ECDHE_RSA_WITH_RC4_128_SHA":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA)
|
||||
case "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA)
|
||||
case "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
|
||||
case "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA)
|
||||
case "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256":
|
||||
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256)
|
||||
case "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256)
|
||||
case "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
|
||||
case "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256":
|
||||
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
|
||||
case "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)
|
||||
case "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384":
|
||||
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384)
|
||||
case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305)
|
||||
case "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
|
||||
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305)
|
||||
}
|
||||
}
|
||||
this.cipherSuites = suites
|
||||
}
|
||||
148
internal/configs/serverconfigs/sslconfigs/ssl_go_1.12.go
Normal file
148
internal/configs/serverconfigs/sslconfigs/ssl_go_1.12.go
Normal file
@@ -0,0 +1,148 @@
|
||||
// +build go1.12
|
||||
|
||||
package sslconfigs
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"os"
|
||||
)
|
||||
|
||||
var AllTlsVersions = []TLSVersion{"SSL 3.0", "TLS 1.0", "TLS 1.1", "TLS 1.2", "TLS 1.3"}
|
||||
|
||||
var AllTLSCipherSuites = []TLSCipherSuite{
|
||||
"TLS_RSA_WITH_RC4_128_SHA",
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_AES_128_GCM_SHA256",
|
||||
"TLS_AES_256_GCM_SHA384",
|
||||
"TLS_CHACHA20_POLY1305_SHA256",
|
||||
}
|
||||
|
||||
var TLSModernCipherSuites = []string{
|
||||
"TLS_AES_128_GCM_SHA256",
|
||||
"TLS_CHACHA20_POLY1305_SHA256",
|
||||
"TLS_AES_256_GCM_SHA384",
|
||||
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
}
|
||||
|
||||
var TLSIntermediateCipherSuites = []string{
|
||||
"TLS_AES_128_GCM_SHA256",
|
||||
"TLS_CHACHA20_POLY1305_SHA256",
|
||||
"TLS_AES_256_GCM_SHA384",
|
||||
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
}
|
||||
|
||||
func (this *SSLConfig) convertMinVersion() {
|
||||
switch this.MinVersion {
|
||||
case "SSL 3.0":
|
||||
this.minVersion = tls.VersionSSL30
|
||||
case "TLS 1.0":
|
||||
this.minVersion = tls.VersionTLS10
|
||||
case "TLS 1.1":
|
||||
this.minVersion = tls.VersionTLS11
|
||||
case "TLS 1.2":
|
||||
this.minVersion = tls.VersionTLS12
|
||||
case "TLS 1.3":
|
||||
this.minVersion = tls.VersionTLS13
|
||||
|
||||
os.Setenv("GODEBUG", "tls13=1") // TODO should be removed in go 1.14, in go 1.12 tls IS NOT FULL IMPLEMENTED YET
|
||||
default:
|
||||
this.minVersion = tls.VersionTLS10
|
||||
}
|
||||
}
|
||||
|
||||
func (this *SSLConfig) initCipherSuites() {
|
||||
// cipher suites
|
||||
suites := []uint16{}
|
||||
for _, suite := range this.CipherSuites {
|
||||
switch suite {
|
||||
case "TLS_RSA_WITH_RC4_128_SHA":
|
||||
suites = append(suites, tls.TLS_RSA_WITH_RC4_128_SHA)
|
||||
case "TLS_RSA_WITH_3DES_EDE_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA)
|
||||
case "TLS_RSA_WITH_AES_128_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_RSA_WITH_AES_128_CBC_SHA)
|
||||
case "TLS_RSA_WITH_AES_256_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_RSA_WITH_AES_256_CBC_SHA)
|
||||
case "TLS_RSA_WITH_AES_128_CBC_SHA256":
|
||||
suites = append(suites, tls.TLS_RSA_WITH_AES_128_CBC_SHA256)
|
||||
case "TLS_RSA_WITH_AES_128_GCM_SHA256":
|
||||
suites = append(suites, tls.TLS_RSA_WITH_AES_128_GCM_SHA256)
|
||||
case "TLS_RSA_WITH_AES_256_GCM_SHA384":
|
||||
suites = append(suites, tls.TLS_RSA_WITH_AES_256_GCM_SHA384)
|
||||
case "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA":
|
||||
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA)
|
||||
case "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
|
||||
case "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
|
||||
case "TLS_ECDHE_RSA_WITH_RC4_128_SHA":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA)
|
||||
case "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA)
|
||||
case "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
|
||||
case "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA)
|
||||
case "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256":
|
||||
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256)
|
||||
case "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256)
|
||||
case "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
|
||||
case "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256":
|
||||
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
|
||||
case "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)
|
||||
case "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384":
|
||||
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384)
|
||||
case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":
|
||||
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305)
|
||||
case "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
|
||||
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305)
|
||||
case "TLS_AES_128_GCM_SHA256":
|
||||
suites = append(suites, tls.TLS_AES_128_GCM_SHA256)
|
||||
case "TLS_AES_256_GCM_SHA384":
|
||||
suites = append(suites, tls.TLS_AES_256_GCM_SHA384)
|
||||
case "TLS_CHACHA20_POLY1305_SHA256":
|
||||
suites = append(suites, tls.TLS_CHACHA20_POLY1305_SHA256)
|
||||
}
|
||||
}
|
||||
this.cipherSuites = suites
|
||||
}
|
||||
63
internal/configs/serverconfigs/sslconfigs/ssl_hsts.go
Normal file
63
internal/configs/serverconfigs/sslconfigs/ssl_hsts.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package sslconfigs
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/configutils"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HSTS设置
|
||||
// 参考: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
|
||||
type HSTSConfig struct {
|
||||
On bool `yaml:"on" json:"on"`
|
||||
MaxAge int `yaml:"maxAge" json:"maxAge"` // 单位秒
|
||||
IncludeSubDomains bool `yaml:"includeSubDomains" json:"includeSubDomains"`
|
||||
Preload bool `yaml:"preload" json:"preload"`
|
||||
Domains []string `yaml:"domains" json:"domains"`
|
||||
|
||||
hasDomains bool
|
||||
headerValue string
|
||||
}
|
||||
|
||||
// 校验
|
||||
func (this *HSTSConfig) Init() error {
|
||||
this.hasDomains = len(this.Domains) > 0
|
||||
this.headerValue = this.asHeaderValue()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 判断是否匹配域名
|
||||
func (this *HSTSConfig) Match(domain string) bool {
|
||||
if !this.hasDomains {
|
||||
return true
|
||||
}
|
||||
return configutils.MatchDomains(this.Domains, domain)
|
||||
}
|
||||
|
||||
// Header Key
|
||||
func (this *HSTSConfig) HeaderKey() string {
|
||||
return "Strict-Transport-Security"
|
||||
}
|
||||
|
||||
// 取得当前的Header值
|
||||
func (this *HSTSConfig) HeaderValue() string {
|
||||
return this.headerValue
|
||||
}
|
||||
|
||||
// 转换为Header值
|
||||
func (this *HSTSConfig) asHeaderValue() string {
|
||||
b := strings.Builder{}
|
||||
b.WriteString("max-age=")
|
||||
if this.MaxAge > 0 {
|
||||
b.WriteString(strconv.Itoa(this.MaxAge))
|
||||
} else {
|
||||
b.WriteString("31536000") // 1 year
|
||||
}
|
||||
if this.IncludeSubDomains {
|
||||
b.WriteString("; includeSubDomains")
|
||||
}
|
||||
if this.Preload {
|
||||
b.WriteString("; preload")
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
39
internal/configs/serverconfigs/sslconfigs/ssl_hsts_test.go
Normal file
39
internal/configs/serverconfigs/sslconfigs/ssl_hsts_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package sslconfigs
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHSTSConfig(t *testing.T) {
|
||||
h := &HSTSConfig{}
|
||||
h.Init()
|
||||
t.Log(h.HeaderValue())
|
||||
|
||||
h.IncludeSubDomains = true
|
||||
h.Init()
|
||||
t.Log(h.HeaderValue())
|
||||
|
||||
h.Preload = true
|
||||
h.Init()
|
||||
t.Log(h.HeaderValue())
|
||||
|
||||
h.IncludeSubDomains = false
|
||||
h.Init()
|
||||
t.Log(h.HeaderValue())
|
||||
|
||||
h.MaxAge = 86400
|
||||
h.Init()
|
||||
t.Log(h.HeaderValue())
|
||||
|
||||
a := assert.NewAssertion(t)
|
||||
a.IsTrue(h.Match("abc.com"))
|
||||
|
||||
h.Domains = []string{"abc.com"}
|
||||
h.Init()
|
||||
a.IsTrue(h.Match("abc.com"))
|
||||
|
||||
h.Domains = []string{"1.abc.com"}
|
||||
h.Init()
|
||||
a.IsFalse(h.Match("abc.com"))
|
||||
}
|
||||
10
internal/configs/serverconfigs/web_config.go
Normal file
10
internal/configs/serverconfigs/web_config.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package serverconfigs
|
||||
|
||||
type WebConfig struct {
|
||||
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||
|
||||
Locations []*LocationConfig `yaml:"locations" json:"locations"` // 路径规则 TODO
|
||||
|
||||
// 本地静态资源配置
|
||||
Root string `yaml:"root" json:"root"` // 资源根目录 TODO
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package configs
|
||||
|
||||
type WebConfig struct {
|
||||
Locations []*LocationConfig `yaml:"locations"` // 路径规则
|
||||
|
||||
// 本地静态资源配置
|
||||
Root string `yaml:"root" json:"root"` // 资源根目录
|
||||
}
|
||||
Reference in New Issue
Block a user