| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2019-07-06 11:41:35 +08:00
										 |  |  | 	"net" | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"reflect" | 
					
						
							| 
									
										
										
										
											2021-03-10 06:43:16 +08:00
										 |  |  | 	"regexp" | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 	"github.com/minio/madmin-go" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/handlers" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // recordRequest - records the first recLen bytes
 | 
					
						
							|  |  |  | // of a given io.Reader
 | 
					
						
							|  |  |  | type recordRequest struct { | 
					
						
							|  |  |  | 	// Data source to record
 | 
					
						
							|  |  |  | 	io.Reader | 
					
						
							|  |  |  | 	// Response body should be logged
 | 
					
						
							|  |  |  | 	logBody bool | 
					
						
							|  |  |  | 	// Internal recording buffer
 | 
					
						
							|  |  |  | 	buf bytes.Buffer | 
					
						
							| 
									
										
										
										
											2019-07-19 06:29:17 +08:00
										 |  |  | 	// request headers
 | 
					
						
							|  |  |  | 	headers http.Header | 
					
						
							|  |  |  | 	// total bytes read including header size
 | 
					
						
							|  |  |  | 	bytesRead int | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *recordRequest) Read(p []byte) (n int, err error) { | 
					
						
							|  |  |  | 	n, err = r.Reader.Read(p) | 
					
						
							| 
									
										
										
										
											2019-07-19 06:29:17 +08:00
										 |  |  | 	r.bytesRead += n | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 	if r.logBody { | 
					
						
							|  |  |  | 		r.buf.Write(p[:n]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return n, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return n, err | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-07-19 06:29:17 +08:00
										 |  |  | func (r *recordRequest) Size() int { | 
					
						
							|  |  |  | 	sz := r.bytesRead | 
					
						
							|  |  |  | 	for k, v := range r.headers { | 
					
						
							|  |  |  | 		sz += len(k) + len(v) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return sz | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Return the bytes that were recorded.
 | 
					
						
							|  |  |  | func (r *recordRequest) Data() []byte { | 
					
						
							| 
									
										
										
										
											2019-07-11 02:49:02 +08:00
										 |  |  | 	// If body logging is enabled then we return the actual body
 | 
					
						
							|  |  |  | 	if r.logBody { | 
					
						
							|  |  |  | 		return r.buf.Bytes() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// ... otherwise we return <BODY> placeholder
 | 
					
						
							| 
									
										
										
										
											2019-09-24 04:34:28 +08:00
										 |  |  | 	return logger.BodyPlaceHolder | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-10 06:43:16 +08:00
										 |  |  | var ldapPwdRegex = regexp.MustCompile("(^.*?)LDAPPassword=([^&]*?)(&(.*?))?$") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // redact LDAP password if part of string
 | 
					
						
							|  |  |  | func redactLDAPPwd(s string) string { | 
					
						
							|  |  |  | 	parts := ldapPwdRegex.FindStringSubmatch(s) | 
					
						
							|  |  |  | 	if len(parts) > 0 { | 
					
						
							|  |  |  | 		return parts[1] + "LDAPPassword=*REDACTED*" + parts[3] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return s | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 04:55:13 +08:00
										 |  |  | // getOpName sanitizes the operation name for mc
 | 
					
						
							|  |  |  | func getOpName(name string) (op string) { | 
					
						
							|  |  |  | 	op = strings.TrimPrefix(name, "github.com/minio/minio/cmd.") | 
					
						
							|  |  |  | 	op = strings.TrimSuffix(op, "Handler-fm") | 
					
						
							|  |  |  | 	op = strings.Replace(op, "objectAPIHandlers", "s3", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "adminAPIHandlers", "admin", 1) | 
					
						
							| 
									
										
										
										
											2021-04-30 10:01:43 +08:00
										 |  |  | 	op = strings.Replace(op, "(*webAPIHandlers)", "web", 1) | 
					
						
							| 
									
										
										
										
											2019-06-19 04:55:13 +08:00
										 |  |  | 	op = strings.Replace(op, "(*storageRESTServer)", "internal", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "(*peerRESTServer)", "internal", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "(*lockRESTServer)", "internal", 1) | 
					
						
							| 
									
										
										
										
											2020-02-27 04:26:47 +08:00
										 |  |  | 	op = strings.Replace(op, "(*stsAPIHandlers)", "sts", 1) | 
					
						
							| 
									
										
										
										
											2019-06-19 04:55:13 +08:00
										 |  |  | 	op = strings.Replace(op, "LivenessCheckHandler", "healthcheck", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "ReadinessCheckHandler", "healthcheck", 1) | 
					
						
							| 
									
										
										
										
											2020-02-27 04:26:47 +08:00
										 |  |  | 	op = strings.Replace(op, "-fm", "", 1) | 
					
						
							| 
									
										
										
										
											2019-06-19 04:55:13 +08:00
										 |  |  | 	return op | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | // Trace gets trace of http request
 | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | func Trace(f http.HandlerFunc, logBody bool, w http.ResponseWriter, r *http.Request) madmin.TraceInfo { | 
					
						
							| 
									
										
										
										
											2019-06-19 04:55:13 +08:00
										 |  |  | 	name := getOpName(runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()) | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-19 06:29:17 +08:00
										 |  |  | 	// Setup a http request body recorder
 | 
					
						
							| 
									
										
										
										
											2019-09-12 01:21:43 +08:00
										 |  |  | 	reqHeaders := r.Header.Clone() | 
					
						
							| 
									
										
										
										
											2019-07-19 06:29:17 +08:00
										 |  |  | 	reqHeaders.Set("Host", r.Host) | 
					
						
							| 
									
										
										
										
											2019-09-28 01:19:27 +08:00
										 |  |  | 	if len(r.TransferEncoding) == 0 { | 
					
						
							|  |  |  | 		reqHeaders.Set("Content-Length", strconv.Itoa(int(r.ContentLength))) | 
					
						
							| 
									
										
										
										
											2020-09-02 07:58:13 +08:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		reqHeaders.Set("Transfer-Encoding", strings.Join(r.TransferEncoding, ",")) | 
					
						
							| 
									
										
										
										
											2019-07-19 06:29:17 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	reqBodyRecorder := &recordRequest{Reader: r.Body, logBody: logBody, headers: reqHeaders} | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	r.Body = ioutil.NopCloser(reqBodyRecorder) | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	now := time.Now().UTC() | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 	t := madmin.TraceInfo{TraceType: madmin.TraceHTTP, FuncName: name, Time: now} | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 11:41:35 +08:00
										 |  |  | 	t.NodeName = r.Host | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if globalIsDistErasure { | 
					
						
							| 
									
										
										
										
											2021-03-27 02:37:58 +08:00
										 |  |  | 		t.NodeName = globalLocalNodeName | 
					
						
							| 
									
										
										
										
											2019-07-06 11:41:35 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-28 04:19:14 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if t.NodeName == "" { | 
					
						
							|  |  |  | 		t.NodeName = globalLocalNodeName | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// strip only standard port from the host address
 | 
					
						
							|  |  |  | 	if host, port, err := net.SplitHostPort(t.NodeName); err == nil { | 
					
						
							|  |  |  | 		if port == "443" || port == "80" { | 
					
						
							|  |  |  | 			t.NodeName = host | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 	rq := madmin.TraceRequestInfo{ | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 		Time:     now, | 
					
						
							| 
									
										
										
										
											2020-08-11 00:02:29 +08:00
										 |  |  | 		Proto:    r.Proto, | 
					
						
							| 
									
										
										
										
											2019-07-11 02:49:02 +08:00
										 |  |  | 		Method:   r.Method, | 
					
						
							| 
									
										
										
										
											2021-03-11 03:05:38 +08:00
										 |  |  | 		RawQuery: redactLDAPPwd(r.URL.RawQuery), | 
					
						
							| 
									
										
										
										
											2019-09-13 06:29:59 +08:00
										 |  |  | 		Client:   handlers.GetSourceIP(r), | 
					
						
							| 
									
										
										
										
											2019-07-11 02:49:02 +08:00
										 |  |  | 		Headers:  reqHeaders, | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-19 00:30:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-26 23:04:12 +08:00
										 |  |  | 	path := r.URL.RawPath | 
					
						
							|  |  |  | 	if path == "" { | 
					
						
							|  |  |  | 		path = r.URL.Path | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	rq.Path = path | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-19 00:30:52 +08:00
										 |  |  | 	rw := logger.NewResponseWriter(w) | 
					
						
							|  |  |  | 	rw.LogErrBody = true | 
					
						
							|  |  |  | 	rw.LogAllBody = logBody | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Execute call.
 | 
					
						
							|  |  |  | 	f(rw, r) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 	rs := madmin.TraceResponseInfo{ | 
					
						
							| 
									
										
										
										
											2019-07-11 02:49:02 +08:00
										 |  |  | 		Time:       time.Now().UTC(), | 
					
						
							| 
									
										
										
										
											2019-09-24 04:34:28 +08:00
										 |  |  | 		Headers:    rw.Header().Clone(), | 
					
						
							|  |  |  | 		StatusCode: rw.StatusCode, | 
					
						
							|  |  |  | 		Body:       rw.Body(), | 
					
						
							| 
									
										
										
										
											2019-07-11 02:49:02 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-19 00:30:52 +08:00
										 |  |  | 	// Transfer request body
 | 
					
						
							|  |  |  | 	rq.Body = reqBodyRecorder.Data() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	if rs.StatusCode == 0 { | 
					
						
							|  |  |  | 		rs.StatusCode = http.StatusOK | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-11 02:49:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	t.ReqInfo = rq | 
					
						
							|  |  |  | 	t.RespInfo = rs | 
					
						
							| 
									
										
										
										
											2019-07-19 06:29:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 	t.CallStats = madmin.TraceCallStats{ | 
					
						
							| 
									
										
										
										
											2019-09-24 04:34:28 +08:00
										 |  |  | 		Latency:         rs.Time.Sub(rw.StartTime), | 
					
						
							|  |  |  | 		InputBytes:      reqBodyRecorder.Size(), | 
					
						
							|  |  |  | 		OutputBytes:     rw.Size(), | 
					
						
							|  |  |  | 		TimeToFirstByte: rw.TimeToFirstByte, | 
					
						
							| 
									
										
										
										
											2019-08-16 07:17:46 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	return t | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | } |