diff --git a/bindata/config.go b/bindata/config.go new file mode 100644 index 000000000..47e4e561f --- /dev/null +++ b/bindata/config.go @@ -0,0 +1,35 @@ +package bindata + +import ( + "bytes" + "encoding/json" + "fmt" + "path/filepath" + + kyaml "k8s.io/apimachinery/pkg/util/yaml" +) + +func UnstructuredDefaultConfig() (map[string]any, error) { + asset := filepath.Join("assets", "config", "defaultconfig.yaml") + raw, err := Asset(asset) + if err != nil { + return nil, fmt.Errorf("failed to get default config asset asset=%s - %s", asset, err) + } + + rawJSON, err := kyaml.ToJSON(raw) + if err != nil { + return nil, fmt.Errorf("failed to convert asset yaml to JSON asset=%s - %s", asset, err) + } + + return ConvertToUnstructured(rawJSON) +} + +func ConvertToUnstructured(raw []byte) (map[string]interface{}, error) { + decoder := json.NewDecoder(bytes.NewBuffer(raw)) + u := map[string]interface{}{} + if err := decoder.Decode(&u); err != nil { + return nil, err + } + + return u, nil +} diff --git a/pkg/cmd/render/render.go b/pkg/cmd/render/render.go index f34837391..bc3269757 100644 --- a/pkg/cmd/render/render.go +++ b/pkg/cmd/render/render.go @@ -1,7 +1,6 @@ package render import ( - "bytes" "crypto/rand" "crypto/rsa" "crypto/x509" @@ -32,7 +31,6 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - kyaml "k8s.io/apimachinery/pkg/util/yaml" auditv1 "k8s.io/apiserver/pkg/apis/audit/v1" "k8s.io/klog/v2" ) @@ -324,22 +322,10 @@ func (r *renderOpts) Run() error { } func bootstrapDefaultConfig(featureGates featuregates.FeatureGate) ([]byte, error) { - asset := filepath.Join("assets", "config", "defaultconfig.yaml") - raw, err := bindata.Asset(asset) + defaultConfig, err := bindata.UnstructuredDefaultConfig() if err != nil { - return nil, fmt.Errorf("failed to get default config asset asset=%s - %s", asset, err) + return nil, fmt.Errorf("failed to get unstructured default config: %v", err) } - - rawJSON, err := kyaml.ToJSON(raw) - if err != nil { - return nil, fmt.Errorf("failed to convert asset yaml to JSON asset=%s - %s", asset, err) - } - - defaultConfig, err := convertToUnstructured(rawJSON) - if err != nil { - return nil, fmt.Errorf("failed to decode default config into unstructured - %s", err) - } - policy, err := libgoaudit.GetAuditPolicy(configv1.Audit{Profile: configv1.DefaultAuditProfileType}) if err != nil { return nil, fmt.Errorf("failed to retreive default audit policy: %v", err) @@ -359,7 +345,7 @@ func bootstrapDefaultConfig(featureGates featuregates.FeatureGate) ([]byte, erro } if featureGates.Enabled(features.FeatureGateMinimumKubeletVersion) { - if err := node.SetAPIServerArgumentsToEnforceMinimumKubeletVersion(defaultConfig, true); err != nil { + if err := node.SetAPIServerArgumentsToEnforceMinimumKubeletVersion(node.AuthModesFromUnstructured(defaultConfig), defaultConfig, true); err != nil { return nil, err } } @@ -408,16 +394,6 @@ func addAuditPolicyToConfig(config map[string]interface{}, policy *auditv1.Polic return nil } -func convertToUnstructured(raw []byte) (map[string]interface{}, error) { - decoder := json.NewDecoder(bytes.NewBuffer(raw)) - u := map[string]interface{}{} - if err := decoder.Decode(&u); err != nil { - return nil, err - } - - return u, nil -} - func mustReadTemplateFile(fname string) genericrenderoptions.Template { bs, err := ioutil.ReadFile(fname) if err != nil { diff --git a/pkg/cmd/render/render_test.go b/pkg/cmd/render/render_test.go index 42651f810..c531440e9 100644 --- a/pkg/cmd/render/render_test.go +++ b/pkg/cmd/render/render_test.go @@ -14,6 +14,7 @@ import ( configv1 "github.com/openshift/api/config/v1" "github.com/openshift/api/features" kubecontrolplanev1 "github.com/openshift/api/kubecontrolplane/v1" + "github.com/openshift/cluster-kube-apiserver-operator/bindata" libgoaudit "github.com/openshift/library-go/pkg/operator/apiserver/audit" "github.com/openshift/library-go/pkg/operator/configobserver/featuregates" genericrenderoptions "github.com/openshift/library-go/pkg/operator/render/options" @@ -700,7 +701,7 @@ func TestGetDefaultConfigWithAuditPolicy(t *testing.T) { require.NoError(t, err) rawPolicyJSON, err := kyaml.ToJSON(defaultPolicy) require.NoError(t, err) - policyExpected, err := convertToUnstructured(rawPolicyJSON) + policyExpected, err := bindata.ConvertToUnstructured(rawPolicyJSON) require.NoError(t, err) isEqual := equality.Semantic.DeepEqual(policyExpected, auditConfigPolicyGot) diff --git a/pkg/operator/configobservation/node/observe_minimum_kubelet_version.go b/pkg/operator/configobservation/node/observe_minimum_kubelet_version.go index b00a1867f..1a28fec9b 100644 --- a/pkg/operator/configobservation/node/observe_minimum_kubelet_version.go +++ b/pkg/operator/configobservation/node/observe_minimum_kubelet_version.go @@ -5,6 +5,7 @@ import ( configv1 "github.com/openshift/api/config/v1" "github.com/openshift/api/features" + "github.com/openshift/cluster-kube-apiserver-operator/bindata" "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" @@ -19,9 +20,6 @@ var ( authModeFlag = "authorization-mode" apiServerArgs = "apiServerArguments" authModePath = []string{apiServerArgs, authModeFlag} - // The default value for apiServerArguments.authorization-mode. - // Should be synced with bindata/assets/config/defaultconfig.yaml - DefaultAuthorizationModes = []string{"Scope", "SystemMasters", "RBAC", "Node"} ) type minimumKubeletVersionObserver struct { @@ -85,14 +83,30 @@ func (o *minimumKubeletVersionObserver) ObserveMinimumKubeletVersion(genericList type authorizationModeObserver struct { featureGateAccessor featuregates.FeatureGateAccess + authModes []string } func NewAuthorizationModeObserver(featureGateAccessor featuregates.FeatureGateAccess) configobserver.ObserveConfigFunc { + defaultConfig, err := bindata.UnstructuredDefaultConfig() + if err != nil { + // programming error, the built-in configuration should always be valid + panic(err) + } + return (&authorizationModeObserver{ featureGateAccessor: featureGateAccessor, + authModes: AuthModesFromUnstructured(defaultConfig), }).ObserveAuthorizationMode } +func AuthModesFromUnstructured(config map[string]any) []string { + authModes, found, err := unstructured.NestedStringSlice(config, authModePath...) + if !found || err != nil { + return []string{} + } + return authModes +} + // ObserveAuthorizationMode watches the featuregate configuration and generates the apiServerArguments.authorization-mode // It currently hardcodes the default set and adds MinimumKubeletVersion if the feature is set to on. func (o *authorizationModeObserver) ObserveAuthorizationMode(genericListers configobserver.Listers, _ events.Recorder, existingConfig map[string]interface{}) (ret map[string]interface{}, errs []error) { @@ -111,7 +125,7 @@ func (o *authorizationModeObserver) ObserveAuthorizationMode(genericListers conf ret = configobserver.Pruned(ret, authModePath) }() - if err := SetAPIServerArgumentsToEnforceMinimumKubeletVersion(ret, featureGates.Enabled(features.FeatureGateMinimumKubeletVersion)); err != nil { + if err := SetAPIServerArgumentsToEnforceMinimumKubeletVersion(o.authModes, ret, featureGates.Enabled(features.FeatureGateMinimumKubeletVersion)); err != nil { return existingConfig, append(errs, err) } return ret, nil @@ -120,13 +134,14 @@ func (o *authorizationModeObserver) ObserveAuthorizationMode(genericListers conf // SetAPIServerArgumentsToEnforceMinimumKubeletVersion modifies the passed in config // to add the "authorization-mode": "MinimumKubeletVersion" if the feature is on. If it's off, it // removes it instead. -func SetAPIServerArgumentsToEnforceMinimumKubeletVersion(newConfig map[string]interface{}, on bool) error { - defaultSet := DefaultAuthorizationModes +// This function assumes MinimumKubeletVersion auth mode isn't present by default, +// and should likely be removed when it is. +func SetAPIServerArgumentsToEnforceMinimumKubeletVersion(defaultAuthModes []string, newConfig map[string]interface{}, on bool) error { if on { - defaultSet = append(defaultSet, ModeMinimumKubeletVersion) + defaultAuthModes = append(defaultAuthModes, ModeMinimumKubeletVersion) } - sort.Sort(sort.StringSlice(defaultSet)) + sort.Sort(sort.StringSlice(defaultAuthModes)) unstructured.RemoveNestedField(newConfig, authModePath...) - return unstructured.SetNestedStringSlice(newConfig, defaultSet, authModePath...) + return unstructured.SetNestedStringSlice(newConfig, defaultAuthModes, authModePath...) } diff --git a/pkg/operator/configobservation/node/observe_minimum_kubelet_version_test.go b/pkg/operator/configobservation/node/observe_minimum_kubelet_version_test.go index e76beaa19..0eb1d398b 100644 --- a/pkg/operator/configobservation/node/observe_minimum_kubelet_version_test.go +++ b/pkg/operator/configobservation/node/observe_minimum_kubelet_version_test.go @@ -100,6 +100,7 @@ func TestObserveKubeletMinimumVersion(t *testing.T) { func TestSetAPIServerArgumentsToEnforceMinimumKubeletVersion(t *testing.T) { for _, on := range []bool{false, true} { + defaultSet := []string{"Node", "RBAC", "Scope", "SystemMasters"} expectedSet := []any{"Node", "RBAC", "Scope", "SystemMasters"} if on { expectedSet = append([]any{ModeMinimumKubeletVersion}, expectedSet...) @@ -164,7 +165,7 @@ func TestSetAPIServerArgumentsToEnforceMinimumKubeletVersion(t *testing.T) { name += "off" } t.Run(name, func(t *testing.T) { - if err := SetAPIServerArgumentsToEnforceMinimumKubeletVersion(tc.existingConfig, on); err != nil { + if err := SetAPIServerArgumentsToEnforceMinimumKubeletVersion(defaultSet, tc.existingConfig, on); err != nil { t.Fatal(err) }