mirror of https://github.com/fatedier/frp.git
parent
a821db3f45
commit
3621aad1c1
@ -0,0 +1,12 @@
|
||||
So far, there is no mature Go project that does well in parsing `*.ini` files.
|
||||
|
||||
By comparison, we have selected an open source project: `https://github.com/go-ini/ini`.
|
||||
|
||||
This library helped us solve most of the key-value matching, but there are still some problems, such as not supporting parsing `map`.
|
||||
|
||||
We add our own logic on the basis of this library. In the current situationwhich, we need to complete the entire `Unmarshal` in two steps:
|
||||
|
||||
* Step#1, use `go-ini` to complete the basic parameter matching;
|
||||
* Step#2, parse our custom parameters to realize parsing special structure, like `map`, `array`.
|
||||
|
||||
Some of the keywords in `tag`(like inline, extends, etc.) may be different from standard libraries such as `json` and `protobuf` in Go. For details, please refer to the library documentation: https://ini.unknwon.io/docs/intro.
|
@ -0,0 +1,643 @@
|
||||
// Copyright 2020 The frp Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/fatedier/frp/pkg/auth"
|
||||
"github.com/fatedier/frp/pkg/consts"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
testUser = "test"
|
||||
)
|
||||
|
||||
var (
|
||||
testClientBytesWithFull = []byte(`
|
||||
# [common] is integral section
|
||||
[common]
|
||||
server_addr = 0.0.0.9
|
||||
server_port = 7009
|
||||
http_proxy = http://user:passwd@192.168.1.128:8080
|
||||
log_file = ./frpc.log9
|
||||
log_way = file
|
||||
log_level = info9
|
||||
log_max_days = 39
|
||||
disable_log_color = false
|
||||
authenticate_heartbeats = false
|
||||
authenticate_new_work_conns = false
|
||||
token = 12345678
|
||||
oidc_client_id = client-id
|
||||
oidc_client_secret = client-secret
|
||||
oidc_audience = audience
|
||||
oidc_token_endpoint_url = endpoint_url
|
||||
admin_addr = 127.0.0.9
|
||||
admin_port = 7409
|
||||
admin_user = admin9
|
||||
admin_pwd = admin9
|
||||
assets_dir = ./static9
|
||||
pool_count = 59
|
||||
tcp_mux
|
||||
user = your_name
|
||||
login_fail_exit
|
||||
protocol = tcp
|
||||
tls_enable = true
|
||||
tls_cert_file = client.crt
|
||||
tls_key_file = client.key
|
||||
tls_trusted_ca_file = ca.crt
|
||||
dns_server = 8.8.8.9
|
||||
start = ssh,dns
|
||||
heartbeat_interval = 39
|
||||
heartbeat_timeout = 99
|
||||
meta_var1 = 123
|
||||
meta_var2 = 234
|
||||
udp_packet_size = 1509
|
||||
|
||||
# all proxy
|
||||
[ssh]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.9
|
||||
local_port = 29
|
||||
bandwidth_limit = 19MB
|
||||
use_encryption
|
||||
use_compression
|
||||
remote_port = 6009
|
||||
group = test_group
|
||||
group_key = 123456
|
||||
health_check_type = tcp
|
||||
health_check_timeout_s = 3
|
||||
health_check_max_failed = 3
|
||||
health_check_interval_s = 19
|
||||
meta_var1 = 123
|
||||
meta_var2 = 234
|
||||
|
||||
[ssh_random]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.9
|
||||
local_port = 29
|
||||
remote_port = 9
|
||||
|
||||
[range:tcp_port]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.9
|
||||
local_port = 6010-6011,6019
|
||||
remote_port = 6010-6011,6019
|
||||
use_encryption = false
|
||||
use_compression = false
|
||||
|
||||
[dns]
|
||||
type = udp
|
||||
local_ip = 114.114.114.114
|
||||
local_port = 59
|
||||
remote_port = 6009
|
||||
use_encryption
|
||||
use_compression
|
||||
|
||||
[range:udp_port]
|
||||
type = udp
|
||||
local_ip = 114.114.114.114
|
||||
local_port = 6000,6010-6011
|
||||
remote_port = 6000,6010-6011
|
||||
use_encryption
|
||||
use_compression
|
||||
|
||||
[web01]
|
||||
type = http
|
||||
local_ip = 127.0.0.9
|
||||
local_port = 89
|
||||
use_encryption
|
||||
use_compression
|
||||
http_user = admin
|
||||
http_pwd = admin
|
||||
subdomain = web01
|
||||
custom_domains = web02.yourdomain.com
|
||||
locations = /,/pic
|
||||
host_header_rewrite = example.com
|
||||
header_X-From-Where = frp
|
||||
health_check_type = http
|
||||
health_check_url = /status
|
||||
health_check_interval_s = 19
|
||||
health_check_max_failed = 3
|
||||
health_check_timeout_s = 3
|
||||
|
||||
[web02]
|
||||
type = https
|
||||
local_ip = 127.0.0.9
|
||||
local_port = 8009
|
||||
use_encryption
|
||||
use_compression
|
||||
subdomain = web01
|
||||
custom_domains = web02.yourdomain.com
|
||||
proxy_protocol_version = v2
|
||||
|
||||
[secret_tcp]
|
||||
type = stcp
|
||||
sk = abcdefg
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 22
|
||||
use_encryption = false
|
||||
use_compression = false
|
||||
|
||||
[p2p_tcp]
|
||||
type = xtcp
|
||||
sk = abcdefg
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 22
|
||||
use_encryption = false
|
||||
use_compression = false
|
||||
|
||||
[tcpmuxhttpconnect]
|
||||
type = tcpmux
|
||||
multiplexer = httpconnect
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10701
|
||||
custom_domains = tunnel1
|
||||
|
||||
[plugin_unix_domain_socket]
|
||||
type = tcp
|
||||
remote_port = 6003
|
||||
plugin = unix_domain_socket
|
||||
plugin_unix_path = /var/run/docker.sock
|
||||
|
||||
[plugin_http_proxy]
|
||||
type = tcp
|
||||
remote_port = 6004
|
||||
plugin = http_proxy
|
||||
plugin_http_user = abc
|
||||
plugin_http_passwd = abc
|
||||
|
||||
[plugin_socks5]
|
||||
type = tcp
|
||||
remote_port = 6005
|
||||
plugin = socks5
|
||||
plugin_user = abc
|
||||
plugin_passwd = abc
|
||||
|
||||
[plugin_static_file]
|
||||
type = tcp
|
||||
remote_port = 6006
|
||||
plugin = static_file
|
||||
plugin_local_path = /var/www/blog
|
||||
plugin_strip_prefix = static
|
||||
plugin_http_user = abc
|
||||
plugin_http_passwd = abc
|
||||
|
||||
[plugin_https2http]
|
||||
type = https
|
||||
custom_domains = test.yourdomain.com
|
||||
plugin = https2http
|
||||
plugin_local_addr = 127.0.0.1:80
|
||||
plugin_crt_path = ./server.crt
|
||||
plugin_key_path = ./server.key
|
||||
plugin_host_header_rewrite = 127.0.0.1
|
||||
plugin_header_X-From-Where = frp
|
||||
|
||||
[plugin_http2https]
|
||||
type = http
|
||||
custom_domains = test.yourdomain.com
|
||||
plugin = http2https
|
||||
plugin_local_addr = 127.0.0.1:443
|
||||
plugin_host_header_rewrite = 127.0.0.1
|
||||
plugin_header_X-From-Where = frp
|
||||
|
||||
# visitor
|
||||
[secret_tcp_visitor]
|
||||
role = visitor
|
||||
type = stcp
|
||||
server_name = secret_tcp
|
||||
sk = abcdefg
|
||||
bind_addr = 127.0.0.1
|
||||
bind_port = 9000
|
||||
use_encryption = false
|
||||
use_compression = false
|
||||
|
||||
[p2p_tcp_visitor]
|
||||
role = visitor
|
||||
type = xtcp
|
||||
server_name = p2p_tcp
|
||||
sk = abcdefg
|
||||
bind_addr = 127.0.0.1
|
||||
bind_port = 9001
|
||||
use_encryption = false
|
||||
use_compression = false
|
||||
`)
|
||||
)
|
||||
|
||||
func Test_LoadClientCommonConf(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
expected := ClientCommonConf{
|
||||
ClientConfig: auth.ClientConfig{
|
||||
BaseConfig: auth.BaseConfig{
|
||||
AuthenticationMethod: "token",
|
||||
AuthenticateHeartBeats: false,
|
||||
AuthenticateNewWorkConns: false,
|
||||
},
|
||||
TokenConfig: auth.TokenConfig{
|
||||
Token: "12345678",
|
||||
},
|
||||
OidcClientConfig: auth.OidcClientConfig{
|
||||
OidcClientID: "client-id",
|
||||
OidcClientSecret: "client-secret",
|
||||
OidcAudience: "audience",
|
||||
OidcTokenEndpointURL: "endpoint_url",
|
||||
},
|
||||
},
|
||||
ServerAddr: "0.0.0.9",
|
||||
ServerPort: 7009,
|
||||
HTTPProxy: "http://user:passwd@192.168.1.128:8080",
|
||||
LogFile: "./frpc.log9",
|
||||
LogWay: "file",
|
||||
LogLevel: "info9",
|
||||
LogMaxDays: 39,
|
||||
DisableLogColor: false,
|
||||
AdminAddr: "127.0.0.9",
|
||||
AdminPort: 7409,
|
||||
AdminUser: "admin9",
|
||||
AdminPwd: "admin9",
|
||||
AssetsDir: "./static9",
|
||||
PoolCount: 59,
|
||||
TCPMux: true,
|
||||
User: "your_name",
|
||||
LoginFailExit: true,
|
||||
Protocol: "tcp",
|
||||
TLSEnable: true,
|
||||
TLSCertFile: "client.crt",
|
||||
TLSKeyFile: "client.key",
|
||||
TLSTrustedCaFile: "ca.crt",
|
||||
DNSServer: "8.8.8.9",
|
||||
Start: []string{"ssh", "dns"},
|
||||
HeartbeatInterval: 39,
|
||||
HeartbeatTimeout: 99,
|
||||
Metas: map[string]string{
|
||||
"var1": "123",
|
||||
"var2": "234",
|
||||
},
|
||||
UDPPacketSize: 1509,
|
||||
}
|
||||
|
||||
common, err := UnmarshalClientConfFromIni(testClientBytesWithFull)
|
||||
assert.NoError(err)
|
||||
assert.Equal(expected, common)
|
||||
}
|
||||
|
||||
func Test_LoadClientBasicConf(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
proxyExpected := map[string]ProxyConf{
|
||||
testUser + ".ssh": &TCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".ssh",
|
||||
ProxyType: consts.TCPProxy,
|
||||
UseCompression: true,
|
||||
UseEncryption: true,
|
||||
Group: "test_group",
|
||||
GroupKey: "123456",
|
||||
BandwidthLimit: MustBandwidthQuantity("19MB"),
|
||||
Metas: map[string]string{
|
||||
"var1": "123",
|
||||
"var2": "234",
|
||||
},
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.9",
|
||||
LocalPort: 29,
|
||||
},
|
||||
HealthCheckConf: HealthCheckConf{
|
||||
HealthCheckType: consts.TCPProxy,
|
||||
HealthCheckTimeoutS: 3,
|
||||
HealthCheckMaxFailed: 3,
|
||||
HealthCheckIntervalS: 19,
|
||||
HealthCheckAddr: "127.0.0.9:29",
|
||||
},
|
||||
},
|
||||
RemotePort: 6009,
|
||||
},
|
||||
testUser + ".ssh_random": &TCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".ssh_random",
|
||||
ProxyType: consts.TCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.9",
|
||||
LocalPort: 29,
|
||||
},
|
||||
},
|
||||
RemotePort: 9,
|
||||
},
|
||||
testUser + ".tcp_port_0": &TCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".tcp_port_0",
|
||||
ProxyType: consts.TCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.9",
|
||||
LocalPort: 6010,
|
||||
},
|
||||
},
|
||||
RemotePort: 6010,
|
||||
},
|
||||
testUser + ".tcp_port_1": &TCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".tcp_port_1",
|
||||
ProxyType: consts.TCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.9",
|
||||
LocalPort: 6011,
|
||||
},
|
||||
},
|
||||
RemotePort: 6011,
|
||||
},
|
||||
testUser + ".tcp_port_2": &TCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".tcp_port_2",
|
||||
ProxyType: consts.TCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.9",
|
||||
LocalPort: 6019,
|
||||
},
|
||||
},
|
||||
RemotePort: 6019,
|
||||
},
|
||||
testUser + ".dns": &UDPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".dns",
|
||||
ProxyType: consts.UDPProxy,
|
||||
UseEncryption: true,
|
||||
UseCompression: true,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "114.114.114.114",
|
||||
LocalPort: 59,
|
||||
},
|
||||
},
|
||||
RemotePort: 6009,
|
||||
},
|
||||
testUser + ".udp_port_0": &UDPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".udp_port_0",
|
||||
ProxyType: consts.UDPProxy,
|
||||
UseEncryption: true,
|
||||
UseCompression: true,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "114.114.114.114",
|
||||
LocalPort: 6000,
|
||||
},
|
||||
},
|
||||
RemotePort: 6000,
|
||||
},
|
||||
testUser + ".udp_port_1": &UDPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".udp_port_1",
|
||||
ProxyType: consts.UDPProxy,
|
||||
UseEncryption: true,
|
||||
UseCompression: true,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "114.114.114.114",
|
||||
LocalPort: 6010,
|
||||
},
|
||||
},
|
||||
RemotePort: 6010,
|
||||
},
|
||||
testUser + ".udp_port_2": &UDPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".udp_port_2",
|
||||
ProxyType: consts.UDPProxy,
|
||||
UseEncryption: true,
|
||||
UseCompression: true,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "114.114.114.114",
|
||||
LocalPort: 6011,
|
||||
},
|
||||
},
|
||||
RemotePort: 6011,
|
||||
},
|
||||
testUser + ".web01": &HTTPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".web01",
|
||||
ProxyType: consts.HTTPProxy,
|
||||
UseCompression: true,
|
||||
UseEncryption: true,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.9",
|
||||
LocalPort: 89,
|
||||
},
|
||||
HealthCheckConf: HealthCheckConf{
|
||||
HealthCheckType: consts.HTTPProxy,
|
||||
HealthCheckTimeoutS: 3,
|
||||
HealthCheckMaxFailed: 3,
|
||||
HealthCheckIntervalS: 19,
|
||||
HealthCheckURL: "http://127.0.0.9:89/status",
|
||||
},
|
||||
},
|
||||
DomainConf: DomainConf{
|
||||
CustomDomains: []string{"web02.yourdomain.com"},
|
||||
SubDomain: "web01",
|
||||
},
|
||||
Locations: []string{"/", "/pic"},
|
||||
HTTPUser: "admin",
|
||||
HTTPPwd: "admin",
|
||||
HostHeaderRewrite: "example.com",
|
||||
Headers: map[string]string{
|
||||
"X-From-Where": "frp",
|
||||
},
|
||||
},
|
||||
testUser + ".web02": &HTTPSProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".web02",
|
||||
ProxyType: consts.HTTPSProxy,
|
||||
UseCompression: true,
|
||||
UseEncryption: true,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.9",
|
||||
LocalPort: 8009,
|
||||
},
|
||||
ProxyProtocolVersion: "v2",
|
||||
},
|
||||
DomainConf: DomainConf{
|
||||
CustomDomains: []string{"web02.yourdomain.com"},
|
||||
SubDomain: "web01",
|
||||
},
|
||||
},
|
||||
testUser + ".secret_tcp": &STCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".secret_tcp",
|
||||
ProxyType: consts.STCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.1",
|
||||
LocalPort: 22,
|
||||
},
|
||||
},
|
||||
Role: "server",
|
||||
Sk: "abcdefg",
|
||||
},
|
||||
testUser + ".p2p_tcp": &XTCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".p2p_tcp",
|
||||
ProxyType: consts.XTCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.1",
|
||||
LocalPort: 22,
|
||||
},
|
||||
},
|
||||
Role: "server",
|
||||
Sk: "abcdefg",
|
||||
},
|
||||
testUser + ".tcpmuxhttpconnect": &TCPMuxProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".tcpmuxhttpconnect",
|
||||
ProxyType: consts.TCPMuxProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.1",
|
||||
LocalPort: 10701,
|
||||
},
|
||||
},
|
||||
DomainConf: DomainConf{
|
||||
CustomDomains: []string{"tunnel1"},
|
||||
SubDomain: "",
|
||||
},
|
||||
Multiplexer: "httpconnect",
|
||||
},
|
||||
testUser + ".plugin_unix_domain_socket": &TCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".plugin_unix_domain_socket",
|
||||
ProxyType: consts.TCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.1",
|
||||
Plugin: "unix_domain_socket",
|
||||
PluginParams: map[string]string{
|
||||
"plugin_unix_path": "/var/run/docker.sock",
|
||||
},
|
||||
},
|
||||
},
|
||||
RemotePort: 6003,
|
||||
},
|
||||
testUser + ".plugin_http_proxy": &TCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".plugin_http_proxy",
|
||||
ProxyType: consts.TCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.1",
|
||||
Plugin: "http_proxy",
|
||||
PluginParams: map[string]string{
|
||||
"plugin_http_user": "abc",
|
||||
"plugin_http_passwd": "abc",
|
||||
},
|
||||
},
|
||||
},
|
||||
RemotePort: 6004,
|
||||
},
|
||||
testUser + ".plugin_socks5": &TCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".plugin_socks5",
|
||||
ProxyType: consts.TCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.1",
|
||||
Plugin: "socks5",
|
||||
PluginParams: map[string]string{
|
||||
"plugin_user": "abc",
|
||||
"plugin_passwd": "abc",
|
||||
},
|
||||
},
|
||||
},
|
||||
RemotePort: 6005,
|
||||
},
|
||||
testUser + ".plugin_static_file": &TCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".plugin_static_file",
|
||||
ProxyType: consts.TCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.1",
|
||||
Plugin: "static_file",
|
||||
PluginParams: map[string]string{
|
||||
"plugin_local_path": "/var/www/blog",
|
||||
"plugin_strip_prefix": "static",
|
||||
"plugin_http_user": "abc",
|
||||
"plugin_http_passwd": "abc",
|
||||
},
|
||||
},
|
||||
},
|
||||
RemotePort: 6006,
|
||||
},
|
||||
testUser + ".plugin_https2http": &HTTPSProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".plugin_https2http",
|
||||
ProxyType: consts.HTTPSProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.1",
|
||||
Plugin: "https2http",
|
||||
PluginParams: map[string]string{
|
||||
"plugin_local_addr": "127.0.0.1:80",
|
||||
"plugin_crt_path": "./server.crt",
|
||||
"plugin_key_path": "./server.key",
|
||||
"plugin_host_header_rewrite": "127.0.0.1",
|
||||
"plugin_header_X-From-Where": "frp",
|
||||
},
|
||||
},
|
||||
},
|
||||
DomainConf: DomainConf{
|
||||
CustomDomains: []string{"test.yourdomain.com"},
|
||||
},
|
||||
},
|
||||
testUser + ".plugin_http2https": &HTTPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testUser + ".plugin_http2https",
|
||||
ProxyType: consts.HTTPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.1",
|
||||
Plugin: "http2https",
|
||||
PluginParams: map[string]string{
|
||||
"plugin_local_addr": "127.0.0.1:443",
|
||||
"plugin_host_header_rewrite": "127.0.0.1",
|
||||
"plugin_header_X-From-Where": "frp",
|
||||
},
|
||||
},
|
||||
},
|
||||
DomainConf: DomainConf{
|
||||
CustomDomains: []string{"test.yourdomain.com"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
visitorExpected := map[string]VisitorConf{
|
||||
testUser + ".secret_tcp_visitor": &STCPVisitorConf{
|
||||
BaseVisitorConf: BaseVisitorConf{
|
||||
ProxyName: testUser + ".secret_tcp_visitor",
|
||||
ProxyType: consts.STCPProxy,
|
||||
Role: "visitor",
|
||||
Sk: "abcdefg",
|
||||
ServerName: testVisitorPrefix + "secret_tcp",
|
||||
BindAddr: "127.0.0.1",
|
||||
BindPort: 9000,
|
||||
},
|
||||
},
|
||||
testUser + ".p2p_tcp_visitor": &XTCPVisitorConf{
|
||||
BaseVisitorConf: BaseVisitorConf{
|
||||
ProxyName: testUser + ".p2p_tcp_visitor",
|
||||
ProxyType: consts.XTCPProxy,
|
||||
Role: "visitor",
|
||||
Sk: "abcdefg",
|
||||
ServerName: testProxyPrefix + "p2p_tcp",
|
||||
BindAddr: "127.0.0.1",
|
||||
BindPort: 9001,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
proxyActual, visitorActual, err := LoadAllProxyConfsFromIni(testUser, testClientBytesWithFull, nil)
|
||||
assert.NoError(err)
|
||||
assert.Equal(proxyExpected, proxyActual)
|
||||
assert.Equal(visitorExpected, visitorActual)
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,461 @@
|
||||
// Copyright 2020 The frp Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/fatedier/frp/pkg/consts"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
testLoadOptions = ini.LoadOptions{
|
||||
Insensitive: false,
|
||||
InsensitiveSections: false,
|
||||
InsensitiveKeys: false,
|
||||
IgnoreInlineComment: true,
|
||||
AllowBooleanKeys: true,
|
||||
}
|
||||
|
||||
testProxyPrefix = "test."
|
||||
)
|
||||
|
||||
func Test_Proxy_Interface(t *testing.T) {
|
||||
for name := range proxyConfTypeMap {
|
||||
NewConfByType(name)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Proxy_UnmarshalFromIni(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
testcases := []struct {
|
||||
sname string
|
||||
source []byte
|
||||
expected ProxyConf
|
||||
}{
|
||||
|
||||
{
|
||||
sname: "ssh",
|
||||
source: []byte(`
|
||||
[ssh]
|
||||
# tcp | udp | http | https | stcp | xtcp, default is tcp
|
||||
type = tcp
|
||||
local_ip = 127.0.0.9
|
||||
local_port = 29
|
||||
bandwidth_limit = 19MB
|
||||
use_encryption
|
||||
use_compression
|
||||
remote_port = 6009
|
||||
group = test_group
|
||||
group_key = 123456
|
||||
health_check_type = tcp
|
||||
health_check_timeout_s = 3
|
||||
health_check_max_failed = 3
|
||||
health_check_interval_s = 19
|
||||
meta_var1 = 123
|
||||
meta_var2 = 234`),
|
||||
expected: &TCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testProxyPrefix + "ssh",
|
||||
ProxyType: consts.TCPProxy,
|
||||
UseCompression: true,
|
||||
UseEncryption: true,
|
||||
Group: "test_group",
|
||||
GroupKey: "123456",
|
||||
BandwidthLimit: MustBandwidthQuantity("19MB"),
|
||||
Metas: map[string]string{
|
||||
"var1": "123",
|
||||
"var2": "234",
|
||||
},
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.9",
|
||||
LocalPort: 29,
|
||||
},
|
||||
HealthCheckConf: HealthCheckConf{
|
||||
HealthCheckType: consts.TCPProxy,
|
||||
HealthCheckTimeoutS: 3,
|
||||
HealthCheckMaxFailed: 3,
|
||||
HealthCheckIntervalS: 19,
|
||||
HealthCheckAddr: "127.0.0.9:29",
|
||||
},
|
||||
},
|
||||
RemotePort: 6009,
|
||||
},
|
||||
},
|
||||
{
|
||||
sname: "ssh_random",
|
||||
source: []byte(`
|
||||
[ssh_random]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.9
|
||||
local_port = 29
|
||||
remote_port = 9
|
||||
`),
|
||||
expected: &TCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testProxyPrefix + "ssh_random",
|
||||
ProxyType: consts.TCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.9",
|
||||
LocalPort: 29,
|
||||
},
|
||||
},
|
||||
RemotePort: 9,
|
||||
},
|
||||
},
|
||||
{
|
||||
sname: "dns",
|
||||
source: []byte(`
|
||||
[dns]
|
||||
type = udp
|
||||
local_ip = 114.114.114.114
|
||||
local_port = 59
|
||||
remote_port = 6009
|
||||
use_encryption
|
||||
use_compression
|
||||
`),
|
||||
expected: &UDPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testProxyPrefix + "dns",
|
||||
ProxyType: consts.UDPProxy,
|
||||
UseEncryption: true,
|
||||
UseCompression: true,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "114.114.114.114",
|
||||
LocalPort: 59,
|
||||
},
|
||||
},
|
||||
RemotePort: 6009,
|
||||
},
|
||||
},
|
||||
{
|
||||
sname: "web01",
|
||||
source: []byte(`
|
||||
[web01]
|
||||
type = http
|
||||
local_ip = 127.0.0.9
|
||||
local_port = 89
|
||||
use_encryption
|
||||
use_compression
|
||||
http_user = admin
|
||||
http_pwd = admin
|
||||
subdomain = web01
|
||||
custom_domains = web02.yourdomain.com
|
||||
locations = /,/pic
|
||||
host_header_rewrite = example.com
|
||||
header_X-From-Where = frp
|
||||
health_check_type = http
|
||||
health_check_url = /status
|
||||
health_check_interval_s = 19
|
||||
health_check_max_failed = 3
|
||||
health_check_timeout_s = 3
|
||||
`),
|
||||
expected: &HTTPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testProxyPrefix + "web01",
|
||||
ProxyType: consts.HTTPProxy,
|
||||
UseCompression: true,
|
||||
UseEncryption: true,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.9",
|
||||
LocalPort: 89,
|
||||
},
|
||||
HealthCheckConf: HealthCheckConf{
|
||||
HealthCheckType: consts.HTTPProxy,
|
||||
HealthCheckTimeoutS: 3,
|
||||
HealthCheckMaxFailed: 3,
|
||||
HealthCheckIntervalS: 19,
|
||||
HealthCheckURL: "http://127.0.0.9:89/status",
|
||||
},
|
||||
},
|
||||
DomainConf: DomainConf{
|
||||
CustomDomains: []string{"web02.yourdomain.com"},
|
||||
SubDomain: "web01",
|
||||
},
|
||||
Locations: []string{"/", "/pic"},
|
||||
HTTPUser: "admin",
|
||||
HTTPPwd: "admin",
|
||||
HostHeaderRewrite: "example.com",
|
||||
Headers: map[string]string{
|
||||
"X-From-Where": "frp",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
sname: "web02",
|
||||
source: []byte(`
|
||||
[web02]
|
||||
type = https
|
||||
local_ip = 127.0.0.9
|
||||
local_port = 8009
|
||||
use_encryption
|
||||
use_compression
|
||||
subdomain = web01
|
||||
custom_domains = web02.yourdomain.com
|
||||
proxy_protocol_version = v2
|
||||
`),
|
||||
expected: &HTTPSProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testProxyPrefix + "web02",
|
||||
ProxyType: consts.HTTPSProxy,
|
||||
UseCompression: true,
|
||||
UseEncryption: true,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.9",
|
||||
LocalPort: 8009,
|
||||
},
|
||||
ProxyProtocolVersion: "v2",
|
||||
},
|
||||
DomainConf: DomainConf{
|
||||
CustomDomains: []string{"web02.yourdomain.com"},
|
||||
SubDomain: "web01",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
sname: "secret_tcp",
|
||||
source: []byte(`
|
||||
[secret_tcp]
|
||||
type = stcp
|
||||
sk = abcdefg
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 22
|
||||
use_encryption = false
|
||||
use_compression = false
|
||||
`),
|
||||
expected: &STCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testProxyPrefix + "secret_tcp",
|
||||
ProxyType: consts.STCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.1",
|
||||
LocalPort: 22,
|
||||
},
|
||||
},
|
||||
Role: "server",
|
||||
Sk: "abcdefg",
|
||||
},
|
||||
},
|
||||
{
|
||||
sname: "p2p_tcp",
|
||||
source: []byte(`
|
||||
[p2p_tcp]
|
||||
type = xtcp
|
||||
sk = abcdefg
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 22
|
||||
use_encryption = false
|
||||
use_compression = false
|
||||
`),
|
||||
expected: &XTCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testProxyPrefix + "p2p_tcp",
|
||||
ProxyType: consts.XTCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.1",
|
||||
LocalPort: 22,
|
||||
},
|
||||
},
|
||||
Role: "server",
|
||||
Sk: "abcdefg",
|
||||
},
|
||||
},
|
||||
{
|
||||
sname: "tcpmuxhttpconnect",
|
||||
source: []byte(`
|
||||
[tcpmuxhttpconnect]
|
||||
type = tcpmux
|
||||
multiplexer = httpconnect
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 10701
|
||||
custom_domains = tunnel1
|
||||
`),
|
||||
expected: &TCPMuxProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testProxyPrefix + "tcpmuxhttpconnect",
|
||||
ProxyType: consts.TCPMuxProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.1",
|
||||
LocalPort: 10701,
|
||||
},
|
||||
},
|
||||
DomainConf: DomainConf{
|
||||
CustomDomains: []string{"tunnel1"},
|
||||
SubDomain: "",
|
||||
},
|
||||
Multiplexer: "httpconnect",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range testcases {
|
||||
f, err := ini.LoadSources(testLoadOptions, c.source)
|
||||
assert.NoError(err)
|
||||
|
||||
proxyType := f.Section(c.sname).Key("type").String()
|
||||
assert.NotEmpty(proxyType)
|
||||
|
||||
actual := DefaultProxyConf(proxyType)
|
||||
assert.NotNil(actual)
|
||||
|
||||
err = actual.UnmarshalFromIni(testProxyPrefix, c.sname, f.Section(c.sname))
|
||||
assert.NoError(err)
|
||||
assert.Equal(c.expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_RangeProxy_UnmarshalFromIni(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
testcases := []struct {
|
||||
sname string
|
||||
source []byte
|
||||
expected map[string]ProxyConf
|
||||
}{
|
||||
{
|
||||
sname: "range:tcp_port",
|
||||
source: []byte(`
|
||||
[range:tcp_port]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.9
|
||||
local_port = 6010-6011,6019
|
||||
remote_port = 6010-6011,6019
|
||||
use_encryption = false
|
||||
use_compression = false
|
||||
`),
|
||||
expected: map[string]ProxyConf{
|
||||
"tcp_port_0": &TCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testProxyPrefix + "tcp_port_0",
|
||||
ProxyType: consts.TCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.9",
|
||||
LocalPort: 6010,
|
||||
},
|
||||
},
|
||||
RemotePort: 6010,
|
||||
},
|
||||
"tcp_port_1": &TCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testProxyPrefix + "tcp_port_1",
|
||||
ProxyType: consts.TCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.9",
|
||||
LocalPort: 6011,
|
||||
},
|
||||
},
|
||||
RemotePort: 6011,
|
||||
},
|
||||
"tcp_port_2": &TCPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testProxyPrefix + "tcp_port_2",
|
||||
ProxyType: consts.TCPProxy,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "127.0.0.9",
|
||||
LocalPort: 6019,
|
||||
},
|
||||
},
|
||||
RemotePort: 6019,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
sname: "range:udp_port",
|
||||
source: []byte(`
|
||||
[range:udp_port]
|
||||
type = udp
|
||||
local_ip = 114.114.114.114
|
||||
local_port = 6000,6010-6011
|
||||
remote_port = 6000,6010-6011
|
||||
use_encryption
|
||||
use_compression
|
||||
`),
|
||||
expected: map[string]ProxyConf{
|
||||
"udp_port_0": &UDPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testProxyPrefix + "udp_port_0",
|
||||
ProxyType: consts.UDPProxy,
|
||||
UseEncryption: true,
|
||||
UseCompression: true,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "114.114.114.114",
|
||||
LocalPort: 6000,
|
||||
},
|
||||
},
|
||||
RemotePort: 6000,
|
||||
},
|
||||
"udp_port_1": &UDPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testProxyPrefix + "udp_port_1",
|
||||
ProxyType: consts.UDPProxy,
|
||||
UseEncryption: true,
|
||||
UseCompression: true,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "114.114.114.114",
|
||||
LocalPort: 6010,
|
||||
},
|
||||
},
|
||||
RemotePort: 6010,
|
||||
},
|
||||
"udp_port_2": &UDPProxyConf{
|
||||
BaseProxyConf: BaseProxyConf{
|
||||
ProxyName: testProxyPrefix + "udp_port_2",
|
||||
ProxyType: consts.UDPProxy,
|
||||
UseEncryption: true,
|
||||
UseCompression: true,
|
||||
LocalSvrConf: LocalSvrConf{
|
||||
LocalIP: "114.114.114.114",
|
||||
LocalPort: 6011,
|
||||
},
|
||||
},
|
||||
RemotePort: 6011,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range testcases {
|
||||
|
||||
f, err := ini.LoadSources(testLoadOptions, c.source)
|
||||
assert.NoError(err)
|
||||
|
||||
actual := make(map[string]ProxyConf)
|
||||
s := f.Section(c.sname)
|
||||
|
||||
err = renderRangeProxyTemplates(f, s)
|
||||
assert.NoError(err)
|
||||
|
||||
f.DeleteSection(ini.DefaultSection)
|
||||
f.DeleteSection(c.sname)
|
||||
|
||||
for _, section := range f.Sections() {
|
||||
proxyType := section.Key("type").String()
|
||||
newsname := section.Name()
|
||||
|
||||
tmp := DefaultProxyConf(proxyType)
|
||||
err = tmp.UnmarshalFromIni(testProxyPrefix, newsname, section)
|
||||
assert.NoError(err)
|
||||
|
||||
actual[newsname] = tmp
|
||||
}
|
||||
|
||||
assert.Equal(c.expected, actual)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,284 @@
|
||||
// Copyright 2020 The frp Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fatedier/frp/pkg/auth"
|
||||
plugin "github.com/fatedier/frp/pkg/plugin/server"
|
||||
"github.com/fatedier/frp/pkg/util/util"
|
||||
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// ServerCommonConf contains information for a server service. It is
|
||||
// recommended to use GetDefaultServerConf instead of creating this object
|
||||
// directly, so that all unspecified fields have reasonable default values.
|
||||
type ServerCommonConf struct {
|
||||
auth.ServerConfig `ini:",extends" json:"inline"`
|
||||
|
||||
// BindAddr specifies the address that the server binds to. By default,
|
||||
// this value is "0.0.0.0".
|
||||
BindAddr string `ini:"bind_addr" json:"bind_addr"`
|
||||
// BindPort specifies the port that the server listens on. By default, this
|
||||
// value is 7000.
|
||||
BindPort int `ini:"bind_port" json:"bind_port"`
|
||||
// BindUDPPort specifies the UDP port that the server listens on. If this
|
||||
// value is 0, the server will not listen for UDP connections. By default,
|
||||
// this value is 0
|
||||
BindUDPPort int `ini:"bind_udp_port" json:"bind_udp_port"`
|
||||
// KCPBindPort specifies the KCP port that the server listens on. If this
|
||||
// value is 0, the server will not listen for KCP connections. By default,
|
||||
// this value is 0.
|
||||
KCPBindPort int `ini:"kcp_bind_port" json:"kcp_bind_port"`
|
||||
// ProxyBindAddr specifies the address that the proxy binds to. This value
|
||||
// may be the same as BindAddr. By default, this value is "0.0.0.0".
|
||||
ProxyBindAddr string `ini:"proxy_bind_addr" json:"proxy_bind_addr"`
|
||||
// VhostHTTPPort specifies the port that the server listens for HTTP Vhost
|
||||
// requests. If this value is 0, the server will not listen for HTTP
|
||||
// requests. By default, this value is 0.
|
||||
VhostHTTPPort int `ini:"vhost_http_port" json:"vhost_http_port"`
|
||||
// VhostHTTPSPort specifies the port that the server listens for HTTPS
|
||||
// Vhost requests. If this value is 0, the server will not listen for HTTPS
|
||||
// requests. By default, this value is 0.
|
||||
VhostHTTPSPort int `ini:"vhost_https_port" json:"vhost_https_port"`
|
||||
// TCPMuxHTTPConnectPort specifies the port that the server listens for TCP
|
||||
// HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP
|
||||
// requests on one single port. If it's not - it will listen on this value for
|
||||
// HTTP CONNECT requests. By default, this value is 0.
|
||||
TCPMuxHTTPConnectPort int `ini:"tcpmux_httpconnect_port" json:"tcpmux_httpconnect_port"`
|
||||
// VhostHTTPTimeout specifies the response header timeout for the Vhost
|
||||
// HTTP server, in seconds. By default, this value is 60.
|
||||
VhostHTTPTimeout int64 `ini:"vhost_http_timeout" json:"vhost_http_timeout"`
|
||||
// DashboardAddr specifies the address that the dashboard binds to. By
|
||||
// default, this value is "0.0.0.0".
|
||||
DashboardAddr string `ini:"dashboard_addr" json:"dashboard_addr"`
|
||||
// DashboardPort specifies the port that the dashboard listens on. If this
|
||||
// value is 0, the dashboard will not be started. By default, this value is
|
||||
// 0.
|
||||
DashboardPort int `ini:"dashboard_port" json:"dashboard_port"`
|
||||
// DashboardUser specifies the username that the dashboard will use for
|
||||
// login. By default, this value is "admin".
|
||||
DashboardUser string `ini:"dashboard_user" json:"dashboard_user"`
|
||||
// DashboardUser specifies the password that the dashboard will use for
|
||||
// login. By default, this value is "admin".
|
||||
DashboardPwd string `ini:"dashboard_pwd" json:"dashboard_pwd"`
|
||||
// EnablePrometheus will export prometheus metrics on {dashboard_addr}:{dashboard_port}
|
||||
// in /metrics api.
|
||||
EnablePrometheus bool `ini:"enable_prometheus" json:"enable_prometheus"`
|
||||
// AssetsDir specifies the local directory that the dashboard will load
|
||||
// resources from. If this value is "", assets will be loaded from the
|
||||
// bundled executable using statik. By default, this value is "".
|
||||
AssetsDir string `ini:"assets_dir" json:"assets_dir"`
|
||||
// LogFile specifies a file where logs will be written to. This value will
|
||||
// only be used if LogWay is set appropriately. By default, this value is
|
||||
// "console".
|
||||
LogFile string `ini:"log_file" json:"log_file"`
|
||||
// LogWay specifies the way logging is managed. Valid values are "console"
|
||||
// or "file". If "console" is used, logs will be printed to stdout. If
|
||||
// "file" is used, logs will be printed to LogFile. By default, this value
|
||||
// is "console".
|
||||
LogWay string `ini:"log_way" json:"log_way"`
|
||||
// LogLevel specifies the minimum log level. Valid values are "trace",
|
||||
// "debug", "info", "warn", and "error". By default, this value is "info".
|
||||
LogLevel string `ini:"log_level" json:"log_level"`
|
||||
// LogMaxDays specifies the maximum number of days to store log information
|
||||
// before deletion. This is only used if LogWay == "file". By default, this
|
||||
// value is 0.
|
||||
LogMaxDays int64 `ini:"log_max_days" json:"log_max_days"`
|
||||
// DisableLogColor disables log colors when LogWay == "console" when set to
|
||||
// true. By default, this value is false.
|
||||
DisableLogColor bool `ini:"disable_log_color" json:"disable_log_color"`
|
||||
// DetailedErrorsToClient defines whether to send the specific error (with
|
||||
// debug info) to frpc. By default, this value is true.
|
||||
DetailedErrorsToClient bool `ini:"detailed_errors_to_client" json:"detailed_errors_to_client"`
|
||||
|
||||
// SubDomainHost specifies the domain that will be attached to sub-domains
|
||||
// requested by the client when using Vhost proxying. For example, if this
|
||||
// value is set to "frps.com" and the client requested the subdomain
|
||||
// "test", the resulting URL would be "test.frps.com". By default, this
|
||||
// value is "".
|
||||
SubDomainHost string `ini:"subdomain_host" json:"subdomain_host"`
|
||||
// TCPMux toggles TCP stream multiplexing. This allows multiple requests
|
||||
// from a client to share a single TCP connection. By default, this value
|
||||
// is true.
|
||||
TCPMux bool `ini:"tcp_mux" json:"tcp_mux"`
|
||||
// Custom404Page specifies a path to a custom 404 page to display. If this
|
||||
// value is "", a default page will be displayed. By default, this value is
|
||||
// "".
|
||||
Custom404Page string `ini:"custom_404_page" json:"custom_404_page"`
|
||||
|
||||
// AllowPorts specifies a set of ports that clients are able to proxy to.
|
||||
// If the length of this value is 0, all ports are allowed. By default,
|
||||
// this value is an empty set.
|
||||
AllowPorts map[int]struct{} `ini:"-" json:"-"`
|
||||
// MaxPoolCount specifies the maximum pool size for each proxy. By default,
|
||||
// this value is 5.
|
||||
MaxPoolCount int64 `ini:"max_pool_count" json:"max_pool_count"`
|
||||
// MaxPortsPerClient specifies the maximum number of ports a single client
|
||||
// may proxy to. If this value is 0, no limit will be applied. By default,
|
||||
// this value is 0.
|
||||
MaxPortsPerClient int64 `ini:"max_ports_per_client" json:"max_ports_per_client"`
|
||||
// TLSOnly specifies whether to only accept TLS-encrypted connections.
|
||||
// By default, the value is false.
|
||||
TLSOnly bool `ini:"tls_only" json:"tls_only"`
|
||||
// TLSCertFile specifies the path of the cert file that the server will
|
||||
// load. If "tls_cert_file", "tls_key_file" are valid, the server will use this
|
||||
// supplied tls configuration. Otherwise, the server will use the tls
|
||||
// configuration generated by itself.
|
||||
TLSCertFile string `ini:"tls_cert_file" json:"tls_cert_file"`
|
||||
// TLSKeyFile specifies the path of the secret key that the server will
|
||||
// load. If "tls_cert_file", "tls_key_file" are valid, the server will use this
|
||||
// supplied tls configuration. Otherwise, the server will use the tls
|
||||
// configuration generated by itself.
|
||||
TLSKeyFile string `ini:"tls_key_file" json:"tls_key_file"`
|
||||
// TLSTrustedCaFile specifies the paths of the client cert files that the
|
||||
// server will load. It only works when "tls_only" is true. If
|
||||
// "tls_trusted_ca_file" is valid, the server will verify each client's
|
||||
// certificate.
|
||||
TLSTrustedCaFile string `ini:"tls_trusted_ca_file" json:"tls_trusted_ca_file"`
|
||||
// HeartBeatTimeout specifies the maximum time to wait for a heartbeat
|
||||
// before terminating the connection. It is not recommended to change this
|
||||
// value. By default, this value is 90.
|
||||
HeartbeatTimeout int64 `ini:"heartbeat_timeout" json:"heartbeat_timeout"`
|
||||
// UserConnTimeout specifies the maximum time to wait for a work
|
||||
// connection. By default, this value is 10.
|
||||
UserConnTimeout int64 `ini:"user_conn_timeout" json:"user_conn_timeout"`
|
||||
// HTTPPlugins specify the server plugins support HTTP protocol.
|
||||
HTTPPlugins map[string]plugin.HTTPPluginOptions `ini:"-" json:"http_plugins"`
|
||||
// UDPPacketSize specifies the UDP packet size
|
||||
// By default, this value is 1500
|
||||
UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"`
|
||||
}
|
||||
|
||||
// GetDefaultServerConf returns a server configuration with reasonable
|
||||
// defaults.
|
||||
func GetDefaultServerConf() ServerCommonConf {
|
||||
return ServerCommonConf{
|
||||
ServerConfig: auth.GetDefaultServerConf(),
|
||||
BindAddr: "0.0.0.0",
|
||||
BindPort: 7000,
|
||||
BindUDPPort: 0,
|
||||
KCPBindPort: 0,
|
||||
ProxyBindAddr: "0.0.0.0",
|
||||
VhostHTTPPort: 0,
|
||||
VhostHTTPSPort: 0,
|
||||
TCPMuxHTTPConnectPort: 0,
|
||||
VhostHTTPTimeout: 60,
|
||||
DashboardAddr: "0.0.0.0",
|
||||
DashboardPort: 0,
|
||||
DashboardUser: "admin",
|
||||
DashboardPwd: "admin",
|
||||
EnablePrometheus: false,
|
||||
AssetsDir: "",
|
||||
LogFile: "console",
|
||||
LogWay: "console",
|
||||
LogLevel: "info",
|
||||
LogMaxDays: 3,
|
||||
DisableLogColor: false,
|
||||
DetailedErrorsToClient: true,
|
||||
SubDomainHost: "",
|
||||
TCPMux: true,
|
||||
AllowPorts: make(map[int]struct{}),
|
||||
MaxPoolCount: 5,
|
||||
MaxPortsPerClient: 0,
|
||||
TLSOnly: false,
|
||||
TLSCertFile: "",
|
||||
TLSKeyFile: "",
|
||||
TLSTrustedCaFile: "",
|
||||
HeartbeatTimeout: 90,
|
||||
UserConnTimeout: 10,
|
||||
Custom404Page: "",
|
||||
HTTPPlugins: make(map[string]plugin.HTTPPluginOptions),
|
||||
UDPPacketSize: 1500,
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *ServerCommonConf) Check() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func UnmarshalServerConfFromIni(source interface{}) (ServerCommonConf, error) {
|
||||
|
||||
f, err := ini.LoadSources(ini.LoadOptions{
|
||||
Insensitive: false,
|
||||
InsensitiveSections: false,
|
||||
InsensitiveKeys: false,
|
||||
IgnoreInlineComment: true,
|
||||
AllowBooleanKeys: true,
|
||||
}, source)
|
||||
if err != nil {
|
||||
return ServerCommonConf{}, err
|
||||
}
|
||||
|
||||
s, err := f.GetSection("common")
|
||||
if err != nil {
|
||||
// TODO: add error info
|
||||
return ServerCommonConf{}, err
|
||||
}
|
||||
|
||||
common := GetDefaultServerConf()
|
||||
err = s.MapTo(&common)
|
||||
if err != nil {
|
||||
return ServerCommonConf{}, err
|
||||
}
|
||||
|
||||
// allow_ports
|
||||
allowPortStr := s.Key("allow_ports").String()
|
||||
if allowPortStr != "" {
|
||||
allowPorts, err := util.ParseRangeNumbers(allowPortStr)
|
||||
if err != nil {
|
||||
return ServerCommonConf{}, fmt.Errorf("Parse conf error: allow_ports: %v", err)
|
||||
}
|
||||
for _, port := range allowPorts {
|
||||
common.AllowPorts[int(port)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// plugin.xxx
|
||||
pluginOpts := make(map[string]plugin.HTTPPluginOptions)
|
||||
for _, section := range f.Sections() {
|
||||
name := section.Name()
|
||||
if !strings.HasPrefix(name, "plugin.") {
|
||||
continue
|
||||
}
|
||||
|
||||
opt, err := loadHTTPPluginOpt(section)
|
||||
if err != nil {
|
||||
return ServerCommonConf{}, err
|
||||
}
|
||||
|
||||
pluginOpts[opt.Name] = *opt
|
||||
}
|
||||
common.HTTPPlugins = pluginOpts
|
||||
|
||||
return common, nil
|
||||
}
|
||||
|
||||
func loadHTTPPluginOpt(section *ini.Section) (*plugin.HTTPPluginOptions, error) {
|
||||
name := strings.TrimSpace(strings.TrimPrefix(section.Name(), "plugin."))
|
||||
|
||||
opt := new(plugin.HTTPPluginOptions)
|
||||
err := section.MapTo(opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opt.Name = name
|
||||
|
||||
return opt, nil
|
||||
}
|
@ -1,482 +0,0 @@
|
||||
// Copyright 2016 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/fatedier/frp/pkg/auth"
|
||||
plugin "github.com/fatedier/frp/pkg/plugin/server"
|
||||
"github.com/fatedier/frp/pkg/util/util"
|
||||
|
||||
ini "github.com/vaughan0/go-ini"
|
||||
)
|
||||
|
||||
// ServerCommonConf contains information for a server service. It is
|
||||
// recommended to use GetDefaultServerConf instead of creating this object
|
||||
// directly, so that all unspecified fields have reasonable default values.
|
||||
type ServerCommonConf struct {
|
||||
auth.ServerConfig
|
||||
// BindAddr specifies the address that the server binds to. By default,
|
||||
// this value is "0.0.0.0".
|
||||
BindAddr string `json:"bind_addr"`
|
||||
// BindPort specifies the port that the server listens on. By default, this
|
||||
// value is 7000.
|
||||
BindPort int `json:"bind_port"`
|
||||
// BindUDPPort specifies the UDP port that the server listens on. If this
|
||||
// value is 0, the server will not listen for UDP connections. By default,
|
||||
// this value is 0
|
||||
BindUDPPort int `json:"bind_udp_port"`
|
||||
// KCPBindPort specifies the KCP port that the server listens on. If this
|
||||
// value is 0, the server will not listen for KCP connections. By default,
|
||||
// this value is 0.
|
||||
KCPBindPort int `json:"kcp_bind_port"`
|
||||
// ProxyBindAddr specifies the address that the proxy binds to. This value
|
||||
// may be the same as BindAddr. By default, this value is "0.0.0.0".
|
||||
ProxyBindAddr string `json:"proxy_bind_addr"`
|
||||
// VhostHTTPPort specifies the port that the server listens for HTTP Vhost
|
||||
// requests. If this value is 0, the server will not listen for HTTP
|
||||
// requests. By default, this value is 0.
|
||||
VhostHTTPPort int `json:"vhost_http_port"`
|
||||
// VhostHTTPSPort specifies the port that the server listens for HTTPS
|
||||
// Vhost requests. If this value is 0, the server will not listen for HTTPS
|
||||
// requests. By default, this value is 0.
|
||||
VhostHTTPSPort int `json:"vhost_https_port"`
|
||||
// TCPMuxHTTPConnectPort specifies the port that the server listens for TCP
|
||||
// HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP
|
||||
// requests on one single port. If it's not - it will listen on this value for
|
||||
// HTTP CONNECT requests. By default, this value is 0.
|
||||
TCPMuxHTTPConnectPort int `json:"tcpmux_httpconnect_port"`
|
||||
// VhostHTTPTimeout specifies the response header timeout for the Vhost
|
||||
// HTTP server, in seconds. By default, this value is 60.
|
||||
VhostHTTPTimeout int64 `json:"vhost_http_timeout"`
|
||||
// DashboardAddr specifies the address that the dashboard binds to. By
|
||||
// default, this value is "0.0.0.0".
|
||||
DashboardAddr string `json:"dashboard_addr"`
|
||||
// DashboardPort specifies the port that the dashboard listens on. If this
|
||||
// value is 0, the dashboard will not be started. By default, this value is
|
||||
// 0.
|
||||
DashboardPort int `json:"dashboard_port"`
|
||||
// DashboardUser specifies the username that the dashboard will use for
|
||||
// login. By default, this value is "admin".
|
||||
DashboardUser string `json:"dashboard_user"`
|
||||
// DashboardUser specifies the password that the dashboard will use for
|
||||
// login. By default, this value is "admin".
|
||||
DashboardPwd string `json:"dashboard_pwd"`
|
||||
// EnablePrometheus will export prometheus metrics on {dashboard_addr}:{dashboard_port}
|
||||
// in /metrics api.
|
||||
EnablePrometheus bool `json:"enable_prometheus"`
|
||||
// AssetsDir specifies the local directory that the dashboard will load
|
||||
// resources from. If this value is "", assets will be loaded from the
|
||||
// bundled executable using statik. By default, this value is "".
|
||||
AssetsDir string `json:"assets_dir"`
|
||||
// LogFile specifies a file where logs will be written to. This value will
|
||||
// only be used if LogWay is set appropriately. By default, this value is
|
||||
// "console".
|
||||
LogFile string `json:"log_file"`
|
||||
// LogWay specifies the way logging is managed. Valid values are "console"
|
||||
// or "file". If "console" is used, logs will be printed to stdout. If
|
||||
// "file" is used, logs will be printed to LogFile. By default, this value
|
||||
// is "console".
|
||||
LogWay string `json:"log_way"`
|
||||
// LogLevel specifies the minimum log level. Valid values are "trace",
|
||||
// "debug", "info", "warn", and "error". By default, this value is "info".
|
||||
LogLevel string `json:"log_level"`
|
||||
// LogMaxDays specifies the maximum number of days to store log information
|
||||
// before deletion. This is only used if LogWay == "file". By default, this
|
||||
// value is 0.
|
||||
LogMaxDays int64 `json:"log_max_days"`
|
||||
// DisableLogColor disables log colors when LogWay == "console" when set to
|
||||
// true. By default, this value is false.
|
||||
DisableLogColor bool `json:"disable_log_color"`
|
||||
// DetailedErrorsToClient defines whether to send the specific error (with
|
||||
// debug info) to frpc. By default, this value is true.
|
||||
DetailedErrorsToClient bool `json:"detailed_errors_to_client"`
|
||||
|
||||
// SubDomainHost specifies the domain that will be attached to sub-domains
|
||||
// requested by the client when using Vhost proxying. For example, if this
|
||||
// value is set to "frps.com" and the client requested the subdomain
|
||||
// "test", the resulting URL would be "test.frps.com". By default, this
|
||||
// value is "".
|
||||
SubDomainHost string `json:"subdomain_host"`
|
||||
// TCPMux toggles TCP stream multiplexing. This allows multiple requests
|
||||
// from a client to share a single TCP connection. By default, this value
|
||||
// is true.
|
||||
TCPMux bool `json:"tcp_mux"`
|
||||
// Custom404Page specifies a path to a custom 404 page to display. If this
|
||||
// value is "", a default page will be displayed. By default, this value is
|
||||
// "".
|
||||
Custom404Page string `json:"custom_404_page"`
|
||||
|
||||
// AllowPorts specifies a set of ports that clients are able to proxy to.
|
||||
// If the length of this value is 0, all ports are allowed. By default,
|
||||
// this value is an empty set.
|
||||
AllowPorts map[int]struct{}
|
||||
// MaxPoolCount specifies the maximum pool size for each proxy. By default,
|
||||
// this value is 5.
|
||||
MaxPoolCount int64 `json:"max_pool_count"`
|
||||
// MaxPortsPerClient specifies the maximum number of ports a single client
|
||||
// may proxy to. If this value is 0, no limit will be applied. By default,
|
||||
// this value is 0.
|
||||
MaxPortsPerClient int64 `json:"max_ports_per_client"`
|
||||
// TLSOnly specifies whether to only accept TLS-encrypted connections.
|
||||
// By default, the value is false.
|
||||
TLSOnly bool `json:"tls_only"`
|
||||
// TLSCertFile specifies the path of the cert file that the server will
|
||||
// load. If "tls_cert_file", "tls_key_file" are valid, the server will use this
|
||||
// supplied tls configuration. Otherwise, the server will use the tls
|
||||
// configuration generated by itself.
|
||||
TLSCertFile string `json:"tls_cert_file"`
|
||||
// TLSKeyFile specifies the path of the secret key that the server will
|
||||
// load. If "tls_cert_file", "tls_key_file" are valid, the server will use this
|
||||
// supplied tls configuration. Otherwise, the server will use the tls
|
||||
// configuration generated by itself.
|
||||
TLSKeyFile string `json:"tls_key_file"`
|
||||
// TLSTrustedCaFile specifies the paths of the client cert files that the
|
||||
// server will load. It only works when "tls_only" is true. If
|
||||
// "tls_trusted_ca_file" is valid, the server will verify each client's
|
||||
// certificate.
|
||||
TLSTrustedCaFile string `json:"tls_trusted_ca_file"`
|
||||
// HeartBeatTimeout specifies the maximum time to wait for a heartbeat
|
||||
// before terminating the connection. It is not recommended to change this
|
||||
// value. By default, this value is 90.
|
||||
HeartbeatTimeout int64 `json:"heartbeat_timeout"`
|
||||
// UserConnTimeout specifies the maximum time to wait for a work
|
||||
// connection. By default, this value is 10.
|
||||
UserConnTimeout int64 `json:"user_conn_timeout"`
|
||||
// HTTPPlugins specify the server plugins support HTTP protocol.
|
||||
HTTPPlugins map[string]plugin.HTTPPluginOptions `json:"http_plugins"`
|
||||
// UDPPacketSize specifies the UDP packet size
|
||||
// By default, this value is 1500
|
||||
UDPPacketSize int64 `json:"udp_packet_size"`
|
||||
}
|
||||
|
||||
// GetDefaultServerConf returns a server configuration with reasonable
|
||||
// defaults.
|
||||
func GetDefaultServerConf() ServerCommonConf {
|
||||
return ServerCommonConf{
|
||||
BindAddr: "0.0.0.0",
|
||||
BindPort: 7000,
|
||||
BindUDPPort: 0,
|
||||
KCPBindPort: 0,
|
||||
ProxyBindAddr: "0.0.0.0",
|
||||
VhostHTTPPort: 0,
|
||||
VhostHTTPSPort: 0,
|
||||
TCPMuxHTTPConnectPort: 0,
|
||||
VhostHTTPTimeout: 60,
|
||||
DashboardAddr: "0.0.0.0",
|
||||
DashboardPort: 0,
|
||||
DashboardUser: "admin",
|
||||
DashboardPwd: "admin",
|
||||
EnablePrometheus: false,
|
||||
AssetsDir: "",
|
||||
LogFile: "console",
|
||||
LogWay: "console",
|
||||
LogLevel: "info",
|
||||
LogMaxDays: 3,
|
||||
DisableLogColor: false,
|
||||
DetailedErrorsToClient: true,
|
||||
SubDomainHost: "",
|
||||
TCPMux: true,
|
||||
AllowPorts: make(map[int]struct{}),
|
||||
MaxPoolCount: 5,
|
||||
MaxPortsPerClient: 0,
|
||||
TLSOnly: false,
|
||||
TLSCertFile: "",
|
||||
TLSKeyFile: "",
|
||||
TLSTrustedCaFile: "",
|
||||
HeartbeatTimeout: 90,
|
||||
UserConnTimeout: 10,
|
||||
Custom404Page: "",
|
||||
HTTPPlugins: make(map[string]plugin.HTTPPluginOptions),
|
||||
UDPPacketSize: 1500,
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalServerConfFromIni parses the contents of a server configuration ini
|
||||
// file and returns the resulting server configuration.
|
||||
func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error) {
|
||||
cfg = GetDefaultServerConf()
|
||||
|
||||
conf, err := ini.Load(strings.NewReader(content))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("parse ini conf file error: %v", err)
|
||||
return ServerCommonConf{}, err
|
||||
}
|
||||
|
||||
UnmarshalPluginsFromIni(conf, &cfg)
|
||||
|
||||
cfg.ServerConfig = auth.UnmarshalServerConfFromIni(conf)
|
||||
|
||||
var (
|
||||
tmpStr string
|
||||
ok bool
|
||||
v int64
|
||||
)
|
||||
if tmpStr, ok = conf.Get("common", "bind_addr"); ok {
|
||||
cfg.BindAddr = tmpStr
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "bind_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid bind_port")
|
||||
return
|
||||
}
|
||||
cfg.BindPort = int(v)
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "bind_udp_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid bind_udp_port")
|
||||
return
|
||||
}
|
||||
cfg.BindUDPPort = int(v)
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "kcp_bind_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid kcp_bind_port")
|
||||
return
|
||||
}
|
||||
cfg.KCPBindPort = int(v)
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "proxy_bind_addr"); ok {
|
||||
cfg.ProxyBindAddr = tmpStr
|
||||
} else {
|
||||
cfg.ProxyBindAddr = cfg.BindAddr
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "vhost_http_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid vhost_http_port")
|
||||
return
|
||||
}
|
||||
cfg.VhostHTTPPort = int(v)
|
||||
} else {
|
||||
cfg.VhostHTTPPort = 0
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "vhost_https_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid vhost_https_port")
|
||||
return
|
||||
}
|
||||
cfg.VhostHTTPSPort = int(v)
|
||||
} else {
|
||||
cfg.VhostHTTPSPort = 0
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "tcpmux_httpconnect_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid tcpmux_httpconnect_port")
|
||||
return
|
||||
}
|
||||
cfg.TCPMuxHTTPConnectPort = int(v)
|
||||
} else {
|
||||
cfg.TCPMuxHTTPConnectPort = 0
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "vhost_http_timeout"); ok {
|
||||
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
||||
if errRet != nil || v < 0 {
|
||||
err = fmt.Errorf("Parse conf error: invalid vhost_http_timeout")
|
||||
return
|
||||
}
|
||||
cfg.VhostHTTPTimeout = v
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "dashboard_addr"); ok {
|
||||
cfg.DashboardAddr = tmpStr
|
||||
} else {
|
||||
cfg.DashboardAddr = cfg.BindAddr
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "dashboard_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid dashboard_port")
|
||||
return
|
||||
}
|
||||
cfg.DashboardPort = int(v)
|
||||
} else {
|
||||
cfg.DashboardPort = 0
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "dashboard_user"); ok {
|
||||
cfg.DashboardUser = tmpStr
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "dashboard_pwd"); ok {
|
||||
cfg.DashboardPwd = tmpStr
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "enable_prometheus"); ok && tmpStr == "true" {
|
||||
cfg.EnablePrometheus = true
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "assets_dir"); ok {
|
||||
cfg.AssetsDir = tmpStr
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "log_file"); ok {
|
||||
cfg.LogFile = tmpStr
|
||||
if cfg.LogFile == "console" {
|
||||
cfg.LogWay = "console"
|
||||
} else {
|
||||
cfg.LogWay = "file"
|
||||
}
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "log_level"); ok {
|
||||
cfg.LogLevel = tmpStr
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "log_max_days"); ok {
|
||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||
if err == nil {
|
||||
cfg.LogMaxDays = v
|
||||
}
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "disable_log_color"); ok && tmpStr == "true" {
|
||||
cfg.DisableLogColor = true
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "detailed_errors_to_client"); ok && tmpStr == "false" {
|
||||
cfg.DetailedErrorsToClient = false
|
||||
} else {
|
||||
cfg.DetailedErrorsToClient = true
|
||||
}
|
||||
|
||||
if allowPortsStr, ok := conf.Get("common", "allow_ports"); ok {
|
||||
// e.g. 1000-2000,2001,2002,3000-4000
|
||||
ports, errRet := util.ParseRangeNumbers(allowPortsStr)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("Parse conf error: allow_ports: %v", errRet)
|
||||
return
|
||||
}
|
||||
|
||||
for _, port := range ports {
|
||||
cfg.AllowPorts[int(port)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "max_pool_count"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid max_pool_count")
|
||||
return
|
||||
}
|
||||
|
||||
if v < 0 {
|
||||
err = fmt.Errorf("Parse conf error: invalid max_pool_count")
|
||||
return
|
||||
}
|
||||
cfg.MaxPoolCount = v
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "max_ports_per_client"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
|
||||
return
|
||||
}
|
||||
|
||||
if v < 0 {
|
||||
err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
|
||||
return
|
||||
}
|
||||
cfg.MaxPortsPerClient = v
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "subdomain_host"); ok {
|
||||
cfg.SubDomainHost = strings.ToLower(strings.TrimSpace(tmpStr))
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" {
|
||||
cfg.TCPMux = false
|
||||
} else {
|
||||
cfg.TCPMux = true
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "custom_404_page"); ok {
|
||||
cfg.Custom404Page = tmpStr
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok {
|
||||
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect")
|
||||
return
|
||||
}
|
||||
cfg.HeartbeatTimeout = v
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "tls_only"); ok && tmpStr == "true" {
|
||||
cfg.TLSOnly = true
|
||||
} else {
|
||||
cfg.TLSOnly = false
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "udp_packet_size"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid udp_packet_size")
|
||||
return
|
||||
}
|
||||
cfg.UDPPacketSize = v
|
||||
}
|
||||
|
||||
if tmpStr, ok := conf.Get("common", "tls_cert_file"); ok {
|
||||
cfg.TLSCertFile = tmpStr
|
||||
}
|
||||
|
||||
if tmpStr, ok := conf.Get("common", "tls_key_file"); ok {
|
||||
cfg.TLSKeyFile = tmpStr
|
||||
}
|
||||
|
||||
if tmpStr, ok := conf.Get("common", "tls_trusted_ca_file"); ok {
|
||||
cfg.TLSTrustedCaFile = tmpStr
|
||||
cfg.TLSOnly = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func UnmarshalPluginsFromIni(sections ini.File, cfg *ServerCommonConf) {
|
||||
for name, section := range sections {
|
||||
if strings.HasPrefix(name, "plugin.") {
|
||||
name = strings.TrimSpace(strings.TrimPrefix(name, "plugin."))
|
||||
var tls_verify, err = strconv.ParseBool(section["tls_verify"])
|
||||
if err != nil {
|
||||
tls_verify = true
|
||||
}
|
||||
options := plugin.HTTPPluginOptions{
|
||||
Name: name,
|
||||
Addr: section["addr"],
|
||||
Path: section["path"],
|
||||
Ops: strings.Split(section["ops"], ","),
|
||||
TLSVerify: tls_verify,
|
||||
}
|
||||
for i := range options.Ops {
|
||||
options.Ops[i] = strings.TrimSpace(options.Ops[i])
|
||||
}
|
||||
cfg.HTTPPlugins[name] = options
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *ServerCommonConf) Check() error {
|
||||
return nil
|
||||
}
|
@ -0,0 +1,207 @@
|
||||
// Copyright 2020 The frp Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/fatedier/frp/pkg/auth"
|
||||
"github.com/fatedier/frp/pkg/plugin/server"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_LoadServerCommonConf(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
testcases := []struct {
|
||||
source []byte
|
||||
expected ServerCommonConf
|
||||
}{
|
||||
{
|
||||
source: []byte(`
|
||||
# [common] is integral section
|
||||
[common]
|
||||
bind_addr = 0.0.0.9
|
||||
bind_port = 7009
|
||||
bind_udp_port = 7008
|
||||
kcp_bind_port = 7007
|
||||
proxy_bind_addr = 127.0.0.9
|
||||
vhost_http_port = 89
|
||||
vhost_https_port = 449
|
||||
vhost_http_timeout = 69
|
||||
tcpmux_httpconnect_port = 1339
|
||||
dashboard_addr = 0.0.0.9
|
||||
dashboard_port = 7509
|
||||
dashboard_user = admin9
|
||||
dashboard_pwd = admin9
|
||||
enable_prometheus
|
||||
assets_dir = ./static9
|
||||
log_file = ./frps.log9
|
||||
log_way = file
|
||||
log_level = info9
|
||||
log_max_days = 39
|
||||
disable_log_color = false
|
||||
detailed_errors_to_client
|
||||
authentication_method = token
|
||||
authenticate_heartbeats = false
|
||||
authenticate_new_work_conns = false
|
||||
token = 123456789
|
||||
oidc_issuer = test9
|
||||
oidc_audience = test9
|
||||
oidc_skip_expiry_check
|
||||
oidc_skip_issuer_check
|
||||
heartbeat_timeout = 99
|
||||
user_conn_timeout = 9
|
||||
allow_ports = 10-12,99
|
||||
max_pool_count = 59
|
||||
max_ports_per_client = 9
|
||||
tls_only = false
|
||||
tls_cert_file = server.crt
|
||||
tls_key_file = server.key
|
||||
tls_trusted_ca_file = ca.crt
|
||||
subdomain_host = frps.com
|
||||
tcp_mux
|
||||
udp_packet_size = 1509
|
||||
[plugin.user-manager]
|
||||
addr = 127.0.0.1:9009
|
||||
path = /handler
|
||||
ops = Login
|
||||
[plugin.port-manager]
|
||||
addr = 127.0.0.1:9009
|
||||
path = /handler
|
||||
ops = NewProxy
|
||||
tls_verify
|
||||
`),
|
||||
expected: ServerCommonConf{
|
||||
ServerConfig: auth.ServerConfig{
|
||||
BaseConfig: auth.BaseConfig{
|
||||
AuthenticationMethod: "token",
|
||||
AuthenticateHeartBeats: false,
|
||||
AuthenticateNewWorkConns: false,
|
||||
},
|
||||
TokenConfig: auth.TokenConfig{
|
||||
Token: "123456789",
|
||||
},
|
||||
OidcServerConfig: auth.OidcServerConfig{
|
||||
OidcIssuer: "test9",
|
||||
OidcAudience: "test9",
|
||||
OidcSkipExpiryCheck: true,
|
||||
OidcSkipIssuerCheck: true,
|
||||
},
|
||||
},
|
||||
BindAddr: "0.0.0.9",
|
||||
BindPort: 7009,
|
||||
BindUDPPort: 7008,
|
||||
KCPBindPort: 7007,
|
||||
ProxyBindAddr: "127.0.0.9",
|
||||
VhostHTTPPort: 89,
|
||||
VhostHTTPSPort: 449,
|
||||
VhostHTTPTimeout: 69,
|
||||
TCPMuxHTTPConnectPort: 1339,
|
||||
DashboardAddr: "0.0.0.9",
|
||||
DashboardPort: 7509,
|
||||
DashboardUser: "admin9",
|
||||
DashboardPwd: "admin9",
|
||||
EnablePrometheus: true,
|
||||
AssetsDir: "./static9",
|
||||
LogFile: "./frps.log9",
|
||||
LogWay: "file",
|
||||
LogLevel: "info9",
|
||||
LogMaxDays: 39,
|
||||
DisableLogColor: false,
|
||||
DetailedErrorsToClient: true,
|
||||
HeartbeatTimeout: 99,
|
||||
UserConnTimeout: 9,
|
||||
AllowPorts: map[int]struct{}{
|
||||
10: struct{}{},
|
||||
11: struct{}{},
|
||||
12: struct{}{},
|
||||
99: struct{}{},
|
||||
},
|
||||
MaxPoolCount: 59,
|
||||
MaxPortsPerClient: 9,
|
||||
TLSOnly: false,
|
||||
TLSCertFile: "server.crt",
|
||||
TLSKeyFile: "server.key",
|
||||
TLSTrustedCaFile: "ca.crt",
|
||||
SubDomainHost: "frps.com",
|
||||
TCPMux: true,
|
||||
UDPPacketSize: 1509,
|
||||
|
||||
HTTPPlugins: map[string]plugin.HTTPPluginOptions{
|
||||
"user-manager": {
|
||||
Name: "user-manager",
|
||||
Addr: "127.0.0.1:9009",
|
||||
Path: "/handler",
|
||||
Ops: []string{"Login"},
|
||||
},
|
||||
"port-manager": {
|
||||
Name: "port-manager",
|
||||
Addr: "127.0.0.1:9009",
|
||||
Path: "/handler",
|
||||
Ops: []string{"NewProxy"},
|
||||
TLSVerify: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
source: []byte(`
|
||||
# [common] is integral section
|
||||
[common]
|
||||
bind_addr = 0.0.0.9
|
||||
bind_port = 7009
|
||||
bind_udp_port = 7008
|
||||
`),
|
||||
expected: ServerCommonConf{
|
||||
ServerConfig: auth.ServerConfig{
|
||||
BaseConfig: auth.BaseConfig{
|
||||
AuthenticationMethod: "token",
|
||||
AuthenticateHeartBeats: false,
|
||||
AuthenticateNewWorkConns: false,
|
||||
},
|
||||
},
|
||||
BindAddr: "0.0.0.9",
|
||||
BindPort: 7009,
|
||||
BindUDPPort: 7008,
|
||||
ProxyBindAddr: "0.0.0.0",
|
||||
VhostHTTPTimeout: 60,
|
||||
DashboardAddr: "0.0.0.0",
|
||||
DashboardUser: "admin",
|
||||
DashboardPwd: "admin",
|
||||
EnablePrometheus: false,
|
||||
LogFile: "console",
|
||||
LogWay: "console",
|
||||
LogLevel: "info",
|
||||
LogMaxDays: 3,
|
||||
DetailedErrorsToClient: true,
|
||||
TCPMux: true,
|
||||
AllowPorts: make(map[int]struct{}),
|
||||
MaxPoolCount: 5,
|
||||
HeartbeatTimeout: 90,
|
||||
UserConnTimeout: 10,
|
||||
HTTPPlugins: make(map[string]plugin.HTTPPluginOptions),
|
||||
UDPPacketSize: 1500,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range testcases {
|
||||
actual, err := UnmarshalServerConfFromIni(c.source)
|
||||
assert.NoError(err)
|
||||
assert.Equal(c.expected, actual)
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
// Copyright 2020 The frp Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GetMapWithoutPrefix(set map[string]string, prefix string) map[string]string {
|
||||
m := make(map[string]string)
|
||||
|
||||
for key, value := range set {
|
||||
if strings.HasPrefix(key, prefix) {
|
||||
m[strings.TrimPrefix(key, prefix)] = value
|
||||
}
|
||||
}
|
||||
|
||||
if len(m) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func GetMapByPrefix(set map[string]string, prefix string) map[string]string {
|
||||
m := make(map[string]string)
|
||||
|
||||
for key, value := range set {
|
||||
if strings.HasPrefix(key, prefix) {
|
||||
m[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
if len(m) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
// Copyright 2020 The frp Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/fatedier/frp/pkg/consts"
|
||||
|
||||
"gopkg.in/ini.v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const testVisitorPrefix = "test."
|
||||
|
||||
func Test_Visitor_Interface(t *testing.T) {
|
||||
for name := range visitorConfTypeMap {
|
||||
DefaultVisitorConf(name)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Visitor_UnmarshalFromIni(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
testcases := []struct {
|
||||
sname string
|
||||
source []byte
|
||||
expected VisitorConf
|
||||
}{
|
||||
{
|
||||
sname: "secret_tcp_visitor",
|
||||
source: []byte(`
|
||||
[secret_tcp_visitor]
|
||||
role = visitor
|
||||
type = stcp
|
||||
server_name = secret_tcp
|
||||
sk = abcdefg
|
||||
bind_addr = 127.0.0.1
|
||||
bind_port = 9000
|
||||
use_encryption = false
|
||||
use_compression = false
|
||||
`),
|
||||
expected: &STCPVisitorConf{
|
||||
BaseVisitorConf: BaseVisitorConf{
|
||||
ProxyName: testVisitorPrefix + "secret_tcp_visitor",
|
||||
ProxyType: consts.STCPProxy,
|
||||
Role: "visitor",
|
||||
Sk: "abcdefg",
|
||||
ServerName: testVisitorPrefix + "secret_tcp",
|
||||
BindAddr: "127.0.0.1",
|
||||
BindPort: 9000,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
sname: "p2p_tcp_visitor",
|
||||
source: []byte(`
|
||||
[p2p_tcp_visitor]
|
||||
role = visitor
|
||||
type = xtcp
|
||||
server_name = p2p_tcp
|
||||
sk = abcdefg
|
||||
bind_addr = 127.0.0.1
|
||||
bind_port = 9001
|
||||
use_encryption = false
|
||||
use_compression = false
|
||||
`),
|
||||
expected: &XTCPVisitorConf{
|
||||
BaseVisitorConf: BaseVisitorConf{
|
||||
ProxyName: testVisitorPrefix + "p2p_tcp_visitor",
|
||||
ProxyType: consts.XTCPProxy,
|
||||
Role: "visitor",
|
||||
Sk: "abcdefg",
|
||||
ServerName: testProxyPrefix + "p2p_tcp",
|
||||
BindAddr: "127.0.0.1",
|
||||
BindPort: 9001,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range testcases {
|
||||
f, err := ini.LoadSources(testLoadOptions, c.source)
|
||||
assert.NoError(err)
|
||||
|
||||
visitorType := f.Section(c.sname).Key("type").String()
|
||||
assert.NotEmpty(visitorType)
|
||||
|
||||
actual := DefaultVisitorConf(visitorType)
|
||||
assert.NotNil(actual)
|
||||
|
||||
err = actual.UnmarshalFromIni(testVisitorPrefix, c.sname, f.Section(c.sname))
|
||||
assert.NoError(err)
|
||||
assert.Equal(c.expected, actual)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue