Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: AUTH-543: OIDC/OAuth resource configuration #740

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9af586b
common: add helper func to determine whether OIDC is enabled on KAS pods
liouk Nov 13, 2024
65ae585
operator: set number of replicas of workloads with a custom wrapper
liouk Nov 20, 2024
3c687a7
deployment: bypass precondition checks if OIDC is available
liouk Nov 20, 2024
f74f0b3
endpointaccessible: disable controller checks upon specific conditions
liouk Nov 13, 2024
3850c62
proxyconfig: disable proxy checks when external OIDC config is available
liouk Nov 14, 2024
ebe2b3f
ingressnodesavailable: disable checks when external OIDC config is av…
liouk Nov 14, 2024
bc6089e
ingressstate: disable checks when external OIDC config is available
liouk Nov 14, 2024
a808174
readiness: disable checks when external OIDC config is available
liouk Nov 14, 2024
2b6ca40
oauthclientscontroller: remove operands when external OIDC config is …
liouk Nov 14, 2024
35e4f19
metadata: remove operands if external OIDC config is available
liouk Nov 14, 2024
9b2ff76
operator: configure static resources that depend on oidc
liouk Nov 20, 2024
7af8526
routercerts: remove operands if external OIDC config is available
liouk Nov 15, 2024
edfb3e2
serviceca: remove operands if external OIDC config is available
liouk Nov 15, 2024
a3d37a3
payload: remove operands if external OIDC config is available
liouk Nov 15, 2024
3725212
customroute: remove operands and clear custom route ingress status if…
liouk Nov 18, 2024
3d9fb80
operator: enable or disable API services depending on whether OIDC is…
liouk Nov 20, 2024
15d1883
controllers: add switchable informer controller
liouk Nov 22, 2024
5198aea
oauthclientscontroller: use a switched informer for oauthclients
liouk Nov 22, 2024
09d5d77
fixup! readiness: disable checks when external OIDC config is available
liouk Nov 29, 2024
4200edd
fixup! customroute: remove operands and clear custom route ingress st…
liouk Nov 29, 2024
4bdb1b4
fixup! controllers: add switchable informer controller
liouk Dec 3, 2024
e4d2153
fixup! oauthclientscontroller: use a switched informer for oauthclients
liouk Dec 3, 2024
a1cf995
fixup! common: add helper func to determine whether OIDC is enabled o…
liouk Dec 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions pkg/controllers/common/external_oidc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package common

import (
"fmt"
"strings"

configv1 "github.com/openshift/api/config/v1"
configv1listers "github.com/openshift/client-go/config/listers/config/v1"
operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1"

"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/util/sets"
corelistersv1 "k8s.io/client-go/listers/core/v1"
)

// ExternalOIDCConfigAvailable checks the kubeapiservers/cluster resource for KAS pod
// rollout status; it returns true if auth type is OIDC, all KAS pods are currently on a revision
// that includes the structured auth-config ConfigMap, and the KAS args include the respective
// arg that enables usage of the structured auth-config. It returns false otherwise.
func ExternalOIDCConfigAvailable(authLister configv1listers.AuthenticationLister, kasLister operatorv1listers.KubeAPIServerLister, cmLister corelistersv1.ConfigMapLister) (bool, error) {
auth, err := authLister.Get("cluster")
if err != nil {
return false, err
}

if auth.Spec.Type != configv1.AuthenticationTypeOIDC {
return false, nil
}

kas, err := kasLister.Get("cluster")
if err != nil {
return false, err
}

observedRevisions := sets.New[int32]()
for _, nodeStatus := range kas.Status.NodeStatuses {
observedRevisions.Insert(nodeStatus.CurrentRevision)
}

for _, revision := range observedRevisions.UnsortedList() {
// ensure every observed revision includes an auth-config revisioned configmap
_, err := cmLister.ConfigMaps("openshift-kube-apiserver").Get(fmt.Sprintf("auth-config-%d", revision))
if errors.IsNotFound(err) {
return false, nil
} else if err != nil {
return false, err
}

// every observed revision includes a copy of the KAS config configmap
cm, err := cmLister.ConfigMaps("openshift-kube-apiserver").Get(fmt.Sprintf("config-%d", revision))
if err != nil {
return false, err
}

// ensure the KAS config of every observed revision contains the appropriate CLI arg for OIDC
// but not the respective ones for OAuth
if !strings.Contains(cm.Data["config.yaml"], `"oauthMetadataFile":""`) ||
strings.Contains(cm.Data["config.yaml"], `"authentication-token-webhook-config-file":`) ||
!strings.Contains(cm.Data["config.yaml"], `"authentication-config":["/etc/kubernetes/static-pod-resources/configmaps/auth-config/auth-config.json"]`) {
return false, nil
}
}

return true, nil
}
311 changes: 311 additions & 0 deletions pkg/controllers/common/external_oidc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
package common

