From 9a260cb8aa62bfd89e192092959f51ee5168d16c Mon Sep 17 00:00:00 2001 From: Damon Zhao Date: Sun, 14 Aug 2016 12:16:38 +0800 Subject: [PATCH] Add basic auth to dashboard --- README.md | 5 +++- README_zh.md | 5 +++- conf/frps.ini | 3 +++ src/models/server/config.go | 38 +++++++++++++++++++---------- src/models/server/dashboard.go | 44 +++++++++++++++++++++++++++++++++- 5 files changed, 79 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 638f1578..43c0286c 100644 --- a/README.md +++ b/README.md @@ -149,9 +149,12 @@ Configure a port for dashboard to enable this feature: ```ini [common] dashboard_port = 7500 +# dashboard's username and password are both optional,if not set, default is admin. +dashboard_username = abc +dashboard_password = abc ``` -Then visit `http://[server_addr]:7500` to see dashboard. +Then visit `http://[server_addr]:7500` to see dashboard, default username and password are both `admin`. ![dashboard](/doc/pic/dashboard.png) diff --git a/README_zh.md b/README_zh.md index 364592f4..c602a378 100644 --- a/README_zh.md +++ b/README_zh.md @@ -146,9 +146,12 @@ frp 目前正在前期开发阶段,master 分支用于发布稳定版本,dev ```ini [common] dashboard_port = 7500 +# dashboard 用户名密码可选,默认都为 admin +dashboard_username = abc +dashboard_password = abc ``` -打开浏览器通过 `http://[server_addr]:7500` 访问 dashboard 界面。 +打开浏览器通过 `http://[server_addr]:7500` 访问 dashboard 界面,用户名密码默认为 `admin`。 ![dashboard](/doc/pic/dashboard.png) diff --git a/conf/frps.ini b/conf/frps.ini index 4a6d47f8..6163bc98 100644 --- a/conf/frps.ini +++ b/conf/frps.ini @@ -9,6 +9,9 @@ vhost_http_port = 80 vhost_https_port = 443 # if you want to configure or reload frps by dashboard, dashboard_port must be set dashboard_port = 7500 +# dashboard username and password for basic protect, if not set, both default value is admin +dashboard_username = abc +dashboard_password = abc # dashboard assets directory(only for debug mode) # assets_dir = ./static # console or real logFile path like ./frps.log diff --git a/src/models/server/config.go b/src/models/server/config.go index e3296d05..58ae0da3 100644 --- a/src/models/server/config.go +++ b/src/models/server/config.go @@ -30,19 +30,21 @@ import ( // common config var ( - ConfigFile string = "./frps.ini" - BindAddr string = "0.0.0.0" - BindPort int64 = 7000 - VhostHttpPort int64 = 0 // if VhostHttpPort equals 0, don't listen a public port for http protocol - VhostHttpsPort int64 = 0 // if VhostHttpsPort equals 0, don't listen a public port for https protocol - DashboardPort int64 = 0 // if DashboardPort equals 0, dashboard is not available - AssetsDir string = "" - LogFile string = "console" - LogWay string = "console" // console or file - LogLevel string = "info" - LogMaxDays int64 = 3 - PrivilegeMode bool = false - PrivilegeToken string = "" + ConfigFile string = "./frps.ini" + BindAddr string = "0.0.0.0" + BindPort int64 = 7000 + VhostHttpPort int64 = 0 // if VhostHttpPort equals 0, don't listen a public port for http protocol + VhostHttpsPort int64 = 0 // if VhostHttpsPort equals 0, don't listen a public port for https protocol + DashboardPort int64 = 0 // if DashboardPort equals 0, dashboard is not available + DashboardUsername string = "admin" + DashboardPassword string = "admin" + AssetsDir string = "" + LogFile string = "console" + LogWay string = "console" // console or file + LogLevel string = "info" + LogMaxDays int64 = 3 + PrivilegeMode bool = false + PrivilegeToken string = "" // if PrivilegeAllowPorts is not nil, tcp proxies which remote port exist in this map can be connected PrivilegeAllowPorts map[int64]struct{} @@ -119,6 +121,16 @@ func loadCommonConf(confFile string) error { DashboardPort = 0 } + tmpStr, ok = conf.Get("common", "dashboard_username") + if ok { + DashboardUsername = tmpStr + } + + tmpStr, ok = conf.Get("common", "dashboard_password") + if ok { + DashboardPassword = tmpStr + } + tmpStr, ok = conf.Get("common", "assets_dir") if ok { AssetsDir = tmpStr diff --git a/src/models/server/dashboard.go b/src/models/server/dashboard.go index 4be7a6de..8cb201b8 100644 --- a/src/models/server/dashboard.go +++ b/src/models/server/dashboard.go @@ -15,9 +15,11 @@ package server import ( + "encoding/base64" "fmt" "net" "net/http" + "strings" "time" "github.com/fatedier/frp/src/assets" @@ -38,7 +40,7 @@ func RunDashboardServer(addr string, port int64) (err error) { // view, see dashboard_view.go mux.Handle("/favicon.ico", http.FileServer(assets.FileSystem)) mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(assets.FileSystem))) - mux.HandleFunc("/", viewDashboard) + mux.HandleFunc("/", use(viewDashboard, basicAuth)) address := fmt.Sprintf("%s:%d", addr, port) server := &http.Server{ @@ -58,3 +60,43 @@ func RunDashboardServer(addr string, port int64) (err error) { go server.Serve(ln) return } + +func use(h http.HandlerFunc, middleware ...func(http.HandlerFunc) http.HandlerFunc) http.HandlerFunc { + for _, m := range middleware { + h = m(h) + } + + return h +} + +func basicAuth(h http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) + + s := strings.SplitN(r.Header.Get("Authorization"), " ", 2) + if len(s) != 2 { + http.Error(w, "Not authorized", 401) + return + } + + b, err := base64.StdEncoding.DecodeString(s[1]) + if err != nil { + http.Error(w, err.Error(), 401) + return + } + + pair := strings.SplitN(string(b), ":", 2) + if len(pair) != 2 { + http.Error(w, "Not authorized", 401) + return + } + + if pair[0] != DashboardUsername || pair[1] != DashboardPassword { + http.Error(w, "Not authorized", 401) + return + } + + h.ServeHTTP(w, r) + } +}