mirror of https://github.com/fatedier/frp
auth: enhance OIDC client with TLS and proxy configuration options (#4990)
parent
7cfa546b55
commit
abf4942e8a
|
@ -1,3 +1,4 @@
|
||||||
## Features
|
## 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.
|
* 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.
|
||||||
|
|
|
@ -149,9 +149,15 @@ func NewService(options ServiceOptions) (*Service, error) {
|
||||||
}
|
}
|
||||||
webServer = ws
|
webServer = ws
|
||||||
}
|
}
|
||||||
|
|
||||||
|
authSetter, err := auth.NewAuthSetter(options.Common.Auth)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
s := &Service{
|
s := &Service{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
authSetter: auth.NewAuthSetter(options.Common.Auth),
|
authSetter: authSetter,
|
||||||
webServer: webServer,
|
webServer: webServer,
|
||||||
common: options.Common,
|
common: options.Common,
|
||||||
configFilePath: options.ConfigFilePath,
|
configFilePath: options.ConfigFilePath,
|
||||||
|
|
|
@ -55,6 +55,20 @@ auth.token = "12345678"
|
||||||
# auth.oidc.additionalEndpointParams.audience = "https://dev.auth.com/api/v2/"
|
# auth.oidc.additionalEndpointParams.audience = "https://dev.auth.com/api/v2/"
|
||||||
# auth.oidc.additionalEndpointParams.var1 = "foobar"
|
# 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
|
# Set admin address for control frpc's action by http api such as reload
|
||||||
webServer.addr = "127.0.0.1"
|
webServer.addr = "127.0.0.1"
|
||||||
webServer.port = 7400
|
webServer.port = 7400
|
||||||
|
|
|
@ -27,16 +27,19 @@ type Setter interface {
|
||||||
SetNewWorkConn(*msg.NewWorkConn) error
|
SetNewWorkConn(*msg.NewWorkConn) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthSetter(cfg v1.AuthClientConfig) (authProvider Setter) {
|
func NewAuthSetter(cfg v1.AuthClientConfig) (authProvider Setter, err error) {
|
||||||
switch cfg.Method {
|
switch cfg.Method {
|
||||||
case v1.AuthMethodToken:
|
case v1.AuthMethodToken:
|
||||||
authProvider = NewTokenAuth(cfg.AdditionalScopes, cfg.Token)
|
authProvider = NewTokenAuth(cfg.AdditionalScopes, cfg.Token)
|
||||||
case v1.AuthMethodOIDC:
|
case v1.AuthMethodOIDC:
|
||||||
authProvider = NewOidcAuthSetter(cfg.AdditionalScopes, cfg.OIDC)
|
authProvider, err = NewOidcAuthSetter(cfg.AdditionalScopes, cfg.OIDC)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
default:
|
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 {
|
type Verifier interface {
|
||||||
|
|
|
@ -16,23 +16,72 @@ package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"github.com/coreos/go-oidc/v3/oidc"
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/oauth2/clientcredentials"
|
"golang.org/x/oauth2/clientcredentials"
|
||||||
|
|
||||||
v1 "github.com/fatedier/frp/pkg/config/v1"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"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 {
|
type OidcAuthProvider struct {
|
||||||
additionalAuthScopes []v1.AuthScope
|
additionalAuthScopes []v1.AuthScope
|
||||||
|
|
||||||
tokenGenerator *clientcredentials.Config
|
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)
|
eps := make(map[string][]string)
|
||||||
for k, v := range cfg.AdditionalEndpointParams {
|
for k, v := range cfg.AdditionalEndpointParams {
|
||||||
eps[k] = []string{v}
|
eps[k] = []string{v}
|
||||||
|
@ -50,14 +99,30 @@ func NewOidcAuthSetter(additionalAuthScopes []v1.AuthScope, cfg v1.AuthOIDCClien
|
||||||
EndpointParams: eps,
|
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{
|
return &OidcAuthProvider{
|
||||||
additionalAuthScopes: additionalAuthScopes,
|
additionalAuthScopes: additionalAuthScopes,
|
||||||
tokenGenerator: tokenGenerator,
|
tokenGenerator: tokenGenerator,
|
||||||
}
|
httpClient: httpClient,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *OidcAuthProvider) generateAccessToken() (accessToken string, err error) {
|
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 {
|
if err != nil {
|
||||||
return "", fmt.Errorf("couldn't generate OIDC token for login: %v", err)
|
return "", fmt.Errorf("couldn't generate OIDC token for login: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,6 +228,17 @@ type AuthOIDCClientConfig struct {
|
||||||
// AdditionalEndpointParams specifies additional parameters to be sent
|
// AdditionalEndpointParams specifies additional parameters to be sent
|
||||||
// this field will be transfer to map[string][]string in OIDC token generator.
|
// this field will be transfer to map[string][]string in OIDC token generator.
|
||||||
AdditionalEndpointParams map[string]string `json:"additionalEndpointParams,omitempty"`
|
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 {
|
type VirtualNetConfig struct {
|
||||||
|
|
Loading…
Reference in New Issue