From 4dadaac905420bbf7329e1835123a64d3133deef Mon Sep 17 00:00:00 2001 From: Eric Larssen Date: Sat, 13 Aug 2016 09:32:11 -0500 Subject: [PATCH] Add basic auth to dashboard --- README.md | 19 ++++++++------- README_zh.md | 5 +++- conf/frps.ini | 3 +++ src/models/server/config.go | 38 +++++++++++++++++++---------- src/models/server/dashboard.go | 44 +++++++++++++++++++++++++++++++++- 5 files changed, 86 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index a46fdc8e..43c0286c 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ ## What is frp? -frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet.Now, it supports tcp, http and https protocol when requests can be forwarded by domains to backward web services. +frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet. Now, it supports tcp, http and https protocol when requests can be forwarded by domains to backward web services. ## Catalog * [What can I do with frp?](#what-can-i-do-with-frp) * [Status](#status) * [Architecture](#architecture) -* [Example Usage](#example-usage) +* [Example Usage](#example-usage) * [Communicate with your computer in LAN by SSH](#communicate-with-your-computer-in-lan-by-ssh) * [Visit your web service in LAN by custom domains](#visit-your-web-service-in-lan-by-custom-domains) * [Features](#features) @@ -37,9 +37,9 @@ frp is a fast reverse proxy to help you expose a local server behind a NAT or fi ## Status -frp is under development and you can try it with latest release version.Master branch for releasing stable version when dev branch for developing. +frp is under development and you can try it with latest release version. Master branch for releasing stable version when dev branch for developing. -**We may change any protocol and can't promise backward compatible.Please check the release log when upgrading.** +**We may change any protocol and can't promise backward compatible. Please check the release log when upgrading.** ## Architecture @@ -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) @@ -286,9 +289,9 @@ This feature is fit for a large number of short connections. 2. Enable and specify the number of connection pool: - ```ini + ```ini # frpc.ini - [ssh] + [ssh] type = tcp local_port = 22 pool_count = 10 @@ -300,7 +303,7 @@ When forwarding to a local port, frp does not modify the tunneled HTTP requests ```ini # frpc.ini -[web] +[web] privilege_mode = true type = http local_port = 80 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) + } +}