round_trippers.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package transport
  14. import (
  15. "fmt"
  16. "net/http"
  17. "strings"
  18. "time"
  19. "k8s.io/klog"
  20. utilnet "k8s.io/apimachinery/pkg/util/net"
  21. )
  22. // HTTPWrappersForConfig wraps a round tripper with any relevant layered
  23. // behavior from the config. Exposed to allow more clients that need HTTP-like
  24. // behavior but then must hijack the underlying connection (like WebSocket or
  25. // HTTP2 clients). Pure HTTP clients should use the RoundTripper returned from
  26. // New.
  27. func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTripper, error) {
  28. if config.WrapTransport != nil {
  29. rt = config.WrapTransport(rt)
  30. }
  31. rt = DebugWrappers(rt)
  32. // Set authentication wrappers
  33. switch {
  34. case config.HasBasicAuth() && config.HasTokenAuth():
  35. return nil, fmt.Errorf("username/password or bearer token may be set, but not both")
  36. case config.HasTokenAuth():
  37. rt = NewBearerAuthRoundTripper(config.BearerToken, rt)
  38. case config.HasBasicAuth():
  39. rt = NewBasicAuthRoundTripper(config.Username, config.Password, rt)
  40. }
  41. if len(config.UserAgent) > 0 {
  42. rt = NewUserAgentRoundTripper(config.UserAgent, rt)
  43. }
  44. if len(config.Impersonate.UserName) > 0 ||
  45. len(config.Impersonate.Groups) > 0 ||
  46. len(config.Impersonate.Extra) > 0 {
  47. rt = NewImpersonatingRoundTripper(config.Impersonate, rt)
  48. }
  49. return rt, nil
  50. }
  51. // DebugWrappers wraps a round tripper and logs based on the current log level.
  52. func DebugWrappers(rt http.RoundTripper) http.RoundTripper {
  53. switch {
  54. case bool(klog.V(9)):
  55. rt = newDebuggingRoundTripper(rt, debugCurlCommand, debugURLTiming, debugResponseHeaders)
  56. case bool(klog.V(8)):
  57. rt = newDebuggingRoundTripper(rt, debugJustURL, debugRequestHeaders, debugResponseStatus, debugResponseHeaders)
  58. case bool(klog.V(7)):
  59. rt = newDebuggingRoundTripper(rt, debugJustURL, debugRequestHeaders, debugResponseStatus)
  60. case bool(klog.V(6)):
  61. rt = newDebuggingRoundTripper(rt, debugURLTiming)
  62. }
  63. return rt
  64. }
  65. type requestCanceler interface {
  66. CancelRequest(*http.Request)
  67. }
  68. type authProxyRoundTripper struct {
  69. username string
  70. groups []string
  71. extra map[string][]string
  72. rt http.RoundTripper
  73. }
  74. // NewAuthProxyRoundTripper provides a roundtripper which will add auth proxy fields to requests for
  75. // authentication terminating proxy cases
  76. // assuming you pull the user from the context:
  77. // username is the user.Info.GetName() of the user
  78. // groups is the user.Info.GetGroups() of the user
  79. // extra is the user.Info.GetExtra() of the user
  80. // extra can contain any additional information that the authenticator
  81. // thought was interesting, for example authorization scopes.
  82. // In order to faithfully round-trip through an impersonation flow, these keys
  83. // MUST be lowercase.
  84. func NewAuthProxyRoundTripper(username string, groups []string, extra map[string][]string, rt http.RoundTripper) http.RoundTripper {
  85. return &authProxyRoundTripper{
  86. username: username,
  87. groups: groups,
  88. extra: extra,
  89. rt: rt,
  90. }
  91. }
  92. func (rt *authProxyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
  93. req = utilnet.CloneRequest(req)
  94. SetAuthProxyHeaders(req, rt.username, rt.groups, rt.extra)
  95. return rt.rt.RoundTrip(req)
  96. }
  97. // SetAuthProxyHeaders stomps the auth proxy header fields. It mutates its argument.
  98. func SetAuthProxyHeaders(req *http.Request, username string, groups []string, extra map[string][]string) {
  99. req.Header.Del("X-Remote-User")
  100. req.Header.Del("X-Remote-Group")
  101. for key := range req.Header {
  102. if strings.HasPrefix(strings.ToLower(key), strings.ToLower("X-Remote-Extra-")) {
  103. req.Header.Del(key)
  104. }
  105. }
  106. req.Header.Set("X-Remote-User", username)
  107. for _, group := range groups {
  108. req.Header.Add("X-Remote-Group", group)
  109. }
  110. for key, values := range extra {
  111. for _, value := range values {
  112. req.Header.Add("X-Remote-Extra-"+headerKeyEscape(key), value)
  113. }
  114. }
  115. }
  116. func (rt *authProxyRoundTripper) CancelRequest(req *http.Request) {
  117. if canceler, ok := rt.rt.(requestCanceler); ok {
  118. canceler.CancelRequest(req)
  119. } else {
  120. klog.Errorf("CancelRequest not implemented by %T", rt.rt)
  121. }
  122. }
  123. func (rt *authProxyRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
  124. type userAgentRoundTripper struct {
  125. agent string
  126. rt http.RoundTripper
  127. }
  128. func NewUserAgentRoundTripper(agent string, rt http.RoundTripper) http.RoundTripper {
  129. return &userAgentRoundTripper{agent, rt}
  130. }
  131. func (rt *userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
  132. if len(req.Header.Get("User-Agent")) != 0 {
  133. return rt.rt.RoundTrip(req)
  134. }
  135. req = utilnet.CloneRequest(req)
  136. req.Header.Set("User-Agent", rt.agent)
  137. return rt.rt.RoundTrip(req)
  138. }
  139. func (rt *userAgentRoundTripper) CancelRequest(req *http.Request) {
  140. if canceler, ok := rt.rt.(requestCanceler); ok {
  141. canceler.CancelRequest(req)
  142. } else {
  143. klog.Errorf("CancelRequest not implemented by %T", rt.rt)
  144. }
  145. }
  146. func (rt *userAgentRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
  147. type basicAuthRoundTripper struct {
  148. username string
  149. password string
  150. rt http.RoundTripper
  151. }
  152. // NewBasicAuthRoundTripper will apply a BASIC auth authorization header to a
  153. // request unless it has already been set.
  154. func NewBasicAuthRoundTripper(username, password string, rt http.RoundTripper) http.RoundTripper {
  155. return &basicAuthRoundTripper{username, password, rt}
  156. }
  157. func (rt *basicAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
  158. if len(req.Header.Get("Authorization")) != 0 {
  159. return rt.rt.RoundTrip(req)
  160. }
  161. req = utilnet.CloneRequest(req)
  162. req.SetBasicAuth(rt.username, rt.password)
  163. return rt.rt.RoundTrip(req)
  164. }
  165. func (rt *basicAuthRoundTripper) CancelRequest(req *http.Request) {
  166. if canceler, ok := rt.rt.(requestCanceler); ok {
  167. canceler.CancelRequest(req)
  168. } else {
  169. klog.Errorf("CancelRequest not implemented by %T", rt.rt)
  170. }
  171. }
  172. func (rt *basicAuthRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
  173. // These correspond to the headers used in pkg/apis/authentication. We don't want the package dependency,
  174. // but you must not change the values.
  175. const (
  176. // ImpersonateUserHeader is used to impersonate a particular user during an API server request
  177. ImpersonateUserHeader = "Impersonate-User"
  178. // ImpersonateGroupHeader is used to impersonate a particular group during an API server request.
  179. // It can be repeated multiplied times for multiple groups.
  180. ImpersonateGroupHeader = "Impersonate-Group"
  181. // ImpersonateUserExtraHeaderPrefix is a prefix for a header used to impersonate an entry in the
  182. // extra map[string][]string for user.Info. The key for the `extra` map is suffix.
  183. // The same key can be repeated multiple times to have multiple elements in the slice under a single key.
  184. // For instance:
  185. // Impersonate-Extra-Foo: one
  186. // Impersonate-Extra-Foo: two
  187. // results in extra["Foo"] = []string{"one", "two"}
  188. ImpersonateUserExtraHeaderPrefix = "Impersonate-Extra-"
  189. )
  190. type impersonatingRoundTripper struct {
  191. impersonate ImpersonationConfig
  192. delegate http.RoundTripper
  193. }
  194. // NewImpersonatingRoundTripper will add an Act-As header to a request unless it has already been set.
  195. func NewImpersonatingRoundTripper(impersonate ImpersonationConfig, delegate http.RoundTripper) http.RoundTripper {
  196. return &impersonatingRoundTripper{impersonate, delegate}
  197. }
  198. func (rt *impersonatingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
  199. // use the user header as marker for the rest.
  200. if len(req.Header.Get(ImpersonateUserHeader)) != 0 {
  201. return rt.delegate.RoundTrip(req)
  202. }
  203. req = utilnet.CloneRequest(req)
  204. req.Header.Set(ImpersonateUserHeader, rt.impersonate.UserName)
  205. for _, group := range rt.impersonate.Groups {
  206. req.Header.Add(ImpersonateGroupHeader, group)
  207. }
  208. for k, vv := range rt.impersonate.Extra {
  209. for _, v := range vv {
  210. req.Header.Add(ImpersonateUserExtraHeaderPrefix+headerKeyEscape(k), v)
  211. }
  212. }
  213. return rt.delegate.RoundTrip(req)
  214. }
  215. func (rt *impersonatingRoundTripper) CancelRequest(req *http.Request) {
  216. if canceler, ok := rt.delegate.(requestCanceler); ok {
  217. canceler.CancelRequest(req)
  218. } else {
  219. klog.Errorf("CancelRequest not implemented by %T", rt.delegate)
  220. }
  221. }
  222. func (rt *impersonatingRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.delegate }
  223. type bearerAuthRoundTripper struct {
  224. bearer string
  225. rt http.RoundTripper
  226. }
  227. // NewBearerAuthRoundTripper adds the provided bearer token to a request
  228. // unless the authorization header has already been set.
  229. func NewBearerAuthRoundTripper(bearer string, rt http.RoundTripper) http.RoundTripper {
  230. return &bearerAuthRoundTripper{bearer, rt}
  231. }
  232. func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
  233. if len(req.Header.Get("Authorization")) != 0 {
  234. return rt.rt.RoundTrip(req)
  235. }
  236. req = utilnet.CloneRequest(req)
  237. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", rt.bearer))
  238. return rt.rt.RoundTrip(req)
  239. }
  240. func (rt *bearerAuthRoundTripper) CancelRequest(req *http.Request) {
  241. if canceler, ok := rt.rt.(requestCanceler); ok {
  242. canceler.CancelRequest(req)
  243. } else {
  244. klog.Errorf("CancelRequest not implemented by %T", rt.rt)
  245. }
  246. }
  247. func (rt *bearerAuthRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
  248. // requestInfo keeps track of information about a request/response combination
  249. type requestInfo struct {
  250. RequestHeaders http.Header
  251. RequestVerb string
  252. RequestURL string
  253. ResponseStatus string
  254. ResponseHeaders http.Header
  255. ResponseErr error
  256. Duration time.Duration
  257. }
  258. // newRequestInfo creates a new RequestInfo based on an http request
  259. func newRequestInfo(req *http.Request) *requestInfo {
  260. return &requestInfo{
  261. RequestURL: req.URL.String(),
  262. RequestVerb: req.Method,
  263. RequestHeaders: req.Header,
  264. }
  265. }
  266. // complete adds information about the response to the requestInfo
  267. func (r *requestInfo) complete(response *http.Response, err error) {
  268. if err != nil {
  269. r.ResponseErr = err
  270. return
  271. }
  272. r.ResponseStatus = response.Status
  273. r.ResponseHeaders = response.Header
  274. }
  275. // toCurl returns a string that can be run as a command in a terminal (minus the body)
  276. func (r *requestInfo) toCurl() string {
  277. headers := ""
  278. for key, values := range r.RequestHeaders {
  279. for _, value := range values {
  280. headers += fmt.Sprintf(` -H %q`, fmt.Sprintf("%s: %s", key, value))
  281. }
  282. }
  283. return fmt.Sprintf("curl -k -v -X%s %s '%s'", r.RequestVerb, headers, r.RequestURL)
  284. }
  285. // debuggingRoundTripper will display information about the requests passing
  286. // through it based on what is configured
  287. type debuggingRoundTripper struct {
  288. delegatedRoundTripper http.RoundTripper
  289. levels map[debugLevel]bool
  290. }
  291. type debugLevel int
  292. const (
  293. debugJustURL debugLevel = iota
  294. debugURLTiming
  295. debugCurlCommand
  296. debugRequestHeaders
  297. debugResponseStatus
  298. debugResponseHeaders
  299. )
  300. func newDebuggingRoundTripper(rt http.RoundTripper, levels ...debugLevel) *debuggingRoundTripper {
  301. drt := &debuggingRoundTripper{
  302. delegatedRoundTripper: rt,
  303. levels: make(map[debugLevel]bool, len(levels)),
  304. }
  305. for _, v := range levels {
  306. drt.levels[v] = true
  307. }
  308. return drt
  309. }
  310. func (rt *debuggingRoundTripper) CancelRequest(req *http.Request) {
  311. if canceler, ok := rt.delegatedRoundTripper.(requestCanceler); ok {
  312. canceler.CancelRequest(req)
  313. } else {
  314. klog.Errorf("CancelRequest not implemented by %T", rt.delegatedRoundTripper)
  315. }
  316. }
  317. func (rt *debuggingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
  318. reqInfo := newRequestInfo(req)
  319. if rt.levels[debugJustURL] {
  320. klog.Infof("%s %s", reqInfo.RequestVerb, reqInfo.RequestURL)
  321. }
  322. if rt.levels[debugCurlCommand] {
  323. klog.Infof("%s", reqInfo.toCurl())
  324. }
  325. if rt.levels[debugRequestHeaders] {
  326. klog.Infof("Request Headers:")
  327. for key, values := range reqInfo.RequestHeaders {
  328. for _, value := range values {
  329. klog.Infof(" %s: %s", key, value)
  330. }
  331. }
  332. }
  333. startTime := time.Now()
  334. response, err := rt.delegatedRoundTripper.RoundTrip(req)
  335. reqInfo.Duration = time.Since(startTime)
  336. reqInfo.complete(response, err)
  337. if rt.levels[debugURLTiming] {
  338. klog.Infof("%s %s %s in %d milliseconds", reqInfo.RequestVerb, reqInfo.RequestURL, reqInfo.ResponseStatus, reqInfo.Duration.Nanoseconds()/int64(time.Millisecond))
  339. }
  340. if rt.levels[debugResponseStatus] {
  341. klog.Infof("Response Status: %s in %d milliseconds", reqInfo.ResponseStatus, reqInfo.Duration.Nanoseconds()/int64(time.Millisecond))
  342. }
  343. if rt.levels[debugResponseHeaders] {
  344. klog.Infof("Response Headers:")
  345. for key, values := range reqInfo.ResponseHeaders {
  346. for _, value := range values {
  347. klog.Infof(" %s: %s", key, value)
  348. }
  349. }
  350. }
  351. return response, err
  352. }
  353. func (rt *debuggingRoundTripper) WrappedRoundTripper() http.RoundTripper {
  354. return rt.delegatedRoundTripper
  355. }
  356. func legalHeaderByte(b byte) bool {
  357. return int(b) < len(legalHeaderKeyBytes) && legalHeaderKeyBytes[b]
  358. }
  359. func shouldEscape(b byte) bool {
  360. // url.PathUnescape() returns an error if any '%' is not followed by two
  361. // hexadecimal digits, so we'll intentionally encode it.
  362. return !legalHeaderByte(b) || b == '%'
  363. }
  364. func headerKeyEscape(key string) string {
  365. buf := strings.Builder{}
  366. for i := 0; i < len(key); i++ {
  367. b := key[i]
  368. if shouldEscape(b) {
  369. // %-encode bytes that should be escaped:
  370. // https://tools.ietf.org/html/rfc3986#section-2.1
  371. fmt.Fprintf(&buf, "%%%02X", b)
  372. continue
  373. }
  374. buf.WriteByte(b)
  375. }
  376. return buf.String()
  377. }
  378. // legalHeaderKeyBytes was copied from net/http/lex.go's isTokenTable.
  379. // See https://httpwg.github.io/specs/rfc7230.html#rule.token.separators
  380. var legalHeaderKeyBytes = [127]bool{
  381. '%': true,
  382. '!': true,
  383. '#': true,
  384. '$': true,
  385. '&': true,
  386. '\'': true,
  387. '*': true,
  388. '+': true,
  389. '-': true,
  390. '.': true,
  391. '0': true,
  392. '1': true,
  393. '2': true,
  394. '3': true,
  395. '4': true,
  396. '5': true,
  397. '6': true,
  398. '7': true,
  399. '8': true,
  400. '9': true,
  401. 'A': true,
  402. 'B': true,
  403. 'C': true,
  404. 'D': true,
  405. 'E': true,
  406. 'F': true,
  407. 'G': true,
  408. 'H': true,
  409. 'I': true,
  410. 'J': true,
  411. 'K': true,
  412. 'L': true,
  413. 'M': true,
  414. 'N': true,
  415. 'O': true,
  416. 'P': true,
  417. 'Q': true,
  418. 'R': true,
  419. 'S': true,
  420. 'T': true,
  421. 'U': true,
  422. 'W': true,
  423. 'V': true,
  424. 'X': true,
  425. 'Y': true,
  426. 'Z': true,
  427. '^': true,
  428. '_': true,
  429. '`': true,
  430. 'a': true,
  431. 'b': true,
  432. 'c': true,
  433. 'd': true,
  434. 'e': true,
  435. 'f': true,
  436. 'g': true,
  437. 'h': true,
  438. 'i': true,
  439. 'j': true,
  440. 'k': true,
  441. 'l': true,
  442. 'm': true,
  443. 'n': true,
  444. 'o': true,
  445. 'p': true,
  446. 'q': true,
  447. 'r': true,
  448. 's': true,
  449. 't': true,
  450. 'u': true,
  451. 'v': true,
  452. 'w': true,
  453. 'x': true,
  454. 'y': true,
  455. 'z': true,
  456. '|': true,
  457. '~': true,
  458. }