import (
"testing"

configv1 "github.com/openshift/api/config/v1"
operatorv1 "github.com/openshift/api/operator/v1"
configv1listers "github.com/openshift/client-go/config/listers/config/v1"
operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corelistersv1 "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
)

const (
kasConfigJSONWithOIDC = `"spec":{"apiServerArguments":{"authentication-config":["/etc/kubernetes/static-pod-resources/configmaps/auth-config/auth-config.json"]},"oauthMetadataFile":""}`
kasConfigJSONWithoutOIDC = `"spec":{"apiServerArguments":{"authentication-token-webhook-config-file":["/etc/kubernetes/static-pod-resources/secrets/webhook-authenticator/kubeConfig"]},"oauthMetadataFile":"/etc/kubernetes/static-pod-resources/configmaps/oauth-metadata/oauthMetadata"}`
)

func TestExternalOIDCConfigAvailable(t *testing.T) {
for _, tt := range []struct {
name string
configMaps []*corev1.ConfigMap
authType configv1.AuthenticationType
nodeStatuses []operatorv1.NodeStatus
expectAvailable bool
expectError bool
}{
{
name: "oidc disabled, no rollout",
configMaps: []*corev1.ConfigMap{cm("config-10", "config.yaml", kasConfigJSONWithoutOIDC)},
authType: configv1.AuthenticationTypeIntegratedOAuth,
nodeStatuses: []operatorv1.NodeStatus{
{CurrentRevision: 10},
{CurrentRevision: 10},
{CurrentRevision: 10},
},
expectAvailable: false,
expectError: false,
},
{
name: "oidc getting enabled, rollout in progress",
configMaps: []*corev1.ConfigMap{
cm("config-10", "config.yaml", kasConfigJSONWithoutOIDC),
cm("config-11", "config.yaml", kasConfigJSONWithOIDC),
cm("auth-config-11", "", ""),
},
authType: configv1.AuthenticationTypeOIDC,
nodeStatuses: []operatorv1.NodeStatus{
{CurrentRevision: 10, TargetRevision: 11},
{CurrentRevision: 10},
{CurrentRevision: 10},
},
expectAvailable: false,
expectError: false,
},
{
name: "oidc getting enabled, rollout in progress, one node ready",
configMaps: []*corev1.ConfigMap{
cm("config-10", "config.yaml", kasConfigJSONWithoutOIDC),
cm("config-11", "config.yaml", kasConfigJSONWithOIDC),
cm("auth-config-11", "", ""),
},
authType: configv1.AuthenticationTypeOIDC,
nodeStatuses: []operatorv1.NodeStatus{
{CurrentRevision: 11},
{CurrentRevision: 10, TargetRevision: 11},
{CurrentRevision: 10},
},
expectAvailable: false,
expectError: false,
},
{
name: "oidc getting enabled, rollout in progress, two nodes ready",
configMaps: []*corev1.ConfigMap{
cm("config-10", "config.yaml", kasConfigJSONWithoutOIDC),
cm("config-11", "config.yaml", kasConfigJSONWithOIDC),
cm("auth-config-11", "", ""),
},
authType: configv1.AuthenticationTypeOIDC,
nodeStatuses: []operatorv1.NodeStatus{
{CurrentRevision: 11},
{CurrentRevision: 11},
{CurrentRevision: 10, TargetRevision: 11},
},
expectAvailable: false,
expectError: false,
},
{
name: "oidc got enabled",
configMaps: []*corev1.ConfigMap{
cm("config-11", "config.yaml", kasConfigJSONWithOIDC),
cm("auth-config-11", "", ""),
},
authType: configv1.AuthenticationTypeOIDC,
nodeStatuses: []operatorv1.NodeStatus{
{CurrentRevision: 11},
{CurrentRevision: 11},
{CurrentRevision: 11},
},
expectAvailable: true,
expectError: false,
},
{
name: "oidc enabled, rollout in progress",
configMaps: []*corev1.ConfigMap{
cm("config-11", "config.yaml", kasConfigJSONWithOIDC),
cm("config-12", "config.yaml", kasConfigJSONWithOIDC),
cm("auth-config-11", "", ""),
cm("auth-config-12", "", ""),
},
authType: configv1.AuthenticationTypeOIDC,
nodeStatuses: []operatorv1.NodeStatus{
{CurrentRevision: 11, TargetRevision: 12},
{CurrentRevision: 11},
{CurrentRevision: 11},
},
expectAvailable: true,
expectError: false,
},
{
name: "oidc enabled, rollout in progress, one node ready",
configMaps: []*corev1.ConfigMap{
cm("config-11", "config.yaml", kasConfigJSONWithOIDC),
cm("config-12", "config.yaml", kasConfigJSONWithOIDC),
cm("auth-config-11", "", ""),
cm("auth-config-12", "", ""),
},
authType: configv1.AuthenticationTypeOIDC,
nodeStatuses: []operatorv1.NodeStatus{
{CurrentRevision: 12},
{CurrentRevision: 11, TargetRevision: 12},
{CurrentRevision: 11},
},
expectAvailable: true,
expectError: false,
},
{
name: "oidc enabled, rollout in progress, two nodes ready",
configMaps: []*corev1.ConfigMap{
cm("config-11", "config.yaml", kasConfigJSONWithOIDC),
cm("config-12", "config.yaml", kasConfigJSONWithOIDC),
cm("auth-config-11", "", ""),
cm("auth-config-12", "", ""),
},
authType: configv1.AuthenticationTypeOIDC,
nodeStatuses: []operatorv1.NodeStatus{
{CurrentRevision: 12},
{CurrentRevision: 12},
{CurrentRevision: 11, TargetRevision: 12},
},
expectAvailable: true,
expectError: false,
},
{
name: "oidc still enabled",
configMaps: []*corev1.ConfigMap{
cm("config-11", "config.yaml", kasConfigJSONWithOIDC),
cm("config-12", "config.yaml", kasConfigJSONWithOIDC),
cm("auth-config-11", "", ""),
cm("auth-config-12", "", ""),
},
authType: configv1.AuthenticationTypeOIDC,
nodeStatuses: []operatorv1.NodeStatus{
{CurrentRevision: 12},
{CurrentRevision: 12},
{CurrentRevision: 12},
},
expectAvailable: true,
expectError: false,
},
{
name: "oidc getting disabled, rollout in progress",
configMaps: []*corev1.ConfigMap{
cm("config-11", "config.yaml", kasConfigJSONWithOIDC),
cm("config-12", "config.yaml", kasConfigJSONWithOIDC),
cm("config-13", "config.yaml", kasConfigJSONWithoutOIDC),
cm("auth-config-11", "", ""),
cm("auth-config-12", "", ""),
},
authType: configv1.AuthenticationTypeIntegratedOAuth,
nodeStatuses: []operatorv1.NodeStatus{
{CurrentRevision: 12, TargetRevision: 13},
{CurrentRevision: 12},
{CurrentRevision: 12},
},
expectAvailable: false,
expectError: false,
},
{
name: "oidc getting disabled, rollout in progress, one node ready",
configMaps: []*corev1.ConfigMap{
cm("config-11", "config.yaml", kasConfigJSONWithOIDC),
cm("config-12", "config.yaml", kasConfigJSONWithOIDC),
cm("config-13", "config.yaml", kasConfigJSONWithoutOIDC),
cm("auth-config-11", "", ""),
cm("auth-config-12", "", ""),
},
authType: configv1.AuthenticationTypeIntegratedOAuth,
nodeStatuses: []operatorv1.NodeStatus{
{CurrentRevision: 13},
{CurrentRevision: 12, TargetRevision: 13},
{CurrentRevision: 12},
},
expectAvailable: false,
expectError: false,
},
{
name: "oidc getting disabled, rollout in progress, two nodes ready",
authType: configv1.AuthenticationTypeIntegratedOAuth,
configMaps: []*corev1.ConfigMap{
cm("config-11", "config.yaml", kasConfigJSONWithOIDC),
cm("config-11", "config.yaml", kasConfigJSONWithOIDC),
cm("config-13", "config.yaml", kasConfigJSONWithoutOIDC),
cm("auth-config-11", "", ""),
cm("auth-config-12", "", ""),
},
nodeStatuses: []operatorv1.NodeStatus{
{CurrentRevision: 13},
{CurrentRevision: 13},
{CurrentRevision: 12, TargetRevision: 13},
},
expectAvailable: false,
expectError: false,
},
{
name: "oidc got disabled",
configMaps: []*corev1.ConfigMap{
cm("config-11", "config.yaml", kasConfigJSONWithOIDC),
cm("config-12", "config.yaml", kasConfigJSONWithOIDC),
cm("config-13", "config.yaml", kasConfigJSONWithoutOIDC),
cm("auth-config-11", "", ""),
cm("auth-config-12", "", ""),
},
authType: configv1.AuthenticationTypeIntegratedOAuth,
nodeStatuses: []operatorv1.NodeStatus{
{CurrentRevision: 13},
{CurrentRevision: 13},
{CurrentRevision: 13},
},
expectAvailable: false,
expectError: false,
},
} {
t.Run(tt.name, func(t *testing.T) {

cmIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
for _, cm := range tt.configMaps {
cmIndexer.Add(cm)
}

kasIndexer := cache.NewIndexer(func(obj interface{}) (string, error) {
return "cluster", nil
}, cache.Indexers{})

kasIndexer.Add(&operatorv1.KubeAPIServer{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster",
},
Status: operatorv1.KubeAPIServerStatus{
StaticPodOperatorStatus: operatorv1.StaticPodOperatorStatus{
NodeStatuses: tt.nodeStatuses,
},
},
})

authIndexer := cache.NewIndexer(func(obj interface{}) (string, error) {
return "cluster", nil
}, cache.Indexers{})

authIndexer.Add(&configv1.Authentication{
Spec: configv1.AuthenticationSpec{
Type: tt.authType,
},
})

available, err := ExternalOIDCConfigAvailable(
configv1listers.NewAuthenticationLister(authIndexer),
operatorv1listers.NewKubeAPIServerLister(kasIndexer),
corelistersv1.NewConfigMapLister(cmIndexer),
)

if tt.expectError != (err != nil) {
t.Fatalf("expected error %v; got %v", tt.expectError, err)
}

if tt.expectAvailable != available {
t.Fatalf("expected available %v; got %v", tt.expectAvailable, available)
}
})
}
}

func cm(name, dataKey, dataValue string) *corev1.ConfigMap {
cm := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: "openshift-kube-apiserver",
},
}

if len(dataKey) > 0 {
cm.Data = map[string]string{
dataKey: dataValue,
}
}

return cm
}
Loading