Merge pull request #3010 from fatedier/dev

release v0.44.0
This commit is contained in:
fatedier 2022-07-11 00:10:43 +08:00 committed by GitHub
commit 8888610d83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 142 additions and 63 deletions

View File

@ -477,6 +477,21 @@ dashboard_pwd = admin
Then visit `http://[server_addr]:7500` to see the dashboard, with username and password both being `admin`.
Additionally, you can use HTTPS port by using your domains wildcard or normal SSL certificate:
```ini
[common]
dashboard_port = 7500
# dashboard's username and password are both optional
dashboard_user = admin
dashboard_pwd = admin
dashboard_tls_mode = true
dashboard_tls_cert_file = server.crt
dashboard_tls_key_file = server.key
```
Then visit `https://[server_addr]:7500` to see the dashboard in secure HTTPS connection, with username and password both being `admin`.
![dashboard](/doc/pic/dashboard.png)
### Admin UI

View File

@ -1,5 +1,8 @@
### New
* Added `route_by_http_user` in `http` and `tcpmux` proxy to support route to different clients by HTTP basic auth user.
* `CONNECT` method can be forwarded in `http` type proxy.
* Added `tcpmux_passthrough` in `tcpmux` proxy. If true, `CONNECT` request will be forwarded to frpc.
* Use auto generated certificates if `plugin_key_path` and `plugin_crt_path` are empty for plugin `https2https` and `https2http`.
* Server dashboard supports TLS configs.
### Fix
* xtcp error with IPv6 address.

View File

@ -18,9 +18,11 @@ import (
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"os"
"sort"
"strconv"
"strings"
"github.com/fatedier/frp/client/proxy"
@ -105,48 +107,48 @@ func NewProxyStatusResp(status *proxy.WorkingStatus, serverAddr string) ProxySta
switch cfg := status.Cfg.(type) {
case *config.TCPProxyConf:
if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort)
psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
}
psr.Plugin = cfg.Plugin
if status.Err != "" {
psr.RemoteAddr = fmt.Sprintf("%s:%d", serverAddr, cfg.RemotePort)
psr.RemoteAddr = net.JoinHostPort(serverAddr, strconv.Itoa(cfg.RemotePort))
} else {
psr.RemoteAddr = serverAddr + status.RemoteAddr
}
case *config.UDPProxyConf:
if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort)
psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
}
if status.Err != "" {
psr.RemoteAddr = fmt.Sprintf("%s:%d", serverAddr, cfg.RemotePort)
psr.RemoteAddr = net.JoinHostPort(serverAddr, strconv.Itoa(cfg.RemotePort))
} else {
psr.RemoteAddr = serverAddr + status.RemoteAddr
}
case *config.HTTPProxyConf:
if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort)
psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
}
psr.Plugin = cfg.Plugin
psr.RemoteAddr = status.RemoteAddr
case *config.HTTPSProxyConf:
if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort)
psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
}
psr.Plugin = cfg.Plugin
psr.RemoteAddr = status.RemoteAddr
case *config.STCPProxyConf:
if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort)
psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
}
psr.Plugin = cfg.Plugin
case *config.XTCPProxyConf:
if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort)
psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
}
psr.Plugin = cfg.Plugin
case *config.SUDPProxyConf:
if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort)
psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
}
psr.Plugin = cfg.Plugin
}

View File

@ -17,7 +17,6 @@ package proxy
import (
"bytes"
"context"
"fmt"
"io"
"net"
"strconv"
@ -307,7 +306,7 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
Sid: natHoleSidMsg.Sid,
}
raddr, _ := net.ResolveUDPAddr("udp",
fmt.Sprintf("%s:%d", pxy.clientCfg.ServerAddr, pxy.serverUDPPort))
net.JoinHostPort(pxy.clientCfg.ServerAddr, strconv.Itoa(pxy.serverUDPPort)))
clientConn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
xl.Error("dial server udp addr error: %v", err)
@ -415,7 +414,7 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
}
func (pxy *XTCPProxy) sendDetectMsg(addr string, port int, laddr *net.UDPAddr, content []byte) (err error) {
daddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", addr, port))
daddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(addr, strconv.Itoa(port)))
if err != nil {
return err
}
@ -448,7 +447,7 @@ type UDPProxy struct {
}
func (pxy *UDPProxy) Run() (err error) {
pxy.localAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pxy.cfg.LocalIP, pxy.cfg.LocalPort))
pxy.localAddr, err = net.ResolveUDPAddr("udp", net.JoinHostPort(pxy.cfg.LocalIP, strconv.Itoa(pxy.cfg.LocalPort)))
if err != nil {
return
}
@ -570,7 +569,7 @@ type SUDPProxy struct {
}
func (pxy *SUDPProxy) Run() (err error) {
pxy.localAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pxy.cfg.LocalIP, pxy.cfg.LocalPort))
pxy.localAddr, err = net.ResolveUDPAddr("udp", net.JoinHostPort(pxy.cfg.LocalIP, strconv.Itoa(pxy.cfg.LocalPort)))
if err != nil {
return
}

View File

