dial.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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 proxy
  14. import (
  15. "context"
  16. "crypto/tls"
  17. "fmt"
  18. "net"
  19. "net/http"
  20. "net/url"
  21. utilnet "k8s.io/apimachinery/pkg/util/net"
  22. "k8s.io/apimachinery/third_party/forked/golang/netutil"
  23. "k8s.io/klog/v2"
  24. )
  25. // dialURL will dial the specified URL using the underlying dialer held by the passed
  26. // RoundTripper. The primary use of this method is to support proxying upgradable connections.
  27. // For this reason this method will prefer to negotiate http/1.1 if the URL scheme is https.
  28. // If you wish to ensure ALPN negotiates http2 then set NextProto=[]string{"http2"} in the
  29. // TLSConfig of the http.Transport
  30. func dialURL(ctx context.Context, url *url.URL, transport http.RoundTripper) (net.Conn, error) {
  31. dialAddr := netutil.CanonicalAddr(url)
  32. dialer, err := utilnet.DialerFor(transport)
  33. if err != nil {
  34. klog.V(5).Infof("Unable to unwrap transport %T to get dialer: %v", transport, err)
  35. }
  36. switch url.Scheme {
  37. case "http":
  38. if dialer != nil {
  39. return dialer(ctx, "tcp", dialAddr)
  40. }
  41. var d net.Dialer
  42. return d.DialContext(ctx, "tcp", dialAddr)
  43. case "https":
  44. // Get the tls config from the transport if we recognize it
  45. tlsConfig, err := utilnet.TLSClientConfig(transport)
  46. if err != nil {
  47. klog.V(5).Infof("Unable to unwrap transport %T to get at TLS config: %v", transport, err)
  48. }
  49. if dialer != nil {
  50. // We have a dialer; use it to open the connection, then
  51. // create a tls client using the connection.
  52. netConn, err := dialer(ctx, "tcp", dialAddr)
  53. if err != nil {
  54. return nil, err
  55. }
  56. if tlsConfig == nil {
  57. // tls.Client requires non-nil config
  58. klog.Warning("using custom dialer with no TLSClientConfig. Defaulting to InsecureSkipVerify")
  59. // tls.Handshake() requires ServerName or InsecureSkipVerify
  60. tlsConfig = &tls.Config{
  61. InsecureSkipVerify: true,
  62. }
  63. } else if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify {
  64. // tls.HandshakeContext() requires ServerName or InsecureSkipVerify
  65. // infer the ServerName from the hostname we're connecting to.
  66. inferredHost := dialAddr
  67. if host, _, err := net.SplitHostPort(dialAddr); err == nil {
  68. inferredHost = host
  69. }
  70. // Make a copy to avoid polluting the provided config
  71. tlsConfigCopy := tlsConfig.Clone()
  72. tlsConfigCopy.ServerName = inferredHost
  73. tlsConfig = tlsConfigCopy
  74. }
  75. // Since this method is primarily used within a "Connection: Upgrade" call we assume the caller is
  76. // going to write HTTP/1.1 request to the wire. http2 should not be allowed in the TLSConfig.NextProtos,
  77. // so we explicitly set that here. We only do this check if the TLSConfig support http/1.1.
  78. if supportsHTTP11(tlsConfig.NextProtos) {
  79. tlsConfig = tlsConfig.Clone()
  80. tlsConfig.NextProtos = []string{"http/1.1"}
  81. }
  82. tlsConn := tls.Client(netConn, tlsConfig)
  83. if err := tlsConn.HandshakeContext(ctx); err != nil {
  84. netConn.Close()
  85. return nil, err
  86. }
  87. return tlsConn, nil
  88. } else {
  89. // Dial.
  90. tlsDialer := tls.Dialer{
  91. Config: tlsConfig,
  92. }
  93. return tlsDialer.DialContext(ctx, "tcp", dialAddr)
  94. }
  95. default:
  96. return nil, fmt.Errorf("unknown scheme: %s", url.Scheme)
  97. }
  98. }
  99. func supportsHTTP11(nextProtos []string) bool {
  100. if len(nextProtos) == 0 {
  101. return true
  102. }
  103. for _, proto := range nextProtos {
  104. if proto == "http/1.1" {
  105. return true
  106. }
  107. }
  108. return false
  109. }