diff --git a/.golangci.yml b/.golangci.yml index 3ba2c60f..f7c0e8bd 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -39,6 +39,7 @@ linters: - G404 - G501 - G115 + - G204 severity: low confidence: low govet: diff --git a/cmd/frpc/sub/proxy.go b/cmd/frpc/sub/proxy.go index 64c0aade..0b0251a2 100644 --- a/cmd/frpc/sub/proxy.go +++ b/cmd/frpc/sub/proxy.go @@ -78,7 +78,7 @@ func NewProxyCommand(name string, c v1.ProxyConfigurer, clientCfg *v1.ClientComm os.Exit(1) } - unsafeFeatures := v1.UnsafeFeatures{TokenSourceExec: slices.Contains(allowUnsafe, TokenSourceExec)} + unsafeFeatures := v1.NewUnsafeFeatures(allowUnsafe) if _, err := validation.ValidateClientCommonConfig(clientCfg, unsafeFeatures); err != nil { fmt.Println(err) os.Exit(1) @@ -108,7 +108,7 @@ func NewVisitorCommand(name string, c v1.VisitorConfigurer, clientCfg *v1.Client fmt.Println(err) os.Exit(1) } - unsafeFeatures := v1.UnsafeFeatures{TokenSourceExec: slices.Contains(allowUnsafe, TokenSourceExec)} + unsafeFeatures := v1.NewUnsafeFeatures(allowUnsafe) if _, err := validation.ValidateClientCommonConfig(clientCfg, unsafeFeatures); err != nil { fmt.Println(err) os.Exit(1) diff --git a/cmd/frpc/sub/root.go b/cmd/frpc/sub/root.go index b9a373b1..68d69158 100644 --- a/cmd/frpc/sub/root.go +++ b/cmd/frpc/sub/root.go @@ -21,7 +21,6 @@ import ( "os" "os/signal" "path/filepath" - "slices" "sync" "syscall" "time" @@ -37,18 +36,12 @@ import ( "github.com/fatedier/frp/pkg/util/version" ) -type UnsafeFeature = string - -const ( - TokenSourceExec UnsafeFeature = "TokenSourceExec" -) - var ( cfgFile string cfgDir string showVersion bool strictConfigMode bool - allowUnsafe []UnsafeFeature + allowUnsafe []string ) func init() { @@ -56,7 +49,7 @@ func init() { rootCmd.PersistentFlags().StringVarP(&cfgDir, "config_dir", "", "", "config directory, run one frpc service for each file in config directory") rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc") rootCmd.PersistentFlags().BoolVarP(&strictConfigMode, "strict_config", "", true, "strict config parsing mode, unknown fields will cause an errors") - rootCmd.PersistentFlags().StringSliceVarP(&allowUnsafe, "allow_unsafe", "", []string{}, "allowed unsafe features, one or more of: TokenSourceExec") + rootCmd.PersistentFlags().StringSliceVarP(&allowUnsafe, "allow-unsafe", "", []string{}, "allowed unsafe features, one or more of: TokenSourceExec") } var rootCmd = &cobra.Command{ @@ -68,7 +61,7 @@ var rootCmd = &cobra.Command{ return nil } - unsafeFeatures := v1.UnsafeFeatures{TokenSourceExec: slices.Contains(allowUnsafe, TokenSourceExec)} + unsafeFeatures := v1.NewUnsafeFeatures(allowUnsafe) // If cfgDir is not empty, run multiple frpc service for each config file in cfgDir. // Note that it's only designed for testing. It's not guaranteed to be stable. diff --git a/cmd/frpc/sub/verify.go b/cmd/frpc/sub/verify.go index 2198114c..c0c9d0e2 100644 --- a/cmd/frpc/sub/verify.go +++ b/cmd/frpc/sub/verify.go @@ -17,7 +17,6 @@ package sub import ( "fmt" "os" - "slices" "github.com/spf13/cobra" @@ -44,7 +43,7 @@ var verifyCmd = &cobra.Command{ fmt.Println(err) os.Exit(1) } - unsafeFeatures := v1.UnsafeFeatures{TokenSourceExec: slices.Contains(allowUnsafe, TokenSourceExec)} + unsafeFeatures := v1.NewUnsafeFeatures(allowUnsafe) warning, err := validation.ValidateAllClientConfig(cliCfg, proxyCfgs, visitorCfgs, unsafeFeatures) if warning != nil { fmt.Printf("WARNING: %v\n", warning) diff --git a/pkg/config/v1/client.go b/pkg/config/v1/client.go index 72a19fb3..8528ff7e 100644 --- a/pkg/config/v1/client.go +++ b/pkg/config/v1/client.go @@ -249,6 +249,22 @@ type VirtualNetConfig struct { Address string `json:"address,omitempty"` } +const ( + UnsafeFeatureTokenSourceExec = "TokenSourceExec" +) + type UnsafeFeatures struct { - TokenSourceExec bool + features map[string]bool +} + +func NewUnsafeFeatures(allowed []string) UnsafeFeatures { + features := make(map[string]bool) + for _, f := range allowed { + features[f] = true + } + return UnsafeFeatures{features: features} +} + +func (u UnsafeFeatures) IsEnabled(feature string) bool { + return u.features[feature] } diff --git a/pkg/config/v1/validation/client.go b/pkg/config/v1/validation/client.go index 7734a5a4..1c21f01d 100644 --- a/pkg/config/v1/validation/client.go +++ b/pkg/config/v1/validation/client.go @@ -52,7 +52,7 @@ func ValidateClientCommonConfig(c *v1.ClientCommonConfig, unsafeFeatures v1.Unsa // Validate tokenSource if specified if c.Auth.TokenSource != nil { - if c.Auth.TokenSource.Type == "exec" && !unsafeFeatures.TokenSourceExec { + if c.Auth.TokenSource.Type == "exec" && !unsafeFeatures.IsEnabled(v1.UnsafeFeatureTokenSourceExec) { errs = AppendError(errs, fmt.Errorf("unsafe 'exec' not allowed for auth.tokenSource.type")) } if err := c.Auth.TokenSource.Validate(); err != nil { @@ -62,10 +62,12 @@ func ValidateClientCommonConfig(c *v1.ClientCommonConfig, unsafeFeatures v1.Unsa if c.Auth.OIDC.TokenSource != nil { // Validate oidc.tokenSource mutual exclusivity with other fields of oidc - if c.Auth.OIDC.ClientID != "" || c.Auth.OIDC.ClientSecret != "" || c.Auth.OIDC.Audience != "" || c.Auth.OIDC.Scope != "" || c.Auth.OIDC.TokenEndpointURL != "" || len(c.Auth.OIDC.AdditionalEndpointParams) > 0 || c.Auth.OIDC.TrustedCaFile != "" || c.Auth.OIDC.InsecureSkipVerify || c.Auth.OIDC.ProxyURL != "" { + if c.Auth.OIDC.ClientID != "" || c.Auth.OIDC.ClientSecret != "" || c.Auth.OIDC.Audience != "" || + c.Auth.OIDC.Scope != "" || c.Auth.OIDC.TokenEndpointURL != "" || len(c.Auth.OIDC.AdditionalEndpointParams) > 0 || + c.Auth.OIDC.TrustedCaFile != "" || c.Auth.OIDC.InsecureSkipVerify || c.Auth.OIDC.ProxyURL != "" { errs = AppendError(errs, fmt.Errorf("cannot specify both auth.oidc.tokenSource and any other field of auth.oidc")) } - if c.Auth.OIDC.TokenSource.Type == "exec" && !unsafeFeatures.TokenSourceExec { + if c.Auth.OIDC.TokenSource.Type == "exec" && !unsafeFeatures.IsEnabled(v1.UnsafeFeatureTokenSourceExec) { errs = AppendError(errs, fmt.Errorf("unsafe 'exec' not allowed for auth.oidc.tokenSource.type")) } } @@ -114,7 +116,12 @@ func ValidateClientCommonConfig(c *v1.ClientCommonConfig, unsafeFeatures v1.Unsa return warnings, errs } -func ValidateAllClientConfig(c *v1.ClientCommonConfig, proxyCfgs []v1.ProxyConfigurer, visitorCfgs []v1.VisitorConfigurer, unsafeFeatures v1.UnsafeFeatures) (Warning, error) { +func ValidateAllClientConfig( + c *v1.ClientCommonConfig, + proxyCfgs []v1.ProxyConfigurer, + visitorCfgs []v1.VisitorConfigurer, + unsafeFeatures v1.UnsafeFeatures, +) (Warning, error) { var warnings Warning if c != nil { warning, err := ValidateClientCommonConfig(c, unsafeFeatures)