certreloader.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. package tlsconfig
  2. import (
  3. "crypto/tls"
  4. "crypto/x509"
  5. "fmt"
  6. "io/ioutil"
  7. "runtime"
  8. "sync"
  9. "github.com/cloudflare/cloudflared/logger"
  10. "github.com/getsentry/raven-go"
  11. "github.com/pkg/errors"
  12. "github.com/urfave/cli/v2"
  13. )
  14. const (
  15. OriginCAPoolFlag = "origin-ca-pool"
  16. CaCertFlag = "cacert"
  17. edgeTLSServerName = "cftunnel.com"
  18. )
  19. // CertReloader can load and reload a TLS certificate from a particular filepath.
  20. // Hooks into tls.Config's GetCertificate to allow a TLS server to update its certificate without restarting.
  21. type CertReloader struct {
  22. sync.Mutex
  23. certificate *tls.Certificate
  24. certPath string
  25. keyPath string
  26. }
  27. // NewCertReloader makes a CertReloader. It loads the cert during initialization to make sure certPath and keyPath are valid
  28. func NewCertReloader(certPath, keyPath string) (*CertReloader, error) {
  29. cr := new(CertReloader)
  30. cr.certPath = certPath
  31. cr.keyPath = keyPath
  32. if err := cr.LoadCert(); err != nil {
  33. return nil, err
  34. }
  35. return cr, nil
  36. }
  37. // Cert returns the TLS certificate most recently read by the CertReloader.
  38. func (cr *CertReloader) Cert(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
  39. cr.Lock()
  40. defer cr.Unlock()
  41. return cr.certificate, nil
  42. }
  43. // LoadCert loads a TLS certificate from the CertReloader's specified filepath.
  44. // Call this after writing a new certificate to the disk (e.g. after renewing a certificate)
  45. func (cr *CertReloader) LoadCert() error {
  46. cr.Lock()
  47. defer cr.Unlock()
  48. cert, err := tls.LoadX509KeyPair(cr.certPath, cr.keyPath)
  49. // Keep the old certificate if there's a problem reading the new one.
  50. if err != nil {
  51. raven.CaptureError(fmt.Errorf("Error parsing X509 key pair: %v", err), nil)
  52. return err
  53. }
  54. cr.certificate = &cert
  55. return nil
  56. }
  57. func LoadOriginCA(c *cli.Context, logger logger.Service) (*x509.CertPool, error) {
  58. var originCustomCAPool []byte
  59. originCAPoolFilename := c.String(OriginCAPoolFlag)
  60. if originCAPoolFilename != "" {
  61. var err error
  62. originCustomCAPool, err = ioutil.ReadFile(originCAPoolFilename)
  63. if err != nil {
  64. return nil, errors.Wrap(err, fmt.Sprintf("unable to read the file %s for --%s", originCAPoolFilename, OriginCAPoolFlag))
  65. }
  66. }
  67. originCertPool, err := loadOriginCertPool(originCustomCAPool, logger)
  68. if err != nil {
  69. return nil, errors.Wrap(err, "error loading the certificate pool")
  70. }
  71. // Windows users should be notified that they can use the flag
  72. if runtime.GOOS == "windows" && originCAPoolFilename == "" {
  73. logger.Infof("cloudflared does not support loading the system root certificate pool on Windows. Please use the --%s to specify it", OriginCAPoolFlag)
  74. }
  75. return originCertPool, nil
  76. }
  77. func LoadCustomOriginCA(originCAFilename string) (*x509.CertPool, error) {
  78. // First, obtain the system certificate pool
  79. certPool, err := x509.SystemCertPool()
  80. if err != nil {
  81. certPool = x509.NewCertPool()
  82. }
  83. // Next, append the Cloudflare CAs into the system pool
  84. cfRootCA, err := GetCloudflareRootCA()
  85. if err != nil {
  86. return nil, errors.Wrap(err, "could not append Cloudflare Root CAs to cloudflared certificate pool")
  87. }
  88. for _, cert := range cfRootCA {
  89. certPool.AddCert(cert)
  90. }
  91. if originCAFilename == "" {
  92. return certPool, nil
  93. }
  94. customOriginCA, err := ioutil.ReadFile(originCAFilename)
  95. if err != nil {
  96. return nil, errors.Wrap(err, fmt.Sprintf("unable to read the file %s", originCAFilename))
  97. }
  98. if !certPool.AppendCertsFromPEM(customOriginCA) {
  99. return nil, fmt.Errorf("error appending custom CA to cert pool")
  100. }
  101. return certPool, nil
  102. }
  103. func CreateTunnelConfig(c *cli.Context) (*tls.Config, error) {
  104. var rootCAs []string
  105. if c.String(CaCertFlag) != "" {
  106. rootCAs = append(rootCAs, c.String(CaCertFlag))
  107. }
  108. userConfig := &TLSParameters{RootCAs: rootCAs, ServerName: edgeTLSServerName}
  109. tlsConfig, err := GetConfig(userConfig)
  110. if err != nil {
  111. return nil, err
  112. }
  113. if tlsConfig.RootCAs == nil {
  114. rootCAPool := x509.NewCertPool()
  115. cfRootCA, err := GetCloudflareRootCA()
  116. if err != nil {
  117. return nil, errors.Wrap(err, "could not append Cloudflare Root CAs to cloudflared certificate pool")
  118. }
  119. for _, cert := range cfRootCA {
  120. rootCAPool.AddCert(cert)
  121. }
  122. tlsConfig.RootCAs = rootCAPool
  123. }
  124. if tlsConfig.ServerName == "" && !tlsConfig.InsecureSkipVerify {
  125. return nil, fmt.Errorf("either ServerName or InsecureSkipVerify must be specified in the tls.Config")
  126. }
  127. return tlsConfig, nil
  128. }
  129. func loadOriginCertPool(originCAPoolPEM []byte, logger logger.Service) (*x509.CertPool, error) {
  130. // Get the global pool
  131. certPool, err := loadGlobalCertPool(logger)
  132. if err != nil {
  133. return nil, err
  134. }
  135. // Then, add any custom origin CA pool the user may have passed
  136. if originCAPoolPEM != nil {
  137. if !certPool.AppendCertsFromPEM(originCAPoolPEM) {
  138. logger.Info("could not append the provided origin CA to the cloudflared certificate pool")
  139. }
  140. }
  141. return certPool, nil
  142. }
  143. func loadGlobalCertPool(logger logger.Service) (*x509.CertPool, error) {
  144. // First, obtain the system certificate pool
  145. certPool, err := x509.SystemCertPool()
  146. if err != nil {
  147. if runtime.GOOS != "windows" { // See https://github.com/golang/go/issues/16736
  148. logger.Infof("error obtaining the system certificates: %s", err)
  149. }
  150. certPool = x509.NewCertPool()
  151. }
  152. // Next, append the Cloudflare CAs into the system pool
  153. cfRootCA, err := GetCloudflareRootCA()
  154. if err != nil {
  155. return nil, errors.Wrap(err, "could not append Cloudflare Root CAs to cloudflared certificate pool")
  156. }
  157. for _, cert := range cfRootCA {
  158. certPool.AddCert(cert)
  159. }
  160. // Finally, add the Hello certificate into the pool (since it's self-signed)
  161. helloCert, err := GetHelloCertificateX509()
  162. if err != nil {
  163. return nil, errors.Wrap(err, "could not append Hello server certificate to cloudflared certificate pool")
  164. }
  165. certPool.AddCert(helloCert)
  166. return certPool, nil
  167. }