2023-09-06 10:18:02 +08:00
|
|
|
// Copyright 2023 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 v1
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
|
|
|
|
"github.com/samber/lo"
|
|
|
|
|
|
|
|
"github.com/fatedier/frp/pkg/config/types"
|
|
|
|
"github.com/fatedier/frp/pkg/msg"
|
|
|
|
"github.com/fatedier/frp/pkg/util/util"
|
|
|
|
)
|
|
|
|
|
|
|
|
type ProxyTransport struct {
|
|
|
|
// UseEncryption controls whether or not communication with the server will
|
|
|
|
// be encrypted. Encryption is done using the tokens supplied in the server
|
|
|
|
// and client configuration.
|
|
|
|
UseEncryption bool `json:"useEncryption,omitempty"`
|
|
|
|
// UseCompression controls whether or not communication with the server
|
|
|
|
// will be compressed.
|
|
|
|
UseCompression bool `json:"useCompression,omitempty"`
|
|
|
|
// BandwidthLimit limit the bandwidth
|
|
|
|
// 0 means no limit
|
|
|
|
BandwidthLimit types.BandwidthQuantity `json:"bandwidthLimit,omitempty"`
|
|
|
|
// BandwidthLimitMode specifies whether to limit the bandwidth on the
|
|
|
|
// client or server side. Valid values include "client" and "server".
|
|
|
|
// By default, this value is "client".
|
|
|
|
BandwidthLimitMode string `json:"bandwidthLimitMode,omitempty"`
|
|
|
|
// ProxyProtocolVersion specifies which protocol version to use. Valid
|
|
|
|
// values include "v1", "v2", and "". If the value is "", a protocol
|
|
|
|
// version will be automatically selected. By default, this value is "".
|
|
|
|
ProxyProtocolVersion string `json:"proxyProtocolVersion,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type LoadBalancerConfig struct {
|
|
|
|
// Group specifies which group the is a part of. The server will use
|
|
|
|
// this information to load balance proxies in the same group. If the value
|
|
|
|
// is "", this will not be in a group.
|
|
|
|
Group string `json:"group"`
|
|
|
|
// GroupKey specifies a group key, which should be the same among proxies
|
|
|
|
// of the same group.
|
|
|
|
GroupKey string `json:"groupKey,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type ProxyBackend struct {
|
|
|
|
// LocalIP specifies the IP address or host name of the backend.
|
|
|
|
LocalIP string `json:"localIP,omitempty"`
|
|
|
|
// LocalPort specifies the port of the backend.
|
|
|
|
LocalPort int `json:"localPort,omitempty"`
|
|
|
|
|
|
|
|
// Plugin specifies what plugin should be used for handling connections. If this value
|
|
|
|
// is set, the LocalIP and LocalPort values will be ignored.
|
|
|
|
Plugin TypedClientPluginOptions `json:"plugin,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// HealthCheckConfig configures health checking. This can be useful for load
|
|
|
|
// balancing purposes to detect and remove proxies to failing services.
|
|
|
|
type HealthCheckConfig struct {
|
|
|
|
// Type specifies what protocol to use for health checking.
|
|
|
|
// Valid values include "tcp", "http", and "". If this value is "", health
|
|
|
|
// checking will not be performed.
|
|
|
|
//
|
|
|
|
// If the type is "tcp", a connection will be attempted to the target
|
|
|
|
// server. If a connection cannot be established, the health check fails.
|
|
|
|
//
|
|
|
|
// If the type is "http", a GET request will be made to the endpoint
|
|
|
|
// specified by HealthCheckURL. If the response is not a 200, the health
|
|
|
|
// check fails.
|
|
|
|
Type string `json:"type"` // tcp | http
|
|
|
|
// TimeoutSeconds specifies the number of seconds to wait for a health
|
|
|
|
// check attempt to connect. If the timeout is reached, this counts as a
|
|
|
|
// health check failure. By default, this value is 3.
|
|
|
|
TimeoutSeconds int `json:"timeoutSeconds,omitempty"`
|
|
|
|
// MaxFailed specifies the number of allowed failures before the
|
|
|
|
// is stopped. By default, this value is 1.
|
|
|
|
MaxFailed int `json:"maxFailed,omitempty"`
|
|
|
|
// IntervalSeconds specifies the time in seconds between health
|
|
|
|
// checks. By default, this value is 10.
|
|
|
|
IntervalSeconds int `json:"intervalSeconds"`
|
|
|
|
// Path specifies the path to send health checks to if the
|
|
|
|
// health check type is "http".
|
|
|
|
Path string `json:"path,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type DomainConfig struct {
|
|
|
|
CustomDomains []string `json:"customDomains,omitempty"`
|
|
|
|
SubDomain string `json:"subdomain,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type ProxyBaseConfig struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
Type string `json:"type"`
|
|
|
|
Transport ProxyTransport `json:"transport,omitempty"`
|
|
|
|
// metadata info for each proxy
|
|
|
|
Metadatas map[string]string `json:"metadatas,omitempty"`
|
|
|
|
LoadBalancer LoadBalancerConfig `json:"loadBalancer,omitempty"`
|
|
|
|
HealthCheck HealthCheckConfig `json:"healthCheck,omitempty"`
|
|
|
|
ProxyBackend
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ProxyBaseConfig) GetBaseConfig() *ProxyBaseConfig {
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ProxyBaseConfig) Complete(namePrefix string) {
|
|
|
|
c.Name = lo.Ternary(namePrefix == "", "", namePrefix+".") + c.Name
|
|
|
|
c.LocalIP = util.EmptyOr(c.LocalIP, "127.0.0.1")
|
|
|
|
c.Transport.BandwidthLimitMode = util.EmptyOr(c.Transport.BandwidthLimitMode, types.BandwidthLimitModeClient)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ProxyBaseConfig) MarshalToMsg(m *msg.NewProxy) {
|
|
|
|
m.ProxyName = c.Name
|
|
|
|
m.ProxyType = c.Type
|
|
|
|
m.UseEncryption = c.Transport.UseEncryption
|
|
|
|
m.UseCompression = c.Transport.UseCompression
|
|
|
|
m.BandwidthLimit = c.Transport.BandwidthLimit.String()
|
|
|
|
// leave it empty for default value to reduce traffic
|
|
|
|
if c.Transport.BandwidthLimitMode != "client" {
|
|
|
|
m.BandwidthLimitMode = c.Transport.BandwidthLimitMode
|
|
|
|
}
|
|
|
|
m.Group = c.LoadBalancer.Group
|
|
|
|
m.GroupKey = c.LoadBalancer.GroupKey
|
|
|
|
m.Metas = c.Metadatas
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ProxyBaseConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
|
|
c.Name = m.ProxyName
|
|
|
|
c.Type = m.ProxyType
|
|
|
|
c.Transport.UseEncryption = m.UseEncryption
|
|
|
|
c.Transport.UseCompression = m.UseCompression
|
|
|
|
if m.BandwidthLimit != "" {
|
|
|
|
c.Transport.BandwidthLimit, _ = types.NewBandwidthQuantity(m.BandwidthLimit)
|
|
|
|
}
|
|
|
|
if m.BandwidthLimitMode != "" {
|
|
|
|
c.Transport.BandwidthLimitMode = m.BandwidthLimitMode
|
|
|
|
}
|
|
|
|
c.LoadBalancer.Group = m.Group
|
|
|
|
c.LoadBalancer.GroupKey = m.GroupKey
|
|
|
|
c.Metadatas = m.Metas
|
|
|
|
}
|
|
|
|
|
|
|
|
type TypedProxyConfig struct {
|
|
|
|
Type string `json:"type"`
|
|
|
|
ProxyConfigurer
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *TypedProxyConfig) UnmarshalJSON(b []byte) error {
|
|
|
|
if len(b) == 4 && string(b) == "null" {
|
|
|
|
return errors.New("type is required")
|
|
|
|
}
|
|
|
|
|
|
|
|
typeStruct := struct {
|
|
|
|
Type string `json:"type"`
|
|
|
|
}{}
|
|
|
|
if err := json.Unmarshal(b, &typeStruct); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
c.Type = typeStruct.Type
|
2023-09-20 15:18:50 +08:00
|
|
|
configurer := NewProxyConfigurerByType(ProxyType(typeStruct.Type))
|
2023-09-06 10:18:02 +08:00
|
|
|
if configurer == nil {
|
|
|
|
return fmt.Errorf("unknown proxy type: %s", typeStruct.Type)
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(b, configurer); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.ProxyConfigurer = configurer
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type ProxyConfigurer interface {
|
|
|
|
Complete(namePrefix string)
|
|
|
|
GetBaseConfig() *ProxyBaseConfig
|
|
|
|
// MarshalToMsg marshals this config into a msg.NewProxy message. This
|
|
|
|
// function will be called on the frpc side.
|
|
|
|
MarshalToMsg(*msg.NewProxy)
|
|
|
|
// UnmarshalFromMsg unmarshals a msg.NewProxy message into this config.
|
|
|
|
// This function will be called on the frps side.
|
|
|
|
UnmarshalFromMsg(*msg.NewProxy)
|
|
|
|
}
|
|
|
|
|
2023-09-20 15:18:50 +08:00
|
|
|
type ProxyType string
|
|
|
|
|
|
|
|
const (
|
|
|
|
ProxyTypeTCP ProxyType = "tcp"
|
|
|
|
ProxyTypeUDP ProxyType = "udp"
|
|
|
|
ProxyTypeTCPMUX ProxyType = "tcpmux"
|
|
|
|
ProxyTypeHTTP ProxyType = "http"
|
|
|
|
ProxyTypeHTTPS ProxyType = "https"
|
|
|
|
ProxyTypeSTCP ProxyType = "stcp"
|
|
|
|
ProxyTypeXTCP ProxyType = "xtcp"
|
|
|
|
ProxyTypeSUDP ProxyType = "sudp"
|
|
|
|
)
|
|
|
|
|
|
|
|
var proxyConfigTypeMap = map[ProxyType]reflect.Type{
|
|
|
|
ProxyTypeTCP: reflect.TypeOf(TCPProxyConfig{}),
|
|
|
|
ProxyTypeUDP: reflect.TypeOf(UDPProxyConfig{}),
|
|
|
|
ProxyTypeHTTP: reflect.TypeOf(HTTPProxyConfig{}),
|
|
|
|
ProxyTypeHTTPS: reflect.TypeOf(HTTPSProxyConfig{}),
|
|
|
|
ProxyTypeTCPMUX: reflect.TypeOf(TCPMuxProxyConfig{}),
|
|
|
|
ProxyTypeSTCP: reflect.TypeOf(STCPProxyConfig{}),
|
|
|
|
ProxyTypeXTCP: reflect.TypeOf(XTCPProxyConfig{}),
|
|
|
|
ProxyTypeSUDP: reflect.TypeOf(SUDPProxyConfig{}),
|
2023-09-06 10:18:02 +08:00
|
|
|
}
|
|
|
|
|
2023-09-20 15:18:50 +08:00
|
|
|
func NewProxyConfigurerByType(proxyType ProxyType) ProxyConfigurer {
|
2023-09-06 10:18:02 +08:00
|
|
|
v, ok := proxyConfigTypeMap[proxyType]
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return reflect.New(v).Interface().(ProxyConfigurer)
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ProxyConfigurer = &TCPProxyConfig{}
|
|
|
|
|
|
|
|
type TCPProxyConfig struct {
|
|
|
|
ProxyBaseConfig
|
|
|
|
|
|
|
|
RemotePort int `json:"remotePort,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *TCPProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
|
|
|
|
|
|
|
m.RemotePort = c.RemotePort
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *TCPProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
|
|
|
|
|
|
|
c.RemotePort = m.RemotePort
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ProxyConfigurer = &UDPProxyConfig{}
|
|
|
|
|
|
|
|
type UDPProxyConfig struct {
|
|
|
|
ProxyBaseConfig
|
|
|
|
|
|
|
|
RemotePort int `json:"remotePort,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *UDPProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
|
|
|
|
|
|
|
m.RemotePort = c.RemotePort
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *UDPProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
|
|
|
|
|
|
|
c.RemotePort = m.RemotePort
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ProxyConfigurer = &HTTPProxyConfig{}
|
|
|
|
|
|
|
|
type HTTPProxyConfig struct {
|
|
|
|
ProxyBaseConfig
|
|
|
|
DomainConfig
|
|
|
|
|
|
|
|
Locations []string `json:"locations,omitempty"`
|
|
|
|
HTTPUser string `json:"httpUser,omitempty"`
|
|
|
|
HTTPPassword string `json:"httpPassword,omitempty"`
|
|
|
|
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
|
|
|
|
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
|
2023-10-10 16:48:13 +08:00
|
|
|
RouteByHTTPUser string `json:"routeByHTTPUser,omitempty"`
|
2023-09-06 10:18:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *HTTPProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
|
|
|
|
|
|
|
m.CustomDomains = c.CustomDomains
|
|
|
|
m.SubDomain = c.SubDomain
|
|
|
|
m.Locations = c.Locations
|
|
|
|
m.HostHeaderRewrite = c.HostHeaderRewrite
|
|
|
|
m.HTTPUser = c.HTTPUser
|
|
|
|
m.HTTPPwd = c.HTTPPassword
|
|
|
|
m.Headers = c.RequestHeaders.Set
|
|
|
|
m.RouteByHTTPUser = c.RouteByHTTPUser
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *HTTPProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
|
|
|
|
|
|
|
c.CustomDomains = m.CustomDomains
|
|
|
|
c.SubDomain = m.SubDomain
|
|
|
|
c.Locations = m.Locations
|
|
|
|
c.HostHeaderRewrite = m.HostHeaderRewrite
|
|
|
|
c.HTTPUser = m.HTTPUser
|
|
|
|
c.HTTPPassword = m.HTTPPwd
|
|
|
|
c.RequestHeaders.Set = m.Headers
|
|
|
|
c.RouteByHTTPUser = m.RouteByHTTPUser
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ProxyConfigurer = &HTTPSProxyConfig{}
|
|
|
|
|
|
|
|
type HTTPSProxyConfig struct {
|
|
|
|
ProxyBaseConfig
|
|
|
|
DomainConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *HTTPSProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
|
|
|
|
|
|
|
m.CustomDomains = c.CustomDomains
|
|
|
|
m.SubDomain = c.SubDomain
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *HTTPSProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
|
|
|
|
|
|
|
c.CustomDomains = m.CustomDomains
|
|
|
|
c.SubDomain = m.SubDomain
|
|
|
|
}
|
|
|
|
|
2023-09-20 15:18:50 +08:00
|
|
|
type TCPMultiplexerType string
|
|
|
|
|
|
|
|
const (
|
|
|
|
TCPMultiplexerHTTPConnect TCPMultiplexerType = "httpconnect"
|
|
|
|
)
|
|
|
|
|
2023-09-06 10:18:02 +08:00
|
|
|
var _ ProxyConfigurer = &TCPMuxProxyConfig{}
|
|
|
|
|
|
|
|
type TCPMuxProxyConfig struct {
|
|
|
|
ProxyBaseConfig
|
|
|
|
DomainConfig
|
|
|
|
|
|
|
|
HTTPUser string `json:"httpUser,omitempty"`
|
|
|
|
HTTPPassword string `json:"httpPassword,omitempty"`
|
2023-10-10 16:48:13 +08:00
|
|
|
RouteByHTTPUser string `json:"routeByHTTPUser,omitempty"`
|
2023-09-06 10:18:02 +08:00
|
|
|
Multiplexer string `json:"multiplexer,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *TCPMuxProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
|
|
|
|
|
|
|
m.CustomDomains = c.CustomDomains
|
|
|
|
m.SubDomain = c.SubDomain
|
|
|
|
m.Multiplexer = c.Multiplexer
|
|
|
|
m.HTTPUser = c.HTTPUser
|
|
|
|
m.HTTPPwd = c.HTTPPassword
|
|
|
|
m.RouteByHTTPUser = c.RouteByHTTPUser
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *TCPMuxProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
|
|
|
|
|
|
|
c.CustomDomains = m.CustomDomains
|
|
|
|
c.SubDomain = m.SubDomain
|
|
|
|
c.Multiplexer = m.Multiplexer
|
|
|
|
c.HTTPUser = m.HTTPUser
|
|
|
|
c.HTTPPassword = m.HTTPPwd
|
|
|
|
c.RouteByHTTPUser = m.RouteByHTTPUser
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ProxyConfigurer = &STCPProxyConfig{}
|
|
|
|
|
|
|
|
type STCPProxyConfig struct {
|
|
|
|
ProxyBaseConfig
|
|
|
|
|
|
|
|
Secretkey string `json:"secretKey,omitempty"`
|
|
|
|
AllowUsers []string `json:"allowUsers,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *STCPProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
|
|
|
|
|
|
|
m.Sk = c.Secretkey
|
|
|
|
m.AllowUsers = c.AllowUsers
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *STCPProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
|
|
|
|
|
|
|
c.Secretkey = m.Sk
|
|
|
|
c.AllowUsers = m.AllowUsers
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ProxyConfigurer = &XTCPProxyConfig{}
|
|
|
|
|
|
|
|
type XTCPProxyConfig struct {
|
|
|
|
ProxyBaseConfig
|
|
|
|
|
|
|
|
Secretkey string `json:"secretKey,omitempty"`
|
|
|
|
AllowUsers []string `json:"allowUsers,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *XTCPProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
|
|
|
|
|
|
|
m.Sk = c.Secretkey
|
|
|
|
m.AllowUsers = c.AllowUsers
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *XTCPProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
|
|
|
|
|
|
|
c.Secretkey = m.Sk
|
|
|
|
c.AllowUsers = m.AllowUsers
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ProxyConfigurer = &SUDPProxyConfig{}
|
|
|
|
|
|
|
|
type SUDPProxyConfig struct {
|
|
|
|
ProxyBaseConfig
|
|
|
|
|
|
|
|
Secretkey string `json:"secretKey,omitempty"`
|
|
|
|
AllowUsers []string `json:"allowUsers,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *SUDPProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
|
|
|
|
|
|
|
m.Sk = c.Secretkey
|
|
|
|
m.AllowUsers = c.AllowUsers
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *SUDPProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
|
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
|
|
|
|
|
|
|
c.Secretkey = m.Sk
|
|
|
|
c.AllowUsers = m.AllowUsers
|
|
|
|
}
|