storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/handlers/proxy.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2018 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package handlers 18 19 import ( 20 "net" 21 "net/http" 22 "regexp" 23 "strings" 24 ) 25 26 var ( 27 // De-facto standard header keys. 28 xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For") 29 xForwardedHost = http.CanonicalHeaderKey("X-Forwarded-Host") 30 xForwardedPort = http.CanonicalHeaderKey("X-Forwarded-Port") 31 xForwardedProto = http.CanonicalHeaderKey("X-Forwarded-Proto") 32 xForwardedScheme = http.CanonicalHeaderKey("X-Forwarded-Scheme") 33 xRealIP = http.CanonicalHeaderKey("X-Real-IP") 34 ) 35 36 var ( 37 // RFC7239 defines a new "Forwarded: " header designed to replace the 38 // existing use of X-Forwarded-* headers. 39 // e.g. Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43 40 forwarded = http.CanonicalHeaderKey("Forwarded") 41 // Allows for a sub-match of the first value after 'for=' to the next 42 // comma, semi-colon or space. The match is case-insensitive. 43 forRegex = regexp.MustCompile(`(?i)(?:for=)([^(;|,| )]+)(.*)`) 44 // Allows for a sub-match for the first instance of scheme (http|https) 45 // prefixed by 'proto='. The match is case-insensitive. 46 protoRegex = regexp.MustCompile(`(?i)^(;|,| )+(?:proto=)(https|http)`) 47 ) 48 49 // GetSourceScheme retrieves the scheme from the X-Forwarded-Proto and RFC7239 50 // Forwarded headers (in that order). 51 func GetSourceScheme(r *http.Request) string { 52 var scheme string 53 54 // Retrieve the scheme from X-Forwarded-Proto. 55 if proto := r.Header.Get(xForwardedProto); proto != "" { 56 scheme = strings.ToLower(proto) 57 } else if proto = r.Header.Get(xForwardedScheme); proto != "" { 58 scheme = strings.ToLower(proto) 59 } else if proto := r.Header.Get(forwarded); proto != "" { 60 // match should contain at least two elements if the protocol was 61 // specified in the Forwarded header. The first element will always be 62 // the 'for=', which we ignore, subsequently we proceed to look for 63 // 'proto=' which should precede right after `for=` if not 64 // we simply ignore the values and return empty. This is in line 65 // with the approach we took for returning first ip from multiple 66 // params. 67 if match := forRegex.FindStringSubmatch(proto); len(match) > 1 { 68 if match = protoRegex.FindStringSubmatch(match[2]); len(match) > 1 { 69 scheme = strings.ToLower(match[2]) 70 } 71 } 72 } 73 74 return scheme 75 } 76 77 // GetSourceIPFromHeaders retrieves the IP from the X-Forwarded-For, X-Real-IP 78 // and RFC7239 Forwarded headers (in that order) 79 func GetSourceIPFromHeaders(r *http.Request) string { 80 var addr string 81 82 if fwd := r.Header.Get(xForwardedFor); fwd != "" { 83 // Only grab the first (client) address. Note that '192.168.0.1, 84 // 10.1.1.1' is a valid key for X-Forwarded-For where addresses after 85 // the first may represent forwarding proxies earlier in the chain. 86 s := strings.Index(fwd, ", ") 87 if s == -1 { 88 s = len(fwd) 89 } 90 addr = fwd[:s] 91 } else if fwd := r.Header.Get(xRealIP); fwd != "" { 92 // X-Real-IP should only contain one IP address (the client making the 93 // request). 94 addr = fwd 95 } else if fwd := r.Header.Get(forwarded); fwd != "" { 96 // match should contain at least two elements if the protocol was 97 // specified in the Forwarded header. The first element will always be 98 // the 'for=' capture, which we ignore. In the case of multiple IP 99 // addresses (for=8.8.8.8, 8.8.4.4, 172.16.1.20 is valid) we only 100 // extract the first, which should be the client IP. 101 if match := forRegex.FindStringSubmatch(fwd); len(match) > 1 { 102 // IPv6 addresses in Forwarded headers are quoted-strings. We strip 103 // these quotes. 104 addr = strings.Trim(match[1], `"`) 105 } 106 } 107 108 return addr 109 } 110 111 // GetSourceIP retrieves the IP from the request headers 112 // and falls back to r.RemoteAddr when necessary. 113 func GetSourceIP(r *http.Request) string { 114 addr := GetSourceIPFromHeaders(r) 115 if addr != "" { 116 return addr 117 } 118 119 // Default to remote address if headers not set. 120 addr, _, _ = net.SplitHostPort(r.RemoteAddr) 121 return addr 122 }