/
server.go
300 lines (274 loc) · 12 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
// 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/HaidyCao/frp_0362/pkg/auth"
plugin "github.com/HaidyCao/frp_0362/pkg/plugin/server"
"github.com/HaidyCao/frp_0362/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"`
// 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.
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: "",
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 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("invalid 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 (cfg *ServerCommonConf) Complete() {
if cfg.LogFile == "console" {
cfg.LogWay = "console"
} else {
cfg.LogWay = "file"
}
if cfg.ProxyBindAddr == "" {
cfg.ProxyBindAddr = cfg.BindAddr
}
if cfg.TLSTrustedCaFile != "" {
cfg.TLSOnly = true
}
}
func (cfg *ServerCommonConf) Validate() error {
return 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
}