Skip to content

Commit

Permalink
Make pod webhook actually work
Browse files Browse the repository at this point in the history
  • Loading branch information
orishoshan committed Nov 18, 2023
1 parent e1e26ab commit 9fd30a9
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 38 deletions.
9 changes: 9 additions & 0 deletions src/operator/config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ rules:
- patch
- update
- watch
- apiGroups:
- admissionregistration.k8s.io
resources:
- mutatingwebhookconfigurations
verbs:
- get
- list
- patch
- update
- apiGroups:
- apps
resources:
Expand Down
2 changes: 0 additions & 2 deletions src/operator/controllers/metadata/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions src/operator/controllers/metadata/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
32 changes: 19 additions & 13 deletions src/operator/controllers/webhooks/pod_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
Expand All @@ -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)
}
Expand All @@ -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)
Expand All @@ -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 {
Expand Down
5 changes: 1 addition & 4 deletions src/operator/go.mod

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 2 additions & 14 deletions src/operator/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 9 additions & 5 deletions src/operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand All @@ -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{
Expand Down Expand Up @@ -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})

}
Expand Down

0 comments on commit 9fd30a9

Please sign in to comment.