mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-21 17:48:01 +00:00
oidc authentication plugin: don't trim issuer URLs with trailing slashes
The issuer URL passed to the plugin must identically match the issuer URL returned by OpenID Connect discovery. However, the plugin currently trims all trailing slashes from issuer URLs, causing a mismatch. Since the go-oidc client already handles this case correctly, don't trim the path.
This commit is contained in:
parent
ed763b8034
commit
bc3dc12203
@ -33,7 +33,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
@ -174,7 +173,7 @@ func (a *OIDCAuthenticator) client() (*oidc.Client, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to initialize client.
|
// Try to initialize client.
|
||||||
providerConfig, err := oidc.FetchProviderConfig(a.httpClient, strings.TrimSuffix(a.issuerURL, "/"))
|
providerConfig, err := oidc.FetchProviderConfig(a.httpClient, a.issuerURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("oidc authenticator: failed to fetch provider discovery data: %v", err)
|
glog.Errorf("oidc authenticator: failed to fetch provider discovery data: %v", err)
|
||||||
return nil, fmt.Errorf("fetch provider config: %v", err)
|
return nil, fmt.Errorf("fetch provider config: %v", err)
|
||||||
|
@ -110,14 +110,13 @@ func TestTLSConfig(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
func() {
|
func() {
|
||||||
op := oidctesting.NewOIDCProvider(t)
|
op := oidctesting.NewOIDCProvider(t, "")
|
||||||
srv, err := op.ServeTLSWithKeyPair(tc.serverCertFile, tc.serverKeyFile)
|
srv, err := op.ServeTLSWithKeyPair(tc.serverCertFile, tc.serverKeyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%s: %v", tc.testCase, err)
|
t.Errorf("%s: %v", tc.testCase, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
op.AddMinimalProviderConfig(srv)
|
|
||||||
|
|
||||||
issuer := srv.URL
|
issuer := srv.URL
|
||||||
clientID := "client-foo"
|
clientID := "client-foo"
|
||||||
@ -178,8 +177,6 @@ func TestTLSConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOIDCAuthentication(t *testing.T) {
|
func TestOIDCAuthentication(t *testing.T) {
|
||||||
var err error
|
|
||||||
|
|
||||||
cert := path.Join(os.TempDir(), "oidc-cert")
|
cert := path.Join(os.TempDir(), "oidc-cert")
|
||||||
key := path.Join(os.TempDir(), "oidc-key")
|
key := path.Join(os.TempDir(), "oidc-key")
|
||||||
|
|
||||||
@ -188,17 +185,17 @@ func TestOIDCAuthentication(t *testing.T) {
|
|||||||
|
|
||||||
oidctesting.GenerateSelfSignedCert(t, "127.0.0.1", cert, key)
|
oidctesting.GenerateSelfSignedCert(t, "127.0.0.1", cert, key)
|
||||||
|
|
||||||
|
// Ensure all tests pass when the issuer is not at a base URL.
|
||||||
|
for _, path := range []string{"", "/path/with/trailing/slash/"} {
|
||||||
|
|
||||||
// Create a TLS server and a client.
|
// Create a TLS server and a client.
|
||||||
op := oidctesting.NewOIDCProvider(t)
|
op := oidctesting.NewOIDCProvider(t, path)
|
||||||
srv, err := op.ServeTLSWithKeyPair(cert, key)
|
srv, err := op.ServeTLSWithKeyPair(cert, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Cannot start server: %v", err)
|
t.Fatalf("Cannot start server: %v", err)
|
||||||
}
|
}
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
|
|
||||||
// A provider config with all required fields.
|
|
||||||
op.AddMinimalProviderConfig(srv)
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
userClaim string
|
userClaim string
|
||||||
groupsClaim string
|
groupsClaim string
|
||||||
@ -304,3 +301,4 @@ func TestOIDCAuthentication(t *testing.T) {
|
|||||||
client.Close()
|
client.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -33,6 +33,7 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -43,7 +44,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// NewOIDCProvider provides a bare minimum OIDC IdP Server useful for testing.
|
// NewOIDCProvider provides a bare minimum OIDC IdP Server useful for testing.
|
||||||
func NewOIDCProvider(t *testing.T) *OIDCProvider {
|
func NewOIDCProvider(t *testing.T, issuerPath string) *OIDCProvider {
|
||||||
privKey, err := key.GeneratePrivateKey()
|
privKey, err := key.GeneratePrivateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Cannot create OIDC Provider: %v", err)
|
t.Fatalf("Cannot create OIDC Provider: %v", err)
|
||||||
@ -53,10 +54,11 @@ func NewOIDCProvider(t *testing.T) *OIDCProvider {
|
|||||||
op := &OIDCProvider{
|
op := &OIDCProvider{
|
||||||
Mux: http.NewServeMux(),
|
Mux: http.NewServeMux(),
|
||||||
PrivKey: privKey,
|
PrivKey: privKey,
|
||||||
|
issuerPath: issuerPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
op.Mux.HandleFunc("/.well-known/openid-configuration", op.handleConfig)
|
op.Mux.HandleFunc(path.Join(issuerPath, "/.well-known/openid-configuration"), op.handleConfig)
|
||||||
op.Mux.HandleFunc("/keys", op.handleKeys)
|
op.Mux.HandleFunc(path.Join(issuerPath, "/keys"), op.handleKeys)
|
||||||
|
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
@ -65,6 +67,7 @@ type OIDCProvider struct {
|
|||||||
Mux *http.ServeMux
|
Mux *http.ServeMux
|
||||||
PCFG oidc.ProviderConfig
|
PCFG oidc.ProviderConfig
|
||||||
PrivKey *key.PrivateKey
|
PrivKey *key.PrivateKey
|
||||||
|
issuerPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (op *OIDCProvider) ServeTLSWithKeyPair(cert, key string) (*httptest.Server, error) {
|
func (op *OIDCProvider) ServeTLSWithKeyPair(cert, key string) (*httptest.Server, error) {
|
||||||
@ -77,20 +80,31 @@ func (op *OIDCProvider) ServeTLSWithKeyPair(cert, key string) (*httptest.Server,
|
|||||||
return nil, fmt.Errorf("Cannot load cert/key pair: %v", err)
|
return nil, fmt.Errorf("Cannot load cert/key pair: %v", err)
|
||||||
}
|
}
|
||||||
srv.StartTLS()
|
srv.StartTLS()
|
||||||
return srv, nil
|
|
||||||
|
// The issuer's URL is extended by an optional path. This ensures that the plugin can
|
||||||
|
// handle issuers that use a non-root path for discovery (see kubernetes/kubernetes#29749).
|
||||||
|
srv.URL = srv.URL + op.issuerPath
|
||||||
|
|
||||||
|
u, err := url.Parse(srv.URL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pathFor := func(p string) *url.URL {
|
||||||
|
u2 := *u // Shallow copy.
|
||||||
|
u2.Path = path.Join(u2.Path, p)
|
||||||
|
return &u2
|
||||||
}
|
}
|
||||||
|
|
||||||
func (op *OIDCProvider) AddMinimalProviderConfig(srv *httptest.Server) {
|
|
||||||
op.PCFG = oidc.ProviderConfig{
|
op.PCFG = oidc.ProviderConfig{
|
||||||
Issuer: MustParseURL(srv.URL),
|
Issuer: u,
|
||||||
AuthEndpoint: MustParseURL(srv.URL + "/auth"),
|
AuthEndpoint: pathFor("/auth"),
|
||||||
TokenEndpoint: MustParseURL(srv.URL + "/token"),
|
TokenEndpoint: pathFor("/token"),
|
||||||
KeysEndpoint: MustParseURL(srv.URL + "/keys"),
|
KeysEndpoint: pathFor("/keys"),
|
||||||
ResponseTypesSupported: []string{"code"},
|
ResponseTypesSupported: []string{"code"},
|
||||||
SubjectTypesSupported: []string{"public"},
|
SubjectTypesSupported: []string{"public"},
|
||||||
IDTokenSigningAlgValues: []string{"RS256"},
|
IDTokenSigningAlgValues: []string{"RS256"},
|
||||||
}
|
}
|
||||||
|
return srv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (op *OIDCProvider) handleConfig(w http.ResponseWriter, req *http.Request) {
|
func (op *OIDCProvider) handleConfig(w http.ResponseWriter, req *http.Request) {
|
||||||
@ -122,14 +136,6 @@ func (op *OIDCProvider) handleKeys(w http.ResponseWriter, req *http.Request) {
|
|||||||
w.Write(b)
|
w.Write(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MustParseURL(s string) *url.URL {
|
|
||||||
u, err := url.Parse(s)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Errorf("Failed to parse url: %v", err))
|
|
||||||
}
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// generateSelfSignedCert generates a self-signed cert/key pairs and writes to the certPath/keyPath.
|
// generateSelfSignedCert generates a self-signed cert/key pairs and writes to the certPath/keyPath.
|
||||||
// This method is mostly identical to crypto.GenerateSelfSignedCert except for the 'IsCA' and 'KeyUsage'
|
// This method is mostly identical to crypto.GenerateSelfSignedCert except for the 'IsCA' and 'KeyUsage'
|
||||||
// in the certificate template. (Maybe we can merge these two methods).
|
// in the certificate template. (Maybe we can merge these two methods).
|
||||||
|
@ -51,13 +51,12 @@ func TestNewOIDCAuthProvider(t *testing.T) {
|
|||||||
defer os.Remove(tempDir)
|
defer os.Remove(tempDir)
|
||||||
|
|
||||||
oidctesting.GenerateSelfSignedCert(t, "127.0.0.1", cert, key)
|
oidctesting.GenerateSelfSignedCert(t, "127.0.0.1", cert, key)
|
||||||
op := oidctesting.NewOIDCProvider(t)
|
op := oidctesting.NewOIDCProvider(t, "")
|
||||||
srv, err := op.ServeTLSWithKeyPair(cert, key)
|
srv, err := op.ServeTLSWithKeyPair(cert, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Cannot start server %v", err)
|
t.Fatalf("Cannot start server %v", err)
|
||||||
}
|
}
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
op.AddMinimalProviderConfig(srv)
|
|
||||||
|
|
||||||
certData, err := ioutil.ReadFile(cert)
|
certData, err := ioutil.ReadFile(cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user