mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-26 05:03:09 +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
|
EnableIndex bool
|
||||||
EnableProfiling bool
|
EnableProfiling bool
|
||||||
|
DebugSocketPath string
|
||||||
EnableDiscovery bool
|
EnableDiscovery bool
|
||||||
|
|
||||||
// Requires generic profiling enabled
|
// Requires generic profiling enabled
|
||||||
@ -360,6 +361,7 @@ func NewConfig(codecs serializer.CodecFactory) *Config {
|
|||||||
EnableIndex: true,
|
EnableIndex: true,
|
||||||
EnableDiscovery: true,
|
EnableDiscovery: true,
|
||||||
EnableProfiling: true,
|
EnableProfiling: true,
|
||||||
|
DebugSocketPath: "",
|
||||||
EnableMetrics: true,
|
EnableMetrics: true,
|
||||||
MaxRequestsInFlight: 400,
|
MaxRequestsInFlight: 400,
|
||||||
MaxMutatingRequestsInFlight: 200,
|
MaxMutatingRequestsInFlight: 200,
|
||||||
@ -631,6 +633,11 @@ func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*G
|
|||||||
return c.BuildHandlerChainFunc(handler, c.Config)
|
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())
|
apiServerHandler := NewAPIServerHandler(name, c.Serializer, handlerChainBuilder, delegationTarget.UnprotectedHandler())
|
||||||
|
|
||||||
s := &GenericAPIServer{
|
s := &GenericAPIServer{
|
||||||
@ -645,6 +652,7 @@ func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*G
|
|||||||
EquivalentResourceRegistry: c.EquivalentResourceRegistry,
|
EquivalentResourceRegistry: c.EquivalentResourceRegistry,
|
||||||
HandlerChainWaitGroup: c.HandlerChainWaitGroup,
|
HandlerChainWaitGroup: c.HandlerChainWaitGroup,
|
||||||
Handler: apiServerHandler,
|
Handler: apiServerHandler,
|
||||||
|
UnprotectedDebugSocket: debugSocket,
|
||||||
|
|
||||||
listedPathProvider: apiServerHandler,
|
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.
|
// 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))
|
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.EnableMetrics {
|
||||||
if c.EnableProfiling {
|
if c.EnableProfiling {
|
||||||
|
@ -32,6 +32,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
utilwaitgroup "k8s.io/apimachinery/pkg/util/waitgroup"
|
utilwaitgroup "k8s.io/apimachinery/pkg/util/waitgroup"
|
||||||
"k8s.io/apimachinery/pkg/version"
|
"k8s.io/apimachinery/pkg/version"
|
||||||
@ -136,6 +137,10 @@ type GenericAPIServer struct {
|
|||||||
// Handler holds the handlers being used by this API server
|
// Handler holds the handlers being used by this API server
|
||||||
Handler *APIServerHandler
|
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 is a lister which provides the set of paths to show at /
|
||||||
listedPathProvider routes.ListedPathProvider
|
listedPathProvider routes.ListedPathProvider
|
||||||
|
|
||||||
@ -467,6 +472,14 @@ func (s preparedGenericAPIServer) Run(stopCh <-chan struct{}) error {
|
|||||||
// Clean up resources on shutdown.
|
// Clean up resources on shutdown.
|
||||||
defer s.Destroy()
|
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
|
// spawn a new goroutine for closing the MuxAndDiscoveryComplete signal
|
||||||
// registration happens during construction of the generic api server
|
// registration happens during construction of the generic api server
|
||||||
// the last server in the chain aggregates signals from the previous instances
|
// the last server in the chain aggregates signals from the previous instances
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
|
|
||||||
type FeatureOptions struct {
|
type FeatureOptions struct {
|
||||||
EnableProfiling bool
|
EnableProfiling bool
|
||||||
|
DebugSocketPath string
|
||||||
EnableContentionProfiling bool
|
EnableContentionProfiling bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ func NewFeatureOptions() *FeatureOptions {
|
|||||||
|
|
||||||
return &FeatureOptions{
|
return &FeatureOptions{
|
||||||
EnableProfiling: defaults.EnableProfiling,
|
EnableProfiling: defaults.EnableProfiling,
|
||||||
|
DebugSocketPath: defaults.DebugSocketPath,
|
||||||
EnableContentionProfiling: defaults.EnableContentionProfiling,
|
EnableContentionProfiling: defaults.EnableContentionProfiling,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,6 +48,8 @@ func (o *FeatureOptions) AddFlags(fs *pflag.FlagSet) {
|
|||||||
"Enable profiling via web interface host:port/debug/pprof/")
|
"Enable profiling via web interface host:port/debug/pprof/")
|
||||||
fs.BoolVar(&o.EnableContentionProfiling, "contention-profiling", o.EnableContentionProfiling,
|
fs.BoolVar(&o.EnableContentionProfiling, "contention-profiling", o.EnableContentionProfiling,
|
||||||
"Enable lock contention profiling, if profiling is enabled")
|
"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 {
|
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.EnableProfiling = o.EnableProfiling
|
||||||
|
c.DebugSocketPath = o.DebugSocketPath
|
||||||
c.EnableContentionProfiling = o.EnableContentionProfiling
|
c.EnableContentionProfiling = o.EnableContentionProfiling
|
||||||
|
|
||||||
return nil
|
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