diff --git a/main.go b/main.go index 04edb79b..052c87f3 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ import ( "github.com/metal-stack/v" coreosv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" zalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + appsv1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" @@ -24,12 +25,15 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" databasev1 "github.com/fi-ts/postgreslet/api/v1" "github.com/fi-ts/postgreslet/controllers" "github.com/fi-ts/postgreslet/pkg/etcdmanager" "github.com/fi-ts/postgreslet/pkg/lbmanager" "github.com/fi-ts/postgreslet/pkg/operatormanager" + "github.com/fi-ts/postgreslet/pkg/webhooks" firewall "github.com/metal-stack/firewall-controller/api/v1" "github.com/spf13/viper" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -84,6 +88,7 @@ const ( tlsClusterIssuerFlg = "tls-cluster-issuer" tlsSubDomainFlg = "tls-sub-domain" enablePatroniFailsafeModeFlg = "enable-patroni-failsafe-mode" + enableFsGroupChangePolicyWebhookFlg = "enable-fsgroup-change-policy-webhook" ) var ( @@ -98,6 +103,7 @@ func init() { _ = firewall.AddToScheme(scheme) _ = zalando.AddToScheme(scheme) _ = coreosv1.AddToScheme(scheme) + _ = appsv1.AddToScheme(scheme) // +kubebuilder:scaffold:scheme _ = cmapi.AddToScheme(scheme) } @@ -143,6 +149,7 @@ func main() { enableBootstrapStandbyFromS3 bool enableSuperUserForDBO bool enablePatroniFailsafeMode bool + enableFsGroupChangePolicyWebhook bool portRangeStart int portRangeSize int @@ -305,6 +312,9 @@ func main() { viper.SetDefault(enablePatroniFailsafeModeFlg, true) enablePatroniFailsafeMode = viper.GetBool(enablePatroniFailsafeModeFlg) + viper.SetDefault(enableFsGroupChangePolicyWebhookFlg, true) + enableFsGroupChangePolicyWebhook = viper.GetBool(enableFsGroupChangePolicyWebhookFlg) + ctrl.Log.Info("flag", metricsAddrSvcMgrFlg, metricsAddrSvcMgr, metricsAddrCtrlMgrFlg, metricsAddrCtrlMgr, @@ -349,6 +359,7 @@ func main() { tlsClusterIssuerFlg, tlsClusterIssuer, tlsSubDomainFlg, tlsSubDomain, enablePatroniFailsafeModeFlg, enablePatroniFailsafeMode, + enableFsGroupChangePolicyWebhookFlg, enableFsGroupChangePolicyWebhook, ) svcClusterConf := ctrl.GetConfigOrDie() @@ -486,6 +497,19 @@ func main() { } // +kubebuilder:scaffold:builder + if enableFsGroupChangePolicyWebhook { + svcClusterMgr.GetWebhookServer().Register( + "/mutate-apps-v1-statefulset", + &webhook.Admission{ + Handler: &webhooks.FsGroupChangePolicySetter{ + SvcClient: svcClusterMgr.GetClient(), + Decoder: admission.NewDecoder(svcClusterMgr.GetScheme()), + Log: ctrl.Log.WithName("webhooks").WithName("FsGroupChangePolicySetter"), + }, + }, + ) + } + ctx := context.Background() // update all existing operators to the current version @@ -506,4 +530,5 @@ func main() { setupLog.Error(err, "problem running control plane cluster manager") os.Exit(1) } + } diff --git a/pkg/webhooks/fsGroupChangePolicySetter.go b/pkg/webhooks/fsGroupChangePolicySetter.go new file mode 100644 index 00000000..2ac05404 --- /dev/null +++ b/pkg/webhooks/fsGroupChangePolicySetter.go @@ -0,0 +1,51 @@ +package webhooks + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/go-logr/logr" + appsv1 "k8s.io/api/apps/v1" + v1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// +kubebuilder:webhook:path=/mutate-apps-v1-statefulset,mutating=true,failurePolicy=ignore,groups=apps,resources=statefulsets,verbs=create;update,versions=v1,name=fsgroupchangepolicy.postgres.fits.cloud + +// FsGroupChangePolicySetter Adds securityContext.fsGroupChangePolicy=OnRootMismatch when the securityContext.fsGroup field is set +type FsGroupChangePolicySetter struct { + SvcClient client.Client + Decoder *admission.Decoder + Log logr.Logger +} + +func (a *FsGroupChangePolicySetter) Handle(ctx context.Context, req admission.Request) admission.Response { + log := a.Log.WithValues("name", req.Name, "ns", req.Namespace) + log.V(1).Info("handling admission request") + + sts := &appsv1.StatefulSet{} + err := a.Decoder.Decode(req, sts) + if err != nil { + log.Error(err, "failed to decode request") + return admission.Errored(http.StatusBadRequest, err) + } + + // when the fsGroup field is set, also set the fsGroupChangePolicy to OnRootMismatch + if sts.Spec.Template.Spec.SecurityContext != nil && sts.Spec.Template.Spec.SecurityContext.FSGroup != nil { + p := v1.FSGroupChangeOnRootMismatch + sts.Spec.Template.Spec.SecurityContext.FSGroupChangePolicy = &p + log.V(1).Info("Mutating StatefulSet", "sts", sts) + } + + marshaledSts, err := json.Marshal(sts) + if err != nil { + log.Error(err, "failed to marshal response") + return admission.Errored(http.StatusInternalServerError, err) + } + + log.V(1).Info("done") + return admission.PatchResponseFromRaw(req.Object.Raw, marshaledSts) +}