Merge pull request #40 from fatedier/fatedier/privilege_mode

support privilege mode
This commit is contained in:
fatedier 2016-06-26 22:43:02 +08:00 committed by GitHub
commit 1bad5c6561
13 changed files with 252 additions and 53 deletions

View File

@ -9,6 +9,8 @@ log_level = info
log_max_days = 3
# for authentication
auth_token = 123
# for privilege mode
privilege_key = 12345678
# ssh is the proxy name same as server's configuration
[ssh]
@ -32,3 +34,21 @@ use_gzip = true
type = http
local_ip = 127.0.0.1
local_port = 8000
[privilege_ssh]
# if privilege_mode is enabled, this proxy will be created automatically
privilege_mode = true
type = tcp
local_ip = 127.0.0.1
local_port = 22
use_encryption = true
use_gzip = false
remote_port = 6001
[privilege_web]
privilege_mode = true
type = http
local_ip = 127.0.0.1
local_port = 80
use_gzip = true
custom_domains = web03.yourdomain.com

View File

@ -12,6 +12,9 @@ log_file = ./frps.log
# debug, info, warn, error
log_level = info
log_max_days = 3
# if you enable privilege mode, frpc can create a proxy without pre-configure in frps when privilege_key is correct
privilege_mode = true
privilege_key = 12345678
# ssh is the proxy name, client will use this name and auth_token to connect to server
[ssh]

View File

