
Implement a simplified version of STCP functionality. * **Client Proxy Configuration and Handling** - Add `STCPProxyConfig` to the `pxyConfs` slice in `client/proxy/general_tcp.go`. * **Client Visitor Configuration and Handling** - Add `STCPVisitorConfig` to the `cfg` struct in `client/visitor/stcp.go`. * **Command Registration and Initialization** - Add `STCP` to the `proxyTypes` and `visitorTypes` slices in `cmd/frpc/sub/proxy.go`. * **Configuration Examples** - Add example configurations for `STCP` proxy and visitor in `conf/frpc_full_example.toml`. - Add example configurations for `STCP` proxy and visitor in `conf/legacy/frpc_legacy_full.ini`. * **Configuration Structures** - Add `STCPProxyConfig` struct and include it in the `proxyConfigTypeMap` in `pkg/config/v1/proxy.go`. - Add `STCPVisitorConfig` struct and include it in the `visitorConfigTypeMap` in `pkg/config/v1/visitor.go`. * **Configuration Validation** - Add validation for `STCPProxyConfig` in `pkg/config/v1/validation/proxy.go`. - Add validation for `STCPVisitorConfig` in `pkg/config/v1/validation/visitor.go`. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/fatedier/frp?shareId=XXXX-XXXX-XXXX-XXXX).
257 lines
7.4 KiB
Go
257 lines
7.4 KiB
Go
package validation
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
|
|
"k8s.io/apimachinery/pkg/util/validation"
|
|
|
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
|
)
|
|
|
|
func validateProxyBaseConfigForClient(c *v1.ProxyBaseConfig) error {
|
|
if c.Name == "" {
|
|
return errors.New("name should not be empty")
|
|
}
|
|
|
|
if err := ValidateAnnotations(c.Annotations); err != nil {
|
|
return err
|
|
}
|
|
if !slices.Contains([]string{"", "v1", "v2"}, c.Transport.ProxyProtocolVersion) {
|
|
return fmt.Errorf("not support proxy protocol version: %s", c.Transport.ProxyProtocolVersion)
|
|
}
|
|
if !slices.Contains([]string{"client", "server"}, c.Transport.BandwidthLimitMode) {
|
|
return fmt.Errorf("bandwidth limit mode should be client or server")
|
|
}
|
|
|
|
if c.Plugin.Type == "" {
|
|
if err := ValidatePort(c.LocalPort, "localPort"); err != nil {
|
|
return fmt.Errorf("localPort: %v", err)
|
|
}
|
|
}
|
|
|
|
if !slices.Contains([]string{"", "tcp", "http"}, c.HealthCheck.Type) {
|
|
return fmt.Errorf("not support health check type: %s", c.HealthCheck.Type)
|
|
}
|
|
if c.HealthCheck.Type != "" {
|
|
if c.HealthCheck.Type == "http" &&
|
|
c.HealthCheck.Path == "" {
|
|
return fmt.Errorf("health check path should not be empty")
|
|
}
|
|
}
|
|
|
|
if c.Plugin.Type != "" {
|
|
if err := ValidateClientPluginOptions(c.Plugin.ClientPluginOptions); err != nil {
|
|
return fmt.Errorf("plugin %s: %v", c.Plugin.Type, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateProxyBaseConfigForServer(c *v1.ProxyBaseConfig) error {
|
|
if err := ValidateAnnotations(c.Annotations); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateDomainConfigForClient(c *v1.DomainConfig) error {
|
|
if c.SubDomain == "" && len(c.CustomDomains) == 0 {
|
|
return errors.New("subdomain and custom domains should not be both empty")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateDomainConfigForServer(c *v1.DomainConfig, s *v1.ServerConfig) error {
|
|
for _, domain := range c.CustomDomains {
|
|
if s.SubDomainHost != "" && len(strings.Split(s.SubDomainHost, ".")) < len(strings.Split(domain, ".")) {
|
|
if strings.Contains(domain, s.SubDomainHost) {
|
|
return fmt.Errorf("custom domain [%s] should not belong to subdomain host [%s]", domain, s.SubDomainHost)
|
|
}
|
|
}
|
|
}
|
|
|
|
if c.SubDomain != "" {
|
|
if s.SubDomainHost == "" {
|
|
return errors.New("subdomain is not supported because this feature is not enabled in server")
|
|
}
|
|
|
|
if strings.contains(c.SubDomain, ".") || strings.contains(c.SubDomain, "*") {
|
|
return errors.New("'.' and '*' are not supported in subdomain")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ValidateProxyConfigurerForClient(c v1.ProxyConfigurer) error {
|
|
base := c.GetBaseConfig()
|
|
if err := validateProxyBaseConfigForClient(base); err != nil {
|
|
return err
|
|
}
|
|
|
|
switch v := c.(type) {
|
|
case *v1.TCPProxyConfig:
|
|
return validateTCPProxyConfigForClient(v)
|
|
case *v1.UDPProxyConfig:
|
|
return validateUDPProxyConfigForClient(v)
|
|
case *v1.TCPMuxProxyConfig:
|
|
return validateTCPMuxProxyConfigForClient(v)
|
|
case *v1.HTTPProxyConfig:
|
|
return validateHTTPProxyConfigForClient(v)
|
|
case *v1.HTTPSProxyConfig:
|
|
return validateHTTPSProxyConfigForClient(v)
|
|
case *v1.STCPProxyConfig:
|
|
return validateSTCPProxyConfigForClient(v)
|
|
case *v1.XTCPProxyConfig:
|
|
return validateXTCPProxyConfigForClient(v)
|
|
case *v1.SUDPProxyConfig:
|
|
return validateSUDPProxyConfigForClient(v)
|
|
}
|
|
return errors.New("unknown proxy config type")
|
|
}
|
|
|
|
func validateTCPProxyConfigForClient(c *v1.TCPProxyConfig) error {
|
|
return nil
|
|
}
|
|
|
|
func validateUDPProxyConfigForClient(c *v1.UDPProxyConfig) error {
|
|
return nil
|
|
}
|
|
|
|
func validateTCPMuxProxyConfigForClient(c *v1.TCPMuxProxyConfig) error {
|
|
if err := validateDomainConfigForClient(&c.DomainConfig); err != nil {
|
|
return err
|
|
}
|
|
|
|
if !slices.Contains([]string{string(v1.TCPMultiplexerHTTPConnect)}, c.Multiplexer) {
|
|
return fmt.Errorf("not support multiplexer: %s", c.Multiplexer)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateHTTPProxyConfigForClient(c *v1.HTTPProxyConfig) error {
|
|
return validateDomainConfigForClient(&c.DomainConfig)
|
|
}
|
|
|
|
func validateHTTPSProxyConfigForClient(c *v1.HTTPSProxyConfig) error {
|
|
return validateDomainConfigForClient(&c.DomainConfig)
|
|
}
|
|
|
|
func validateSTCPProxyConfigForClient(c *v1.STCPProxyConfig) error {
|
|
return nil
|
|
}
|
|
|
|
func validateXTCPProxyConfigForClient(c *v1.XTCPProxyConfig) error {
|
|
return nil
|
|
}
|
|
|
|
func validateSUDPProxyConfigForClient(c *v1.SUDPProxyConfig) error {
|
|
return nil
|
|
}
|
|
|
|
func ValidateProxyConfigurerForServer(c v1.ProxyConfigurer, s *v1.ServerConfig) error {
|
|
base := c.GetBaseConfig()
|
|
if err := validateProxyBaseConfigForServer(base); err != nil {
|
|
return err
|
|
}
|
|
|
|
switch v := c.(type) {
|
|
case *v1.TCPProxyConfig:
|
|
return validateTCPProxyConfigForServer(v, s)
|
|
case *v1.UDPProxyConfig:
|
|
return validateUDPProxyConfigForServer(v, s)
|
|
case *v1.TCPMuxProxyConfig:
|
|
return validateTCPMuxProxyConfigForServer(v, s)
|
|
case *v1.HTTPProxyConfig:
|
|
return validateHTTPProxyConfigForServer(v, s)
|
|
case *v1.HTTPSProxyConfig:
|
|
return validateHTTPSProxyConfigForServer(v, s)
|
|
case *v1.STCPProxyConfig:
|
|
return validateSTCPProxyConfigForServer(v, s)
|
|
case *v1.XTCPProxyConfig:
|
|
return validateXTCPProxyConfigForServer(v, s)
|
|
case *v1.SUDPProxyConfig:
|
|
return validateSUDPProxyConfigForServer(v, s)
|
|
default:
|
|
return errors.New("unknown proxy config type")
|
|
}
|
|
}
|
|
|
|
func validateTCPProxyConfigForServer(c *v1.TCPProxyConfig, s *v1.ServerConfig) error {
|
|
return nil
|
|
}
|
|
|
|
func validateUDPProxyConfigForServer(c *v1.UDPProxyConfig, s *v1.ServerConfig) error {
|
|
return nil
|
|
}
|
|
|
|
func validateTCPMuxProxyConfigForServer(c *v1.TCPMuxProxyConfig, s *v1.ServerConfig) error {
|
|
if c.Multiplexer == string(v1.TCPMultiplexerHTTPConnect) &&
|
|
s.TCPMuxHTTPConnectPort == 0 {
|
|
return fmt.Errorf("tcpmux with multiplexer httpconnect not supported because this feature is not enabled in server")
|
|
}
|
|
|
|
return validateDomainConfigForServer(&c.DomainConfig, s)
|
|
}
|
|
|
|
func validateHTTPProxyConfigForServer(c *v1.HTTPProxyConfig, s *v1.ServerConfig) error {
|
|
if s.VhostHTTPPort == 0 {
|
|
return fmt.Errorf("type [http] not supported when vhost http port is not set")
|
|
}
|
|
|
|
return validateDomainConfigForServer(&c.DomainConfig, s)
|
|
}
|
|
|
|
func validateHTTPSProxyConfigForServer(c *v1.HTTPSProxyConfig, s *v1.ServerConfig) error {
|
|
if s.VhostHTTPSPort == 0 {
|
|
return fmt.Errorf("type [https] not supported when vhost https port is not set")
|
|
}
|
|
|
|
return validateDomainConfigForServer(&c.DomainConfig, s)
|
|
}
|
|
|
|
func validateSTCPProxyConfigForServer(c *v1.STCPProxyConfig, s *v1.ServerConfig) error {
|
|
return nil
|
|
}
|
|
|
|
func validateXTCPProxyConfigForServer(c *v1.XTCPProxyConfig, s *v1.ServerConfig) error {
|
|
return nil
|
|
}
|
|
|
|
func validateSUDPProxyConfigForServer(c *v1.SUDPProxyConfig, s *v1.ServerConfig) error {
|
|
return nil
|
|
}
|
|
|
|
// ValidateAnnotations validates that a set of annotations are correctly defined.
|
|
func ValidateAnnotations(annotations map[string]string) error {
|
|
if len(annotations) == 0 {
|
|
return nil
|
|
}
|
|
|
|
var errs error
|
|
for k := range annotations {
|
|
for _, msg := range validation.IsQualifiedName(strings.ToLower(k)) {
|
|
errs = AppendError(errs, fmt.Errorf("annotation key %s is invalid: %s", k, msg))
|
|
}
|
|
}
|
|
if err := ValidateAnnotationsSize(annotations); err != nil {
|
|
errs = AppendError(errs, err)
|
|
}
|
|
return errs
|
|
}
|
|
|
|
const TotalAnnotationSizeLimitB int = 256 * (1 << 10) // 256 kB
|
|
|
|
func ValidateAnnotationsSize(annotations map[string]string) error {
|
|
var totalSize int64
|
|
for k, v := range annotations {
|
|
totalSize += (int64)(len(k)) + (int64)(len(v))
|
|
}
|
|
if totalSize > (int64)(TotalAnnotationSizeLimitB) {
|
|
return fmt.Errorf("annotations size %d is larger than limit %d", totalSize, TotalAnnotationSizeLimitB)
|
|
}
|
|
return nil
|
|
}
|