diff --git a/src/runtime/pkg/kata-monitor/pprof.go b/src/runtime/pkg/kata-monitor/pprof.go index 5dac2da03c..62ed70c2e7 100644 --- a/src/runtime/pkg/kata-monitor/pprof.go +++ b/src/runtime/pkg/kata-monitor/pprof.go @@ -10,6 +10,8 @@ import ( "io" "net" "net/http" + "regexp" + "strings" cdshim "github.com/containerd/containerd/runtime/v2/shim" @@ -33,7 +35,13 @@ func (km *KataMonitor) composeSocketAddress(r *http.Request) (string, error) { return shim.SocketAddress(sandbox), nil } -func (km *KataMonitor) proxyRequest(w http.ResponseWriter, r *http.Request) { +func (km *KataMonitor) proxyRequest(w http.ResponseWriter, r *http.Request, + proxyResponse func(req *http.Request, w io.Writer, r io.Reader) error) { + + if proxyResponse == nil { + proxyResponse = copyResponse + } + w.Header().Set("X-Content-Type-Options", "nosniff") socketAddress, err := km.composeSocketAddress(r) @@ -73,38 +81,68 @@ func (km *KataMonitor) proxyRequest(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Disposition", contentDisposition) } - io.Copy(w, output) + err = proxyResponse(r, w, output) + if err != nil { + monitorLog.WithError(err).Errorf("failed proxying %s from %s", uri, socketAddress) + serveError(w, http.StatusInternalServerError, "error retrieving resource") + } } // ExpvarHandler handles other `/debug/vars` requests func (km *KataMonitor) ExpvarHandler(w http.ResponseWriter, r *http.Request) { - km.proxyRequest(w, r) + km.proxyRequest(w, r, nil) } // PprofIndex handles other `/debug/pprof/` requests func (km *KataMonitor) PprofIndex(w http.ResponseWriter, r *http.Request) { - km.proxyRequest(w, r) + if len(strings.TrimPrefix(r.URL.Path, "/debug/pprof/")) == 0 { + km.proxyRequest(w, r, copyResponseAddingSandboxIdToHref) + } else { + km.proxyRequest(w, r, nil) + } } // PprofCmdline handles other `/debug/cmdline` requests func (km *KataMonitor) PprofCmdline(w http.ResponseWriter, r *http.Request) { - km.proxyRequest(w, r) + km.proxyRequest(w, r, nil) } // PprofProfile handles other `/debug/profile` requests func (km *KataMonitor) PprofProfile(w http.ResponseWriter, r *http.Request) { - km.proxyRequest(w, r) + km.proxyRequest(w, r, nil) } // PprofSymbol handles other `/debug/symbol` requests func (km *KataMonitor) PprofSymbol(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") - km.proxyRequest(w, r) + km.proxyRequest(w, r, nil) } // PprofTrace handles other `/debug/trace` requests func (km *KataMonitor) PprofTrace(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Disposition", `attachment; filename="trace"`) - km.proxyRequest(w, r) + km.proxyRequest(w, r, nil) +} + +func copyResponse(req *http.Request, w io.Writer, r io.Reader) error { + _, err := io.Copy(w, r) + return err +} + +func copyResponseAddingSandboxIdToHref(req *http.Request, w io.Writer, r io.Reader) error { + sb, err := getSandboxIDFromReq(req) + if err != nil { + monitorLog.WithError(err).Warning("missing sandbox query in pprof url") + return copyResponse(req, w, r) + } + buf, err := io.ReadAll(r) + if err != nil { + return err + } + + re := regexp.MustCompile(``) + outHtml := re.ReplaceAllString(string(buf), fmt.Sprintf("", sb)) + w.Write([]byte(outHtml)) + return nil } diff --git a/src/runtime/pkg/kata-monitor/pprof_test.go b/src/runtime/pkg/kata-monitor/pprof_test.go new file mode 100644 index 0000000000..e02dc00b12 --- /dev/null +++ b/src/runtime/pkg/kata-monitor/pprof_test.go @@ -0,0 +1,117 @@ +// Copyright (c) 2022 Red Hat Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katamonitor + +import ( + "bytes" + "net/http" + "net/url" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCopyResponseAddingSandboxIdToHref(t *testing.T) { + assert := assert.New(t) + + htmlIn := strings.NewReader(` + + +/debug/pprof/ + + + +/debug/pprof/
+
+Types of profiles available: + + + + + + + + + + + +
CountProfile
27allocs
0block
0cmdline
39goroutine
27heap
0mutex
0profile
10threadcreate
0trace
+
full goroutine stack dump +
+

+Profile Descriptions: +

+

+ +`) + + htmlExpected := bytes.NewBufferString(` + + +/debug/pprof/ + + + +/debug/pprof/
+
+Types of profiles available: + + + + + + + + + + + +
CountProfile
27allocs
0block
0cmdline
39goroutine
27heap
0mutex
0profile
10threadcreate
0trace
+full goroutine stack dump +
+

+Profile Descriptions: +

+

+ +`) + + req := &http.Request{URL: &url.URL{RawQuery: "sandbox=1234567890"}} + buf := bytes.NewBuffer(nil) + copyResponseAddingSandboxIdToHref(req, buf, htmlIn) + assert.Equal(htmlExpected, buf) +}