origin_cert.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. package credentials
  2. import (
  3. "encoding/json"
  4. "encoding/pem"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "github.com/mitchellh/go-homedir"
  9. "github.com/rs/zerolog"
  10. "github.com/cloudflare/cloudflared/config"
  11. )
  12. const (
  13. DefaultCredentialFile = "cert.pem"
  14. OriginCertFlag = "origincert"
  15. )
  16. type namedTunnelToken struct {
  17. ZoneID string `json:"zoneID"`
  18. AccountID string `json:"accountID"`
  19. APIToken string `json:"apiToken"`
  20. }
  21. type OriginCert struct {
  22. ZoneID string
  23. APIToken string
  24. AccountID string
  25. }
  26. // FindDefaultOriginCertPath returns the first path that contains a cert.pem file. If none of the
  27. // DefaultConfigSearchDirectories contains a cert.pem file, return empty string
  28. func FindDefaultOriginCertPath() string {
  29. for _, defaultConfigDir := range config.DefaultConfigSearchDirectories() {
  30. originCertPath, _ := homedir.Expand(filepath.Join(defaultConfigDir, DefaultCredentialFile))
  31. if ok := fileExists(originCertPath); ok {
  32. return originCertPath
  33. }
  34. }
  35. return ""
  36. }
  37. func decodeOriginCert(blocks []byte) (*OriginCert, error) {
  38. if len(blocks) == 0 {
  39. return nil, fmt.Errorf("Cannot decode empty certificate")
  40. }
  41. originCert := OriginCert{}
  42. block, rest := pem.Decode(blocks)
  43. for {
  44. if block == nil {
  45. break
  46. }
  47. switch block.Type {
  48. case "PRIVATE KEY", "CERTIFICATE":
  49. // this is for legacy purposes.
  50. break
  51. case "ARGO TUNNEL TOKEN":
  52. if originCert.ZoneID != "" || originCert.APIToken != "" {
  53. return nil, fmt.Errorf("Found multiple tokens in the certificate")
  54. }
  55. // The token is a string,
  56. // Try the newer JSON format
  57. ntt := namedTunnelToken{}
  58. if err := json.Unmarshal(block.Bytes, &ntt); err == nil {
  59. originCert.ZoneID = ntt.ZoneID
  60. originCert.APIToken = ntt.APIToken
  61. originCert.AccountID = ntt.AccountID
  62. }
  63. default:
  64. return nil, fmt.Errorf("Unknown block %s in the certificate", block.Type)
  65. }
  66. block, rest = pem.Decode(rest)
  67. }
  68. if originCert.ZoneID == "" || originCert.APIToken == "" {
  69. return nil, fmt.Errorf("Missing token in the certificate")
  70. }
  71. return &originCert, nil
  72. }
  73. func readOriginCert(originCertPath string) ([]byte, error) {
  74. originCert, err := os.ReadFile(originCertPath)
  75. if err != nil {
  76. return nil, fmt.Errorf("cannot read %s to load origin certificate", originCertPath)
  77. }
  78. return originCert, nil
  79. }
  80. // FindOriginCert will check to make sure that the certificate exists at the specified file path.
  81. func FindOriginCert(originCertPath string, log *zerolog.Logger) (string, error) {
  82. if originCertPath == "" {
  83. log.Error().Msgf("Cannot determine default origin certificate path. No file %s in %v. You need to specify the origin certificate path by specifying the origincert option in the configuration file, or set TUNNEL_ORIGIN_CERT environment variable", DefaultCredentialFile, config.DefaultConfigSearchDirectories())
  84. return "", fmt.Errorf("client didn't specify origincert path")
  85. }
  86. var err error
  87. originCertPath, err = homedir.Expand(originCertPath)
  88. if err != nil {
  89. log.Err(err).Msgf("Cannot resolve origin certificate path")
  90. return "", fmt.Errorf("cannot resolve path %s", originCertPath)
  91. }
  92. // Check that the user has acquired a certificate using the login command
  93. ok := fileExists(originCertPath)
  94. if !ok {
  95. log.Error().Msgf(`Cannot find a valid certificate for your origin at the path:
  96. %s
  97. If the path above is wrong, specify the path with the -origincert option.
  98. If you don't have a certificate signed by Cloudflare, run the command:
  99. cloudflared login
  100. `, originCertPath)
  101. return "", fmt.Errorf("cannot find a valid certificate at the path %s", originCertPath)
  102. }
  103. return originCertPath, nil
  104. }
  105. // FileExists checks to see if a file exist at the provided path.
  106. func fileExists(path string) bool {
  107. fileStat, err := os.Stat(path)
  108. if err != nil {
  109. return false
  110. }
  111. return !fileStat.IsDir()
  112. }