Skip to content

fix: unify webhook listeners on path based endpoints#304

Open
pepordev wants to merge 2 commits intoexternal-secrets:mainfrom
pepordev:fix/unify-webhook-listeners
Open

fix: unify webhook listeners on path based endpoints#304
pepordev wants to merge 2 commits intoexternal-secrets:mainfrom
pepordev:fix/unify-webhook-listeners

Conversation

@pepordev
Copy link
Copy Markdown
Contributor

@pepordev pepordev commented Apr 15, 2026

fixes #242

I've test it out in our environments with multiples configurations using our keeper-security integration and it is working fine

Signed-off-by: Pedro Parra Ortega <parraortega.pedro@gmail.com>
Signed-off-by: Pedro Parra Ortega <parraortega.pedro@gmail.com>
@Skarlso Skarlso self-requested a review April 16, 2026 07:20
Copy link
Copy Markdown
Contributor

@Skarlso Skarlso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initial round done. I know this is mostly borrowed code. I tried to flag only criticals. Let's leave it off a bit better than we found it. :)

Comment on lines +75 to +81
if _, exists := lm.listeners[manifestName][key]; exists {
if source.Type == schema.WEBHOOK && lm.webhookServer != nil && source.Webhook != nil {
lm.webhookServer.Register(manifestName.Name, lm.context, source.Webhook, lm.client, lm.eventChan, lm.logger)
lm.logger.V(1).Info("updated webhook registration", "manifest", manifestName, "key", key)
} else {
lm.logger.V(1).Info("listener already exists", "key", key)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this will constantly register a webhook. I think you need to hash the webhookconfig into the listener key to avoid it.

Comment on lines +86 to +88
r := newRoute(routeCtx, configName, cfg, k8sClient, eventChan, logger)
s.routes[configName] = r
s.server.Handler = s.buildMux()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is super bad. You are re-assigning a running servier. 🤔 Even with the lock here, listenAndServer does only read without this lock being aware of it. You need to add a test that actually starts the server and call it concurrently with Register a couple times and run it with -race. I'm pretty sure it will trip it immediately.


if r.config != nil && r.config.RetryPolicy != nil {
select {
case r.retryQueue <- &retryMessage{event: event, currentRun: 1, retryAt: time.Now()}:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a send on close panic potential here, since close closes this channel.

You either don't close it and let the garbage collection collect the channel, or we have to redesign this.

Comment on lines +243 to +246
b, err := io.ReadAll(body)
if err != nil {
return "", err
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a limited reader. http.MaxBytesReader(w, req.Body, N) with a sane cap is standard procedure

_, _ = fmt.Fprintln(w, "Couldn't decode webhook payload. Send a valid json")
return
}
_ = req.Body.Close()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add this as a defer at the begin?

Comment on lines -5 to -14
// Path that the webhook will receive the notifications.
// If not present `/webhook` will be used. The path always expects a POST and this is not configurable
// +optional
Path string `json:"path"`

// Address is the address where the webhook will be served in your infrastructure.
// If not present, defaults to `:8090`
// +optional
Address string `json:"address"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will warrant 2.0.0.

}
}

func (r *route) drainRetryQueue() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't really do anything. Since the channel is unbuffered and cancel returns early anyways in processEvent, this will just not do anything.

Comment on lines +44 to +67
func (s *WebhookServer) Start(ctx context.Context) error {
s.logger.Info("Starting shared webhook server", "addr", s.addr)
errCh := make(chan error, 1)
go func() {
err := s.server.ListenAndServe()
errCh <- err
}()
select {
case err := <-errCh:
if err != nil && err != http.ErrServerClosed {
return err
}
return nil
case <-ctx.Done():
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = s.closeHTTPServer(shutdownCtx)
err := <-errCh
if err != nil && err != http.ErrServerClosed {
return err
}
return nil
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, I'd wish we rewrote this alltogether. :D The error handling is pretty prone to race and/or ignoring binding errors and not cleaning up things. But not a prio.

Client: client,
Scheme: scheme,
webhookServer: hook,
eventChan: make(chan events.SecretRotationEvent),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs a sane buffer. 🤔 Especially since it's a shared channel.

Comment thread docs/sources/webhook.md

## How it works

The controller runs a **single shared HTTP server** for all `Config` resources. The listen address is set with the controller flag **`--webhook-bind-address`** (default `:8082`). Each cluster-scoped `Config` is exposed at:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a mismatch between 8082 and whatever the helm template is setting up with 8090.. Something is fishy there. Please double check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

webhook source in configurations create new servers per config

2 participants