From 7cede4a2fc73c9bb8e7fa8b669585b93a9f89547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20Peliz=C3=A4us?= Date: Fri, 16 Oct 2026 11:32:09 +0200 Subject: [PATCH 1/3] internal/state/hooks: Add OnAuthenticate hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julian Pelizäus (cherry picked from commit dfd721934e8f1b23460560b29cd293918b8e02c7) (cherry picked from commit 7223f21d5403a29902c3d2a648511eb72a43a987) --- internal/state/hooks.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/internal/state/hooks.go b/internal/state/hooks.go index 096fb8cf..d8ecb1de 100644 --- a/internal/state/hooks.go +++ b/internal/state/hooks.go @@ -2,6 +2,8 @@ package state import ( "context" + "crypto/x509" + "net/http" "github.com/canonical/microcluster/v2/rest/types" ) @@ -45,4 +47,12 @@ type Hooks struct { // OnDaemonConfigUpdate is a post-action hook that is run on all cluster members when any cluster member receives a local configuration update. OnDaemonConfigUpdate func(ctx context.Context, s State, config types.DaemonConfig) error + + // OnAuthenticate is run when an incoming HTTP request needs to be authenticated. + // If defined it is used as a replacement for the regular mTLS check. + // trustedCerts contains the certs from the truststore. + // Returning true indicates that the request is trusted. + // Returning false indicates that the request is not trusted. + // Returning an error is optional but will always result in a forbidden request. + OnAuthenticate func(ctx context.Context, s State, r *http.Request, trustedCerts map[string]x509.Certificate) (bool, error) } From 553494b644ef25400020fdc7373c742fc694eeed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20Peliz=C3=A4us?= Date: Fri, 16 Oct 2026 11:32:53 +0200 Subject: [PATCH 2/3] rest/access/handlers: Prioritize OnAuthenticate hook over regular mTLS check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julian Pelizäus (cherry picked from commit a56e8faa13b1abf5a1dde67a56c78aa6b63c80c0) (cherry picked from commit 9f2ff6d7f9f5229c87a05250cc9c2b6df9a876dd) --- rest/access/handlers.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rest/access/handlers.go b/rest/access/handlers.go index 23893cc8..fe78a76b 100644 --- a/rest/access/handlers.go +++ b/rest/access/handlers.go @@ -79,6 +79,10 @@ func Authenticate(state state.State, r *http.Request, hostAddress string, truste switch r.Host { case hostAddrPort.WithZone("").String(): + if intState.Hooks != nil && intState.Hooks.OnAuthenticate != nil { + return intState.Hooks.OnAuthenticate(r.Context(), intState, r, trustedCerts) + } + if r.TLS != nil { for _, cert := range r.TLS.PeerCertificates { trusted, fingerprint := util.CheckMutualTLS(*cert, trustedCerts) From 7516a08f682db80aea42c74ce7e501f258d3bed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20Peliz=C3=A4us?= Date: Fri, 16 Oct 2026 14:39:40 +0200 Subject: [PATCH 3/3] microcluster/app: Ignore returned value (errcheck) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only ever send nil into the chan. Therefore switch to struct{} as its done for ctx.Done(). Signed-off-by: Julian Pelizäus (cherry picked from commit 4de6ffd6b409401fd8225912da15612a13b4e127) (cherry picked from commit 9b0625df778257e4705c8d2b7983d92b428b6c64) --- microcluster/app.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microcluster/app.go b/microcluster/app.go index 6dea737d..3ee7d155 100644 --- a/microcluster/app.go +++ b/microcluster/app.go @@ -112,7 +112,7 @@ func (m *MicroCluster) Status(ctx context.Context) (*internalTypes.Server, error // Ready waits for the daemon to report it has finished initial setup and is ready to be bootstrapped or join an // existing cluster. func (m *MicroCluster) Ready(ctx context.Context) error { - finger := make(chan error, 1) + finger := make(chan struct{}, 1) var errLast error go func() { for i := 0; ; i++ { @@ -156,7 +156,7 @@ func (m *MicroCluster) Ready(ctx context.Context) error { continue } - finger <- nil + finger <- struct{}{} return } }()