Skip to content

Commit c1ac8c0

Browse files
committed
trust the in-cluster oauth-server's certificate when constructing an OpenShift client
1 parent 689fb46 commit c1ac8c0

File tree

5 files changed

+130
-56
lines changed

5 files changed

+130
-56
lines changed

providers/openshift/authentication.go

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ import (
1313
"k8s.io/apiserver/pkg/authentication/request/headerrequest"
1414
"k8s.io/apiserver/pkg/server/dynamiccertificates"
1515
authenticationclient "k8s.io/client-go/kubernetes/typed/authentication/v1"
16-
"k8s.io/client-go/rest"
17-
"k8s.io/client-go/tools/clientcmd"
1816
)
1917

2018
type RequestHeaderAuthenticationOptions struct {
@@ -190,33 +188,8 @@ func deserializeStrings(in string) ([]string, error) {
190188
return ret, nil
191189
}
192190

193-
func (s *DelegatingAuthenticationOptions) getClientConfig() (*rest.Config, error) {
194-
var clientConfig *rest.Config
195-
var err error
196-
if len(s.RemoteKubeConfigFile) > 0 {
197-
loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: s.RemoteKubeConfigFile}
198-
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
199-
200-
clientConfig, err = loader.ClientConfig()
201-
202-
} else {
203-
// without the remote kubeconfig file, try to use the in-cluster config. Most addon API servers will
204-
// use this path
205-
clientConfig, err = rest.InClusterConfig()
206-
}
207-
if err != nil {
208-
return nil, err
209-
}
210-
211-
// set high qps/burst limits since this will effectively limit API server responsiveness
212-
clientConfig.QPS = 200
213-
clientConfig.Burst = 400
214-
215-
return clientConfig, nil
216-
}
217-
218191
func (s *DelegatingAuthenticationOptions) newTokenAccessReview() (authenticationclient.TokenReviewInterface, error) {
219-
clientConfig, err := s.getClientConfig()
192+
clientConfig, err := GetClientConfig(s.RemoteKubeConfigFile)
220193
if err != nil {
221194
return nil, err
222195
}

providers/openshift/authorization.go

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import (
88

99
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
1010
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1"
11-
"k8s.io/client-go/rest"
12-
"k8s.io/client-go/tools/clientcmd"
1311
)
1412

1513
// DelegatingAuthorizationOptions provides an easy way for composing API servers to delegate their authorization to
@@ -69,27 +67,11 @@ func (s *DelegatingAuthorizationOptions) ToAuthorizationConfig() (authorizerfact
6967
}
7068

7169
func (s *DelegatingAuthorizationOptions) newSubjectAccessReview() (authorizationclient.SubjectAccessReviewInterface, error) {
72-
var clientConfig *rest.Config
73-
var err error
74-
if len(s.RemoteKubeConfigFile) > 0 {
75-
loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: s.RemoteKubeConfigFile}
76-
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
77-
78-
clientConfig, err = loader.ClientConfig()
79-
80-
} else {
81-
// without the remote kubeconfig file, try to use the in-cluster config. Most addon API servers will
82-
// use this path
83-
clientConfig, err = rest.InClusterConfig()
84-
}
70+
clientConfig, err := GetClientConfig(s.RemoteKubeConfigFile)
8571
if err != nil {
8672
return nil, err
8773
}
8874

89-
// set high qps/burst limits since this will effectively limit API server responsiveness
90-
clientConfig.QPS = 200
91-
clientConfig.Burst = 400
92-
9375
client, err := authorizationclient.NewForConfig(clientConfig)
9476
if err != nil {
9577
return nil, err

providers/openshift/kubeclient.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package openshift
2+
3+
import (
4+
"flag"
5+
6+
"k8s.io/client-go/kubernetes"
7+
)
8+
9+
type KubeClientOptions struct {
10+
// RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server
11+
RemoteKubeConfigFile string
12+
}
13+
14+
func NewKubeClientOptions() *KubeClientOptions {
15+
return &KubeClientOptions{}
16+
}
17+
18+
func (o *KubeClientOptions) Validate() []error {
19+
return []error{}
20+
}
21+
22+
func (o *KubeClientOptions) AddFlags(fs *flag.FlagSet) {
23+
fs.StringVar(&o.RemoteKubeConfigFile, "kubeconfig", o.RemoteKubeConfigFile, ""+
24+
"kubeconfig file pointing at the 'core' OpenShift server that has the oauth-server running on it")
25+
}
26+
27+
func (o *KubeClientOptions) ToKubeClientConfig() (*kubernetes.Clientset, error) {
28+
clientConfig, err := GetClientConfig(o.RemoteKubeConfigFile)
29+
if err != nil {
30+
return nil, err
31+
}
32+
33+
kubeClient, err := kubernetes.NewForConfig(clientConfig)
34+
if err != nil {
35+
return nil, err
36+
}
37+
38+
return kubeClient, nil
39+
}

providers/openshift/provider.go

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,14 @@ import (
2828
authenticationv1 "k8s.io/api/authentication/v1"
2929
authorizationv1 "k8s.io/api/authorization/v1"
3030
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31+
"k8s.io/apimachinery/pkg/fields"
3132
"k8s.io/apimachinery/pkg/util/wait"
3233
"k8s.io/apiserver/pkg/authentication/authenticator"
3334
"k8s.io/apiserver/pkg/authorization/authorizer"
35+
"k8s.io/client-go/informers"
36+
corev1listers "k8s.io/client-go/listers/core/v1"
37+
"k8s.io/client-go/rest"
38+
"k8s.io/client-go/tools/clientcmd"
3439
)
3540

3641
func emptyURL(u *url.URL) bool {
@@ -45,13 +50,15 @@ type OpenShiftProvider struct {
4550

4651
AuthenticationOptions DelegatingAuthenticationOptions
4752
AuthorizationOptions DelegatingAuthorizationOptions
53+
KubeClientOptions KubeClientOptions
4854

49-
authenticator authenticator.Request
50-
authorizer authorizer.Authorizer
51-
defaultRecord authorizer.AttributesRecord
52-
reviews []string
53-
paths recordsByPath
54-
hostreviews map[string][]string
55+
authenticator authenticator.Request
56+
authorizer authorizer.Authorizer
57+
configMapLister corev1listers.ConfigMapLister
58+
defaultRecord authorizer.AttributesRecord
59+
reviews []string
60+
paths recordsByPath
61+
hostreviews map[string][]string
5562

5663
// httpClientCache stores httpClient objects so that new client does not have to
5764
// be created on each request just to prevent CAs content changed
@@ -83,6 +90,7 @@ func (p *OpenShiftProvider) SetClientCAFile(file string) {
8390
}
8491

8592
func (p *OpenShiftProvider) Bind(flags *flag.FlagSet) {
93+
p.KubeClientOptions.AddFlags(flags)
8694
p.AuthenticationOptions.AddFlags(flags)
8795
p.AuthorizationOptions.AddFlags(flags)
8896
}
@@ -145,7 +153,17 @@ func (p *OpenShiftProvider) newOpenShiftClient() (*http.Client, error) {
145153

146154
// try to retrieve a cached client
147155
metadataHash, err := util.GetFilesMetadataHash(capaths)
148-
if httpClient, ok := p.httpClientCache.Load(metadataHash); ok {
156+
if err != nil {
157+
return nil, err
158+
}
159+
160+
oauthServerCert, err := p.configMapLister.ConfigMaps("openshift-config-managed").Get("oauth-serving-cert")
161+
if err != nil {
162+
return nil, err
163+
}
164+
165+
cachedKey := metadataHash + oauthServerCert.ResourceVersion
166+
if httpClient, ok := p.httpClientCache.Load(cachedKey); ok {
149167
return httpClient.(*http.Client), nil
150168
}
151169

@@ -155,6 +173,10 @@ func (p *OpenShiftProvider) newOpenShiftClient() (*http.Client, error) {
155173
return nil, err
156174
}
157175

176+
if ok := pool.AppendCertsFromPEM([]byte(oauthServerCert.Data["ca-bundle.crt"])); !ok {
177+
log.Println("failed to add the oauth-server certificate to the OpenShift client CA bundle")
178+
}
179+
158180
httpClient := &http.Client{
159181
Jar: http.DefaultClient.Jar,
160182
Transport: &http.Transport{
@@ -163,7 +185,7 @@ func (p *OpenShiftProvider) newOpenShiftClient() (*http.Client, error) {
163185
},
164186
Timeout: 1 * time.Minute,
165187
}
166-
p.httpClientCache.Store(metadataHash, httpClient)
188+
p.httpClientCache.Store(cachedKey, httpClient)
167189

168190
return httpClient, nil
169191
}
@@ -310,6 +332,21 @@ func (p *OpenShiftProvider) Complete(data *providers.ProviderData, reviewURL *ur
310332
p.ProviderData = data
311333
p.ReviewURL = reviewURL
312334

335+
kubeClient, err := p.KubeClientOptions.ToKubeClientConfig()
336+
if err != nil {
337+
return err
338+
}
339+
340+
kubeInformersMachineConfigNS := informers.NewSharedInformerFactoryWithOptions(
341+
kubeClient,
342+
10*time.Minute,
343+
informers.WithNamespace("openshift-config-managed"),
344+
informers.WithTweakListOptions(func(opts *metav1.ListOptions) {
345+
opts.FieldSelector = fields.OneTermEqualSelector("metadata.name", "oauth-serving-cert").String()
346+
}))
347+
go kubeInformersMachineConfigNS.Core().V1().ConfigMaps().Informer().Run(context.TODO().Done())
348+
p.configMapLister = kubeInformersMachineConfigNS.Core().V1().ConfigMaps().Lister()
349+
313350
if len(p.paths) > 0 {
314351
log.Printf("Delegation of authentication and authorization to OpenShift is enabled for bearer tokens and client certificates.")
315352

@@ -655,3 +692,28 @@ func getKubeAPIURLWithPath(path string) *url.URL {
655692

656693
return ret
657694
}
695+
696+
func GetClientConfig(remoteKubeConfigFile string) (*rest.Config, error) {
697+
var clientConfig *rest.Config
698+
var err error
699+
if len(remoteKubeConfigFile) > 0 {
700+
loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: remoteKubeConfigFile}
701+
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
702+
703+
clientConfig, err = loader.ClientConfig()
704+
705+
} else {
706+
// without the remote kubeconfig file, try to use the in-cluster config. Most addon API servers will
707+
// use this path
708+
clientConfig, err = rest.InClusterConfig()
709+
}
710+
if err != nil {
711+
return nil, err
712+
}
713+
714+
// set high qps/burst limits since this will effectively limit API server responsiveness
715+
clientConfig.QPS = 200
716+
clientConfig.Burst = 400
717+
718+
return clientConfig, nil
719+
}

providers/openshift/provider_test.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,15 @@ import (
88
"reflect"
99
"testing"
1010

11+
"github.com/stretchr/testify/require"
12+
13+
corev1 "k8s.io/api/core/v1"
14+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1115
"k8s.io/apiserver/pkg/authentication/authenticator"
1216
"k8s.io/apiserver/pkg/authentication/user"
1317
"k8s.io/apiserver/pkg/authorization/authorizer"
18+
corev1listers "k8s.io/client-go/listers/core/v1"
19+
"k8s.io/client-go/tools/cache"
1420
)
1521

1622
type mockAuthRequestHandler struct {
@@ -147,7 +153,19 @@ func TestNewOpenShiftClient(t *testing.T) {
147153
t.Fatalf("failed to write CA cert to tmpfile: %v", err)
148154
}
149155

150-
p := &OpenShiftProvider{}
156+
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
157+
err = indexer.Add(&corev1.ConfigMap{
158+
ObjectMeta: metav1.ObjectMeta{
159+
Name: "oauth-serving-cert",
160+
Namespace: "openshift-config-managed",
161+
},
162+
})
163+
require.NoError(t, err)
164+
165+
p := &OpenShiftProvider{
166+
configMapLister: corev1listers.NewConfigMapLister(indexer),
167+
}
168+
151169
p.paths = recordsByPath{pathRecord{"/someurl", authorizer.AttributesRecord{}}}
152170
p.authenticator = &mockAuthRequestHandler{}
153171
p.authorizer = &mockAuthorizer{}

0 commit comments

Comments
 (0)