Skip to content

Commit 477d55f

Browse files
authored
Merge pull request #1381 from openshift/wire-serviceaccountissuer-v2
OCPBUGS-2198: AUTH-309: Wire support for trusted service account issuers
2 parents 71cf3b4 + 1159bd3 commit 477d55f

File tree

320 files changed

+30590
-8589
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

320 files changed

+30590
-8589
lines changed

Diff for: go.mod

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ require (
1212
github.com/google/go-cmp v0.5.6
1313
github.com/imdario/mergo v0.3.8
1414
github.com/miekg/dns v1.1.25
15-
github.com/openshift/api v0.0.0-20220831183848-09c070622e2c
16-
github.com/openshift/build-machinery-go v0.0.0-20220720161851-9b4f0386f6b0
15+
github.com/openshift/api v0.0.0-20221010120431-ec3e44909d5c
16+
github.com/openshift/build-machinery-go v0.0.0-20220913142420-e25cf57ea46d
1717
github.com/openshift/client-go v0.0.0-20220831193253-4950ae70c8ea
1818
github.com/openshift/library-go v0.0.0-20221004015544-d6c64d0262cc
1919
github.com/pkg/profile v1.5.0 // indirect

Diff for: go.sum

+4-4
Original file line numberDiff line numberDiff line change
@@ -417,10 +417,10 @@ github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
417417
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
418418
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
419419
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
420-
github.com/openshift/api v0.0.0-20220831183848-09c070622e2c h1:7Ar64e7somcImBRqyOLJINqirHeNwB9QdBR2AUNrnPw=
421-
github.com/openshift/api v0.0.0-20220831183848-09c070622e2c/go.mod h1:9JWn+H7X8wEPPc9D63krigXl8r3F1Mt6/lC98brUyhQ=
422-
github.com/openshift/build-machinery-go v0.0.0-20220720161851-9b4f0386f6b0 h1:ucmy9vO5mJToDExXpfBZ80XOufC9g919NsYFvO6D6CY=
423-
github.com/openshift/build-machinery-go v0.0.0-20220720161851-9b4f0386f6b0/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE=
420+
github.com/openshift/api v0.0.0-20221010120431-ec3e44909d5c h1:DCQmLFr7V7yrnx1+OTJBKA4HyEs4QQ4sLXEMCKop4x8=
421+
github.com/openshift/api v0.0.0-20221010120431-ec3e44909d5c/go.mod h1:JRz+ZvTqu9u7t6suhhPTacbFl5K65Y6rJbNM7HjWA3g=
422+
github.com/openshift/build-machinery-go v0.0.0-20220913142420-e25cf57ea46d h1:RR4ah7FfaPR1WePizm0jlrsbmPu91xQZnAsVVreQV1k=
423+
github.com/openshift/build-machinery-go v0.0.0-20220913142420-e25cf57ea46d/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE=
424424
github.com/openshift/client-go v0.0.0-20220831193253-4950ae70c8ea h1:7JbjIzWt3Q75ErY1PAZ+gCA+bErI6HSlpffHFmMMzqM=
425425
github.com/openshift/client-go v0.0.0-20220831193253-4950ae70c8ea/go.mod h1:+J8DqZC60acCdpYkwVy/KH4cudgWiFZRNOBeghCzdGA=
426426
github.com/openshift/library-go v0.0.0-20221004015544-d6c64d0262cc h1:bmOGYOuO1pV6mSxIBwf9Q76pCV/18utG0DYlh4NJPeU=

Diff for: pkg/operator/configobservation/auth/auth_serviceaccountissuer.go

+57-25
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ package auth
22

33
import (
44
"fmt"
5+
operatorv1 "github.com/openshift/api/operator/v1"
6+
"k8s.io/apimachinery/pkg/util/sets"
57
"net/url"
8+
"sort"
69
"strings"
710

811
"k8s.io/klog/v2"
@@ -33,31 +36,28 @@ func ObserveServiceAccountIssuer(
3336
) (map[string]interface{}, []error) {
3437

3538
listers := genericListers.(configobservation.Listers)
36-
ret, errs := observedConfig(existingConfig, listers.AuthConfigLister.Get, listers.InfrastructureLister().Get, recorder)
39+
ret, errs := observedConfig(existingConfig, listers.KubeAPIServerOperatorLister().Get, listers.InfrastructureLister().Get, recorder)
3740
return configobserver.Pruned(ret, serviceAccountIssuerPath, audiencesPath, jwksURIPath), errs
3841
}
3942

4043
// observedConfig returns an unstructured fragment of KubeAPIServerConfig that may
4144
// include an override of the default service account issuer if one was set in the
4245
// Authentication resource.
43-
func observedConfig(
44-
existingConfig map[string]interface{},
45-
getAuthConfig func(string) (*configv1.Authentication, error),
46-
getInfrastructureConfig func(string) (*configv1.Infrastructure, error),
47-
recorder events.Recorder,
48-
) (map[string]interface{}, []error) {
46+
func observedConfig(existingConfig map[string]interface{},
47+
getOperator func(name string) (*operatorv1.KubeAPIServer, error),
48+
getInfrastructureConfig func(string) (*configv1.Infrastructure, error), recorder events.Recorder) (map[string]interface{}, []error) {
4949

5050
errs := []error{}
5151
var issuerChanged bool
52-
var existingIssuer, newIssuer string
52+
var existingActiveIssuer, newActiveIssuer string
5353
// when the issuer will change, indicate that by setting `issuerChanged` to true
5454
// to emit the informative event
5555
defer func() {
5656
if issuerChanged {
5757
recorder.Eventf(
5858
"ObserveServiceAccountIssuer",
5959
"ServiceAccount issuer changed from %v to %v",
60-
existingIssuer, newIssuer,
60+
existingActiveIssuer, newActiveIssuer,
6161
)
6262
}
6363
}()
@@ -68,34 +68,38 @@ func observedConfig(
6868
}
6969

7070
if len(existingIssuers) > 0 {
71-
existingIssuer = existingIssuers[0]
71+
existingActiveIssuer = existingIssuers[0]
7272
}
7373

74-
authConfig, err := getAuthConfig("cluster")
74+
operator, err := getOperator("cluster")
7575
if apierrors.IsNotFound(err) {
76-
klog.Warningf("authentications.config.openshift.io/cluster: not found")
77-
// No issuer if the auth config is missing
78-
authConfig = &configv1.Authentication{}
76+
klog.Warningf("kubeapiserver.operators.openshift.io/cluster: not found")
77+
operator = &operatorv1.KubeAPIServer{}
7978
} else if err != nil {
8079
return existingConfig, append(errs, err)
8180
}
8281

83-
newIssuer = authConfig.Spec.ServiceAccountIssuer
84-
if err := checkIssuer(newIssuer); err != nil {
82+
newActiveIssuer = getActiveServiceAccountIssuer(operator.Status.ServiceAccountIssuers)
83+
if err := checkIssuer(newActiveIssuer); err != nil {
8584
return existingConfig, append(errs, err)
8685
}
8786

88-
if len(newIssuer) != 0 {
89-
issuerChanged = existingIssuer != newIssuer
87+
if len(newActiveIssuer) > 0 {
88+
currentTrustedServiceAccountIssuers := getTrustedServiceAccountIssuers(operator.Status.ServiceAccountIssuers)
89+
issuerChanged = issuersChanged(
90+
existingIssuers,
91+
append([]string{newActiveIssuer}, currentTrustedServiceAccountIssuers...)...,
92+
)
93+
94+
value := []interface{}{newActiveIssuer}
95+
for _, i := range currentTrustedServiceAccountIssuers {
96+
value = append(value, i)
97+
}
9098
// configure the issuer if set by the user and is a valid issuer
9199
return map[string]interface{}{
92100
"apiServerArguments": map[string]interface{}{
93-
"service-account-issuer": []interface{}{
94-
newIssuer,
95-
},
96-
"api-audiences": []interface{}{
97-
newIssuer,
98-
},
101+
"service-account-issuer": value,
102+
"api-audiences": value,
99103
},
100104
}, errs
101105
}
@@ -113,7 +117,7 @@ func observedConfig(
113117
return existingConfig, append(errs, fmt.Errorf("APIServerInternalURL missing from infrastructure/cluster"))
114118
}
115119

116-
issuerChanged = existingIssuer != newIssuer
120+
issuerChanged = existingActiveIssuer != newActiveIssuer
117121
return map[string]interface{}{
118122
"apiServerArguments": map[string]interface{}{
119123
"service-account-jwks-uri": []interface{}{
@@ -123,6 +127,34 @@ func observedConfig(
123127
}, errs
124128
}
125129

130+
// issuersChanged compares the command line flags used for KAS and the operator status service account issuers.
131+
// if these two sets are different, we have a change and we need to update the KAS.
132+
func issuersChanged(kasIssuers []string, trustedOperatorIssuers ...string) bool {
133+
sort.Strings(kasIssuers)
134+
operatorAllIssuers := trustedOperatorIssuers
135+
sort.Strings(operatorAllIssuers)
136+
return !sets.NewString(kasIssuers...).Equal(sets.NewString(operatorAllIssuers...))
137+
}
138+
139+
func getTrustedServiceAccountIssuers(issuers []operatorv1.ServiceAccountIssuerStatus) []string {
140+
result := []string{}
141+
for i := range issuers {
142+
if issuers[i].ExpirationTime != nil {
143+
result = append(result, issuers[i].Name)
144+
}
145+
}
146+
return result
147+
}
148+
149+
func getActiveServiceAccountIssuer(issuers []operatorv1.ServiceAccountIssuerStatus) string {
150+
for i := range issuers {
151+
if issuers[i].ExpirationTime == nil {
152+
return issuers[i].Name
153+
}
154+
}
155+
return ""
156+
}
157+
126158
// checkIssuer validates the issuer in the same way that it will be validated by
127159
// kube-apiserver
128160
func checkIssuer(issuer string) error {

Diff for: pkg/operator/configobservation/auth/auth_serviceaccountissuer_test.go

+48-24
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package auth
33
import (
44
"encoding/json"
55
"fmt"
6+
operatorv1 "github.com/openshift/api/operator/v1"
67
"testing"
8+
"time"
79

810
"github.com/google/go-cmp/cmp"
911
"github.com/stretchr/testify/require"
@@ -23,13 +25,15 @@ func TestObservedConfig(t *testing.T) {
2325
expectedErrInfra := fmt.Errorf("bar")
2426

2527
for _, tc := range []struct {
26-
name string
27-
issuer string
28-
existingIssuer string
29-
authError error
30-
infraError error
31-
expectedIssuer string
32-
expectedChange bool
28+
name string
29+
issuer string
30+
trustedIssuers []string
31+
existingIssuer string
32+
authError error
33+
infraError error
34+
expectedIssuer string
35+
expectedTrustedIssuers []string
36+
expectedChange bool
3337
}{
3438
{
3539
name: "no issuer, no previous issuer",
@@ -57,6 +61,14 @@ func TestObservedConfig(t *testing.T) {
5761
issuer: "https://example.com",
5862
expectedIssuer: "https://example.com",
5963
},
64+
{
65+
name: "issuer set, previous issuer and trusted issuers same",
66+
existingIssuer: "https://example.com",
67+
issuer: "https://example.com",
68+
trustedIssuers: []string{"https://trusted.example.com"},
69+
expectedIssuer: "https://example.com",
70+
expectedTrustedIssuers: []string{"https://trusted.example.com"},
71+
},
6072
{
6173
name: "issuer set, previous issuer different",
6274
existingIssuer: "https://example.com",
@@ -83,9 +95,9 @@ func TestObservedConfig(t *testing.T) {
8395
testRecorder := events.NewInMemoryRecorder("SAIssuerTest")
8496

8597
newConfig, errs := observedConfig(
86-
unstructuredAPIConfigForIssuer(t, tc.existingIssuer),
87-
func(_ string) (*configv1.Authentication, error) {
88-
return authConfigForIssuer(tc.issuer), tc.authError
98+
unstructuredAPIConfigForIssuer(t, tc.existingIssuer, tc.trustedIssuers),
99+
func(_ string) (*operatorv1.KubeAPIServer, error) {
100+
return kasStatusForIssuer(tc.issuer, tc.trustedIssuers...), tc.authError
89101
},
90102
func(_ string) (*configv1.Infrastructure, error) {
91103
return &configv1.Infrastructure{
@@ -101,7 +113,7 @@ func TestObservedConfig(t *testing.T) {
101113
if tc.authError == nil && tc.infraError == nil {
102114
require.Len(t, errs, 0)
103115
}
104-
expectedConfig = apiConfigForIssuer(tc.expectedIssuer)
116+
expectedConfig = apiConfigForIssuer(tc.expectedIssuer, tc.expectedTrustedIssuers)
105117

106118
// Check that errors are passed through
107119
if tc.authError != nil {
@@ -130,22 +142,34 @@ func TestObservedConfig(t *testing.T) {
130142
}
131143
}
132144

133-
func authConfigForIssuer(issuer string) *configv1.Authentication {
134-
return &configv1.Authentication{
135-
Spec: configv1.AuthenticationSpec{
136-
ServiceAccountIssuer: issuer,
145+
func kasStatusForIssuer(active string, trustedIssuers ...string) *operatorv1.KubeAPIServer {
146+
if len(active) == 0 {
147+
return &operatorv1.KubeAPIServer{
148+
Status: operatorv1.KubeAPIServerStatus{},
149+
}
150+
}
151+
status := []operatorv1.ServiceAccountIssuerStatus{
152+
{
153+
Name: active,
154+
},
155+
}
156+
for i := range trustedIssuers {
157+
status = append(status, operatorv1.ServiceAccountIssuerStatus{
158+
Name: trustedIssuers[i],
159+
ExpirationTime: &metav1.Time{Time: time.Now().Add(12 * time.Hour)},
160+
})
161+
}
162+
return &operatorv1.KubeAPIServer{
163+
Status: operatorv1.KubeAPIServerStatus{
164+
ServiceAccountIssuers: status,
137165
},
138166
}
139167
}
140168

141-
func apiConfigForIssuer(issuer string) *kubecontrolplanev1.KubeAPIServerConfig {
169+
func apiConfigForIssuer(issuer string, trustedIssuers []string) *kubecontrolplanev1.KubeAPIServerConfig {
142170
args := map[string]kubecontrolplanev1.Arguments{
143-
"service-account-issuer": {
144-
issuer,
145-
},
146-
"api-audiences": {
147-
issuer,
148-
},
171+
"service-account-issuer": append([]string{issuer}, trustedIssuers...),
172+
"api-audiences": append([]string{issuer}, trustedIssuers...),
149173
}
150174
if len(issuer) == 0 {
151175
delete(args, "service-account-issuer")
@@ -164,8 +188,8 @@ func apiConfigForIssuer(issuer string) *kubecontrolplanev1.KubeAPIServerConfig {
164188
// unstructuredAPIConfigForIssuer round-trips through the golang type
165189
// to ensure the input to the function under test will match what will
166190
// be received at runtime.
167-
func unstructuredAPIConfigForIssuer(t *testing.T, issuer string) map[string]interface{} {
168-
config := apiConfigForIssuer(issuer)
191+
func unstructuredAPIConfigForIssuer(t *testing.T, issuer string, trustedIssuers []string) map[string]interface{} {
192+
config := apiConfigForIssuer(issuer, trustedIssuers)
169193
// Unmarshaling to unstructured requires explicitly setting kind
170194
config.TypeMeta = metav1.TypeMeta{
171195
Kind: "KubeAPIServerConfig",

Diff for: pkg/operator/configobservation/configobservercontroller/observe_config_controller.go

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package configobservercontroller
22

33
import (
4+
operatorv1informers "github.com/openshift/client-go/operator/informers/externalversions"
45
"k8s.io/apimachinery/pkg/util/sets"
56
"k8s.io/client-go/tools/cache"
67

@@ -38,6 +39,7 @@ func NewConfigObserver(
3839
operatorClient v1helpers.StaticPodOperatorClient,
3940
kubeInformersForNamespaces v1helpers.KubeInformersForNamespaces,
4041
configInformer configinformers.SharedInformerFactory,
42+
operatorInformer operatorv1informers.SharedInformerFactory,
4143
resourceSyncer resourcesynccontroller.ResourceSyncer,
4244
eventRecorder events.Recorder,
4345
) *ConfigObserver {
@@ -68,6 +70,7 @@ func NewConfigObserver(
6870
configInformer.Config().V1().Nodes().Informer(),
6971
configInformer.Config().V1().Proxies().Informer(),
7072
configInformer.Config().V1().Schedulers().Informer(),
73+
operatorInformer.Operator().V1().KubeAPIServers().Informer(),
7174
}
7275
for _, ns := range interestingNamespaces {
7376
infomers = append(infomers, kubeInformersForNamespaces.InformersFor(ns).Core().V1().ConfigMaps().Informer())
@@ -92,6 +95,8 @@ func NewConfigObserver(
9295
ConfigSecretLister_: kubeInformersForNamespaces.InformersFor(operatorclient.GlobalUserSpecifiedConfigNamespace).Core().V1().Secrets().Lister(),
9396
ConfigmapLister_: kubeInformersForNamespaces.ConfigMapLister(),
9497

98+
KubeAPIServerOperatorLister_: operatorInformer.Operator().V1().KubeAPIServers().Lister(),
99+
95100
ResourceSync: resourceSyncer,
96101
PreRunCachesSynced: append(preRunCacheSynced,
97102
operatorClient.Informer().HasSynced,

Diff for: pkg/operator/configobservation/interfaces.go

+7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"k8s.io/client-go/tools/cache"
66

77
configlistersv1 "github.com/openshift/client-go/config/listers/config/v1"
8+
operatorlistersv1 "github.com/openshift/client-go/operator/listers/operator/v1"
89
"github.com/openshift/library-go/pkg/operator/configobserver/cloudprovider"
910
libgoetcd "github.com/openshift/library-go/pkg/operator/configobserver/etcd"
1011
"github.com/openshift/library-go/pkg/operator/resourcesynccontroller"
@@ -24,6 +25,8 @@ type Listers struct {
2425
ProxyLister_ configlistersv1.ProxyLister
2526
SchedulerLister configlistersv1.SchedulerLister
2627

28+
KubeAPIServerOperatorLister_ operatorlistersv1.KubeAPIServerLister
29+
2730
ConfigmapLister_ corelistersv1.ConfigMapLister
2831
SecretLister_ corelistersv1.SecretLister
2932
ConfigSecretLister_ corelistersv1.SecretLister
@@ -36,6 +39,10 @@ func (l Listers) APIServerLister() configlistersv1.APIServerLister {
3639
return l.APIServerLister_
3740
}
3841

42+
func (l Listers) KubeAPIServerOperatorLister() operatorlistersv1.KubeAPIServerLister {
43+
return l.KubeAPIServerOperatorLister_
44+
}
45+
3946
func (l Listers) FeatureGateLister() configlistersv1.FeatureGateLister {
4047
return l.FeatureGateLister_
4148
}

0 commit comments

Comments
 (0)