Selaa lähdekoodia

auth: enhance OIDC client with TLS and proxy configuration options (#4990)

fatedier 1 kuukausi sitten
vanhempi
commit
abf4942e8a
6 muutettua tiedostoa jossa 108 lisäystä ja 8 poistoa
  1. 1 0
      Release.md
  2. 7 1
      client/service.go
  3. 14 0
      conf/frpc_full_example.toml
  4. 7 4
      pkg/auth/auth.go
  5. 68 3
      pkg/auth/oidc.go
  6. 11 0
      pkg/config/v1/client.go

+ 1 - 0
Release.md

@@ -1,3 +1,4 @@
 ## Features
 
 * Add NAT traversal configuration options for XTCP proxies and visitors. Support disabling assisted addresses to avoid using slow VPN connections during NAT hole punching.
+* Enhanced OIDC client configuration with support for custom TLS certificate verification and proxy settings. Added `trustedCaFile`, `insecureSkipVerify`, and `proxyURL` options for OIDC token endpoint connections.

+ 7 - 1
client/service.go

@@ -149,9 +149,15 @@ func NewService(options ServiceOptions) (*Service, error) {
 		}
 		webServer = ws
 	}
+
+	authSetter, err := auth.NewAuthSetter(options.Common.Auth)
+	if err != nil {
+		return nil, err
+	}
+
 	s := &Service{
 		ctx:              context.Background(),
-		authSetter:       auth.NewAuthSetter(options.Common.Auth),
+		authSetter:       authSetter,
 		webServer:        webServer,
 		common:           options.Common,
 		configFilePath:   options.ConfigFilePath,

+ 14 - 0
conf/frpc_full_example.toml

@@ -55,6 +55,20 @@ auth.token = "12345678"
 # auth.oidc.additionalEndpointParams.audience = "https://dev.auth.com/api/v2/"
 # auth.oidc.additionalEndpointParams.var1 = "foobar"
 
+# OIDC TLS and proxy configuration
+# Specify a custom CA certificate file for verifying the OIDC token endpoint's TLS certificate.
+# This is useful when the OIDC provider uses a self-signed certificate or a custom CA.
+# auth.oidc.trustedCaFile = "/path/to/ca.crt"
+
+# Skip TLS certificate verification for the OIDC token endpoint.
+# INSECURE: Only use this for debugging purposes, not recommended for production.
+# auth.oidc.insecureSkipVerify = false
+
+# Specify a proxy server for OIDC token endpoint connections.
+# Supports http, https, socks5, and socks5h proxy protocols.
+# If not specified, no proxy is used for OIDC connections.
+# auth.oidc.proxyURL = "http://proxy.example.com:8080"
+
 # Set admin address for control frpc's action by http api such as reload
 webServer.addr = "127.0.0.1"
 webServer.port = 7400

+ 7 - 4
pkg/auth/auth.go

@@ -27,16 +27,19 @@ type Setter interface {
 	SetNewWorkConn(*msg.NewWorkConn) error
 }
 
-func NewAuthSetter(cfg v1.AuthClientConfig) (authProvider Setter) {
+func NewAuthSetter(cfg v1.AuthClientConfig) (authProvider Setter, err error) {
 	switch cfg.Method {
 	case v1.AuthMethodToken:
 		authProvider = NewTokenAuth(cfg.AdditionalScopes, cfg.Token)
 	case v1.AuthMethodOIDC:
-		authProvider = NewOidcAuthSetter(cfg.AdditionalScopes, cfg.OIDC)
+		authProvider, err = NewOidcAuthSetter(cfg.AdditionalScopes, cfg.OIDC)
+		if err != nil {
+			return nil, err
+		}
 	default:
-		panic(fmt.Sprintf("wrong method: '%s'", cfg.Method))
+		return nil, fmt.Errorf("unsupported auth method: %s", cfg.Method)
 	}
-	return authProvider
+	return authProvider, nil
 }
 
 type Verifier interface {

+ 68 - 3
pkg/auth/oidc.go

@@ -16,23 +16,72 @@ package auth
 
 import (
 	"context"
+	"crypto/tls"
+	"crypto/x509"
 	"fmt"
+	"net/http"
+	"net/url"
+	"os"
 	"slices"
 
 	"github.com/coreos/go-oidc/v3/oidc"
+	"golang.org/x/oauth2"
 	"golang.org/x/oauth2/clientcredentials"
 
 	v1 "github.com/fatedier/frp/pkg/config/v1"
 	"github.com/fatedier/frp/pkg/msg"
 )
 
+// createOIDCHTTPClient creates an HTTP client with custom TLS and proxy configuration for OIDC token requests
+func createOIDCHTTPClient(trustedCAFile string, insecureSkipVerify bool, proxyURL string) (*http.Client, error) {
+	// Clone the default transport to get all reasonable defaults
+	transport := http.DefaultTransport.(*http.Transport).Clone()
+
+	// Configure TLS settings
+	if trustedCAFile != "" || insecureSkipVerify {
+		tlsConfig := &tls.Config{
+			InsecureSkipVerify: insecureSkipVerify,
+		}
+
+		if trustedCAFile != "" && !insecureSkipVerify {
+			caCert, err := os.ReadFile(trustedCAFile)
+			if err != nil {
+				return nil, fmt.Errorf("failed to read OIDC CA certificate file %q: %w", trustedCAFile, err)
+			}
+
+			caCertPool := x509.NewCertPool()
+			if !caCertPool.AppendCertsFromPEM(caCert) {
+				return nil, fmt.Errorf("failed to parse OIDC CA certificate from file %q", trustedCAFile)
+			}
+
+			tlsConfig.RootCAs = caCertPool
+		}
+		transport.TLSClientConfig = tlsConfig
+	}
+
+	// Configure proxy settings
+	if proxyURL != "" {
+		parsedURL, err := url.Parse(proxyURL)
+		if err != nil {
+			return nil, fmt.Errorf("failed to parse OIDC proxy URL %q: %w", proxyURL, err)
+		}
+		transport.Proxy = http.ProxyURL(parsedURL)
+	} else {
+		// Explicitly disable proxy to override DefaultTransport's ProxyFromEnvironment
+		transport.Proxy = nil
+	}
+
+	return &http.Client{Transport: transport}, nil
+}
+
 type OidcAuthProvider struct {
 	additionalAuthScopes []v1.AuthScope
 
 	tokenGenerator *clientcredentials.Config
+	httpClient     *http.Client
 }
 
-func NewOidcAuthSetter(additionalAuthScopes []v1.AuthScope, cfg v1.AuthOIDCClientConfig) *OidcAuthProvider {
+func NewOidcAuthSetter(additionalAuthScopes []v1.AuthScope, cfg v1.AuthOIDCClientConfig) (*OidcAuthProvider, error) {
 	eps := make(map[string][]string)
 	for k, v := range cfg.AdditionalEndpointParams {
 		eps[k] = []string{v}
@@ -50,14 +99,30 @@ func NewOidcAuthSetter(additionalAuthScopes []v1.AuthScope, cfg v1.AuthOIDCClien
 		EndpointParams: eps,
 	}
 
+	// Create custom HTTP client if needed
+	var httpClient *http.Client
+	if cfg.TrustedCaFile != "" || cfg.InsecureSkipVerify || cfg.ProxyURL != "" {
+		var err error
+		httpClient, err = createOIDCHTTPClient(cfg.TrustedCaFile, cfg.InsecureSkipVerify, cfg.ProxyURL)
+		if err != nil {
+			return nil, fmt.Errorf("failed to create OIDC HTTP client: %w", err)
+		}
+	}
+
 	return &OidcAuthProvider{
 		additionalAuthScopes: additionalAuthScopes,
 		tokenGenerator:       tokenGenerator,
-	}
+		httpClient:           httpClient,
+	}, nil
 }
 
 func (auth *OidcAuthProvider) generateAccessToken() (accessToken string, err error) {
-	tokenObj, err := auth.tokenGenerator.Token(context.Background())
+	ctx := context.Background()
+	if auth.httpClient != nil {
+		ctx = context.WithValue(ctx, oauth2.HTTPClient, auth.httpClient)
+	}
+
+	tokenObj, err := auth.tokenGenerator.Token(ctx)
 	if err != nil {
 		return "", fmt.Errorf("couldn't generate OIDC token for login: %v", err)
 	}

+ 11 - 0
pkg/config/v1/client.go

@@ -228,6 +228,17 @@ type AuthOIDCClientConfig struct {
 	// AdditionalEndpointParams specifies additional parameters to be sent
 	// this field will be transfer to map[string][]string in OIDC token generator.
 	AdditionalEndpointParams map[string]string `json:"additionalEndpointParams,omitempty"`
+
+	// TrustedCaFile specifies the path to a custom CA certificate file
+	// for verifying the OIDC token endpoint's TLS certificate.
+	TrustedCaFile string `json:"trustedCaFile,omitempty"`
+	// InsecureSkipVerify disables TLS certificate verification for the
+	// OIDC token endpoint. Only use this for debugging, not recommended for production.
+	InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"`
+	// ProxyURL specifies a proxy to use when connecting to the OIDC token endpoint.
+	// Supports http, https, socks5, and socks5h proxy protocols.
+	// If empty, no proxy is used for OIDC connections.
+	ProxyURL string `json:"proxyURL,omitempty"`
 }
 
 type VirtualNetConfig struct {