diff --git a/go.mod b/go.mod index 07736dc..990f6d5 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,10 @@ go 1.22.0 replace k8s.io/client-go => k8s.io/client-go v0.30.1 require ( + github.com/Microsoft/go-winio v0.6.2 github.com/gorilla/mux v1.8.1 github.com/rancher/apiserver v0.0.0-20240708202538-39a6f2535146 + github.com/rancher/dynamiclistener v0.6.0-rc2 github.com/rancher/steve v0.0.0-20240911190153-79304d93b49b github.com/rancher/wrangler/v3 v3.0.0 github.com/sirupsen/logrus v1.9.3 @@ -62,7 +64,6 @@ require ( github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect - github.com/rancher/dynamiclistener v0.6.0-rc2 // indirect github.com/rancher/kubernetes-provider-detector v0.1.5 // indirect github.com/rancher/lasso v0.0.0-20240705194423-b2a060d103c1 // indirect github.com/rancher/norman v0.0.0-20240708202514-a0127673d1b9 // indirect diff --git a/go.sum b/go.sum index 86810c6..4786243 100644 --- a/go.sum +++ b/go.sum @@ -601,6 +601,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= diff --git a/internal/config/flags.go b/internal/config/flags.go index 82c1e97..d581285 100644 --- a/internal/config/flags.go +++ b/internal/config/flags.go @@ -8,6 +8,7 @@ var InsecureSkipTLSVerify bool var SystemDefaultRegistry string var APIUIVersion = "1.1.11" var ShellPodImage string +var BindAddress string func Flags() []cli.Flag { return []cli.Flag{ @@ -30,5 +31,10 @@ func Flags() []cli.Flag { Destination: &APIUIVersion, Value: APIUIVersion, }, + cli.StringFlag{ + Name: "bind-address", + Destination: &BindAddress, + Usage: `Bind address with url format. The supported schemes are unix, tcp and namedpipe, e.g. unix:///path/to/kube-explorer.sock or namedpipe:/\.\pipe\kube-explorer`, + }, } } diff --git a/internal/server/listener.go b/internal/server/listener.go new file mode 100644 index 0000000..3d6d61c --- /dev/null +++ b/internal/server/listener.go @@ -0,0 +1,52 @@ +package server + +import ( + "context" + "log" + "net" + "net/http" + + "github.com/cnrancher/kube-explorer/internal/config" + dynamicserver "github.com/rancher/dynamiclistener/server" + "github.com/rancher/steve/pkg/server" + "github.com/sirupsen/logrus" +) + +func Serve(ctx context.Context, server *server.Server) error { + listener, ipOrPath, err := ensureListener() + if err != nil { + return err + } + if listener != nil { + return serveSocket(ctx, ipOrPath, listener, server) + } + return server.ListenAndServe(ctx, config.Steve.HTTPSListenPort, config.Steve.HTTPListenPort, &dynamicserver.ListenOpts{ + BindHost: ipOrPath, + }) +} + +func serveSocket(ctx context.Context, socketPath string, listener net.Listener, handler http.Handler) error { + logger := logrus.StandardLogger() + errorLog := log.New(logger.WriterLevel(logrus.DebugLevel), "", log.LstdFlags) + socketServer := &http.Server{ + Handler: handler, + ErrorLog: errorLog, + BaseContext: func(_ net.Listener) context.Context { + return ctx + }, + } + go func() { + logrus.Infof("Listening on %s", socketPath) + err := socketServer.Serve(listener) + if err != http.ErrServerClosed && err != nil { + logrus.Fatalf("https server failed: %v", err) + } + }() + go func() { + <-ctx.Done() + _ = socketServer.Shutdown(context.Background()) + _ = listener.Close() + }() + <-ctx.Done() + return ctx.Err() +} diff --git a/internal/server/listener_unix.go b/internal/server/listener_unix.go new file mode 100644 index 0000000..d1df629 --- /dev/null +++ b/internal/server/listener_unix.go @@ -0,0 +1,33 @@ +//go:build unix +// +build unix + +package server + +import ( + "fmt" + "net" + "net/url" + + "github.com/cnrancher/kube-explorer/internal/config" +) + +func ensureListener() (net.Listener, string, error) { + if config.BindAddress == "" { + return nil, "", nil + } + u, err := url.Parse(config.BindAddress) + if err != nil { + return nil, "", err + } + switch u.Scheme { + case "": + return nil, config.BindAddress, nil + case "tcp": + return nil, u.Host, nil + case "unix": + listener, err := net.Listen("unix", u.Path) + return listener, u.Path, err + default: + return nil, "", fmt.Errorf("Unsupported scheme %s, only tcp and unix are supported in UNIX OS", u.Scheme) + } +} diff --git a/internal/server/listener_windows.go b/internal/server/listener_windows.go new file mode 100644 index 0000000..6da3637 --- /dev/null +++ b/internal/server/listener_windows.go @@ -0,0 +1,34 @@ +//go:build windows +// +build windows + +package server + +import ( + "fmt" + "net" + "net/url" + + "github.com/Microsoft/go-winio" + "github.com/cnrancher/kube-explorer/internal/config" +) + +func ensureListener() (net.Listener, string, error) { + if config.BindAddress == "" { + return nil, "", nil + } + u, err := url.Parse(config.BindAddress) + if err != nil { + return nil, "", err + } + switch u.Scheme { + case "": + return nil, config.BindAddress, nil + case "tcp": + return nil, u.Host, nil + case "namedpipe": + listener, err := winio.ListenPipe(u.Path, nil) + return listener, u.Path, err + default: + return nil, "", fmt.Errorf("Unsupported scheme %s, only tcp and namedpipe are supported in windows", u.Scheme) + } +} diff --git a/main.go b/main.go index 8e4c71c..726e010 100644 --- a/main.go +++ b/main.go @@ -38,7 +38,7 @@ func run(_ *cli.Context) error { if err != nil { return err } - return s.ListenAndServe(ctx, keconfig.Steve.HTTPSListenPort, keconfig.Steve.HTTPListenPort, nil) + return server.Serve(ctx, s) } func joinFlags(flags ...[]cli.Flag) []cli.Flag {