@ -212,7 +212,7 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
}
raddr, err := net.ResolveUDPAddr("udp",
fmt.Sprintf("%s:%d", sv.ctl.clientCfg.ServerAddr, sv.ctl.serverUDPPort))
net.JoinHostPort(sv.ctl.clientCfg.ServerAddr, strconv.Itoa(sv.ctl.serverUDPPort)))
if err != nil {
xl.Error("resolve server UDP addr error")
return

View File

@ -37,31 +37,34 @@ var (
cfgFile string
showVersion bool
bindAddr string
bindPort int
bindUDPPort int
kcpBindPort int
proxyBindAddr string
vhostHTTPPort int
vhostHTTPSPort int
vhostHTTPTimeout int64
dashboardAddr string
dashboardPort int
dashboardUser string
dashboardPwd string
enablePrometheus bool
assetsDir string
logFile string
logLevel string
logMaxDays int64
disableLogColor bool
token string
subDomainHost string
tcpMux bool
allowPorts string
maxPoolCount int64
maxPortsPerClient int64
tlsOnly bool
bindAddr string
bindPort int
bindUDPPort int
kcpBindPort int
proxyBindAddr string
vhostHTTPPort int
vhostHTTPSPort int
vhostHTTPTimeout int64
dashboardAddr string
dashboardPort int
dashboardUser string
dashboardPwd string
enablePrometheus bool
assetsDir string
logFile string
logLevel string
logMaxDays int64
disableLogColor bool
token string
subDomainHost string
tcpMux bool
allowPorts string
maxPoolCount int64
maxPortsPerClient int64
tlsOnly bool
dashboardTLSMode bool
dashboardTLSCertFile string
dashboardTLSKeyFile string
)
func init() {
@ -91,6 +94,9 @@ func init() {
rootCmd.PersistentFlags().StringVarP(&allowPorts, "allow_ports", "", "", "allow ports")
rootCmd.PersistentFlags().Int64VarP(&maxPortsPerClient, "max_ports_per_client", "", 0, "max ports per client")
rootCmd.PersistentFlags().BoolVarP(&tlsOnly, "tls_only", "", false, "frps tls only")
rootCmd.PersistentFlags().BoolVarP(&dashboardTLSMode, "dashboard_tls_mode", "", false, "dashboard tls mode")
rootCmd.PersistentFlags().StringVarP(&dashboardTLSCertFile, "dashboard_tls_cert_file", "", "", "dashboard tls cert file")
rootCmd.PersistentFlags().StringVarP(&dashboardTLSKeyFile, "dashboard_tls_key_file", "", "", "dashboard tls key file")
}
var rootCmd = &cobra.Command{
@ -167,6 +173,9 @@ func parseServerCommonCfgFromCmd() (cfg config.ServerCommonConf, err error) {
cfg.DashboardUser = dashboardUser
cfg.DashboardPwd = dashboardPwd
cfg.EnablePrometheus = enablePrometheus
cfg.DashboardTLSCertFile = dashboardTLSCertFile
cfg.DashboardTLSKeyFile = dashboardTLSKeyFile
cfg.DashboardTLSMode = dashboardTLSMode
cfg.LogFile = logFile
cfg.LogLevel = logLevel
cfg.LogMaxDays = logMaxDays

View File

@ -43,6 +43,11 @@ dashboard_port = 7500
dashboard_user = admin
dashboard_pwd = admin
# dashboard TLS mode
dashboard_tls_mode = false
# dashboard_tls_cert_file = server.crt
# dashboard_tls_key_file = server.key
# enable_prometheus will export prometheus metrics on {dashboard_addr}:{dashboard_port} in /metrics api.
enable_prometheus = true

View File

@ -16,7 +16,9 @@ package config
import (
"fmt"
"net"
"reflect"
"strconv"
"strings"
"github.com/fatedier/frp/pkg/consts"
@ -372,7 +374,7 @@ func (cfg *BaseProxyConf) decorate(prefix string, name string, section *ini.Sect
}
if cfg.HealthCheckType == "http" && cfg.Plugin == "" && cfg.HealthCheckURL != "" {
s := fmt.Sprintf("http://%s:%d", cfg.LocalIP, cfg.LocalPort)
s := "http://" + net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
if !strings.HasPrefix(cfg.HealthCheckURL, "/") {
s += "/"
}

View File

@ -74,6 +74,17 @@ type ServerCommonConf struct {
// value is 0, the dashboard will not be started. By default, this value is
// 0.
DashboardPort int `ini:"dashboard_port" json:"dashboard_port" validate:"gte=0,lte=65535"`
// DashboardTLSCertFile specifies the path of the cert file that the server will
// load. If "dashboard_tls_cert_file", "dashboard_tls_key_file" are valid, the server will use this
// supplied tls configuration.
DashboardTLSCertFile string `ini:"dashboard_tls_cert_file" json:"dashboard_tls_cert_file"`
// DashboardTLSKeyFile specifies the path of the secret key that the server will
// load. If "dashboard_tls_cert_file", "dashboard_tls_key_file" are valid, the server will use this
// supplied tls configuration.
DashboardTLSKeyFile string `ini:"dashboard_tls_key_file" json:"dashboard_tls_key_file"`
// DashboardTLSMode specifies the mode of the dashboard between HTTP or HTTPS modes. By
// default, this value is false, which is HTTP mode.
DashboardTLSMode bool `ini:"dashboard_tls_mode" json:"dashboard_tls_mode"`
// DashboardUser specifies the username that the dashboard will use for
// login.
DashboardUser string `ini:"dashboard_user" json:"dashboard_user"`
@ -297,6 +308,23 @@ func (cfg *ServerCommonConf) Complete() {
}
func (cfg *ServerCommonConf) Validate() error {
if cfg.DashboardTLSMode == false {
if cfg.DashboardTLSCertFile != "" {
fmt.Println("WARNING! dashboard_tls_cert_file is invalid when dashboard_tls_mode is false")
}
if cfg.DashboardTLSKeyFile != "" {
fmt.Println("WARNING! dashboard_tls_key_file is invalid when dashboard_tls_mode is false")
}
} else {
if cfg.DashboardTLSCertFile == "" {
return fmt.Errorf("ERROR! dashboard_tls_cert_file must be specified when dashboard_tls_mode is true")
}
if cfg.DashboardTLSKeyFile == "" {
return fmt.Errorf("ERROR! dashboard_tls_cert_file must be specified when dashboard_tls_mode is true")
}
}
return validator.New().Struct(cfg)
}

View File

@ -23,6 +23,7 @@ import (
"net/http/httputil"
"strings"
"github.com/fatedier/frp/pkg/transport"
frpNet "github.com/fatedier/frp/pkg/util/net"
)
@ -58,12 +59,6 @@ func NewHTTPS2HTTPPlugin(params map[string]string) (Plugin, error) {
}
}
if crtPath == "" {
return nil, fmt.Errorf("plugin_crt_path is required")
}
if keyPath == "" {
return nil, fmt.Errorf("plugin_key_path is required")
}
if localAddr == "" {
return nil, fmt.Errorf("plugin_local_addr is required")
}
@ -96,7 +91,16 @@ func NewHTTPS2HTTPPlugin(params map[string]string) (Plugin, error) {
Handler: rp,
}
tlsConfig, err := p.genTLSConfig()
var (
tlsConfig *tls.Config
err error
)
if crtPath != "" || keyPath != "" {
tlsConfig, err = p.genTLSConfig()
} else {
tlsConfig, err = transport.NewServerTLSConfig("", "", "")
tlsConfig.InsecureSkipVerify = true
}
if err != nil {
return nil, fmt.Errorf("gen TLS config error: %v", err)
}

View File

@ -23,6 +23,7 @@ import (
"net/http/httputil"
"strings"
"github.com/fatedier/frp/pkg/transport"
frpNet "github.com/fatedier/frp/pkg/util/net"
)
@ -58,12 +59,6 @@ func NewHTTPS2HTTPSPlugin(params map[string]string) (Plugin, error) {
}
}
if crtPath == "" {
return nil, fmt.Errorf("plugin_crt_path is required")
}
if keyPath == "" {
return nil, fmt.Errorf("plugin_key_path is required")
}
if localAddr == "" {
return nil, fmt.Errorf("plugin_local_addr is required")
}
@ -101,7 +96,16 @@ func NewHTTPS2HTTPSPlugin(params map[string]string) (Plugin, error) {
Handler: rp,
}
tlsConfig, err := p.genTLSConfig()
var (
tlsConfig *tls.Config
err error
)
if crtPath != "" || keyPath != "" {
tlsConfig, err = p.genTLSConfig()
} else {
tlsConfig, err = transport.NewServerTLSConfig("", "", "")
tlsConfig.InsecureSkipVerify = true
}
if err != nil {
return nil, fmt.Errorf("gen TLS config error: %v", err)
}
@ -127,7 +131,7 @@ func (p *HTTPS2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, e
}
func (p *HTTPS2HTTPSPlugin) Name() string {
return PluginHTTPS2HTTP
return PluginHTTPS2HTTPS
}
func (p *HTTPS2HTTPSPlugin) Close() error {

View File

@ -19,7 +19,7 @@ import (
"strings"
)
var version string = "0.43.0"
var version string = "0.44.0"
func Full() string {
return version

View File

@ -15,6 +15,7 @@
package server
import (
"crypto/tls"
"net"
"net/http"
"net/http/pprof"
@ -76,14 +77,21 @@ func (svr *Service) RunDashboardServer(address string) (err error) {
ReadTimeout: httpServerReadTimeout,
WriteTimeout: httpServerWriteTimeout,
}
if address == "" || address == ":" {
address = ":http"
}
ln, err := net.Listen("tcp", address)
if err != nil {
return err
}
if svr.cfg.DashboardTLSMode {
cert, err := tls.LoadX509KeyPair(svr.cfg.DashboardTLSCertFile, svr.cfg.DashboardTLSKeyFile)
if err != nil {
return err
}
tlsCfg := &tls.Config{
Certificates: []tls.Certificate{cert},
}
ln = tls.NewListener(ln, tlsCfg)
}
go server.Serve(ln)
return
}