Drop use of github.com/gorilla/mux

mux is replaced with a simple wrapper around http.ServeMux with middleware chain support

Unfortunately github.com/rootless-containers/rootlesskit/pkg/parent
still uses it so we can't drop the indirect dep yet.

Signed-off-by: Brad Davidson <brad.davidson@rancher.com>
pull/13730/head
Brad Davidson 2026-03-04 06:20:07 +00:00 committed by Brad Davidson
parent 270484f01b
commit 3f5eec4c4e
16 changed files with 237 additions and 127 deletions

2
go.mod
View File

@ -101,7 +101,6 @@ require (
github.com/google/cadvisor v0.53.0
github.com/google/go-containerregistry v0.20.2
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
github.com/inetaf/tcpproxy v0.0.0-20240214030015-3ce58045626c
github.com/ipfs/go-ds-leveldb v0.5.0
@ -299,6 +298,7 @@ require (
github.com/google/go-tpm v0.9.6 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 // indirect

View File

@ -5,10 +5,10 @@ import (
"strconv"
"sync"
"github.com/gorilla/mux"
"github.com/k3s-io/k3s/pkg/daemons/config"
"github.com/k3s-io/k3s/pkg/server/auth"
"github.com/k3s-io/k3s/pkg/util"
"github.com/k3s-io/k3s/pkg/util/mux"
"k8s.io/apiserver/pkg/server"
"k8s.io/apiserver/pkg/server/options"
)
@ -25,7 +25,7 @@ var err error
// Subsequent calls will return the same router.
func Start(ctx context.Context, nodeConfig *config.Node, runtime *config.ControlRuntime) (*mux.Router, error) {
once.Do(func() {
router = mux.NewRouter().SkipClean(true)
router = mux.NewRouter()
config := &server.Config{}
if runtime == nil {

View File

@ -7,7 +7,6 @@ import (
"path/filepath"
"sync"
"github.com/gorilla/mux"
"github.com/k3s-io/k3s/pkg/agent"
"github.com/k3s-io/k3s/pkg/agent/https"
"github.com/k3s-io/k3s/pkg/cli/cmds"
@ -20,6 +19,7 @@ import (
"github.com/k3s-io/k3s/pkg/spegel"
"github.com/k3s-io/k3s/pkg/util"
"github.com/k3s-io/k3s/pkg/util/errors"
"github.com/k3s-io/k3s/pkg/util/mux"
"github.com/k3s-io/k3s/pkg/util/permissions"
"github.com/k3s-io/k3s/pkg/version"
"github.com/k3s-io/k3s/pkg/vpn"

View File

@ -11,7 +11,6 @@ import (
"time"
systemd "github.com/coreos/go-systemd/v22/daemon"
"github.com/gorilla/mux"
"github.com/k3s-io/k3s/pkg/agent"
"github.com/k3s-io/k3s/pkg/agent/https"
"github.com/k3s-io/k3s/pkg/agent/loadbalancer"
@ -30,6 +29,7 @@ import (
"github.com/k3s-io/k3s/pkg/spegel"
"github.com/k3s-io/k3s/pkg/util"
"github.com/k3s-io/k3s/pkg/util/errors"
"github.com/k3s-io/k3s/pkg/util/mux"
"github.com/k3s-io/k3s/pkg/util/permissions"
"github.com/k3s-io/k3s/pkg/version"
"github.com/k3s-io/k3s/pkg/vpn"

View File

@ -13,11 +13,11 @@ import (
"sync"
"time"
"github.com/gorilla/mux"
"github.com/k3s-io/k3s/pkg/cluster/managed"
"github.com/k3s-io/k3s/pkg/etcd"
"github.com/k3s-io/k3s/pkg/nodepassword"
"github.com/k3s-io/k3s/pkg/util"
"github.com/k3s-io/k3s/pkg/util/mux"
"github.com/k3s-io/k3s/pkg/version"
"github.com/sirupsen/logrus"
apierrors "k8s.io/apimachinery/pkg/api/errors"
@ -144,10 +144,10 @@ func (c *Cluster) deleteNodePasswdSecret(ctx context.Context) {
// handlerNoEtcd wraps a handler with an error message indicating that etcd is not deployed.
func handlerNoEtcd(handler http.Handler) http.Handler {
r := mux.NewRouter().SkipClean(true)
r := mux.NewRouter()
// Wildcard route for anything after /db/
r.HandleFunc("/db/{_:.*}", func(resp http.ResponseWriter, r *http.Request) {
r.HandleFunc("/db/", func(resp http.ResponseWriter, r *http.Request) {
util.SendError(errors.New("etcd datastore disabled"), resp, r, http.StatusBadRequest)
})

View File

@ -19,7 +19,6 @@ import (
"time"
"github.com/google/uuid"
"github.com/gorilla/mux"
"github.com/k3s-io/k3s/pkg/clientaccess"
"github.com/k3s-io/k3s/pkg/cluster/managed"
"github.com/k3s-io/k3s/pkg/daemons/config"
@ -31,6 +30,7 @@ import (
"github.com/k3s-io/k3s/pkg/signals"
"github.com/k3s-io/k3s/pkg/util"
"github.com/k3s-io/k3s/pkg/util/errors"
"github.com/k3s-io/k3s/pkg/util/mux"
"github.com/k3s-io/k3s/pkg/version"
kine "github.com/k3s-io/kine/pkg/app"
"github.com/k3s-io/kine/pkg/client"
@ -744,16 +744,16 @@ 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 {
r := mux.NewRouter().SkipClean(true)
r := mux.NewRouter()
r.NotFoundHandler = next
ir := r.Path("/db/info").Subrouter()
ir := r.SubRouter("/db/info")
ir.Use(auth.IsLocalOrHasRole(e.config, version.Program+":server"))
ir.Handle("", e.infoHandler())
ir.Handle("/", e.infoHandler())
sr := r.Path("/db/snapshot").Subrouter()
sr := r.SubRouter("/db/snapshot")
sr.Use(auth.HasRole(e.config, version.Program+":server"))
sr.Handle("", e.snapshotHandler())
sr.Handle("/", e.snapshotHandler())
return r
}

View File

@ -14,10 +14,10 @@ import (
"text/template"
"time"
"github.com/gorilla/mux"
"github.com/k3s-io/k3s/pkg/daemons/config"
"github.com/k3s-io/k3s/pkg/etcd/snapshot"
"github.com/k3s-io/k3s/pkg/util"
"github.com/k3s-io/k3s/pkg/util/mux"
"github.com/k3s-io/k3s/tests/mock"
"github.com/rancher/dynamiclistener/cert"
"github.com/rancher/wrangler/v3/pkg/generated/controllers/core"
@ -1554,65 +1554,72 @@ func s3Router(t *testing.T) http.Handler {
// badbucket returns 404 for all requests
// authbucket returns 200 for HeadBucket, 403 for all others
// others return 200 for objects with name prefix snapshot, 404 for all others
router := mux.NewRouter().SkipClean(true)
router := mux.NewRouter()
// HeadBucket
router.Path("/{bucket}/").Methods(http.MethodHead).HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
if vars["bucket"] == "badbucket" {
rw.WriteHeader(http.StatusNotFound)
}
})
// ListObjectsV2
router.Path("/{bucket}/").Methods(http.MethodGet).HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
switch vars["bucket"] {
case "badbucket":
rw.WriteHeader(http.StatusNotFound)
case "authbucket":
rw.WriteHeader(http.StatusForbidden)
default:
prefix := r.URL.Query().Get("prefix")
filtered := []object{}
for _, object := range objects {
if strings.HasPrefix(object.Key, prefix) {
filtered = append(filtered, object)
router.HandleFunc("GET /{bucket}/{$}", func(rw http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodHead:
if r.PathValue("bucket") == "badbucket" {
rw.WriteHeader(http.StatusNotFound)
}
case http.MethodGet:
switch r.PathValue("bucket") {
case "badbucket":
rw.WriteHeader(http.StatusNotFound)
case "authbucket":
rw.WriteHeader(http.StatusForbidden)
default:
prefix := r.URL.Query().Get("prefix")
filtered := []object{}
for _, object := range objects {
if strings.HasPrefix(object.Key, prefix) {
filtered = append(filtered, object)
}
}
if err := listResponse.Execute(rw, bucket{Name: r.PathValue("bucket"), Prefix: prefix, Objects: filtered}); err != nil {
t.Errorf("Failed to generate ListObjectsV2 response, error = %v", err)
rw.WriteHeader(http.StatusInternalServerError)
}
}
if err := listResponse.Execute(rw, bucket{Name: vars["bucket"], Prefix: prefix, Objects: filtered}); err != nil {
t.Errorf("Failed to generate ListObjectsV2 response, error = %v", err)
rw.WriteHeader(http.StatusInternalServerError)
}
})
// HeadObject
// GetObject
router.HandleFunc("GET /{bucket}/{object...}", func(rw http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodHead:
switch r.PathValue("bucket") {
case "badbucket":
rw.WriteHeader(http.StatusNotFound)
case "authbucket":
rw.WriteHeader(http.StatusForbidden)
default:
if strings.Contains(r.PathValue("object"), "bad") {
rw.WriteHeader(http.StatusNotFound)
} else {
rw.Header().Add("last-modified", time.Now().In(gmt).Format(time.RFC1123))
}
}
case http.MethodGet:
switch r.PathValue("bucket") {
case "badbucket":
rw.WriteHeader(http.StatusNotFound)
case "authbucket":
rw.WriteHeader(http.StatusForbidden)
default:
if strings.Contains(r.PathValue("object"), "bad") {
rw.WriteHeader(http.StatusNotFound)
} else {
rw.Header().Add("last-modified", time.Now().In(gmt).Format(time.RFC1123))
rw.Write([]byte("test snapshot file\n"))
}
}
}
})
// HeadObject - snapshot
router.Path("/{bucket}/{prefix:.*}snapshot-{snapshot}").Methods(http.MethodHead).HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
switch vars["bucket"] {
case "badbucket":
rw.WriteHeader(http.StatusNotFound)
case "authbucket":
rw.WriteHeader(http.StatusForbidden)
default:
rw.Header().Add("last-modified", time.Now().In(gmt).Format(time.RFC1123))
}
})
// GetObject - snapshot
router.Path("/{bucket}/{prefix:.*}snapshot-{snapshot}").Methods(http.MethodGet).HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
switch vars["bucket"] {
case "badbucket":
rw.WriteHeader(http.StatusNotFound)
case "authbucket":
rw.WriteHeader(http.StatusForbidden)
default:
rw.Header().Add("last-modified", time.Now().In(gmt).Format(time.RFC1123))
rw.Write([]byte("test snapshot file\n"))
}
})
// PutObject/DeleteObject - snapshot
router.Path("/{bucket}/{prefix:.*}snapshot-{snapshot}").Methods(http.MethodPut, http.MethodDelete).HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
switch vars["bucket"] {
// PutObject
router.HandleFunc("PUT /{bucket}/{object...}", func(rw http.ResponseWriter, r *http.Request) {
switch r.PathValue("bucket") {
case "badbucket":
rw.WriteHeader(http.StatusNotFound)
case "authbucket":
@ -1623,45 +1630,27 @@ func s3Router(t *testing.T) http.Handler {
}
}
})
// HeadObject - snapshot metadata
router.Path("/{bucket}/{prefix:.*}.metadata/snapshot-{snapshot}").Methods(http.MethodHead).HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
switch vars["bucket"] {
case "badbucket":
rw.WriteHeader(http.StatusNotFound)
case "authbucket":
rw.WriteHeader(http.StatusForbidden)
default:
rw.Header().Add("last-modified", time.Now().In(gmt).Format(time.RFC1123))
}
})
// GetObject - snapshot metadata
router.Path("/{bucket}/{prefix:.*}.metadata/snapshot-{snapshot}").Methods(http.MethodGet).HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
switch vars["bucket"] {
case "badbucket":
rw.WriteHeader(http.StatusNotFound)
case "authbucket":
rw.WriteHeader(http.StatusForbidden)
default:
rw.Header().Add("last-modified", time.Now().In(gmt).Format(time.RFC1123))
rw.Write([]byte("test snapshot metadata\n"))
}
})
// PutObject/DeleteObject - snapshot metadata
router.Path("/{bucket}/{prefix:.*}.metadata/snapshot-{snapshot}").Methods(http.MethodPut, http.MethodDelete).HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
switch vars["bucket"] {
// DeleteObject
router.HandleFunc("DELETE /{bucket}/{object...}", func(rw http.ResponseWriter, r *http.Request) {
switch r.PathValue("bucket") {
case "badbucket":
rw.WriteHeader(http.StatusNotFound)
case "authbucket":
rw.WriteHeader(http.StatusForbidden)
default:
if r.Method == http.MethodDelete {
rw.WriteHeader(http.StatusNoContent)
if strings.Contains(r.PathValue("object"), "bad") {
rw.WriteHeader(http.StatusNotFound)
} else {
rw.WriteHeader(http.StatusNoContent)
}
}
}
})
router.NotFoundHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
logrus.Errorf("Failed to match %q", r.URL)
rw.WriteHeader(http.StatusInternalServerError)
})
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
scheme := "http"
if r.TLS != nil {

View File

@ -12,6 +12,7 @@ import (
"github.com/k3s-io/k3s/pkg/util"
"github.com/k3s-io/k3s/pkg/util/errors"
"github.com/sirupsen/logrus"
apierrors "k8s.io/apimachinery/pkg/api/errors"
)
type SnapshotOperation string
@ -178,7 +179,7 @@ func (e *ETCD) withRequest(sr *SnapshotRequest) *ETCD {
// getSnapshotRequest unmarshalls the snapshot operation request from a client.
func getSnapshotRequest(req *http.Request) (*SnapshotRequest, error) {
if req.Method != http.MethodPost {
return nil, http.ErrNotSupported
return nil, apierrors.NewMethodNotSupported(k3s.Resource("snapshot"), req.Method)
}
sr := &SnapshotRequest{}
b, err := io.ReadAll(req.Body)

View File

@ -4,11 +4,11 @@ import (
"context"
"errors"
"github.com/gorilla/mux"
"github.com/k3s-io/k3s/pkg/agent/https"
"github.com/k3s-io/k3s/pkg/agent/loadbalancer"
"github.com/k3s-io/k3s/pkg/daemons/config"
"github.com/k3s-io/k3s/pkg/etcd/snapshotmetrics"
"github.com/k3s-io/k3s/pkg/util/mux"
"github.com/prometheus/client_golang/prometheus/promhttp"
lassometrics "github.com/rancher/lasso/pkg/metrics"
rdmetrics "github.com/rancher/remotedialer/metrics"

View File

@ -10,10 +10,10 @@ import (
"sync"
"time"
"github.com/gorilla/mux"
"github.com/k3s-io/k3s/pkg/daemons/config"
"github.com/k3s-io/k3s/pkg/util"
"github.com/k3s-io/k3s/pkg/util/errors"
"github.com/k3s-io/k3s/pkg/version"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
@ -105,13 +105,12 @@ func getNodeInfo(req *http.Request) (*nodeInfo, error) {
return nil, errors.New("auth user not set")
}
program := mux.Vars(req)["program"]
nodeName := req.Header.Get(program + "-Node-Name")
nodeName := req.Header.Get(version.Program + "-Node-Name")
if nodeName == "" {
return nil, errors.New("node name not set")
}
nodePassword := req.Header.Get(program + "-Node-Password")
nodePassword := req.Header.Get(version.Program + "-Node-Password")
if nodePassword == "" {
return nil, errors.New("node password not set")
}

View File

@ -5,9 +5,9 @@ import (
"errors"
"net/http/pprof"
"github.com/gorilla/mux"
"github.com/k3s-io/k3s/pkg/agent/https"
"github.com/k3s-io/k3s/pkg/daemons/config"
"github.com/k3s-io/k3s/pkg/util/mux"
)
// DefaultProfiler the default instance of a performance profiling server
@ -33,6 +33,6 @@ func (c *Config) Start(ctx context.Context, nodeConfig *config.Node) error {
mRouter.HandleFunc("/debug/pprof/profile", pprof.Profile)
mRouter.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mRouter.HandleFunc("/debug/pprof/trace", pprof.Trace)
mRouter.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
mRouter.HandleFunc("/debug/pprof/", pprof.Index)
return nil
}

View File

@ -5,9 +5,9 @@ import (
"net"
"net/http"
"github.com/gorilla/mux"
"github.com/k3s-io/k3s/pkg/daemons/config"
"github.com/k3s-io/k3s/pkg/util"
"github.com/k3s-io/k3s/pkg/util/mux"
"github.com/k3s-io/k3s/pkg/version"
"github.com/sirupsen/logrus"
"k8s.io/apiserver/pkg/apis/apiserver"

View File

@ -13,18 +13,20 @@ import (
"strings"
"time"
"github.com/gorilla/mux"
"github.com/k3s-io/k3s/pkg/cli/cmds"
"github.com/k3s-io/k3s/pkg/daemons/config"
"github.com/k3s-io/k3s/pkg/etcd"
"github.com/k3s-io/k3s/pkg/nodepassword"
"github.com/k3s-io/k3s/pkg/util"
"github.com/k3s-io/k3s/pkg/util/errors"
"github.com/k3s-io/k3s/pkg/version"
certutil "github.com/rancher/dynamiclistener/cert"
"github.com/sirupsen/logrus"
discoveryv1 "k8s.io/api/discovery/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/authentication/user"
@ -66,8 +68,7 @@ func ServingKubeletCert(control *config.Control, auth nodepassword.NodeAuthValid
}
ips := []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")}
program := mux.Vars(req)["program"]
if nodeIP := req.Header.Get(program + "-Node-IP"); nodeIP != "" {
if nodeIP := req.Header.Get(version.Program + "-Node-IP"); nodeIP != "" {
for _, v := range strings.Split(nodeIP, ",") {
ip := net.ParseIP(v)
if ip == nil {
@ -115,9 +116,8 @@ func ClientKubeProxyCert(control *config.Control) http.Handler {
func ClientControllerCert(control *config.Control) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
program := mux.Vars(req)["program"]
signAndSend(resp, req, control.Runtime.ClientCA, control.Runtime.ClientCAKey, control.Runtime.ClientK3sControllerKey, certutil.Config{
CommonName: "system:" + program + "-controller",
CommonName: "system:" + version.Program + "-controller",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
})
@ -314,7 +314,7 @@ func getCACertAndKey(caCertFile, caKeyFile string) ([]*x509.Certificate, crypto.
// If the request is not a POST, or cannot be parsed as a request, an error is returned.
func getCSR(req *http.Request) (*x509.CertificateRequest, error) {
if req.Method != http.MethodPost {
return nil, mux.ErrMethodMismatch
return nil, apierrors.NewMethodNotSupported(schema.GroupResource{}, req.Method)
}
csrBytes, err := io.ReadAll(req.Body)
if err != nil {

View File

@ -5,11 +5,11 @@ import (
"net/http"
"path/filepath"
"github.com/gorilla/mux"
"github.com/k3s-io/k3s/pkg/cli/cmds"
"github.com/k3s-io/k3s/pkg/daemons/config"
"github.com/k3s-io/k3s/pkg/nodepassword"
"github.com/k3s-io/k3s/pkg/server/auth"
"github.com/k3s-io/k3s/pkg/util/mux"
"github.com/k3s-io/k3s/pkg/version"
"k8s.io/apiserver/pkg/authentication/user"
bootstrapapi "k8s.io/cluster-bootstrap/token/api"
@ -30,26 +30,26 @@ var (
func NewHandler(ctx context.Context, control *config.Control, cfg *cmds.Server) http.Handler {
nodeAuth := nodepassword.GetNodeAuthValidator(ctx, control)
prefix := "/v1-{program}"
authed := mux.NewRouter().SkipClean(true)
prefix := "/v1-" + version.Program
authed := mux.NewRouter()
authed.NotFoundHandler = APIServer(control, cfg)
authed.Use(auth.HasRole(control, version.Program+":agent", user.NodesGroup, bootstrapapi.BootstrapDefaultGroup), auth.RequestInfo(), auth.MaxInFlight(maxNonMutatingAgentRequests, maxMutatingAgentRequests))
authed.Handle(prefix+"/serving-kubelet.crt", ServingKubeletCert(control, nodeAuth))
authed.Handle(prefix+"/client-kubelet.crt", ClientKubeletCert(control, nodeAuth))
authed.Handle(prefix+"/client-kube-proxy.crt", ClientKubeProxyCert(control))
authed.Handle(prefix+"/client-{program}-controller.crt", ClientControllerCert(control))
authed.Handle(prefix+"/client-"+version.Program+"-controller.crt", ClientControllerCert(control))
authed.Handle(prefix+"/client-ca.crt", File(control.Runtime.ClientCA))
authed.Handle(prefix+"/server-ca.crt", File(control.Runtime.ServerCA))
authed.Handle(prefix+"/apiservers", APIServers(control))
authed.Handle(prefix+"/config", Config(control, cfg))
authed.Handle(prefix+"/readyz", Readyz(control))
nodeAuthed := mux.NewRouter().SkipClean(true)
nodeAuthed := mux.NewRouter()
nodeAuthed.NotFoundHandler = authed
nodeAuthed.Use(auth.HasRole(control, user.NodesGroup))
nodeAuthed.Handle(prefix+"/connect", control.Runtime.Tunnel)
serverAuthed := mux.NewRouter().SkipClean(true)
serverAuthed := mux.NewRouter()
serverAuthed.NotFoundHandler = nodeAuthed
serverAuthed.Use(auth.HasRole(control, version.Program+":server"))
serverAuthed.Handle(prefix+"/encrypt/status", EncryptionStatus(control))
@ -58,15 +58,14 @@ func NewHandler(ctx context.Context, control *config.Control, cfg *cmds.Server)
serverAuthed.Handle(prefix+"/server-bootstrap", Bootstrap(control))
serverAuthed.Handle(prefix+"/token", TokenRequest(ctx, control))
systemAuthed := mux.NewRouter().SkipClean(true)
systemAuthed := mux.NewRouter()
systemAuthed.NotFoundHandler = serverAuthed
systemAuthed.MethodNotAllowedHandler = serverAuthed
systemAuthed.Use(auth.HasRole(control, user.SystemPrivilegedGroup))
systemAuthed.Methods(http.MethodConnect).Handler(control.Runtime.Tunnel)
systemAuthed.Handle("CONNECT /", control.Runtime.Tunnel)
router := mux.NewRouter().SkipClean(true)
router := mux.NewRouter()
router.NotFoundHandler = systemAuthed
router.PathPrefix(staticURL).Handler(Static(staticURL, filepath.Join(control.DataDir, "static")))
router.Handle(staticURL, Static(staticURL, filepath.Join(control.DataDir, "static")))
router.Handle("/cacerts", CACerts(control))
router.Handle("/ping", Ping())

View File

@ -18,7 +18,6 @@ import (
"github.com/containerd/containerd/v2/core/remotes/docker"
"github.com/go-logr/logr"
"github.com/gorilla/mux"
leveldb "github.com/ipfs/go-ds-leveldb"
ipfslog "github.com/ipfs/go-log/v2"
"github.com/k3s-io/k3s/pkg/agent/https"
@ -26,6 +25,7 @@ import (
"github.com/k3s-io/k3s/pkg/server/auth"
"github.com/k3s-io/k3s/pkg/util/errors"
"github.com/k3s-io/k3s/pkg/util/logger"
"github.com/k3s-io/k3s/pkg/util/mux"
"github.com/k3s-io/k3s/pkg/version"
"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p/core/crypto"
@ -294,10 +294,10 @@ func (c *Config) Start(ctx context.Context, nodeConfig *config.Node, criReadyCha
if err != nil {
return err
}
mRouter.PathPrefix("/v2").Handler(regSvr.Handler)
sRouter := mRouter.PathPrefix("/v1-{program}/p2p").Subrouter()
mRouter.Handle("/v2/", regSvr.Handler)
sRouter := mRouter.SubRouter("/v1-" + version.Program + "/p2p")
sRouter.Use(auth.MaxInFlight(maxNonMutatingPeerInfoRequests, maxMutatingPeerInfoRequests))
sRouter.Handle("", c.peerInfo())
sRouter.Handle("/", c.peerInfo())
// Wait up to 5 seconds for the p2p network to find peers.
if err := wait.PollUntilContextTimeout(ctx, time.Second, resolveTimeout, true, func(ctx context.Context) (bool, error) {

122
pkg/util/mux/mux.go Normal file
View File

@ -0,0 +1,122 @@
package mux
import (
"net/http"
)
// MiddlewareFunc is the function signature of middlewares. The middleware is expected
// to return a handler that does work and then either writes a response, or calls the
// provided handler for additional processing.
type MiddlewareFunc func(http.Handler) http.Handler
// Handler wraps http.Handler and allows selectively running middleware on a matched route
type Handler interface {
http.Handler
Matched() bool
}
// muxHandler is a wrapper around http.Handler,
// used to differentiate between http.ServeMux internal
// handlers and handlers from this package.
type muxHandler struct {
handler http.Handler
}
// ServeHTTP calls the wrapped function of the same name.
func (mh *muxHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
mh.handler.ServeHTTP(rw, req)
}
func (mh *muxHandler) Matched() bool {
return true
}
// rootHandler runs the router's NotFoundHandler if one is set,
// or returns a fixed NotFound error. Middlewares are run
// if the NotFound handler was registered as a match for the root path.
type rootHandler struct {
r *Router
rootMatch bool
}
func (rh *rootHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if rh.r.NotFoundHandler != nil {
rh.r.NotFoundHandler.ServeHTTP(rw, req)
} else {
rw.WriteHeader(http.StatusNotFound)
}
}
func (rh *rootHandler) Matched() bool {
return rh.rootMatch && rh.r.NotFoundHandler != nil
}
// Router wraps http.ServeMux, adding functionality to call
// middlewares on matched requests.
type Router struct {
NotFoundHandler http.Handler
sm *http.ServeMux
rootHandler *rootHandler
middlewares []MiddlewareFunc
}
// NewRouter creates a new Router
func NewRouter() *Router {
r := &Router{sm: http.NewServeMux()}
r.rootHandler = &rootHandler{r: r}
r.sm.Handle("/", r.rootHandler)
return r
}
// Use registers one or more middlewares. Middlewares are only run
// when a route is matched.
func (r *Router) Use(mwfs ...MiddlewareFunc) {
r.middlewares = append(r.middlewares, mwfs...)
}
// SubRouter registers a route pattern, and returns a new router. Middlewares
// will only run if the subrouter pattern was matched.
// Note that the base pattern for the subrouter is NOT automatically
// prefixed to paths registered beneath it.
func (r *Router) SubRouter(pattern string) *Router {
sr := NewRouter()
r.Handle(pattern, sr)
return sr
}
// Handle registers a route pattern to call a handler
func (r *Router) Handle(pattern string, handler http.Handler) {
handler = &muxHandler{handler: handler}
if pattern == "/" && r.NotFoundHandler == nil {
r.rootHandler.rootMatch = true
r.NotFoundHandler = handler
} else {
r.sm.Handle(pattern, handler)
}
}
// HandleFunc registers a route pattern to call a handler function
func (r *Router) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
r.Handle(pattern, http.HandlerFunc(handler))
}
// ServeHTTP handles the request, running middlewares if a registered pattern
// has been matched.
func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
next := http.Handler(r.sm)
// fix up path for CONNECT requests so that they do not get redirected
if req.Method == http.MethodConnect && req.URL.Path == "" {
req.URL.Path = "/"
}
// only run middlewares if this is a mux handler; other handlers are
// http.ServeMux internal handlers that indicate no pattern was matched, and
// we should not run middleware.
handler, _ := r.sm.Handler(req)
if h, ok := handler.(Handler); ok && h.Matched() {
for i := len(r.middlewares) - 1; i >= 0; i-- {
next = r.middlewares[i](next)
}
}
next.ServeHTTP(rw, req)
}