cert.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package main
  4. import (
  5. "crypto/tls"
  6. "crypto/x509"
  7. "errors"
  8. "fmt"
  9. "net/http"
  10. "path/filepath"
  11. "regexp"
  12. "golang.org/x/crypto/acme/autocert"
  13. )
  14. var unsafeHostnameCharacters = regexp.MustCompile(`[^a-zA-Z0-9-\.]`)
  15. type certProvider interface {
  16. // TLSConfig creates a new TLS config suitable for net/http.Server servers.
  17. //
  18. // The returned Config must have a GetCertificate function set and that
  19. // function must return a unique *tls.Certificate for each call. The
  20. // returned *tls.Certificate will be mutated by the caller to append to the
  21. // (*tls.Certificate).Certificate field.
  22. TLSConfig() *tls.Config
  23. // HTTPHandler handle ACME related request, if any.
  24. HTTPHandler(fallback http.Handler) http.Handler
  25. }
  26. func certProviderByCertMode(mode, dir, hostname string) (certProvider, error) {
  27. if dir == "" {
  28. return nil, errors.New("missing required --certdir flag")
  29. }
  30. switch mode {
  31. case "letsencrypt":
  32. certManager := &autocert.Manager{
  33. Prompt: autocert.AcceptTOS,
  34. HostPolicy: autocert.HostWhitelist(hostname),
  35. Cache: autocert.DirCache(dir),
  36. }
  37. if hostname == "derp.tailscale.com" {
  38. certManager.HostPolicy = prodAutocertHostPolicy
  39. certManager.Email = "[email protected]"
  40. }
  41. return certManager, nil
  42. case "manual":
  43. return NewManualCertManager(dir, hostname)
  44. default:
  45. return nil, fmt.Errorf("unsupport cert mode: %q", mode)
  46. }
  47. }
  48. type manualCertManager struct {
  49. cert *tls.Certificate
  50. hostname string
  51. }
  52. // NewManualCertManager returns a cert provider which read certificate by given hostname on create.
  53. func NewManualCertManager(certdir, hostname string) (certProvider, error) {
  54. keyname := unsafeHostnameCharacters.ReplaceAllString(hostname, "")
  55. crtPath := filepath.Join(certdir, keyname+".crt")
  56. keyPath := filepath.Join(certdir, keyname+".key")
  57. cert, err := tls.LoadX509KeyPair(crtPath, keyPath)
  58. if err != nil {
  59. return nil, fmt.Errorf("can not load x509 key pair for hostname %q: %w", keyname, err)
  60. }
  61. // ensure hostname matches with the certificate
  62. x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
  63. if err != nil {
  64. return nil, fmt.Errorf("can not load cert: %w", err)
  65. }
  66. if err := x509Cert.VerifyHostname(hostname); err != nil {
  67. return nil, fmt.Errorf("cert invalid for hostname %q: %w", hostname, err)
  68. }
  69. return &manualCertManager{cert: &cert, hostname: hostname}, nil
  70. }
  71. func (m *manualCertManager) TLSConfig() *tls.Config {
  72. return &tls.Config{
  73. Certificates: nil,
  74. NextProtos: []string{
  75. "h2", "http/1.1", // enable HTTP/2
  76. },
  77. GetCertificate: m.getCertificate,
  78. }
  79. }
  80. func (m *manualCertManager) getCertificate(hi *tls.ClientHelloInfo) (*tls.Certificate, error) {
  81. if hi.ServerName != m.hostname {
  82. return nil, fmt.Errorf("cert mismatch with hostname: %q", hi.ServerName)
  83. }
  84. // Return a shallow copy of the cert so the caller can append to its
  85. // Certificate field.
  86. certCopy := new(tls.Certificate)
  87. *certCopy = *m.cert
  88. certCopy.Certificate = certCopy.Certificate[:len(certCopy.Certificate):len(certCopy.Certificate)]
  89. return certCopy, nil
  90. }
  91. func (m *manualCertManager) HTTPHandler(fallback http.Handler) http.Handler {
  92. return fallback
  93. }