Skip to content
This repository was archived by the owner on Apr 22, 2024. It is now read-only.

Commit 2cbc13d

Browse files
committed
Allow to configure the IDP trusted CA from a file
1 parent cfe4795 commit 2cbc13d

File tree

11 files changed

+401
-119
lines changed

11 files changed

+401
-119
lines changed

config/gen/go/v1/oidc/config.pb.go

+118-60
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/gen/go/v1/oidc/config.pb.validate.go

+40-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/v1/oidc/config.proto

+19-10
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ message OIDCConfig {
109109
// self-signed certificate for testing purposes, but basically should not be set to
110110
// true in any other cases.
111111
// Optional.
112-
bool skip_verify_peer_cert = 3;
112+
// Deprecated: Use the one from the OIDCConfig instead.
113+
bool skip_verify_peer_cert = 3 [deprecated = true];
113114
}
114115

115116
oneof jwks_config {
@@ -189,9 +190,23 @@ message OIDCConfig {
189190
uint32 idle_session_timeout = 13;
190191

191192
// When specified, the Authservice will trust the specified Certificate Authority when performing HTTPS calls to
192-
// the Token Endpoint of the OIDC Identity Provider.
193-
// Optional.
194-
string trusted_certificate_authority = 14;
193+
// the OIDC Identity Provider.
194+
oneof trusted_ca_config {
195+
// String PEM-encoded certificate authority to trust when performing HTTPS calls to the OIDC Identity Provider.
196+
// Optional.
197+
string trusted_certificate_authority = 14;
198+
199+
// The file path to the PEM-encoded certificate authority to trust when performing HTTPS calls to the OIDC Identity Provider.
200+
// Optional.
201+
string trusted_certificate_authority_file = 20;
202+
203+
// If set to true, the verification of the destination certificate will be skipped when
204+
// making a request to the Token Endpoint. This option is useful when you want to use a
205+
// self-signed certificate for testing purposes, but basically should not be set to true
206+
// in any other cases.
207+
// Optional.
208+
bool skip_verify_peer_cert = 18;
209+
}
195210

196211
// The Authservice makes two kinds of direct network connections directly to the OIDC Provider.
197212
// Both are POST requests to the configured `token_uri` of the OIDC Provider.
@@ -213,10 +228,4 @@ message OIDCConfig {
213228
// Optional.
214229
RedisConfig redis_session_store_config = 16;
215230

216-
// If set to true, the verification of the destination certificate will be skipped when
217-
// making a request to the Token Endpoint. This option is useful when you want to use a
218-
// self-signed certificate for testing purposes, but basically should not be set to true
219-
// in any other cases.
220-
// Optional.
221-
bool skip_verify_peer_cert = 18;
222231
}

e2e/keycloak/authz-config.json

+1-4
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
{
1010
"oidc": {
1111
"configuration_uri": "https://host.docker.internal:9443/realms/master/.well-known/openid-configuration",
12-
"jwks_fetcher": {
13-
"skip_verify_peer_cert": true
14-
},
1512
"proxy_uri": "http://idp-proxy:9000",
1613
"callback_uri": "https://host.docker.internal:8443/callback",
1714
"client_id": "authservice",
@@ -28,7 +25,7 @@
2825
"redis_session_store_config": {
2926
"server_uri": "redis://redis:6379"
3027
},
31-
"skip_verify_peer_cert": true
28+
"trusted_certificate_authority_file": "/etc/authservice/certs/ca.crt"
3229
}
3330
}
3431
]

e2e/keycloak/docker-compose.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ services:
6363
- type: bind
6464
source: authz-config.json
6565
target: /etc/authservice/config.json
66+
- type: bind
67+
source: certs
68+
target: /etc/authservice/certs
6669
extra_hosts: # Required when running on Linux
6770
- "host.docker.internal:host-gateway"
6871

internal/authz/oidc.go

+3-14
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ package authz
1616

1717
import (
1818
"context"
19-
"crypto/tls"
20-
"crypto/x509"
2119
"encoding/json"
22-
"errors"
2320
"fmt"
2421
"io"
2522
"net/http"
@@ -90,18 +87,10 @@ func NewOIDCHandler(cfg *oidcv1.OIDCConfig, jwks oidc.JWKSProvider,
9087

9188
func getHTTPClient(cfg *oidcv1.OIDCConfig) (*http.Client, error) {
9289
transport := http.DefaultTransport.(*http.Transport).Clone()
93-
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: cfg.SkipVerifyPeerCert}
9490

95-
if cfg.GetTrustedCertificateAuthority() != "" {
96-
certPool, err := x509.SystemCertPool()
97-
if err != nil {
98-
return nil, fmt.Errorf("error creating system cert pool: %w", err)
99-
}
100-
transport.TLSClientConfig.RootCAs = certPool
101-
102-
if ok := certPool.AppendCertsFromPEM([]byte(cfg.GetTrustedCertificateAuthority())); !ok {
103-
return nil, errors.New("could no load trusted certificate authority")
104-
}
91+
var err error
92+
if transport.TLSClientConfig, err = internal.LoadTLSConfig(cfg); err != nil {
93+
return nil, err
10594
}
10695

10796
if cfg.ProxyUri != "" {

internal/authz/oidc_test.go

-16
Original file line numberDiff line numberDiff line change
@@ -1333,20 +1333,6 @@ func TestLoadWellKnownConfigError(t *testing.T) {
13331333
require.Error(t, err) // Fail to retrieve the dynamic config since the test server is not running
13341334
}
13351335

1336-
const smallCAPem = `-----BEGIN CERTIFICATE-----
1337-
MIIB8TCCAZugAwIBAgIJANZ3fvnlU+1IMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNV
1338-
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRAwDgYDVQQKDAdUZXRyYXRlMRQw
1339-
EgYDVQQLDAtFbmdpbmVlcmluZzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDIx
1340-
NjE1MzExOFoXDTI0MDIxNzE1MzExOFowXjELMAkGA1UEBhMCVVMxEzARBgNVBAgM
1341-
CkNhbGlmb3JuaWExEDAOBgNVBAoMB1RldHJhdGUxFDASBgNVBAsMC0VuZ2luZWVy
1342-
aW5nMRIwEAYDVQQDDAlsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEA
1343-
17tRxNJNLZVu2ntW/ehw5BneJFV+o7UmpCipv0zBtMtgJw2Z04fYiipaXgwg/sVL
1344-
wnyFgbhd0OgoIEg+ND38iQIDAQABozwwOjASBgNVHRMBAf8ECDAGAQH/AgEBMA4G
1345-
A1UdDwEB/wQEAwIC5DAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQEL
1346-
BQADQQAnQuyYJ6FbTuwtduT1ZCDcXMqTKcLb4ex3iaowflGubQuCX41yIprFScN4
1347-
2P5SpEcFlILZiK6vRzyPmuWEQVVr
1348-
-----END CERTIFICATE-----`
1349-
13501336
func TestNewOIDCHandler(t *testing.T) {
13511337

13521338
tests := []struct {
@@ -1356,8 +1342,6 @@ func TestNewOIDCHandler(t *testing.T) {
13561342
}{
13571343
{"empty", &oidcv1.OIDCConfig{}, false},
13581344
{"proxy uri", &oidcv1.OIDCConfig{ProxyUri: "http://proxy"}, false},
1359-
{"trusted CA-invalid", &oidcv1.OIDCConfig{TrustedCertificateAuthority: "<ca pem>"}, true},
1360-
{"trusted CA-valid", &oidcv1.OIDCConfig{TrustedCertificateAuthority: smallCAPem}, false},
13611345
}
13621346

13631347
for _, tt := range tests {

internal/oidc/jwks.go

+15-10
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ package oidc
1616

1717
import (
1818
"context"
19-
"crypto/tls"
2019
"errors"
2120
"fmt"
2221
"net/http"
@@ -94,37 +93,43 @@ func (j *DefaultJWKSProvider) GracefulStop() {
9493
// Get the JWKS for the given OIDC configuration
9594
func (j *DefaultJWKSProvider) Get(ctx context.Context, config *oidcv1.OIDCConfig) (jwk.Set, error) {
9695
if config.GetJwksFetcher() != nil {
97-
return j.fetchDynamic(ctx, config.GetJwksFetcher())
96+
return j.fetchDynamic(ctx, config)
9897
}
9998
return j.fetchStatic(config.GetJwks())
10099
}
101100

102101
// fetchDynamic fetches the JWKS from the given URI. If the JWKS URI is already know, the JWKS will be returned from
103102
// the cache. Otherwise, the JWKS will be fetched from the URI and the cache will be configured to periodically
104103
// refresh the JWKS.
105-
func (j *DefaultJWKSProvider) fetchDynamic(ctx context.Context, config *oidcv1.OIDCConfig_JwksFetcherConfig) (jwk.Set, error) {
104+
func (j *DefaultJWKSProvider) fetchDynamic(ctx context.Context, config *oidcv1.OIDCConfig) (jwk.Set, error) {
106105
log := j.log.Context(ctx)
106+
jwksConfig := config.GetJwksFetcher()
107107

108-
if !j.cache.IsRegistered(config.JwksUri) {
108+
if !j.cache.IsRegistered(jwksConfig.JwksUri) {
109109
transport := http.DefaultTransport.(*http.Transport).Clone()
110-
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: config.SkipVerifyPeerCert}
110+
111+
var err error
112+
if transport.TLSClientConfig, err = internal.LoadTLSConfig(config); err != nil {
113+
return nil, fmt.Errorf("error loading TLS config: %w", err)
114+
}
115+
111116
client := &http.Client{Transport: transport}
112-
refreshInterval := time.Duration(config.PeriodicFetchIntervalSec) * time.Second
117+
refreshInterval := time.Duration(jwksConfig.PeriodicFetchIntervalSec) * time.Second
113118
if refreshInterval == 0 {
114119
refreshInterval = DefaultFetchInterval
115120
}
116121

117-
log.Info("configuring JWKS auto refresh", "jwks", config.JwksUri, "interval", refreshInterval, "skip_verify", config.SkipVerifyPeerCert)
122+
log.Info("configuring JWKS auto refresh", "jwks", jwksConfig.JwksUri, "interval", refreshInterval, "skip_verify", config.GetSkipVerifyPeerCert())
118123

119-
j.cache.Configure(config.JwksUri,
124+
j.cache.Configure(jwksConfig.JwksUri,
120125
jwk.WithHTTPClient(client),
121126
jwk.WithRefreshInterval(refreshInterval),
122127
)
123128
}
124129

125-
log.Debug("fetching JWKS", "jwks", config.JwksUri)
130+
log.Debug("fetching JWKS", "jwks", jwksConfig.JwksUri)
126131

127-
jwks, err := j.cache.Fetch(ctx, config.JwksUri)
132+
jwks, err := j.cache.Fetch(ctx, jwksConfig.JwksUri)
128133
if err != nil {
129134
return nil, fmt.Errorf("%w: %v", ErrJWKSFetch, err)
130135
}

internal/oidc/jwks_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,11 @@ func TestDynamicJWKSProvider(t *testing.T) {
185185
JwksFetcher: &oidcv1.OIDCConfig_JwksFetcherConfig{
186186
JwksUri: server.URL,
187187
PeriodicFetchIntervalSec: 1,
188-
SkipVerifyPeerCert: true,
189188
},
190189
},
190+
TrustedCaConfig: &oidcv1.OIDCConfig_SkipVerifyPeerCert{
191+
SkipVerifyPeerCert: true,
192+
},
191193
}
192194

193195
keys, err := cache.Get(context.Background(), config)

internal/tls.go

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2024 Tetrate
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package internal
16+
17+
import (
18+
"crypto/tls"
19+
"crypto/x509"
20+
"errors"
21+
"fmt"
22+
"os"
23+
)
24+
25+
// AuthServiceTLSConfig is an interface for the TLS configuration of the AuthService.
26+
type AuthServiceTLSConfig interface {
27+
// GetTrustedCertificateAuthority returns the trusted certificate authority PEM.
28+
GetTrustedCertificateAuthority() string
29+
// GetTrustedCertificateAuthorityFile returns the path to the trusted certificate authority file.
30+
GetTrustedCertificateAuthorityFile() string
31+
// GetSkipVerifyPeerCert returns whether to skip verification of the peer certificate.
32+
GetSkipVerifyPeerCert() bool
33+
}
34+
35+
// LoadTLSConfig loads a TLS configuration from the given AuthServiceTLSConfig.
36+
func LoadTLSConfig(config AuthServiceTLSConfig) (*tls.Config, error) {
37+
tlsConfig := &tls.Config{}
38+
39+
// Load the trusted CA PEM from the config
40+
var ca []byte
41+
switch {
42+
43+
case config.GetTrustedCertificateAuthority() != "":
44+
ca = []byte(config.GetTrustedCertificateAuthority())
45+
46+
case config.GetTrustedCertificateAuthorityFile() != "":
47+
var err error
48+
ca, err = os.ReadFile(config.GetTrustedCertificateAuthorityFile())
49+
if err != nil {
50+
return nil, fmt.Errorf("error reading trusted CA file: %w", err)
51+
}
52+
53+
case config.GetSkipVerifyPeerCert():
54+
tlsConfig.InsecureSkipVerify = true
55+
56+
default:
57+
// No CA or skip verification, return nil TLS config
58+
return nil, nil
59+
}
60+
61+
// Add the loaded CA to the TLS config
62+
if len(ca) != 0 {
63+
certPool, err := x509.SystemCertPool()
64+
if err != nil {
65+
return nil, fmt.Errorf("error creating system cert pool: %w", err)
66+
}
67+
68+
if ok := certPool.AppendCertsFromPEM(ca); !ok {
69+
return nil, errors.New("could no load trusted certificate authority")
70+
}
71+
72+
tlsConfig.RootCAs = certPool
73+
}
74+
75+
return tlsConfig, nil
76+
}

0 commit comments

Comments
 (0)