diff --git a/README.md b/README.md index 7325897..eca3c32 100644 --- a/README.md +++ b/README.md @@ -23,18 +23,18 @@ A V2board node server based on multi core, modified from XrayR. ## 功能介绍 -| 功能 | v2ray | trojan | shadowsocks | -|-----------|-------|--------|-------------| -| 自动申请tls证书 | √ | √ | √ | -| 自动续签tls证书 | √ | √ | √ | -| 在线人数统计 | √ | √ | √ | -| 审计规则 | √ | √ | √ | -| 自定义DNS | √ | √ | √ | -| 在线IP数限制 | √ | √ | √ | -| 连接数限制 | √ | √ | √ | -| 跨节点IP数限制 | √ | √ | √ | -| 按照用户限速 | √ | √ | √ | -| 动态限速(未测试) | √ | √ | √ | +| 功能 | v2ray | trojan | shadowsocks | hysteria | +|-----------|-------|--------|-------------|----------| +| 自动申请tls证书 | √ | √ | √ | √ | +| 自动续签tls证书 | √ | √ | √ | √ | +| 在线人数统计 | √ | √ | √ | √ | +| 审计规则 | √ | √ | √ | | +| 自定义DNS | √ | √ | √ | √ | +| 在线IP数限制 | √ | √ | √ | | +| 连接数限制 | √ | √ | √ | | +| 跨节点IP数限制 | √ | √ | √ | | +| 按照用户限速 | √ | √ | √ | | +| 动态限速(未测试) | √ | √ | √ | | ## TODO diff --git a/api/panel/node.go b/api/panel/node.go index 82cb4a8..e731d42 100644 --- a/api/panel/node.go +++ b/api/panel/node.go @@ -11,7 +11,9 @@ import ( ) type CommonNodeRsp struct { + Host string `json:"host"` ServerPort int `json:"server_port"` + ServerName string `json:"server_name"` Routes []Route `json:"routes"` BaseConfig BaseConfig `json:"base_config"` } @@ -54,11 +56,11 @@ type NodeInfo struct { Id int Type string Rules []*regexp.Regexp + Host string Port int Network string NetworkSettings json.RawMessage Tls bool - Host string ServerName string UpMbps int DownMbps int @@ -75,16 +77,14 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) { if err = c.checkResponse(r, path, err); err != nil { return } - err = json.Unmarshal(r.Body(), &node) - if err != nil { - return - } if c.etag == r.Header().Get("ETag") { // node info not changed return nil, nil } // parse common params - node.Id = c.NodeId - node.Type = c.NodeType + node = &NodeInfo{ + Id: c.NodeId, + Type: c.NodeType, + } common := CommonNodeRsp{} err = json.Unmarshal(r.Body(), &common) if err != nil { @@ -103,6 +103,9 @@ func (c *Client) GetNodeInfo() (node *NodeInfo, err error) { } } } + node.ServerName = common.ServerName + node.Host = common.Host + node.Port = common.ServerPort node.PullInterval = intervalToTime(common.BaseConfig.PullInterval) node.PushInterval = intervalToTime(common.BaseConfig.PushInterval) // parse protocol params diff --git a/api/panel/panel.go b/api/panel/panel.go index 1659ad7..f6e90dd 100644 --- a/api/panel/panel.go +++ b/api/panel/panel.go @@ -43,9 +43,9 @@ func New(c *conf.ApiConfig) (*Client, error) { client.SetBaseURL(c.APIHost) // Check node type c.NodeType = strings.ToLower(c.NodeType) - if c.NodeType != "v2ray" && - c.NodeType != "trojan" && - c.NodeType != "shadowsocks" { + switch c.NodeType { + case "v2ray", "trojan", "shadowsocks", "hysteria": + default: return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType) } // Create Key for each requests diff --git a/api/panel/utils.go b/api/panel/utils.go index 56aeb0f..938bec9 100644 --- a/api/panel/utils.go +++ b/api/panel/utils.go @@ -20,7 +20,7 @@ func (c *Client) checkResponse(res *resty.Response, path string, err error) erro } if res.StatusCode() != 200 { body := res.Body() - return fmt.Errorf("request %s failed: %s, %s", c.assembleURL(path), string(body), err) + return fmt.Errorf("request %s failed: %s", c.assembleURL(path), string(body)) } return nil } diff --git a/common/builder/inbound.go b/common/builder/inbound.go index ae9f5bb..dc576f9 100644 --- a/common/builder/inbound.go +++ b/common/builder/inbound.go @@ -25,7 +25,7 @@ func BuildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s case "v2ray": err = buildV2ray(config, nodeInfo, in) case "trojan": - err = buildTrojan(config, nodeInfo, in) + err = buildTrojan(config, in) case "shadowsocks": err = buildShadowsocks(config, nodeInfo, in) default: @@ -104,7 +104,6 @@ func BuildInbound(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, tag s // Support ProxyProtocol for any transport protocol if *in.StreamSetting.Network != "tcp" && *in.StreamSetting.Network != "ws" && - *in.StreamSetting.Network != "" && config.EnableProxyProtocol { socketConfig := &coreConf.SocketConfig{ AcceptProxyProtocol: config.EnableProxyProtocol, @@ -177,7 +176,7 @@ func buildV2ray(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound return nil } -func buildTrojan(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inbound *coreConf.InboundDetourConfig) error { +func buildTrojan(config *conf.ControllerConfig, inbound *coreConf.InboundDetourConfig) error { inbound.Protocol = "trojan" if config.XrayOptions.EnableFallback { // Set fallback @@ -196,10 +195,7 @@ func buildTrojan(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, inboun s := []byte("{}") inbound.Settings = (*json.RawMessage)(&s) } - if nodeInfo.Network == "" { - nodeInfo.Network = "tcp" - } - t := coreConf.TransportProtocol(nodeInfo.Network) + t := coreConf.TransportProtocol("tcp") inbound.StreamSetting = &coreConf.StreamConfig{Network: &t} return nil } diff --git a/conf/node.go b/conf/node.go index db0cfda..ac1b134 100644 --- a/conf/node.go +++ b/conf/node.go @@ -15,15 +15,15 @@ type ApiConfig struct { } type ControllerConfig struct { - DisableUploadTraffic bool `yaml:"DisableUploadTraffic"` - DisableGetRule bool `yaml:"DisableGetRule"` - ListenIP string `yaml:"ListenIP"` - SendIP string `yaml:"SendIP"` - EnableProxyProtocol bool `yaml:"EnableProxyProtocol"` - XrayOptions *XrayOptions `yaml:"XrayOptions"` - HyOptions *HyOptions `yaml:"HyOptions"` - LimitConfig LimitConfig `yaml:"LimitConfig"` - CertConfig *CertConfig `yaml:"CertConfig"` + DisableUploadTraffic bool `yaml:"DisableUploadTraffic"` + DisableGetRule bool `yaml:"DisableGetRule"` + ListenIP string `yaml:"ListenIP"` + SendIP string `yaml:"SendIP"` + EnableProxyProtocol bool `yaml:"EnableProxyProtocol"` + XrayOptions XrayOptions `yaml:"XrayOptions"` + HyOptions HyOptions `yaml:"HyOptions"` + LimitConfig LimitConfig `yaml:"LimitConfig"` + CertConfig *CertConfig `yaml:"CertConfig"` } type XrayOptions struct { diff --git a/core/hy/counter.go b/core/hy/counter.go index 5d62a74..6228bfa 100644 --- a/core/hy/counter.go +++ b/core/hy/counter.go @@ -1,7 +1,6 @@ package hy import ( - "github.com/apernet/hysteria/core/cs" "sync" "sync/atomic" ) @@ -17,8 +16,10 @@ type counters struct { //ConnGauge atomic.Int64 } -func NewUserTrafficCounter() cs.TrafficCounter { - return new(UserTrafficCounter) +func NewUserTrafficCounter() *UserTrafficCounter { + return &UserTrafficCounter{ + counters: map[string]*counters{}, + } } func (c *UserTrafficCounter) getCounters(auth string) *counters { diff --git a/core/hy/hy.go b/core/hy/hy.go index 88ced0c..a5eca71 100644 --- a/core/hy/hy.go +++ b/core/hy/hy.go @@ -3,15 +3,20 @@ package hy import ( "fmt" "github.com/Yuzuki616/V2bX/conf" + vCore "github.com/Yuzuki616/V2bX/core" "github.com/hashicorp/go-multierror" "sync" ) +func init() { + vCore.RegisterCore("hy", NewHy) +} + type Hy struct { servers sync.Map } -func New(_ *conf.CoreConfig) (*Hy, error) { +func NewHy(_ *conf.CoreConfig) (vCore.Core, error) { return &Hy{ servers: sync.Map{}, }, nil diff --git a/core/hy/node.go b/core/hy/node.go index 0000c30..a39908b 100644 --- a/core/hy/node.go +++ b/core/hy/node.go @@ -12,6 +12,10 @@ func (h *Hy) AddNode(tag string, info *panel.NodeInfo, c *conf.ControllerConfig) if info.Type != "hysteria" { return errors.New("the core not support " + info.Type) } + switch c.CertConfig.CertMode { + case "reality", "none", "": + return errors.New("hysteria need normal tls cert") + } s := NewServer(tag) err := s.runServer(info, c) if err != nil { diff --git a/core/hy/server.go b/core/hy/server.go index 8fe4de0..fda7f6c 100644 --- a/core/hy/server.go +++ b/core/hy/server.go @@ -2,7 +2,6 @@ package hy import ( "crypto/tls" - "errors" "fmt" "github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/conf" @@ -33,7 +32,7 @@ var serverPacketConnFuncFactoryMap = map[string]pktconns.ServerPacketConnFuncFac type Server struct { tag string - counter UserTrafficCounter + counter *UserTrafficCounter users sync.Map running atomic.Bool *cs.Server @@ -46,9 +45,9 @@ func NewServer(tag string) *Server { } func (s *Server) runServer(node *panel.NodeInfo, c *conf.ControllerConfig) error { - if c.HyOptions == nil { + /*if c.HyOptions == nil { return errors.New("hy options is not vail") - } + }*/ // Resolver if len(c.HyOptions.Resolver) > 0 { err := setResolver(c.HyOptions.Resolver) @@ -136,7 +135,7 @@ func (s *Server) runServer(node *panel.NodeInfo, c *conf.ControllerConfig) error aclEngine.DefaultAction = acl.ActionDirect }*/ // Prometheus - trafficCounter := NewUserTrafficCounter() + s.counter = NewUserTrafficCounter() // Packet conn pktConnFuncFactory := serverPacketConnFuncFactoryMap[""] if pktConnFuncFactory == nil { @@ -155,7 +154,7 @@ func (s *Server) runServer(node *panel.NodeInfo, c *conf.ControllerConfig) error up, down := SpeedTrans(node.UpMbps, node.DownMbps) s.Server, err = cs.NewServer(tlsConfig, quicConfig, pktConn, transport.DefaultServerTransport, up, down, false, aclEngine, - s.connectFunc, s.disconnectFunc, tcpRequestFunc, tcpErrorFunc, udpRequestFunc, udpErrorFunc, trafficCounter) + s.connectFunc, s.disconnectFunc, tcpRequestFunc, tcpErrorFunc, udpRequestFunc, udpErrorFunc, s.counter) if err != nil { return fmt.Errorf("new server error: %s", err) } @@ -173,25 +172,26 @@ func (s *Server) runServer(node *panel.NodeInfo, c *conf.ControllerConfig) error return nil } -func (s *Server) authByUser(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string, string) { - if email, ok := s.users.Load(string(auth)); ok { - return true, email.(string), "Done" +func (s *Server) authByUser(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) { + if _, ok := s.users.Load(string(auth)); ok { + return true, "Done" } - return false, "", "Failed" + return false, "Failed" } func (s *Server) connectFunc(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) { - ok, email, msg := s.authByUser(addr, auth, sSend, sRecv) + ok, msg := s.authByUser(addr, auth, sSend, sRecv) if !ok { logrus.WithFields(logrus.Fields{ "src": defaultIPMasker.Mask(addr.String()), }).Info("Authentication failed, client rejected") - } else { - logrus.WithFields(logrus.Fields{ - "src": defaultIPMasker.Mask(addr.String()), - "email": email, - }).Info("Client connected") + return false, msg } + logrus.WithFields(logrus.Fields{ + "src": defaultIPMasker.Mask(addr.String()), + "Uuid": string(auth), + "Tag": s.tag, + }).Info("Client connected") return ok, msg } diff --git a/core/hy/server_test.go b/core/hy/server_test.go index 5d5f5ce..95c8f9a 100644 --- a/core/hy/server_test.go +++ b/core/hy/server_test.go @@ -3,10 +3,12 @@ package hy import ( "github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/conf" + "github.com/sirupsen/logrus" "testing" ) func TestServer(t *testing.T) { + logrus.SetLevel(logrus.DebugLevel) s := NewServer("test") t.Log(s.runServer(&panel.NodeInfo{ Port: 11415, @@ -15,11 +17,12 @@ func TestServer(t *testing.T) { HyObfs: "atresssdaaaadd", }, &conf.ControllerConfig{ ListenIP: "127.0.0.1", - HyOptions: &conf.HyOptions{}, + HyOptions: conf.HyOptions{}, CertConfig: &conf.CertConfig{ CertFile: "../../test_data/1.pem", KeyFile: "../../test_data/1.key", }, })) + s.users.Store("test1111", struct{}{}) select {} } diff --git a/core/hy/user.go b/core/hy/user.go index f069b3d..30cb651 100644 --- a/core/hy/user.go +++ b/core/hy/user.go @@ -1,6 +1,7 @@ package hy import ( + "encoding/base64" "errors" "github.com/Yuzuki616/V2bX/core" ) @@ -18,12 +19,13 @@ func (h *Hy) AddUsers(p *core.AddUsersParams) (int, error) { } func (h *Hy) GetUserTraffic(tag, uuid string, reset bool) (up int64, down int64) { - s, _ := h.servers.Load(tag) - c := &s.(*Server).counter - up = c.getCounters(uuid).UpCounter.Load() - down = c.getCounters(uuid).DownCounter.Load() + v, _ := h.servers.Load(tag) + s := v.(*Server) + auth := base64.StdEncoding.EncodeToString([]byte(uuid)) + up = s.counter.getCounters(auth).UpCounter.Load() + down = s.counter.getCounters(uuid).DownCounter.Load() if reset { - c.Reset(uuid) + s.counter.Reset(uuid) } return } diff --git a/example/config.yml.example b/example/config.yml.example index 4588845..49f3c37 100644 --- a/example/config.yml.example +++ b/example/config.yml.example @@ -26,20 +26,24 @@ Nodes: ControllerConfig: ListenIP: 0.0.0.0 # IP address you want to listen SendIP: 0.0.0.0 # IP address you want to send pacakage - EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well - DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy - EnableTFO: false # Enable TCP Fast Open - EnableVless: false # Enable Vless for V2ray Type - EnableXtls: false # Enable xtls-rprx-vision, only vless - EnableProxyProtocol: false # Only works for WebSocket and TCP - EnableFallback: false # Only support for Trojan and Vless - FallBackConfigs: # Support multiple fallbacks - - SNI: # TLS SNI(Server Name Indication), Empty for any - Alpn: # Alpn, Empty for any - Path: # HTTP PATH, Empty for any - Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details. - ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for dsable - + XrayOptions: + EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well + DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy + EnableTFO: false # Enable TCP Fast Open + EnableVless: false # Enable Vless for V2ray Type + EnableXtls: false # Enable xtls-rprx-vision, only vless + EnableProxyProtocol: false # Only works for WebSocket and TCP + EnableFallback: false # Only support for Trojan and Vless + FallBackConfigs: # Support multiple fallbacks + - SNI: # TLS SNI(Server Name Indication), Empty for any + Alpn: # Alpn, Empty for any + Path: # HTTP PATH, Empty for any + Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details. + ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for dsable + HyOptions: + Resolver: "udp://1.1.1.1:53", // DNS resolver address + ResolvePreference: 64 # DNS IPv4/IPv6 preference. Available options: "64" (IPv6 first, fallback to IPv4), "46" (IPv4 first, fallback to IPv6), "6" (IPv6 only), "4" (IPv4 only) + SendDevice: "eth0" # Bind device for outbound connections (usually requires root) LimitConfig: EnableRealtime: false # Check device limit on real time SpeedLimit: 0 # Mbps, Local settings will replace remote settings, 0 means disable diff --git a/node/node.go b/node/node.go index 4cb414b..ae56252 100644 --- a/node/node.go +++ b/node/node.go @@ -1,6 +1,7 @@ package node import ( + "fmt" "github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/conf" vCore "github.com/Yuzuki616/V2bX/core" @@ -25,7 +26,11 @@ func (n *Node) Start(nodes []*conf.NodeConfig, core vCore.Core) error { n.controllers[i] = NewController(core, p, c.ControllerConfig) err = n.controllers[i].Start() if err != nil { - return err + return fmt.Errorf("start node controller [%s-%s-%d] error: %s", + c.ApiConfig.NodeType, + c.ApiConfig.APIHost, + c.ApiConfig.NodeID, + err) } } return nil