You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

132 lines
3.8 KiB

  1. // Copyright 2018 Google Inc. All Rights Reserved.
  2. //
  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. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Package transport provides a mechanism to send requests with https cert,
  15. // key, and CA.
  16. package transport
  17. import (
  18. "crypto/tls"
  19. "crypto/x509"
  20. "fmt"
  21. "io/ioutil"
  22. "net/http"
  23. "sync"
  24. "github.com/google/pprof/internal/plugin"
  25. )
  26. type transport struct {
  27. cert *string
  28. key *string
  29. ca *string
  30. caCertPool *x509.CertPool
  31. certs []tls.Certificate
  32. initOnce sync.Once
  33. initErr error
  34. }
  35. const extraUsage = ` -tls_cert TLS client certificate file for fetching profile and symbols
  36. -tls_key TLS private key file for fetching profile and symbols
  37. -tls_ca TLS CA certs file for fetching profile and symbols`
  38. // New returns a round tripper for making requests with the
  39. // specified cert, key, and ca. The flags tls_cert, tls_key, and tls_ca are
  40. // added to the flagset to allow a user to specify the cert, key, and ca. If
  41. // the flagset is nil, no flags will be added, and users will not be able to
  42. // use these flags.
  43. func New(flagset plugin.FlagSet) http.RoundTripper {
  44. if flagset == nil {
  45. return &transport{}
  46. }
  47. flagset.AddExtraUsage(extraUsage)
  48. return &transport{
  49. cert: flagset.String("tls_cert", "", "TLS client certificate file for fetching profile and symbols"),
  50. key: flagset.String("tls_key", "", "TLS private key file for fetching profile and symbols"),
  51. ca: flagset.String("tls_ca", "", "TLS CA certs file for fetching profile and symbols"),
  52. }
  53. }
  54. // initialize uses the cert, key, and ca to initialize the certs
  55. // to use these when making requests.
  56. func (tr *transport) initialize() error {
  57. var cert, key, ca string
  58. if tr.cert != nil {
  59. cert = *tr.cert
  60. }
  61. if tr.key != nil {
  62. key = *tr.key
  63. }
  64. if tr.ca != nil {
  65. ca = *tr.ca
  66. }
  67. if cert != "" && key != "" {
  68. tlsCert, err := tls.LoadX509KeyPair(cert, key)
  69. if err != nil {
  70. return fmt.Errorf("could not load certificate/key pair specified by -tls_cert and -tls_key: %v", err)
  71. }
  72. tr.certs = []tls.Certificate{tlsCert}
  73. } else if cert == "" && key != "" {
  74. return fmt.Errorf("-tls_key is specified, so -tls_cert must also be specified")
  75. } else if cert != "" && key == "" {
  76. return fmt.Errorf("-tls_cert is specified, so -tls_key must also be specified")
  77. }
  78. if ca != "" {
  79. caCertPool := x509.NewCertPool()
  80. caCert, err := ioutil.ReadFile(ca)
  81. if err != nil {
  82. return fmt.Errorf("could not load CA specified by -tls_ca: %v", err)
  83. }
  84. caCertPool.AppendCertsFromPEM(caCert)
  85. tr.caCertPool = caCertPool
  86. }
  87. return nil
  88. }
  89. // RoundTrip executes a single HTTP transaction, returning
  90. // a Response for the provided Request.
  91. func (tr *transport) RoundTrip(req *http.Request) (*http.Response, error) {
  92. tr.initOnce.Do(func() {
  93. tr.initErr = tr.initialize()
  94. })
  95. if tr.initErr != nil {
  96. return nil, tr.initErr
  97. }
  98. tlsConfig := &tls.Config{
  99. RootCAs: tr.caCertPool,
  100. Certificates: tr.certs,
  101. }
  102. if req.URL.Scheme == "https+insecure" {
  103. // Make shallow copy of request, and req.URL, so the request's URL can be
  104. // modified.
  105. r := *req
  106. *r.URL = *req.URL
  107. req = &r
  108. tlsConfig.InsecureSkipVerify = true
  109. req.URL.Scheme = "https"
  110. }
  111. transport := http.Transport{
  112. Proxy: http.ProxyFromEnvironment,
  113. TLSClientConfig: tlsConfig,
  114. }
  115. return transport.RoundTrip(req)
  116. }