Skip to content

Commit fbd7a09

Browse files
authored
Merge pull request #3998 from skoeva/auth8
backend: auth: Extract RefreshAndCacheNewToken from headlamp.go
2 parents 2bbd5c9 + 2ae85c2 commit fbd7a09

File tree

4 files changed

+242
-129
lines changed

4 files changed

+242
-129
lines changed

backend/cmd/headlamp.go

Lines changed: 3 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"compress/gzip"
2222
"context"
2323
"crypto/tls"
24-
"crypto/x509"
2524
"encoding/base64"
2625
"encoding/json"
2726
"errors"
@@ -631,7 +630,7 @@ func createHeadlampHandler(config *HeadlampConfig) http.Handler {
631630
return
632631
}
633632

634-
ctx = configureTLSContext(ctx, oidcAuthConfig.SkipTLSVerify, oidcAuthConfig.CACert)
633+
ctx = auth.ConfigureTLSContext(ctx, oidcAuthConfig.SkipTLSVerify, oidcAuthConfig.CACert)
635634

636635
if config.oidcValidatorIdpIssuerURL != "" {
637636
ctx = oidc.InsecureIssuerURLContext(ctx, config.oidcValidatorIdpIssuerURL)
@@ -805,69 +804,6 @@ func createHeadlampHandler(config *HeadlampConfig) http.Handler {
805804
return r
806805
}
807806

808-
// configureTLSContext configures TLS settings for the HTTP client in the context.
809-
// If skipTLSVerify is true, TLS verification will be skipped.
810-
// If caCert is provided, it will be added to the certificate pool for TLS verification.
811-
func configureTLSContext(ctx context.Context, skipTLSVerify *bool, caCert *string) context.Context {
812-
if skipTLSVerify != nil && *skipTLSVerify {
813-
tlsSkipTransport := &http.Transport{
814-
// the gosec linter is disabled here because we are explicitly requesting to skip TLS verification.
815-
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec
816-
}
817-
ctx = oidc.ClientContext(ctx, &http.Client{Transport: tlsSkipTransport})
818-
}
819-
820-
if caCert != nil {
821-
caCertPool := x509.NewCertPool()
822-
if !caCertPool.AppendCertsFromPEM([]byte(*caCert)) {
823-
// Log error but continue with original context
824-
logger.Log(logger.LevelError, nil,
825-
errors.New("failed to append ca cert to pool"), "couldn't add custom cert to context")
826-
return ctx
827-
}
828-
829-
// the gosec linter is disabled because gosec promotes using a minVersion of TLS 1.2 or higher.
830-
// since we are using a custom CA cert configured by the user, we are not forcing a minVersion.
831-
customTransport := &http.Transport{
832-
TLSClientConfig: &tls.Config{ //nolint:gosec
833-
RootCAs: caCertPool,
834-
},
835-
}
836-
837-
ctx = oidc.ClientContext(ctx, &http.Client{Transport: customTransport})
838-
}
839-
840-
return ctx
841-
}
842-
843-
func refreshAndCacheNewToken(oidcAuthConfig *kubeconfig.OidcConfig,
844-
cache cache.Cache[interface{}],
845-
tokenType, token, issuerURL string,
846-
) (*oauth2.Token, error) {
847-
ctx := context.Background()
848-
ctx = configureTLSContext(ctx, oidcAuthConfig.SkipTLSVerify, oidcAuthConfig.CACert)
849-
850-
// get provider
851-
provider, err := oidc.NewProvider(ctx, issuerURL)
852-
if err != nil {
853-
return nil, fmt.Errorf("getting provider: %v", err)
854-
}
855-
// get refresh token
856-
newToken, err := auth.GetNewToken(
857-
oidcAuthConfig.ClientID,
858-
oidcAuthConfig.ClientSecret,
859-
cache,
860-
tokenType,
861-
token,
862-
provider.Endpoint().TokenURL,
863-
)
864-
if err != nil {
865-
return nil, fmt.Errorf("refreshing token: %v", err)
866-
}
867-
868-
return newToken, nil
869-
}
870-
871807
func (c *HeadlampConfig) refreshAndSetToken(oidcAuthConfig *kubeconfig.OidcConfig,
872808
cache cache.Cache[interface{}], token string,
873809
w http.ResponseWriter, r *http.Request, cluster string, span trace.Span, ctx context.Context,
@@ -883,7 +819,8 @@ func (c *HeadlampConfig) refreshAndSetToken(oidcAuthConfig *kubeconfig.OidcConfi
883819
idpIssuerURL = oidcAuthConfig.IdpIssuerURL
884820
}
885821

886-
newToken, err := refreshAndCacheNewToken(
822+
newToken, err := auth.RefreshAndCacheNewToken(
823+
ctx,
887824
oidcAuthConfig,
888825
cache,
889826
tokenType,

backend/cmd/headlamp_test.go

Lines changed: 0 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"crypto/x509"
2424
"encoding/base64"
2525
"encoding/json"
26-
"encoding/pem"
2726
"fmt"
2827
"io"
2928
"net/http"
@@ -1290,68 +1289,6 @@ func TestProcessTokenProtocol(t *testing.T) {
12901289
}
12911290
}
12921291

1293-
// TestConfigureTLSContext_NoConfig tests when both skipTLSVerify and caCert are not set.
1294-
func TestConfigureTLSContext_NoConfig(t *testing.T) {
1295-
baseCtx := context.Background()
1296-
resultCtx := configureTLSContext(baseCtx, nil, nil)
1297-
1298-
// Context should remain unchanged when no TLS configuration is provided
1299-
assert.Equal(t, baseCtx, resultCtx, "Context should remain unchanged when no TLS configuration is provided")
1300-
}
1301-
1302-
/*
1303-
TestConfigureTLSContext_SkipTLS tests when skipTLSVerify is set to true.
1304-
The OIDC library would use this context to make requests
1305-
We can't directly extract the client, but we can verify the behavior
1306-
by checking that the context was modified (indicating TLS config was applied).
1307-
*/
1308-
func TestConfigureTLSContext_SkipTLS(t *testing.T) {
1309-
// Create a test server that requires TLS
1310-
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1311-
w.WriteHeader(http.StatusOK)
1312-
_, err := w.Write([]byte("TLS connection successful"))
1313-
require.NoError(t, err)
1314-
}))
1315-
defer server.Close()
1316-
1317-
baseCtx := context.Background()
1318-
skipTLSVerify := true
1319-
resultCtx := configureTLSContext(baseCtx, &skipTLSVerify, nil)
1320-
1321-
// Context should be modified when skipTLSVerify is true
1322-
assert.NotEqual(t, baseCtx, resultCtx, "Context should be modified when skipTLSVerify is true")
1323-
1324-
// Test that the configured context can make TLS requests with skip verification
1325-
// This verifies that the TLS configuration was actually applied
1326-
_, err := http.NewRequestWithContext(resultCtx, "GET", server.URL, nil)
1327-
require.NoError(t, err)
1328-
}
1329-
1330-
// TestConfigureTLSContext_CACert tests when caCert is provided.
1331-
func TestConfigureTLSContext_CACert(t *testing.T) {
1332-
// Read the pre-generated CA certificate from testdata
1333-
caCertBytes, err := os.ReadFile("headlamp_testdata/ca.crt")
1334-
require.NoError(t, err)
1335-
1336-
// Test the configureTLSContext function with the CA certificate
1337-
baseCtx := context.Background()
1338-
caCert := string(caCertBytes)
1339-
resultCtx := configureTLSContext(baseCtx, nil, &caCert)
1340-
1341-
// Context should be modified when caCert is provided
1342-
assert.NotEqual(t, baseCtx, resultCtx, "Context should be modified when caCert is provided")
1343-
1344-
// Verify that the CA certificate was parsed correctly by checking if it's valid PEM
1345-
block, _ := pem.Decode([]byte(caCert))
1346-
assert.NotNil(t, block, "CA certificate should be valid PEM format")
1347-
assert.Equal(t, "CERTIFICATE", block.Type, "CA certificate should be of type CERTIFICATE")
1348-
1349-
// Parse the CA certificate to verify it's valid
1350-
caCertParsed, err := x509.ParseCertificate(block.Bytes)
1351-
require.NoError(t, err)
1352-
assert.True(t, caCertParsed.IsCA, "Generated certificate should be a CA certificate")
1353-
}
1354-
13551292
// newFakeK8sServer create a mock k8s server for testing purpose,
13561293
// this would help to test Caching Machanism without making request
13571294
// to the k8s server.