@ -144,8 +144,15 @@ func loginToServer(cli *client.ProxyClient) (c *conn.Conn, err error) {
AuthKey: authKey,
UseEncryption: cli.UseEncryption,
UseGzip: cli.UseGzip,
PrivilegeMode: cli.PrivilegeMode,
ProxyType: cli.Type,
Timestamp: nowTime,
}
if cli.PrivilegeMode {
req.RemotePort = cli.RemotePort
req.CustomDomains = cli.CustomDomains
}
buf, _ := json.Marshal(req)
err = c.Write(string(buf) + "\n")
if err != nil {

View File

@ -194,16 +194,43 @@ func msgSender(s *server.ProxyServer, c *conn.Conn, msgSendChan chan interface{}
// if success, ret equals 0, otherwise greater than 0
func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
ret = 1
// check if proxy name exist
s, ok := server.ProxyServers[req.ProxyName]
if req.PrivilegeMode && !server.PrivilegeMode {
info = fmt.Sprintf("ProxyName [%s], PrivilegeMode is disabled in frps", req.ProxyName)
log.Warn("info")
return
}
var (
s *server.ProxyServer
ok bool
)
s, ok = server.ProxyServers[req.ProxyName]
if req.PrivilegeMode && req.Type == consts.NewCtlConn {
log.Debug("ProxyName [%s], doLogin and privilege mode is enabled", req.ProxyName)
} else {
if !ok {
info = fmt.Sprintf("ProxyName [%s] is not exist", req.ProxyName)
log.Warn(info)
return
}
}
// check authKey
// check authKey or privilegeKey
nowTime := time.Now().Unix()
if req.PrivilegeMode {
privilegeKey := pcrypto.GetAuthKey(req.ProxyName + server.PrivilegeKey + fmt.Sprintf("%d", req.Timestamp))
// privilegeKey avaiable in 15 minutes
if nowTime-req.Timestamp > 15*60 {
info = fmt.Sprintf("ProxyName [%s], privilege mode authorization timeout", req.ProxyName)
log.Warn(info)
return
} else if req.AuthKey != privilegeKey {
log.Debug("%s %s", req.AuthKey, privilegeKey)
info = fmt.Sprintf("ProxyName [%s], privilege mode authorization failed", req.ProxyName)
log.Warn(info)
return
}
} else {
authKey := pcrypto.GetAuthKey(req.ProxyName + s.AuthToken + fmt.Sprintf("%d", req.Timestamp))
// authKey avaiable in 15 minutes
if nowTime-req.Timestamp > 15*60 {
@ -215,9 +242,20 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
log.Warn(info)
return
}
}
// control conn
if req.Type == consts.NewCtlConn {
if req.PrivilegeMode {
s = server.NewProxyServerFromCtlMsg(req)
err := server.CreateProxy(s)
if err != nil {
info = fmt.Sprintf("ProxyName [%s], %v", req.ProxyName, err)
log.Warn(info)
return
}
}
if s.Status == consts.Working {
info = fmt.Sprintf("ProxyName [%s], already in use", req.ProxyName)
log.Warn(info)
@ -248,6 +286,9 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
return
}
log.Info("ProxyName [%s], start proxy success", req.ProxyName)
if req.PrivilegeMode {
log.Info("ProxyName [%s], created by PrivilegeMode", req.ProxyName)
}
} else if req.Type == consts.NewWorkConn {
// work conn
if s.Status != consts.Working {

View File

@ -172,5 +172,8 @@ func main() {
}
log.Info("Start frps success")
if server.PrivilegeMode == true {
log.Info("PrivilegeMode is enabled, you should pay more attention to security issues")
}
ProcessControlConn(l)
}

View File

@ -31,6 +31,9 @@ type ProxyClient struct {
config.BaseConf
LocalIp string
LocalPort int64
RemotePort int64
CustomDomains []string
}
func (p *ProxyClient) GetLocalConn() (c *conn.Conn, err error) {
@ -60,6 +63,7 @@ func (p *ProxyClient) GetRemoteConn(addr string, port int64) (c *conn.Conn, err
Type: consts.NewWorkConn,
ProxyName: p.Name,
AuthKey: authKey,
PrivilegeMode: p.PrivilegeMode,
Timestamp: nowTime,
}

View File

@ -17,6 +17,7 @@ package client
import (
"fmt"
"strconv"
"strings"
ini "github.com/vaughan0/go-ini"
)
@ -29,6 +30,7 @@ var (
LogWay string = "console"
LogLevel string = "info"
LogMaxDays int64 = 3
PrivilegeKey string = ""
HeartBeatInterval int64 = 20
HeartBeatTimeout int64 = 90
)
@ -75,12 +77,15 @@ func LoadConf(confFile string) (err error) {
LogMaxDays, _ = strconv.ParseInt(tmpStr, 10, 64)
}
tmpStr, ok = conf.Get("common", "privilege_key")
if ok {
PrivilegeKey = tmpStr
}
var authToken string
tmpStr, ok = conf.Get("common", "auth_token")
if ok {
authToken = tmpStr
} else {
return fmt.Errorf("auth_token not found")
}
// proxies
@ -90,9 +95,6 @@ func LoadConf(confFile string) (err error) {
// name
proxyClient.Name = name
// auth_token
proxyClient.AuthToken = authToken
// local_ip
proxyClient.LocalIp, ok = section["local_ip"]
if !ok {
@ -101,46 +103,101 @@ func LoadConf(confFile string) (err error) {
}
// local_port
portStr, ok := section["local_port"]
tmpStr, ok = section["local_port"]
if ok {
proxyClient.LocalPort, err = strconv.ParseInt(portStr, 10, 64)
proxyClient.LocalPort, err = strconv.ParseInt(tmpStr, 10, 64)
if err != nil {
return fmt.Errorf("Parse ini file error: proxy [%s] local_port error", proxyClient.Name)
return fmt.Errorf("Parse conf error: proxy [%s] local_port error", proxyClient.Name)
}
} else {
return fmt.Errorf("Parse ini file error: proxy [%s] local_port not found", proxyClient.Name)
return fmt.Errorf("Parse conf error: proxy [%s] local_port not found", proxyClient.Name)
}
// type
proxyClient.Type = "tcp"
typeStr, ok := section["type"]
tmpStr, ok = section["type"]
if ok {
if typeStr != "tcp" && typeStr != "http" && typeStr != "https" {
return fmt.Errorf("Parse ini file error: proxy [%s] type error", proxyClient.Name)
if tmpStr != "tcp" && tmpStr != "http" && tmpStr != "https" {
return fmt.Errorf("Parse conf error: proxy [%s] type error", proxyClient.Name)
}
proxyClient.Type = typeStr
proxyClient.Type = tmpStr
}
// use_encryption
proxyClient.UseEncryption = false
useEncryptionStr, ok := section["use_encryption"]
if ok && useEncryptionStr == "true" {
tmpStr, ok = section["use_encryption"]
if ok && tmpStr == "true" {
proxyClient.UseEncryption = true
}
// use_gzip
proxyClient.UseGzip = false
useGzipStr, ok := section["use_gzip"]
if ok && useGzipStr == "true" {
tmpStr, ok = section["use_gzip"]
if ok && tmpStr == "true" {
proxyClient.UseGzip = true
}
// privilege_mode
proxyClient.PrivilegeMode = false
tmpStr, ok = section["privilege_mode"]
if ok && tmpStr == "true" {
proxyClient.PrivilegeMode = true
}
// configures used in privilege mode
if proxyClient.PrivilegeMode == true {
// auth_token
proxyClient.AuthToken = PrivilegeKey
if proxyClient.Type == "tcp" {
// remote_port
tmpStr, ok = section["remote_port"]
if ok {
proxyClient.RemotePort, err = strconv.ParseInt(tmpStr, 10, 64)
if err != nil {
return fmt.Errorf("Parse conf error: proxy [%s] remote_port error", proxyClient.Name)
}
} else {
return fmt.Errorf("Parse conf error: proxy [%s] remote_port not found", proxyClient.Name)
}
} else if proxyClient.Type == "http" {
domainStr, ok := section["custom_domains"]
if ok {
proxyClient.CustomDomains = strings.Split(domainStr, ",")
if len(proxyClient.CustomDomains) == 0 {
return fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals http", proxyClient.Name)
}
for i, domain := range proxyClient.CustomDomains {
proxyClient.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain))
}
} else {
return fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals http", proxyClient.Name)
}
} else if proxyClient.Type == "https" {
domainStr, ok := section["custom_domains"]
if ok {
proxyClient.CustomDomains = strings.Split(domainStr, ",")
if len(proxyClient.CustomDomains) == 0 {
return fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals https", proxyClient.Name)
}
for i, domain := range proxyClient.CustomDomains {
proxyClient.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain))
}
} else {
return fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals http", proxyClient.Name)
}
}
} else /* proxyClient.PrivilegeMode == false */ {
// authToken
proxyClient.AuthToken = authToken
}
ProxyClients[proxyClient.Name] = proxyClient
}
}
if len(ProxyClients) == 0 {
return fmt.Errorf("Parse ini file error: no proxy config found")
return fmt.Errorf("Parse conf error: no proxy config found")
}
return nil

View File

@ -20,4 +20,5 @@ type BaseConf struct {
Type string
UseEncryption bool
UseGzip bool
PrivilegeMode bool
}

View File

@ -22,11 +22,17 @@ type GeneralRes struct {
// messages between control connections of frpc and frps
type ControlReq struct {
Type int64 `json:"type"`
ProxyName string `json:"proxy_name,omitempty"`
AuthKey string `json:"auth_key, omitempty"`
UseEncryption bool `json:"use_encryption, omitempty"`
UseGzip bool `json:"use_gzip, omitempty"`
Timestamp int64 `json:"timestamp, omitempty"`
ProxyName string `json:"proxy_name"`
AuthKey string `json:"auth_key"`
UseEncryption bool `json:"use_encryption"`
UseGzip bool `json:"use_gzip"`
// configures used if privilege_mode is enabled
PrivilegeMode bool `json:"privilege_mode"`
ProxyType string `json:"proxy_type"`
RemotePort int64 `json:"remote_port"`
CustomDomains []string `json:"custom_domains, omitempty"`
Timestamp int64 `json:"timestamp"`
}
type ControlRes struct {

View File

@ -141,7 +141,6 @@ func pipeDecrypt(r net.Conn, w net.Conn, conf config.BaseConf) (err error) {
}
// gzip
if conf.UseGzip {
log.Warn("%x", res)
res, err = laes.Decompression(res)
if err != nil {
log.Warn("ProxyName [%s], decompression error, %v", conf.Name, err)

View File

@ -22,6 +22,7 @@ import (
ini "github.com/vaughan0/go-ini"
"frp/models/consts"
"frp/utils/log"
"frp/utils/vhost"
)
@ -38,6 +39,8 @@ var (
LogWay string = "console" // console or file
LogLevel string = "info"
LogMaxDays int64 = 3
PrivilegeMode bool = false
PrivilegeKey string = ""
HeartBeatTimeout int64 = 90
UserConnTimeout int64 = 10
@ -132,6 +135,22 @@ func loadCommonConf(confFile string) error {
LogMaxDays = v
}
}
tmpStr, ok = conf.Get("common", "privilege_mode")
if ok {
if tmpStr == "true" {
PrivilegeMode = true
}
}
if PrivilegeMode == true {
tmpStr, ok = conf.Get("common", "privilege_key")
if ok {
PrivilegeKey = tmpStr
} else {
return fmt.Errorf("Parse conf error: privilege_key must be set if privilege_mode is enabled")
}
}
return nil
}
@ -189,6 +208,8 @@ func loadProxyConf(confFile string) (proxyServers map[string]*ProxyServer, err e
for i, domain := range proxyServer.CustomDomains {
proxyServer.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain))
}
} else {
return proxyServers, fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals http", proxyServer.Name)
}
} else if proxyServer.Type == "https" {
// for https
@ -201,6 +222,8 @@ func loadProxyConf(confFile string) (proxyServers map[string]*ProxyServer, err e
for i, domain := range proxyServer.CustomDomains {
proxyServer.CustomDomains[i] = strings.ToLower(strings.TrimSpace(domain))
}
} else {
return proxyServers, fmt.Errorf("Parse conf error: proxy [%s] custom_domains must be set when type equals https", proxyServer.Name)
}
}
proxyServers[proxyServer.Name] = proxyServer
@ -234,14 +257,37 @@ func ReloadConf(confFile string) (err error) {
}
}
// proxies created by PrivilegeMode won't be deleted
for name, oldProxyServer := range ProxyServers {
_, ok := loadProxyServers[name]
if !ok {
if !oldProxyServer.PrivilegeMode {
oldProxyServer.Close()
delete(ProxyServers, name)
log.Info("ProxyName [%s] deleted, close it", name)
} else {
log.Info("ProxyName [%s] created by PrivilegeMode, won't be closed", name)
}
}
}
ProxyServersMutex.Unlock()
return nil
}
func CreateProxy(s *ProxyServer) error {
ProxyServersMutex.Lock()
defer ProxyServersMutex.Unlock()
oldServer, ok := ProxyServers[s.Name]
if ok {
if oldServer.Status == consts.Working {
return fmt.Errorf("this proxy is already working now")
}
oldServer.Close()
if oldServer.PrivilegeMode {
delete(ProxyServers, s.Name)
}
}
s.Init()
ProxyServers[s.Name] = s
return nil
}

View File

@ -52,6 +52,20 @@ func NewProxyServer() (p *ProxyServer) {
return p
}
func NewProxyServerFromCtlMsg(req *msg.ControlReq) (p *ProxyServer) {
p = &ProxyServer{}
p.Name = req.ProxyName
p.Type = req.ProxyType
p.UseEncryption = req.UseEncryption
p.UseGzip = req.UseGzip
p.PrivilegeMode = req.PrivilegeMode
p.BindAddr = BindAddr
p.ListenPort = req.RemotePort
p.CustomDomains = req.CustomDomains
p.AuthToken = PrivilegeKey
return
}
func (p *ProxyServer) Init() {
p.Lock()
p.Status = consts.Idle
@ -161,13 +175,11 @@ func (p *ProxyServer) Close() {
p.Lock()
if p.Status != consts.Closed {
p.Status = consts.Closed
if len(p.listeners) != 0 {
for _, l := range p.listeners {
if l != nil {
l.Close()
}
}
}
close(p.ctlMsgChan)
close(p.workConnChan)
if p.CtlConn != nil {

View File

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