Skip to content
This repository was archived by the owner on Apr 22, 2024. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 14aee28

Browse files
committedFeb 23, 2024·
load client secret from k8s Secret when reading config
Signed-off-by: huabing zhao <[email protected]>
1 parent c924e2c commit 14aee28

14 files changed

+752
-175
lines changed
 

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

+214-87
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

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

‎config/v1/oidc/config.proto

+24-4
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,30 @@ message OIDCConfig {
130130
// Required.
131131
string client_id = 5 [(validate.rules).string.min_len = 1];
132132

133-
// The OIDC client secret assigned to the filter to be used in the
134-
// [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
135-
// Required.
136-
string client_secret = 6 [(validate.rules).string.min_len = 1];
133+
// This message defines a reference to a Kubernetes Secret resource.
134+
message SecretReference {
135+
// The namespace of the referenced Secret, if not set, default to "default" namespace.
136+
string namespace = 1;
137+
138+
// The name of the referenced Secret.
139+
string name = 2 [(validate.rules).string.min_len = 1];
140+
}
141+
142+
oneof client_secret_config {
143+
option(validate.required) = true;
144+
// The OIDC client secret assigned to the filter to be used in the
145+
// [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
146+
// This field keeps the client secret in plain text. Recommend to use `client_secret_ref` instead
147+
// when running in a Kubernetes cluster.
148+
string client_secret = 6;
149+
150+
// The Kubernetes secret that contains the OIDC client secret assigned to the filter to be used in the
151+
// [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
152+
//
153+
// This is an Opaque secret. The client secret should be stored in the key "client-secret".
154+
// This filed is only valid when running in a Kubernetes cluster.
155+
SecretReference client_secret_ref = 20;
156+
}
137157

138158
// Additional scopes passed to the OIDC Provider in the
139159
// [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).

‎go.mod

+5-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ require (
2020
k8s.io/api v0.29.2
2121
k8s.io/apimachinery v0.29.2
2222
k8s.io/client-go v0.29.2
23+
sigs.k8s.io/controller-runtime v0.17.2
2324
)
2425

2526
require (
@@ -32,7 +33,9 @@ require (
3233
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
3334
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
3435
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
35-
github.com/go-logr/logr v1.3.0 // indirect
36+
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
37+
github.com/evanphx/json-patch/v5 v5.8.0 // indirect
38+
github.com/go-logr/logr v1.4.1 // indirect
3639
github.com/go-openapi/jsonpointer v0.19.6 // indirect
3740
github.com/go-openapi/jsonreference v0.20.2 // indirect
3841
github.com/go-openapi/swag v0.22.3 // indirect
@@ -74,5 +77,5 @@ require (
7477
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
7578
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
7679
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
77-
sigs.k8s.io/yaml v1.3.0 // indirect
80+
sigs.k8s.io/yaml v1.4.0 // indirect
7881
)

‎go.sum

+22-7
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,13 @@ github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU
3535
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
3636
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
3737
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
38-
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
38+
github.com/evanphx/json-patch/v5 v5.8.0 h1:lRj6N9Nci7MvzrXuX6HFzU8XjmhPiXPlsKEy1u0KQro=
39+
github.com/evanphx/json-patch/v5 v5.8.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
3940
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
41+
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
42+
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
43+
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
44+
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
4045
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
4146
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
4247
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
@@ -106,10 +111,10 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
106111
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
107112
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
108113
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
109-
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
110-
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
111-
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
112-
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
114+
github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY=
115+
github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw=
116+
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
117+
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
113118
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
114119
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
115120
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -141,13 +146,19 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
141146
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
142147
github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE=
143148
github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
149+
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
150+
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
151+
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
152+
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
144153
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
145154
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
146155
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
147156
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
148157
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
149158
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
150159
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
160+
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
161+
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
151162
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
152163
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
153164
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@@ -234,6 +245,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
234245
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
235246
k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A=
236247
k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0=
248+
k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3Q4lV0=
249+
k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc=
237250
k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
238251
k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
239252
k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg=
@@ -244,9 +257,11 @@ k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/A
244257
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
245258
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
246259
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
260+
sigs.k8s.io/controller-runtime v0.17.2 h1:FwHwD1CTUemg0pW2otk7/U5/i5m2ymzvOXdbeGOUvw0=
261+
sigs.k8s.io/controller-runtime v0.17.2/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s=
247262
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
248263
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
249264
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
250265
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
251-
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
252-
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
266+
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
267+
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

‎internal/authz/oidc_test.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,10 @@ var (
141141
AuthorizationUri: "http://idp-test-server/auth",
142142
CallbackUri: "https://localhost:443/callback",
143143
ClientId: "test-client-id",
144-
ClientSecret: "test-client-secret",
145-
Scopes: []string{"openid", "email"},
144+
ClientSecretConfig: &oidcv1.OIDCConfig_ClientSecret{
145+
ClientSecret: "test-client-secret",
146+
},
147+
Scopes: []string{"openid", "email"},
146148
Logout: &oidcv1.LogoutConfig{
147149
Path: "/logout",
148150
RedirectUri: "http://idp-test-server/logout?with-params",
@@ -161,8 +163,10 @@ var (
161163
ConfigurationUri: "http://idp-test-server/.well-known/openid-configuration",
162164
CallbackUri: "https://localhost:443/callback",
163165
ClientId: "test-client-id",
164-
ClientSecret: "test-client-secret",
165-
Scopes: []string{"openid", "email"},
166+
ClientSecretConfig: &oidcv1.OIDCConfig_ClientSecret{
167+
ClientSecret: "test-client-secret",
168+
},
169+
Scopes: []string{"openid", "email"},
166170
}
167171

168172
wellKnownURIs = `

‎internal/config.go

+85-36
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,20 @@
1515
package internal
1616

1717
import (
18+
"context"
1819
"errors"
1920
"fmt"
2021
"net/url"
2122
"os"
22-
"strings"
2323

2424
"github.com/redis/go-redis/v9"
2525
"github.com/tetratelabs/run"
2626
"google.golang.org/protobuf/encoding/protojson"
2727
"google.golang.org/protobuf/proto"
28+
v1 "k8s.io/api/core/v1"
29+
"k8s.io/apimachinery/pkg/types"
30+
"sigs.k8s.io/controller-runtime/pkg/client"
31+
"sigs.k8s.io/controller-runtime/pkg/client/config"
2832

2933
configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1"
3034
oidcv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/oidc"
@@ -42,8 +46,9 @@ var (
4246
ErrInvalidURL = errors.New("invalid URL")
4347
ErrRequiredURL = errors.New("required URL")
4448
ErrHealthPortInUse = errors.New("health port is already in use by listen port")
45-
ErrMustNotBeRootPath = errors.New("must not be root path")
46-
ErrMustBeDifferentPath = errors.New("must be different path")
49+
50+
// kubeClient is the in-cluster Kubernetes client used to retrieve the client secret from a Kubernetes secret.
51+
kubeClient client.Client
4752
)
4853

4954
// LocalConfigFile is a run.Config that loads the configuration file.
@@ -117,6 +122,12 @@ func (l *LocalConfigFile) Validate() error {
117122
return err
118123
}
119124

125+
// Initialize the client secret from the Kubernetes secret if it is a referenced
126+
// Kubernetes secret.
127+
if err = initClientSecret(&l.Config); err != nil {
128+
return err
129+
}
130+
120131
// Now that all defaults are set and configurations are merged, validate all final settings
121132
return l.Config.ValidateAll()
122133
}
@@ -154,19 +165,6 @@ func mergeAndValidateOIDCConfigs(cfg *configv1.Config) error {
154165

155166
// Set the defaults
156167
applyOIDCDefaults(f.GetOidc())
157-
158-
// validate the logout path is not the root path
159-
if f.GetOidc().GetLogout() != nil {
160-
if isRootPath(f.GetOidc().GetLogout().GetPath()) {
161-
return fmt.Errorf("%w: invalid logout path", ErrMustNotBeRootPath)
162-
}
163-
}
164-
165-
// validate the callback and the logout path are different
166-
callbackURI, _ := url.Parse(f.GetOidc().GetCallbackUri())
167-
if f.GetOidc().GetLogout() != nil && callbackURI.Path == f.GetOidc().GetLogout().GetPath() {
168-
errs = append(errs, fmt.Errorf("%w: callback and logout paths must be different in chain %q", ErrMustBeDifferentPath, fc.Name))
169-
}
170168
}
171169
}
172170
// Clear the default config as it has already been merged. This way there is only one
@@ -237,21 +235,11 @@ func validateOIDCConfigURLs(c *oidcv1.OIDCConfig) error {
237235
if err := validateURL(c.GetJwksFetcher().GetJwksUri()); err != nil {
238236
return fmt.Errorf("%w: invalid JWKS Fetcher URL: %w", ErrInvalidURL, err)
239237
}
240-
241-
// Backwards compatibility with redis tcp:// URIs used in the old authservice
242-
if redisURI := c.GetRedisSessionStoreConfig().GetServerUri(); redisURI != "" {
243-
c.GetRedisSessionStoreConfig().ServerUri = strings.Replace(redisURI, "tcp://", "redis://", 1)
244-
}
245-
246238
if redisURL := c.GetRedisSessionStoreConfig().GetServerUri(); redisURL != "" {
247239
if _, err := redis.ParseURL(redisURL); err != nil {
248240
return fmt.Errorf("%w: invalid Redis session store URL: %w", ErrInvalidURL, err)
249241
}
250242
}
251-
252-
if hasRootPath(c.GetCallbackUri()) {
253-
return fmt.Errorf("%w: invalid callback URL", ErrMustNotBeRootPath)
254-
}
255243
return nil
256244
}
257245

@@ -263,17 +251,78 @@ func validateURL(u string) error {
263251
return err
264252
}
265253

266-
// hasRootPath returns true if the path of the given URL is "/" or empty.
267-
// prerequisite: u is a valid URL.
268-
func hasRootPath(uri string) bool {
269-
if uri == "" {
270-
return false
254+
func initClientSecret(cfg *configv1.Config) error {
255+
var (
256+
err error
257+
errs []error
258+
)
259+
260+
for _, fc := range cfg.Chains {
261+
for _, f := range fc.Filters {
262+
if f.GetOidc() != nil && f.GetOidc().GetClientSecretRef() != nil {
263+
if kubeClient == nil {
264+
kubeClient, err = getKubeClient()
265+
if err != nil {
266+
return fmt.Errorf("error creating kube client: %w", err)
267+
}
268+
}
269+
270+
clientSecret, err := getClientSecretFromK8s(f.GetOidc(), kubeClient)
271+
if err != nil {
272+
errs = append(errs, fmt.Errorf("error getting client secret: %w", err))
273+
}
274+
275+
f.GetOidc().ClientSecretConfig = &oidcv1.OIDCConfig_ClientSecret{
276+
ClientSecret: clientSecret,
277+
}
278+
}
279+
}
280+
}
281+
282+
return errors.Join(errs...)
283+
}
284+
285+
const (
286+
defaultNamespace = "default"
287+
clientSecretKey = "client-secret"
288+
)
289+
290+
// getClientSecretFromK8s retrieves the client secret from the referenced Kubernetes secret.
291+
func getClientSecretFromK8s(c *oidcv1.OIDCConfig, cl client.Client) (string, error) {
292+
if c.GetClientSecretRef() == nil {
293+
return "", fmt.Errorf("client secret ref not found")
294+
}
295+
296+
namespace := c.GetClientSecretRef().Namespace
297+
if namespace == "" {
298+
namespace = defaultNamespace
299+
}
300+
secretName := types.NamespacedName{
301+
Namespace: namespace,
302+
Name: c.GetClientSecretRef().Name,
303+
}
304+
305+
secret := &v1.Secret{}
306+
err := cl.Get(context.Background(), secretName, secret)
307+
if err != nil {
308+
return "", fmt.Errorf("error getting secret: %w", err)
309+
}
310+
clientSecretBytes, ok := secret.Data[clientSecretKey]
311+
if !ok || len(clientSecretBytes) == 0 {
312+
return "", fmt.Errorf("client secret not found in secret %s", secretName.String())
271313
}
272-
parsed, _ := url.Parse(uri)
273-
return isRootPath(parsed.Path)
314+
return string(clientSecretBytes), nil
274315
}
275316

276-
// isRootPath returns true if the path is "/" or empty.
277-
func isRootPath(path string) bool {
278-
return path == "/" || path == ""
317+
func getKubeClient() (client.Client, error) {
318+
cfg, err := config.GetConfig()
319+
if err != nil {
320+
return nil, fmt.Errorf("error getting kube config: %w", err)
321+
}
322+
323+
cl, err := client.New(cfg, client.Options{})
324+
if err != nil {
325+
return nil, fmt.Errorf("errot creating kube client: %w", err)
326+
}
327+
return cl, nil
279328
}

‎internal/config_test.go

+106-12
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ import (
2323
"github.com/tetratelabs/run"
2424
"github.com/tetratelabs/telemetry"
2525
"google.golang.org/protobuf/proto"
26+
v1 "k8s.io/api/core/v1"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
2629

2730
configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1"
2831
mockv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/mock"
@@ -62,10 +65,6 @@ func TestValidateConfig(t *testing.T) {
6265
{"invalid-redis", "testdata/invalid-redis.json", errCheck{is: ErrInvalidURL}},
6366
{"invalid-oidc-uris", "testdata/invalid-oidc-uris.json", errCheck{is: ErrRequiredURL}},
6467
{"invalid-health-port", "testdata/invalid-health-port.json", errCheck{is: ErrHealthPortInUse}},
65-
{"invalid-callback-uri", "testdata/invalid-callback.json", errCheck{is: ErrMustNotBeRootPath}},
66-
{"invalid-logout-path", "testdata/invalid-logout.json", errCheck{is: ErrMustNotBeRootPath}},
67-
{"valid-logout-override-default", "testdata/valid-logout-override-default.json", errCheck{is: nil}},
68-
{"invalid-callback-and-logout-path", "testdata/invalid-callback-logout.json", errCheck{is: ErrMustBeDifferentPath}},
6968
{"oidc-dynamic", "testdata/oidc-dynamic.json", errCheck{is: nil}},
7069
{"valid", "testdata/mock.json", errCheck{is: nil}},
7170
}
@@ -80,7 +79,7 @@ func TestValidateConfig(t *testing.T) {
8079

8180
func TestValidateURLs(t *testing.T) {
8281
const (
83-
validURL = "http://fake/path"
82+
validURL = "http://fake"
8483
invalidURL = "ht tp://invalid"
8584
validRedisURL = "redis://localhost:6379/0"
8685
)
@@ -204,18 +203,19 @@ func TestLoadOIDC(t *testing.T) {
204203
{
205204
Type: &configv1.Filter_Oidc{
206205
Oidc: &oidcv1.OIDCConfig{
207-
AuthorizationUri: "http://fake",
208-
TokenUri: "http://fake",
209-
CallbackUri: "http://fake/callback",
210-
JwksConfig: &oidcv1.OIDCConfig_Jwks{Jwks: "fake-jwks"},
211-
ClientId: "fake-client-id",
212-
ClientSecret: "fake-client-secret",
206+
AuthorizationUri: "http://fake",
207+
TokenUri: "http://fake",
208+
CallbackUri: "http://fake",
209+
JwksConfig: &oidcv1.OIDCConfig_Jwks{Jwks: "fake-jwks"},
210+
ClientId: "fake-client-id",
211+
ClientSecretConfig: &oidcv1.OIDCConfig_ClientSecret{
212+
ClientSecret: "fake-client-secret",
213+
},
213214
CookieNamePrefix: "",
214215
IdToken: &oidcv1.TokenConfig{Preamble: "Bearer", Header: "authorization"},
215216
ProxyUri: "http://fake",
216217
RedisSessionStoreConfig: &oidcv1.RedisConfig{ServerUri: "redis://localhost:6379/0"},
217218
Scopes: []string{scopeOIDC},
218-
Logout: &oidcv1.LogoutConfig{Path: "/logout", RedirectUri: "http://fake"},
219219
},
220220
},
221221
},
@@ -255,3 +255,97 @@ func TestConfigToJSONString(t *testing.T) {
255255
})
256256
}
257257
}
258+
259+
func TestLoadOIDCClientSecret(t *testing.T) {
260+
validSecret := &v1.Secret{
261+
ObjectMeta: metav1.ObjectMeta{
262+
Namespace: "default",
263+
Name: "test-secret",
264+
},
265+
Data: map[string][]byte{
266+
clientSecretKey: []byte("fake-client-secret"),
267+
},
268+
}
269+
invalidSecret := &v1.Secret{
270+
ObjectMeta: metav1.ObjectMeta{
271+
Namespace: "default",
272+
Name: "invalid-secret",
273+
},
274+
Data: map[string][]byte{
275+
clientSecretKey + "-invalid": []byte("fake-client-secret"),
276+
},
277+
}
278+
279+
// inject kube client for the test
280+
kubeClient = fakeclient.NewClientBuilder().WithObjects(validSecret, invalidSecret).Build()
281+
282+
tests := []struct {
283+
name string
284+
configFile string
285+
want *configv1.Config
286+
error string
287+
}{
288+
{
289+
name: "valid-secret",
290+
configFile: "oidc-with-valid-secret-ref",
291+
want: &configv1.Config{
292+
ListenAddress: "0.0.0.0",
293+
ListenPort: 8080,
294+
LogLevel: "debug",
295+
Threads: 1,
296+
Chains: []*configv1.FilterChain{
297+
{
298+
Name: "oidc",
299+
Filters: []*configv1.Filter{
300+
{
301+
Type: &configv1.Filter_Oidc{
302+
Oidc: &oidcv1.OIDCConfig{
303+
AuthorizationUri: "http://fake",
304+
TokenUri: "http://fake",
305+
CallbackUri: "http://fake",
306+
JwksConfig: &oidcv1.OIDCConfig_Jwks{Jwks: "fake-jwks"},
307+
ClientId: "fake-client-id",
308+
ClientSecretConfig: &oidcv1.OIDCConfig_ClientSecret{
309+
ClientSecret: "fake-client-secret",
310+
},
311+
CookieNamePrefix: "",
312+
IdToken: &oidcv1.TokenConfig{Preamble: "Bearer", Header: "authorization"},
313+
ProxyUri: "http://fake",
314+
RedisSessionStoreConfig: &oidcv1.RedisConfig{ServerUri: "redis://localhost:6379/0"},
315+
Scopes: []string{scopeOIDC},
316+
},
317+
},
318+
},
319+
},
320+
},
321+
},
322+
},
323+
error: "",
324+
},
325+
{
326+
name: "valid-secret",
327+
configFile: "oidc-with-invalid-secret-ref",
328+
error: "client secret not found in secret default/invalid-secret",
329+
},
330+
{
331+
name: "valid-secret",
332+
configFile: "oidc-with-non-existing-secret-ref",
333+
error: "secrets \"non-existing-secret\" not found",
334+
},
335+
}
336+
for _, tc := range tests {
337+
t.Run(tc.name, func(t *testing.T) {
338+
var cfg LocalConfigFile
339+
g := run.Group{Logger: telemetry.NoopLogger()}
340+
g.Register(&cfg)
341+
err := g.Run("", "--config-path", fmt.Sprintf("testdata/%s.json", tc.configFile))
342+
if tc.error != "" {
343+
require.Error(t, err)
344+
require.Contains(t, err.Error(), tc.error)
345+
} else {
346+
require.NoError(t, err)
347+
require.True(t, proto.Equal(tc.want, &cfg.Config))
348+
}
349+
})
350+
}
351+
}

‎internal/testdata/oidc-dynamic.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
{
1010
"oidc": {
1111
"configuration_uri": "http://fake",
12-
"callback_uri": "http://fake/callback",
12+
"callback_uri": "http://fake",
1313
"proxy_uri": "http://fake",
1414
"client_id": "fake-client-id",
1515
"client_secret": "fake-client-secret",

‎internal/testdata/oidc-override.json

+2-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"default_oidc_config": {
66
"authorization_uri": "http://default",
77
"token_uri": "http://default",
8-
"callback_uri": "http://fake/callback",
8+
"callback_uri": "http://fake",
99
"proxy_uri": "http://fake"
1010
},
1111
"chains": [
@@ -24,11 +24,7 @@
2424
"header": "authorization"
2525
},
2626
"redis_session_store_config": {
27-
"server_uri": "tcp://localhost:6379/0"
28-
},
29-
"logout": {
30-
"path": "/logout",
31-
"redirect_uri": "http://fake"
27+
"server_uri": "redis://localhost:6379/0"
3228
}
3329
}
3430
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"listen_address": "0.0.0.0",
3+
"listen_port": 8080,
4+
"log_level": "debug",
5+
"chains": [
6+
{
7+
"name": "oidc",
8+
"filters": [
9+
{
10+
"oidc": {
11+
"authorization_uri": "http://fake",
12+
"token_uri": "http://fake",
13+
"callback_uri": "http://fake",
14+
"proxy_uri": "http://fake",
15+
"jwks": "fake-jwks",
16+
"client_id": "fake-client-id",
17+
"client_secret_ref": {
18+
"namespace": "default",
19+
"name": "invalid-secret"
20+
},
21+
"id_token": {
22+
"preamble": "Bearer",
23+
"header": "authorization"
24+
},
25+
"redis_session_store_config": {
26+
"server_uri": "redis://localhost:6379/0"
27+
}
28+
}
29+
}
30+
]
31+
}
32+
]
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"listen_address": "0.0.0.0",
3+
"listen_port": 8080,
4+
"log_level": "debug",
5+
"chains": [
6+
{
7+
"name": "oidc",
8+
"filters": [
9+
{
10+
"oidc": {
11+
"authorization_uri": "http://fake",
12+
"token_uri": "http://fake",
13+
"callback_uri": "http://fake",
14+
"proxy_uri": "http://fake",
15+
"jwks": "fake-jwks",
16+
"client_id": "fake-client-id",
17+
"client_secret_ref": {
18+
"namespace": "default",
19+
"name": "non-existing-secret"
20+
},
21+
"id_token": {
22+
"preamble": "Bearer",
23+
"header": "authorization"
24+
},
25+
"redis_session_store_config": {
26+
"server_uri": "redis://localhost:6379/0"
27+
}
28+
}
29+
}
30+
]
31+
}
32+
]
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"listen_address": "0.0.0.0",
3+
"listen_port": 8080,
4+
"log_level": "debug",
5+
"chains": [
6+
{
7+
"name": "oidc",
8+
"filters": [
9+
{
10+
"oidc": {
11+
"authorization_uri": "http://fake",
12+
"token_uri": "http://fake",
13+
"callback_uri": "http://fake",
14+
"proxy_uri": "http://fake",
15+
"jwks": "fake-jwks",
16+
"client_id": "fake-client-id",
17+
"client_secret_ref": {
18+
"namespace": "default",
19+
"name": "test-secret"
20+
},
21+
"id_token": {
22+
"preamble": "Bearer",
23+
"header": "authorization"
24+
},
25+
"redis_session_store_config": {
26+
"server_uri": "redis://localhost:6379/0"
27+
}
28+
}
29+
}
30+
]
31+
}
32+
]
33+
}

‎internal/testdata/oidc.json

+1-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"oidc": {
1111
"authorization_uri": "http://fake",
1212
"token_uri": "http://fake",
13-
"callback_uri": "http://fake/callback",
13+
"callback_uri": "http://fake",
1414
"proxy_uri": "http://fake",
1515
"jwks": "fake-jwks",
1616
"client_id": "fake-client-id",
@@ -21,10 +21,6 @@
2121
},
2222
"redis_session_store_config": {
2323
"server_uri": "redis://localhost:6379/0"
24-
},
25-
"logout": {
26-
"path": "/logout",
27-
"redirect_uri": "http://fake"
2824
}
2925
}
3026
}

0 commit comments

Comments
 (0)
This repository has been archived.