-
Notifications
You must be signed in to change notification settings - Fork 199
Expand file tree
/
Copy pathexternal_oidc.go
More file actions
151 lines (123 loc) · 5.78 KB
/
external_oidc.go
File metadata and controls
151 lines (123 loc) · 5.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package auth
import (
"fmt"
"path"
configv1 "github.com/openshift/api/config/v1"
"github.com/openshift/api/features"
"github.com/openshift/cluster-kube-apiserver-operator/pkg/operator/configobservation"
"github.com/openshift/cluster-kube-apiserver-operator/pkg/operator/operatorclient"
"github.com/openshift/library-go/pkg/operator/configobserver"
"github.com/openshift/library-go/pkg/operator/configobserver/featuregates"
"github.com/openshift/library-go/pkg/operator/events"
"github.com/openshift/library-go/pkg/operator/resourcesynccontroller"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/klog/v2"
)
const (
SourceAuthConfigCMNamespace = "openshift-config-managed"
AuthConfigCMName = "auth-config"
authConfigKeyName = "auth-config.json"
)
var (
authConfigPath = []string{"apiServerArguments", "authentication-config"}
)
func NewObserveExternalOIDC(featureGateAccessor featuregates.FeatureGateAccess) configobserver.ObserveConfigFunc {
return (&externalOIDC{
featureGateAccessor: featureGateAccessor,
}).ObserveExternalOIDC
}
type externalOIDC struct {
featureGateAccessor featuregates.FeatureGateAccess
}
// ObserveExternalOIDC observes the authentication.config/cluster resource
// and if the type field is set to OIDC, it configures an external OIDC provider
// to the KAS pods by setting the --authentication-config apiserver argument. It also
// takes care of synchronizing the structured auth config file into the apiserver's namespace
// so that it gets mounted as a static file on each node.
func (o *externalOIDC) ObserveExternalOIDC(genericListers configobserver.Listers, recorder events.Recorder, existingConfig map[string]interface{}) (ret map[string]interface{}, _ []error) {
defer func() {
ret = configobserver.Pruned(ret, authConfigPath)
}()
if !o.featureGateAccessor.AreInitialFeatureGatesObserved() {
// if we haven't observed featuregates yet, return the existing
return existingConfig, nil
}
featureGates, err := o.featureGateAccessor.CurrentFeatureGates()
if err != nil {
return existingConfig, []error{err}
}
if !featureGates.Enabled(features.FeatureGateExternalOIDC) {
return existingConfig, nil
}
// When the ExternalOIDCExternalClaimsSourcing feature gate is enabled, the kube-apiserver
// should not have the built-in Structured Authentication Configuration feature configured,
// which this controller handles.
// When this feature goes GA, this controller should be removed.
if featureGates.Enabled(features.FeatureGateExternalOIDCExternalClaimsSourcing) {
return existingConfig, nil
}
listers := genericListers.(configobservation.Listers)
auth, err := listers.AuthConfigLister.Get("cluster")
if errors.IsNotFound(err) {
recorder.Eventf("ObserveExternalOIDC", "authentications.config.openshift.io/cluster: not found")
klog.Warningf("authentications.config.openshift.io/cluster: not found")
return existingConfig, nil
} else if err != nil {
return existingConfig, []error{err}
}
targetAuthConfig, err := listers.ConfigMapLister().ConfigMaps(operatorclient.TargetNamespace).Get(AuthConfigCMName)
if err != nil && !errors.IsNotFound(err) {
return existingConfig, []error{err}
}
if auth.Spec.Type != configv1.AuthenticationTypeOIDC {
// empty source name/namespace effectively deletes target configmap
if err := genericListers.ResourceSyncer().SyncConfigMap(
resourcesynccontroller.ResourceLocation{Namespace: operatorclient.TargetNamespace, Name: AuthConfigCMName},
resourcesynccontroller.ResourceLocation{Namespace: "", Name: ""},
); err != nil {
return existingConfig, []error{err}
}
if targetAuthConfig != nil {
recorder.Eventf("ObserveExternalOIDC", "OIDC auth configmap %s/%s exists; requested deletion", operatorclient.TargetNamespace, AuthConfigCMName)
}
return nil, nil
}
// auth type is OIDC
sourceAuthConfig, err := validateSourceConfigMap(listers)
if err != nil {
return existingConfig, []error{err}
} else if sourceAuthConfig == nil {
klog.Warningf("configmap %s/%s not found; skipping configuration of OIDC", SourceAuthConfigCMNamespace, AuthConfigCMName)
return existingConfig, nil
}
if err := genericListers.ResourceSyncer().SyncConfigMap(
resourcesynccontroller.ResourceLocation{Namespace: operatorclient.TargetNamespace, Name: AuthConfigCMName},
resourcesynccontroller.ResourceLocation{Namespace: sourceAuthConfig.Namespace, Name: sourceAuthConfig.Name},
); err != nil {
return existingConfig, []error{err}
}
if targetAuthConfig == nil {
recorder.Eventf("ObserveExternalOIDC", "OIDC auth configmap %s/%s does not exist; requested sync", operatorclient.TargetNamespace, AuthConfigCMName)
}
observedConfig := make(map[string]interface{})
if err := unstructured.SetNestedStringSlice(observedConfig, []string{path.Join("/etc/kubernetes/static-pod-resources/configmaps/", AuthConfigCMName, authConfigKeyName)}, authConfigPath...); err != nil {
return existingConfig, []error{err}
}
return observedConfig, nil
}
func validateSourceConfigMap(listers configobservation.Listers) (*corev1.ConfigMap, error) {
sourceAuthConfig, err := listers.ConfigMapLister().ConfigMaps(SourceAuthConfigCMNamespace).Get(AuthConfigCMName)
if errors.IsNotFound(err) {
return nil, nil
} else if err != nil {
return nil, fmt.Errorf("failed to get configmap %s/%s: %v", SourceAuthConfigCMNamespace, AuthConfigCMName, err)
}
if data, found := sourceAuthConfig.Data[authConfigKeyName]; !found {
return nil, fmt.Errorf("configmap %s/%s is invalid: key '%s' missing", SourceAuthConfigCMNamespace, AuthConfigCMName, authConfigKeyName)
} else if len(data) == 0 {
return nil, fmt.Errorf("configmap %s/%s is invalid: key '%s' has empty value", SourceAuthConfigCMNamespace, AuthConfigCMName, authConfigKeyName)
}
return sourceAuthConfig, nil
}