diff --git a/src/operator/config/rbac/role.yaml b/src/operator/config/rbac/role.yaml index b3f630e..e76e7fd 100644 --- a/src/operator/config/rbac/role.yaml +++ b/src/operator/config/rbac/role.yaml @@ -27,6 +27,15 @@ rules: - patch - update - watch +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + verbs: + - get + - list + - patch + - update - apiGroups: - apps resources: diff --git a/src/operator/controllers/metadata/annotations.go b/src/operator/controllers/metadata/annotations.go index a9ecb99..6488ffa 100644 --- a/src/operator/controllers/metadata/annotations.go +++ b/src/operator/controllers/metadata/annotations.go @@ -10,8 +10,6 @@ const ( TLSSecretNameAnnotation = "credentials-operator.otterize.com/tls-secret-name" TLSSecretNameAnnotationDeprecated = "spire-integration.otterize.com/tls-secret-name" - // CreateAWSRoleAnnotation by using this annotation a pod marks that the operator should create an AWS IAM role for its service account - CreateAWSRoleAnnotation = "credentials-operator.otterize.com/create-aws-role" // ServiceAccountAWSRoleARNAnnotation is used by EKS (Kubernetes at AWS) to link between service accounts // and IAM roles ServiceAccountAWSRoleARNAnnotation = "eks.amazonaws.com/role-arn" diff --git a/src/operator/controllers/metadata/labels.go b/src/operator/controllers/metadata/labels.go index 16546fe..3314e29 100644 --- a/src/operator/controllers/metadata/labels.go +++ b/src/operator/controllers/metadata/labels.go @@ -13,4 +13,6 @@ const ( // OtterizeServiceAccountLabel is used to label service accounts generated by the credentials-operator OtterizeServiceAccountLabel = "credentials-operator.otterize.com/service-account-managed" + // CreateAWSRoleLabel by using this annotation a pod marks that the operator should create an AWS IAM role for its service account + CreateAWSRoleLabel = "credentials-operator.otterize.com/create-aws-role" ) diff --git a/src/operator/controllers/webhooks/pod_webhook.go b/src/operator/controllers/webhooks/pod_webhook.go index 6c9bdfd..5a27ea6 100644 --- a/src/operator/controllers/webhooks/pod_webhook.go +++ b/src/operator/controllers/webhooks/pod_webhook.go @@ -18,29 +18,30 @@ import ( ) // +kubebuilder:webhook:path=/mutate-v1-pod,mutating=true,failurePolicy=ignore,groups="",sideEffects=NoneOnDryRun,resources=pods,verbs=create;update,versions=v1,admissionReviewVersions=v1,name=pods.credentials-operator.otterize.com +// +kubebuilder:rbac:groups="admissionregistration.k8s.io",resources=mutatingwebhookconfigurations,verbs=get;update;patch;list -type PodWebhookAnnotatesPodServiceAccount struct { +type ServiceAccountAnnotatingPodWebhook struct { client client.Client decoder *admission.Decoder awsAgent *awsagent.Agent } -func NewPodWebhookAnnotatesPodServiceAccount(mgr manager.Manager, awsAgent *awsagent.Agent) *PodWebhookAnnotatesPodServiceAccount { - return &PodWebhookAnnotatesPodServiceAccount{ +func NewServiceAccountAnnotatingPodWebhook(mgr manager.Manager, awsAgent *awsagent.Agent) *ServiceAccountAnnotatingPodWebhook { + return &ServiceAccountAnnotatingPodWebhook{ client: mgr.GetClient(), decoder: admission.NewDecoder(mgr.GetScheme()), awsAgent: awsAgent, } } -func (a *PodWebhookAnnotatesPodServiceAccount) handleOnce(ctx context.Context, pod corev1.Pod, dryRun bool) (outputPod corev1.Pod, patched bool, successMsg string, err error) { - if pod.Annotations == nil { - return pod, false, "no create AWS role annotation - no modifications made", nil +func (a *ServiceAccountAnnotatingPodWebhook) handleOnce(ctx context.Context, pod corev1.Pod, dryRun bool) (outputPod corev1.Pod, patched bool, successMsg string, err error) { + if pod.Labels == nil { + return pod, false, "no create AWS role label - no modifications made", nil } - _, annotationExists := pod.Annotations[metadata.CreateAWSRoleAnnotation] - if !annotationExists { - logrus.Debugf("pod %v doesn't have create AWS IAM role annotation, skipping", pod) - return pod, false, "no create AWS role annotation - no modifications made", nil + _, labelExists := pod.Labels[metadata.CreateAWSRoleLabel] + if !labelExists { + logrus.Debugf("pod %v doesn't have create AWS IAM role label, skipping", pod.Name) + return pod, false, "no create AWS role label - no modifications made", nil } var serviceAccount corev1.ServiceAccount @@ -60,6 +61,10 @@ func (a *PodWebhookAnnotatesPodServiceAccount) handleOnce(ctx context.Context, p updatedServiceAccount.Annotations = make(map[string]string) } + if updatedServiceAccount.Labels == nil { + updatedServiceAccount.Labels = make(map[string]string) + } + // we don't actually create the role here, so that the webhook returns quickly - a ServiceAccount reconciler takes care of it for us. updatedServiceAccount.Annotations[metadata.ServiceAccountAWSRoleARNAnnotation] = roleArn updatedServiceAccount.Labels[metadata.OtterizeServiceAccountLabel] = "true" @@ -70,7 +75,7 @@ func (a *PodWebhookAnnotatesPodServiceAccount) handleOnce(ctx context.Context, p } } - // add label to trigger reinvocation for AWS pod reconciler + // add annotation to trigger reinvocation for AWS pod reconciler if pod.Annotations == nil { pod.Annotations = make(map[string]string) } @@ -80,7 +85,7 @@ func (a *PodWebhookAnnotatesPodServiceAccount) handleOnce(ctx context.Context, p } // dryRun: should not cause any modifications except to the Pod in the request. -func (a *PodWebhookAnnotatesPodServiceAccount) handleWithRetriesOnConflict(ctx context.Context, pod corev1.Pod, dryRun bool) (outputPod corev1.Pod, patched bool, successMsg string, err error) { +func (a *ServiceAccountAnnotatingPodWebhook) handleWithRetriesOnConflict(ctx context.Context, pod corev1.Pod, dryRun bool) (outputPod corev1.Pod, patched bool, successMsg string, err error) { for attempt := 0; attempt < 3; attempt++ { logrus.Debugf("Handling pod '%s' in namespace '%s' (attempt %d out of %d)", pod.Name, pod.Namespace, attempt+1, 3) outputPod, patched, successMsg, err = a.handleOnce(ctx, *pod.DeepCopy(), dryRun) @@ -100,12 +105,13 @@ func (a *PodWebhookAnnotatesPodServiceAccount) handleWithRetriesOnConflict(ctx c panic("unreachable - must have received error or it would have exited in the for loop") } -func (a *PodWebhookAnnotatesPodServiceAccount) Handle(ctx context.Context, req admission.Request) admission.Response { +func (a *ServiceAccountAnnotatingPodWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { pod := corev1.Pod{} err := a.decoder.Decode(req, &pod) if err != nil { return admission.Errored(http.StatusBadRequest, err) } + logrus.Debugf("Got webhook call for pod '%s' in namespace '%s'", pod.Name, pod.Namespace) pod, patched, successMsg, err := a.handleWithRetriesOnConflict(ctx, pod, req.DryRun != nil && *req.DryRun) if err != nil { diff --git a/src/operator/go.mod b/src/operator/go.mod index 1217770..20f8b38 100644 --- a/src/operator/go.mod +++ b/src/operator/go.mod @@ -9,7 +9,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/iam v1.27.2 github.com/bombsimon/logrusr/v3 v3.0.0 github.com/cert-manager/cert-manager v1.12.3 - github.com/otterize/intents-operator/src v0.0.0-20231118165400-e986ef885231 + github.com/otterize/intents-operator/src v0.0.0-20231118174527-3e73c6744e37 github.com/pavlo-v-chernykh/keystore-go/v4 v4.4.1 github.com/samber/lo v1.33.0 github.com/sirupsen/logrus v1.9.0 @@ -32,9 +32,6 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect github.com/DataDog/datadog-go v3.2.0+incompatible // indirect github.com/Microsoft/go-winio v0.6.0 // indirect - github.com/agnivade/levenshtein v1.1.1 // indirect - github.com/alexflint/go-arg v1.4.2 // indirect - github.com/alexflint/go-scalar v1.0.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/aws/aws-sdk-go-v2 v1.23.0 // indirect github.com/aws/aws-sdk-go-v2/config v1.25.3 // indirect diff --git a/src/operator/go.sum b/src/operator/go.sum index 52bd2f7..11fc661 100644 --- a/src/operator/go.sum +++ b/src/operator/go.sum @@ -49,17 +49,11 @@ github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2y github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= -github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= -github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alexflint/go-arg v1.4.2 h1:lDWZAXxpAnZUq4qwb86p/3rIJJ2Li81EoMbTMujhVa0= -github.com/alexflint/go-arg v1.4.2/go.mod h1:9iRbDxne7LcR/GSvEr7ma++GLpdIU1zrghf2y2768kM= -github.com/alexflint/go-scalar v1.0.0 h1:NGupf1XV/Xb04wXskDFzS0KWOLH632W/EO4fAFi+A70= -github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxjy3MxYW0gjYw= github.com/amit7itz/goset v1.2.1 h1:usFphDJfZgwnqfbKT8zI+2juuOgsZ6O8UA7NMRUVG7s= github.com/amit7itz/goset v1.2.1/go.mod h1:i8ni2YcxUMAwLBOkHWpy3glFviYdTcWqCvFgp91EMGI= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= @@ -67,7 +61,6 @@ github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgp github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= @@ -143,7 +136,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE= github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= @@ -347,12 +339,8 @@ github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= -github.com/otterize/intents-operator/src v0.0.0-20231118154841-80b7a6bca6a6 h1:wKK4/rHnti6ZND2scbdYqSCSy06FyffhnbkPYliU0/o= -github.com/otterize/intents-operator/src v0.0.0-20231118154841-80b7a6bca6a6/go.mod h1:Hogw74Z2iakq/bwxwDEEPT8fG+rZpiu6Dm7qmMTUBb4= -github.com/otterize/intents-operator/src v0.0.0-20231118165244-748a655e069d h1:u8n4wWPoqiEdaYWHgJBpLec/yA3QGonVVMB698idEXY= -github.com/otterize/intents-operator/src v0.0.0-20231118165244-748a655e069d/go.mod h1:1V5AvkQodX1eaeZRGCCCSYrG6bN5qRVnUP3UaWAQV3Y= -github.com/otterize/intents-operator/src v0.0.0-20231118165400-e986ef885231 h1:a4QPzUDtM1T0dgNmfPxjVD1D5Mvqj626XdqezMga59M= -github.com/otterize/intents-operator/src v0.0.0-20231118165400-e986ef885231/go.mod h1:1V5AvkQodX1eaeZRGCCCSYrG6bN5qRVnUP3UaWAQV3Y= +github.com/otterize/intents-operator/src v0.0.0-20231118174527-3e73c6744e37 h1:hhQaTT/thGuB3E3f5Fv2wCfaqk1G9gfirqBmdqc3BUk= +github.com/otterize/intents-operator/src v0.0.0-20231118174527-3e73c6744e37/go.mod h1:1V5AvkQodX1eaeZRGCCCSYrG6bN5qRVnUP3UaWAQV3Y= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pavlo-v-chernykh/keystore-go/v4 v4.4.1 h1:FyBdsRqqHH4LctMLL+BL2oGO+ONcIPwn96ctofCVtNE= diff --git a/src/operator/main.go b/src/operator/main.go index 25868ea..e9aa284 100644 --- a/src/operator/main.go +++ b/src/operator/main.go @@ -131,7 +131,7 @@ func main() { var secretsManager tls_pod.SecretsManager var workloadRegistry tls_pod.WorkloadRegistry var enableAWSServiceAccountManagement bool - var awsEksOidcProviderUrl string + var debug bool var userAndPassAcquirer poduserpassword.CloudUserAndPasswordAcquirer flag.StringVar(&metricsAddr, "metrics-bind-address", ":7071", "The address the metric endpoint binds to.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":7072", "The address the probe endpoint binds to.") @@ -142,13 +142,17 @@ func main() { flag.BoolVar(&certManagerUseClusterIssuer, "cert-manager-use-cluster-issuer", false, "Use ClusterIssuer instead of a (namespace bound) Issuer") flag.BoolVar(&useCertManagerApprover, "cert-manager-approve-requests", false, "Make credentials-operator approve its own CertificateRequests") flag.BoolVar(&enableAWSServiceAccountManagement, "enable-aws-serviceaccount-management", false, "Create and bind ServiceAccounts to AWS IAM roles") - flag.StringVar(&awsEksOidcProviderUrl, "eks-oidc-url", "", "EKS OIDC Provider URL") + flag.BoolVar(&debug, "debug", false, "Enable debug logging") flag.BoolVar(&enableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") flag.Parse() + if debug { + logrus.SetLevel(logrus.DebugLevel) + } + ctrl.SetLogger(logrusr.New(logrus.StandardLogger())) mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ @@ -233,14 +237,14 @@ func main() { logrus.WithError(err).Fatal("failed writing certs to file system") } - err = operatorwebhooks.UpdateValidationWebHookCA(context.Background(), - "otterize-credentials-validating-webhook-configuration", certBundle.CertPem) + err = operatorwebhooks.UpdateMutationWebHookCA(context.Background(), + "otterize-credentials-operator-mutating-webhook-configuration", certBundle.CertPem) if err != nil { logrus.WithError(err).Fatal("updating validation webhook certificate failed") } } - podAnnotatorWebhook := webhooks.NewPodWebhookAnnotatesPodServiceAccount(mgr, awsAgent) + podAnnotatorWebhook := webhooks.NewServiceAccountAnnotatingPodWebhook(mgr, awsAgent) mgr.GetWebhookServer().Register("/mutate-v1-pod", &webhook.Admission{Handler: podAnnotatorWebhook}) }