Browse Source

Server Dashboard SSL Support (#2982)

EMRE ÇELİK 2 years ago
parent
commit
218b354f82
5 changed files with 93 additions and 28 deletions
  1. 15 0
      README.md
  2. 34 25
      cmd/frps/root.go
  3. 5 0
      conf/frps_full.ini
  4. 28 0
      pkg/config/server.go
  5. 11 3
      server/dashboard.go

+ 15 - 0
README.md

@@ -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

+ 34 - 25
cmd/frps/root.go

@@ -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

+ 5 - 0
conf/frps_full.ini

@@ -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
 

+ 28 - 0
pkg/config/server.go

@@ -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)
 }
 

+ 11 - 3
server/dashboard.go

@@ -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
 }