mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 04:06:03 +00:00
Allow profiling information to be served on Unix-Domain Socket
Serving profiling information can leak information or expose the apiserver to possible DoS attacks. Serving on a UDS is more secure though slightly less convenient. One can't use `go tool pprof` directly against the socket since it's not supported, but can either run a proxy to copy from the socket over to http, or use `curl --unix-socket` to download the profile and then use `go tool pprof`.
This commit is contained in:
parent
3e26e104bd
commit
667599b0dd
@ -126,6 +126,7 @@ type Config struct {
|
||||
|
||||
EnableIndex bool
|
||||
EnableProfiling bool
|
||||
DebugSocketPath string
|
||||
EnableDiscovery bool
|
||||
|
||||
// Requires generic profiling enabled
|
||||
@ -360,6 +361,7 @@ func NewConfig(codecs serializer.CodecFactory) *Config {
|
||||
EnableIndex: true,
|
||||
EnableDiscovery: true,
|
||||
EnableProfiling: true,
|
||||
DebugSocketPath: "",
|
||||
EnableMetrics: true,
|
||||
MaxRequestsInFlight: 400,
|
||||
MaxMutatingRequestsInFlight: 200,
|
||||
@ -631,6 +633,11 @@ func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*G
|
||||
return c.BuildHandlerChainFunc(handler, c.Config)
|
||||
}
|
||||
|
||||
var debugSocket *routes.DebugSocket
|
||||
if c.DebugSocketPath != "" {
|
||||
debugSocket = routes.NewDebugSocket(c.DebugSocketPath)
|
||||
}
|
||||
|
||||
apiServerHandler := NewAPIServerHandler(name, c.Serializer, handlerChainBuilder, delegationTarget.UnprotectedHandler())
|
||||
|
||||
s := &GenericAPIServer{
|
||||
@ -645,6 +652,7 @@ func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*G
|
||||
EquivalentResourceRegistry: c.EquivalentResourceRegistry,
|
||||
HandlerChainWaitGroup: c.HandlerChainWaitGroup,
|
||||
Handler: apiServerHandler,
|
||||
UnprotectedDebugSocket: debugSocket,
|
||||
|
||||
listedPathProvider: apiServerHandler,
|
||||
|
||||
@ -914,6 +922,13 @@ func installAPI(s *GenericAPIServer, c *Config) {
|
||||
// so far, only logging related endpoints are considered valid to add for these debug flags.
|
||||
routes.DebugFlags{}.Install(s.Handler.NonGoRestfulMux, "v", routes.StringFlagPutHandler(logs.GlogSetter))
|
||||
}
|
||||
if s.UnprotectedDebugSocket != nil {
|
||||
s.UnprotectedDebugSocket.InstallProfiling()
|
||||
s.UnprotectedDebugSocket.InstallDebugFlag("v", routes.StringFlagPutHandler(logs.GlogSetter))
|
||||
if c.EnableContentionProfiling {
|
||||
goruntime.SetBlockProfileRate(1)
|
||||
}
|
||||
}
|
||||
|
||||
if c.EnableMetrics {
|
||||
if c.EnableProfiling {
|
||||
|
@ -32,6 +32,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilwaitgroup "k8s.io/apimachinery/pkg/util/waitgroup"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
@ -136,6 +137,10 @@ type GenericAPIServer struct {
|
||||
// Handler holds the handlers being used by this API server
|
||||
Handler *APIServerHandler
|
||||
|
||||
// UnprotectedDebugSocket is used to serve pprof information in a unix-domain socket. This socket is
|
||||
// not protected by authentication/authorization.
|
||||
UnprotectedDebugSocket *routes.DebugSocket
|
||||
|
||||
// listedPathProvider is a lister which provides the set of paths to show at /
|
||||
listedPathProvider routes.ListedPathProvider
|
||||
|
||||
@ -467,6 +472,14 @@ func (s preparedGenericAPIServer) Run(stopCh <-chan struct{}) error {
|
||||
// Clean up resources on shutdown.
|
||||
defer s.Destroy()
|
||||
|
||||
// If UDS profiling is enabled, start a local http server listening on that socket
|
||||
if s.UnprotectedDebugSocket != nil {
|
||||
go func() {
|
||||
defer utilruntime.HandleCrash()
|
||||
klog.Error(s.UnprotectedDebugSocket.Run(stopCh))
|
||||
}()
|
||||
}
|
||||
|
||||
// spawn a new goroutine for closing the MuxAndDiscoveryComplete signal
|
||||
// registration happens during construction of the generic api server
|
||||
// the last server in the chain aggregates signals from the previous instances
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
|
||||
type FeatureOptions struct {
|
||||
EnableProfiling bool
|
||||
DebugSocketPath string
|
||||
EnableContentionProfiling bool
|
||||
}
|
||||
|
||||
@ -33,6 +34,7 @@ func NewFeatureOptions() *FeatureOptions {
|
||||
|
||||
return &FeatureOptions{
|
||||
EnableProfiling: defaults.EnableProfiling,
|
||||
DebugSocketPath: defaults.DebugSocketPath,
|
||||
EnableContentionProfiling: defaults.EnableContentionProfiling,
|
||||
}
|
||||
}
|
||||
@ -46,6 +48,8 @@ func (o *FeatureOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
"Enable profiling via web interface host:port/debug/pprof/")
|
||||
fs.BoolVar(&o.EnableContentionProfiling, "contention-profiling", o.EnableContentionProfiling,
|
||||
"Enable lock contention profiling, if profiling is enabled")
|
||||
fs.StringVar(&o.DebugSocketPath, "debug-socket-path", o.DebugSocketPath,
|
||||
"Use an unprotected (no authn/authz) unix-domain socket for profiling with the given path")
|
||||
}
|
||||
|
||||
func (o *FeatureOptions) ApplyTo(c *server.Config) error {
|
||||
@ -54,6 +58,7 @@ func (o *FeatureOptions) ApplyTo(c *server.Config) error {
|
||||
}
|
||||
|
||||
c.EnableProfiling = o.EnableProfiling
|
||||
c.DebugSocketPath = o.DebugSocketPath
|
||||
c.EnableContentionProfiling = o.EnableContentionProfiling
|
||||
|
||||
return nil
|
||||
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright 2022 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
// DebugSocket installs profiling and debugflag as a Unix-Domain socket.
|
||||
type DebugSocket struct {
|
||||
path string
|
||||
mux *http.ServeMux
|
||||
}
|
||||
|
||||
// NewDebugSocket creates a new DebugSocket for the given path.
|
||||
func NewDebugSocket(path string) *DebugSocket {
|
||||
return &DebugSocket{
|
||||
path: path,
|
||||
mux: http.NewServeMux(),
|
||||
}
|
||||
}
|
||||
|
||||
// InstallProfiling installs profiling endpoints in the socket.
|
||||
func (s *DebugSocket) InstallProfiling() {
|
||||
s.mux.HandleFunc("/debug/pprof", redirectTo("/debug/pprof/"))
|
||||
s.mux.HandleFunc("/debug/pprof/", pprof.Index)
|
||||
s.mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
||||
s.mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||
s.mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||
s.mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||
}
|
||||
|
||||
// InstallDebugFlag installs debug flag endpoints in the socket.
|
||||
func (s *DebugSocket) InstallDebugFlag(flag string, handler func(http.ResponseWriter, *http.Request)) {
|
||||
f := DebugFlags{}
|
||||
s.mux.HandleFunc("/debug/flags", f.Index)
|
||||
s.mux.HandleFunc("/debug/flags/", f.Index)
|
||||
|
||||
url := path.Join("/debug/flags", flag)
|
||||
s.mux.HandleFunc(url, handler)
|
||||
|
||||
f.addFlag(flag)
|
||||
}
|
||||
|
||||
// Run starts the server and waits for stopCh to be closed to close the server.
|
||||
func (s *DebugSocket) Run(stopCh <-chan struct{}) error {
|
||||
if err := os.Remove(s.path); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to remove (%v): %v", s.path, err)
|
||||
}
|
||||
|
||||
l, err := net.Listen("unix", s.path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("listen error (%v): %v", s.path, err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
srv := http.Server{Handler: s.mux}
|
||||
go func() {
|
||||
<-stopCh
|
||||
srv.Close()
|
||||
}()
|
||||
return srv.Serve(l)
|
||||
}
|
Loading…
Reference in New Issue
Block a user