From 94e29e2ef5d79904f730e2024c8d1682b901b2d5 Mon Sep 17 00:00:00 2001 From: Brad Davidson Date: Mon, 22 Apr 2024 20:23:34 +0000 Subject: [PATCH] Make /db/info available anonymously from localhost Signed-off-by: Brad Davidson --- pkg/etcd/etcd.go | 18 ++++++++++++------ pkg/server/auth/auth.go | 21 ++++++++++++++++++++- pkg/server/router.go | 8 ++++---- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/pkg/etcd/etcd.go b/pkg/etcd/etcd.go index cd29de505473..abf142a908f0 100644 --- a/pkg/etcd/etcd.go +++ b/pkg/etcd/etcd.go @@ -664,12 +664,18 @@ func (e *ETCD) setName(force bool) error { // handler wraps the handler with routes for database info func (e *ETCD) handler(next http.Handler) http.Handler { - mux := mux.NewRouter().SkipClean(true) - mux.Use(auth.Middleware(e.config, version.Program+":server")) - mux.Handle("/db/info", e.infoHandler()) - mux.Handle("/db/snapshot", e.snapshotHandler()) - mux.NotFoundHandler = next - return mux + r := mux.NewRouter().SkipClean(true) + r.NotFoundHandler = next + + ir := r.Path("/db/info").Subrouter() + ir.Use(auth.IsLocalOrHasRole(e.config, version.Program+":server")) + ir.Handle("", e.infoHandler()) + + sr := r.Path("/db/snapshot").Subrouter() + sr.Use(auth.HasRole(e.config, version.Program+":server")) + sr.Handle("", e.snapshotHandler()) + + return r } // infoHandler returns etcd cluster information. This is used by new members when joining the cluster. diff --git a/pkg/server/auth/auth.go b/pkg/server/auth/auth.go index 49056573f7cc..cf0000fdfd27 100644 --- a/pkg/server/auth/auth.go +++ b/pkg/server/auth/auth.go @@ -1,6 +1,7 @@ package auth import ( + "net" "net/http" "github.com/gorilla/mux" @@ -22,6 +23,7 @@ func hasRole(mustRoles []string, roles []string) bool { return false } +// doAuth calls the cluster's authenticator to validate that the client has at least one of the listed roles func doAuth(roles []string, serverConfig *config.Control, next http.Handler, rw http.ResponseWriter, req *http.Request) { switch { case serverConfig == nil: @@ -51,10 +53,27 @@ func doAuth(roles []string, serverConfig *config.Control, next http.Handler, rw next.ServeHTTP(rw, req) } -func Middleware(serverConfig *config.Control, roles ...string) mux.MiddlewareFunc { +// HasRole returns a middleware function that validates that the request +// is being made with at least one of the listed roles. +func HasRole(serverConfig *config.Control, roles ...string) mux.MiddlewareFunc { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { doAuth(roles, serverConfig, next, rw, req) }) } } + +// IsLocalOrHasRole returns a middleware function that validates that the request +// is from a local client or has at least one of the listed roles. +func IsLocalOrHasRole(serverConfig *config.Control, roles ...string) mux.MiddlewareFunc { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + client, _, _ := net.SplitHostPort(req.RemoteAddr) + if client == "127.0.0.1" || client == "::1" { + next.ServeHTTP(rw, req) + } else { + doAuth(roles, serverConfig, next, rw, req) + } + }) + } +} diff --git a/pkg/server/router.go b/pkg/server/router.go index 86d18c58c997..b5b1ad52cdda 100644 --- a/pkg/server/router.go +++ b/pkg/server/router.go @@ -52,7 +52,7 @@ func router(ctx context.Context, config *Config, cfg *cmds.Server) http.Handler prefix := "/v1-" + version.Program authed := mux.NewRouter().SkipClean(true) - authed.Use(auth.Middleware(serverConfig, version.Program+":agent", user.NodesGroup, bootstrapapi.BootstrapDefaultGroup)) + authed.Use(auth.HasRole(serverConfig, version.Program+":agent", user.NodesGroup, bootstrapapi.BootstrapDefaultGroup)) authed.Path(prefix + "/serving-kubelet.crt").Handler(servingKubeletCert(serverConfig, serverConfig.Runtime.ServingKubeletKey, nodeAuth)) authed.Path(prefix + "/client-kubelet.crt").Handler(clientKubeletCert(serverConfig, serverConfig.Runtime.ClientKubeletKey, nodeAuth)) authed.Path(prefix + "/client-kube-proxy.crt").Handler(fileHandler(serverConfig.Runtime.ClientKubeProxyCert, serverConfig.Runtime.ClientKubeProxyKey)) @@ -71,12 +71,12 @@ func router(ctx context.Context, config *Config, cfg *cmds.Server) http.Handler nodeAuthed := mux.NewRouter().SkipClean(true) nodeAuthed.NotFoundHandler = authed - nodeAuthed.Use(auth.Middleware(serverConfig, user.NodesGroup)) + nodeAuthed.Use(auth.HasRole(serverConfig, user.NodesGroup)) nodeAuthed.Path(prefix + "/connect").Handler(serverConfig.Runtime.Tunnel) serverAuthed := mux.NewRouter().SkipClean(true) serverAuthed.NotFoundHandler = nodeAuthed - serverAuthed.Use(auth.Middleware(serverConfig, version.Program+":server")) + serverAuthed.Use(auth.HasRole(serverConfig, version.Program+":server")) serverAuthed.Path(prefix + "/encrypt/status").Handler(encryptionStatusHandler(serverConfig)) serverAuthed.Path(prefix + "/encrypt/config").Handler(encryptionConfigHandler(ctx, serverConfig)) serverAuthed.Path(prefix + "/cert/cacerts").Handler(caCertReplaceHandler(serverConfig)) @@ -86,7 +86,7 @@ func router(ctx context.Context, config *Config, cfg *cmds.Server) http.Handler systemAuthed := mux.NewRouter().SkipClean(true) systemAuthed.NotFoundHandler = serverAuthed systemAuthed.MethodNotAllowedHandler = serverAuthed - systemAuthed.Use(auth.Middleware(serverConfig, user.SystemPrivilegedGroup)) + systemAuthed.Use(auth.HasRole(serverConfig, user.SystemPrivilegedGroup)) systemAuthed.Methods(http.MethodConnect).Handler(serverConfig.Runtime.Tunnel) staticDir := filepath.Join(serverConfig.DataDir, "static")