Browse Source

feat(iam): add TLS configuration support for OIDC provider (#7929)

* feat(iam): add TLS configuration support for OIDC provider

Adds tlsCaCert and tlsInsecureSkipVerify options to OIDC provider configuration to allow using custom CA certificates and skipping verification in development environments.

* fix: use SystemCertPool for custom CA and add security warning

- Use x509.SystemCertPool() to preserve trust in public CAs
- Add warning log when TLSInsecureSkipVerify is enabled
- Addresses code review feedback from gemini-code-assist

* docs: enhance TLS configuration field documentation

- Add explicit warning about TLSInsecureSkipVerify production usage
- Clarify TLSCACert is for custom/self-signed certificates

* security: enforce TLS 1.2 minimum version

- Set MinVersion to TLS 1.2 to prevent downgrade attacks
- Ensures secure communication with OIDC providers

* security: validate CA cert path is absolute

- Add filepath.IsAbs check before reading CA certificate
- Prevents reading unintended files from relative paths
- Fail fast on misconfigured paths
pull/7930/head
Chris Lu 3 weeks ago
committed by GitHub
parent
commit
c405ff1374
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 50
      weed/iam/oidc/oidc_provider.go
  2. 6
      weed/iam/sts/constants.go
  3. 8
      weed/iam/sts/provider_factory.go

50
weed/iam/oidc/oidc_provider.go

@ -5,12 +5,16 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rsa" "crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"net/http" "net/http"
"os"
"path/filepath"
"strings" "strings"
"time" "time"
@ -58,6 +62,13 @@ type OIDCConfig struct {
// JWKSCacheTTLSeconds sets how long to cache JWKS before refresh (default 3600 seconds) // JWKSCacheTTLSeconds sets how long to cache JWKS before refresh (default 3600 seconds)
JWKSCacheTTLSeconds int `json:"jwksCacheTTLSeconds,omitempty"` JWKSCacheTTLSeconds int `json:"jwksCacheTTLSeconds,omitempty"`
// TLSCACert is the path to the CA certificate file for custom/self-signed certificates
TLSCACert string `json:"tlsCaCert,omitempty"`
// TLSInsecureSkipVerify controls whether to skip TLS verification.
// WARNING: Should only be used in development/testing environments. Never use in production.
TLSInsecureSkipVerify bool `json:"tlsInsecureSkipVerify,omitempty"`
} }
// JWKS represents JSON Web Key Set // JWKS represents JSON Web Key Set
@ -124,6 +135,45 @@ func (p *OIDCProvider) Initialize(config interface{}) error {
p.jwksTTL = time.Hour p.jwksTTL = time.Hour
} }
// Configure HTTP client with TLS settings
tlsConfig := &tls.Config{
InsecureSkipVerify: oidcConfig.TLSInsecureSkipVerify,
MinVersion: tls.VersionTLS12, // Prevent TLS downgrade attacks
}
if oidcConfig.TLSInsecureSkipVerify {
glog.Warningf("OIDC provider %q is configured to skip TLS verification. This is insecure and should not be used in production.", p.name)
}
if oidcConfig.TLSCACert != "" {
// Validate that the CA cert path is absolute to prevent reading unintended files
if !filepath.IsAbs(oidcConfig.TLSCACert) {
return fmt.Errorf("TLSCACert must be an absolute path, got: %s", oidcConfig.TLSCACert)
}
caCert, err := os.ReadFile(oidcConfig.TLSCACert)
if err != nil {
return fmt.Errorf("failed to read CA cert file: %w", err)
}
// Start with the system cert pool to trust public CAs, then add the custom one.
rootCAs, _ := x509.SystemCertPool()
if rootCAs == nil {
rootCAs = x509.NewCertPool()
}
if !rootCAs.AppendCertsFromPEM(caCert) {
return fmt.Errorf("failed to append CA cert from file: %s", oidcConfig.TLSCACert)
}
tlsConfig.RootCAs = rootCAs
}
transport := &http.Transport{
TLSClientConfig: tlsConfig,
}
p.httpClient = &http.Client{
Timeout: 30 * time.Second,
Transport: transport,
}
// For testing, we'll skip the actual OIDC client initialization // For testing, we'll skip the actual OIDC client initialization
return nil return nil
} }

6
weed/iam/sts/constants.go

@ -49,8 +49,10 @@ const (
ConfigFieldClientSecret = "clientSecret" ConfigFieldClientSecret = "clientSecret"
ConfigFieldJWKSUri = "jwksUri" ConfigFieldJWKSUri = "jwksUri"
ConfigFieldScopes = "scopes" ConfigFieldScopes = "scopes"
ConfigFieldUserInfoUri = "userInfoUri"
ConfigFieldRedirectUri = "redirectUri"
ConfigFieldUserInfoUri = "userInfoUri"
ConfigFieldRedirectUri = "redirectUri"
ConfigFieldTLSCACert = "tlsCaCert"
ConfigFieldTLSInsecureSkipVerify = "tlsInsecureSkipVerify"
) )
// Error Messages // Error Messages

8
weed/iam/sts/provider_factory.go

@ -115,6 +115,14 @@ func (f *ProviderFactory) convertToOIDCConfig(configMap map[string]interface{})
config.Scopes = scopes config.Scopes = scopes
} }
if tlsCaCert, ok := configMap[ConfigFieldTLSCACert].(string); ok {
config.TLSCACert = tlsCaCert
}
if tlsInsecureSkipVerify, ok := configMap[ConfigFieldTLSInsecureSkipVerify].(bool); ok {
config.TLSInsecureSkipVerify = tlsInsecureSkipVerify
}
// Convert claims mapping // Convert claims mapping
if claimsMapInterface, ok := configMap["claimsMapping"]; ok { if claimsMapInterface, ok := configMap["claimsMapping"]; ok {
claimsMap, err := f.convertToStringMap(claimsMapInterface) claimsMap, err := f.convertToStringMap(claimsMapInterface)

Loading…
Cancel
Save