backend/pkg/auth/auth.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package auth
1818

1919
import (
2020
"context"
21+
"crypto/tls"
22+
"crypto/x509"
2123
"encoding/base64"
2224
"encoding/json"
2325
"errors"
@@ -27,7 +29,9 @@ import (
2729
"strings"
2830
"time"
2931

32+
"github.com/coreos/go-oidc/v3/oidc"
3033
"github.com/kubernetes-sigs/headlamp/backend/pkg/cache"
34+
"github.com/kubernetes-sigs/headlamp/backend/pkg/kubeconfig"
3135
"github.com/kubernetes-sigs/headlamp/backend/pkg/logger"
3236
"golang.org/x/oauth2"
3337
)
@@ -201,3 +205,69 @@ func GetNewToken(clientID, clientSecret string, cache cache.Cache[interface{}],
201205

202206
return newToken, nil
203207
}
208+
209+
// ConfigureTLSContext configures TLS settings for the HTTP client in the context.
210+
// When skipTLSVerify is true, a client that skips verification is installed.
211+
// When caCert is provided, a client with that CA pool is installed and takes precedence,
212+
// re-enabling verification while trusting the supplied certificate bundle.
213+
func ConfigureTLSContext(ctx context.Context, skipTLSVerify *bool, caCert *string) context.Context {
214+
if skipTLSVerify != nil && *skipTLSVerify {
215+
tlsSkipTransport := &http.Transport{
216+
// the gosec linter is disabled here because we are explicitly requesting to skip TLS verification.
217+
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec
218+
}
219+
ctx = oidc.ClientContext(ctx, &http.Client{Transport: tlsSkipTransport})
220+
}
221+
222+
if caCert != nil {
223+
caCertPool := x509.NewCertPool()
224+
if !caCertPool.AppendCertsFromPEM([]byte(*caCert)) {
225+
// Log error but continue with original context
226+
logger.Log(logger.LevelError, nil,
227+
errors.New("failed to append ca cert to pool"), "couldn't add custom cert to context")
228+
return ctx
229+
}
230+
231+
// the gosec linter is disabled because gosec promotes using a minVersion of TLS 1.2 or higher.
232+
// since we are using a custom CA cert configured by the user, we are not forcing a minVersion.
233+
customTransport := &http.Transport{
234+
TLSClientConfig: &tls.Config{ //nolint:gosec
235+
RootCAs: caCertPool,
236+
},
237+
}
238+
239+
ctx = oidc.ClientContext(ctx, &http.Client{Transport: customTransport})
240+
}
241+
242+
return ctx
243+
}
244+
245+
// RefreshAndCacheNewToken obtains a fresh OIDC token using the cached refresh token
246+
// and re-populates the cache so subsequent requests can reuse it. The provided ctx
247+
// controls cancellation and deadlines for all outbound requests during the refresh.
248+
func RefreshAndCacheNewToken(ctx context.Context, oidcAuthConfig *kubeconfig.OidcConfig,
249+
cache cache.Cache[interface{}],
250+
tokenType, token, issuerURL string,
251+
) (*oauth2.Token, error) {
252+
ctx = ConfigureTLSContext(ctx, oidcAuthConfig.SkipTLSVerify, oidcAuthConfig.CACert)
253+
254+
// get provider
255+
provider, err := oidc.NewProvider(ctx, issuerURL)
256+
if err != nil {
257+
return nil, fmt.Errorf("getting provider: %w", err)
258+
}
259+
// get refresh token
260+
newToken, err := GetNewToken(
261+
oidcAuthConfig.ClientID,
262+
oidcAuthConfig.ClientSecret,
263+
cache,
264+
tokenType,
265+
token,
266+
provider.Endpoint().TokenURL,
267+
)
268+
if err != nil {
269+
return nil, fmt.Errorf("refreshing token: %w", err)
270+
}
271+
272+
return newToken, nil
273+
}

0 commit comments

Comments
 (0)