cert.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package cert
  2. import (
  3. "crypto/tls"
  4. "github.com/0xJacky/Nginx-UI/internal/cert/dns"
  5. "github.com/0xJacky/Nginx-UI/internal/logger"
  6. "github.com/0xJacky/Nginx-UI/internal/nginx"
  7. "github.com/0xJacky/Nginx-UI/query"
  8. "github.com/0xJacky/Nginx-UI/settings"
  9. "github.com/go-acme/lego/v4/certificate"
  10. "github.com/go-acme/lego/v4/challenge/http01"
  11. "github.com/go-acme/lego/v4/lego"
  12. legolog "github.com/go-acme/lego/v4/log"
  13. dnsproviders "github.com/go-acme/lego/v4/providers/dns"
  14. "github.com/pkg/errors"
  15. "log"
  16. "net/http"
  17. "os"
  18. "path/filepath"
  19. "strings"
  20. "time"
  21. )
  22. const (
  23. HTTP01 = "http01"
  24. DNS01 = "dns01"
  25. )
  26. func IssueCert(payload *ConfigPayload, logChan chan string, errChan chan error) {
  27. defer func() {
  28. if err := recover(); err != nil {
  29. logger.Error(err)
  30. }
  31. }()
  32. // initial a channelWriter to receive logs
  33. cw := NewChannelWriter()
  34. defer close(errChan)
  35. // initial a logger
  36. l := log.New(os.Stderr, "", log.LstdFlags)
  37. l.SetOutput(cw)
  38. // Hijack the (logger) of lego
  39. legolog.Logger = l
  40. domain := payload.ServerName
  41. l.Println("[INFO] [Nginx UI] Preparing lego configurations")
  42. user, err := payload.GetACMEUser()
  43. if err != nil {
  44. errChan <- errors.Wrap(err, "issue cert get acme user error")
  45. return
  46. }
  47. l.Printf("[INFO] [Nginx UI] ACME User: %s, CA Dir: %s\n", user.Email, user.CADir)
  48. // Start a goroutine to fetch and process logs from channel
  49. go func() {
  50. for msg := range cw.Ch {
  51. logChan <- string(msg)
  52. }
  53. }()
  54. config := lego.NewConfig(user)
  55. config.CADirURL = user.CADir
  56. // Skip TLS check
  57. if config.HTTPClient != nil {
  58. config.HTTPClient.Transport = &http.Transport{
  59. TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
  60. }
  61. }
  62. config.Certificate.KeyType = payload.GetKeyType()
  63. l.Println("[INFO] [Nginx UI] Creating client facilitates communication with the CA server")
  64. // A client facilitates communication with the CA server.
  65. client, err := lego.NewClient(config)
  66. if err != nil {
  67. errChan <- errors.Wrap(err, "issue cert new client error")
  68. return
  69. }
  70. switch payload.ChallengeMethod {
  71. default:
  72. fallthrough
  73. case HTTP01:
  74. l.Println("[INFO] [Nginx UI] Setting HTTP01 challenge provider")
  75. err = client.Challenge.SetHTTP01Provider(
  76. http01.NewProviderServer("",
  77. settings.ServerSettings.HTTPChallengePort,
  78. ),
  79. )
  80. case DNS01:
  81. d := query.DnsCredential
  82. dnsCredential, err := d.FirstByID(payload.DNSCredentialID)
  83. if err != nil {
  84. errChan <- errors.Wrap(err, "get dns credential error")
  85. return
  86. }
  87. l.Println("[INFO] [Nginx UI] Setting DNS01 challenge provider")
  88. code := dnsCredential.Config.Code
  89. pConfig, ok := dns.GetProvider(code)
  90. if !ok {
  91. errChan <- errors.Wrap(err, "provider not found")
  92. }
  93. l.Println("[INFO] [Nginx UI] Setting environment variables")
  94. if dnsCredential.Config.Configuration != nil {
  95. err = pConfig.SetEnv(*dnsCredential.Config.Configuration)
  96. if err != nil {
  97. break
  98. }
  99. defer func() {
  100. pConfig.CleanEnv()
  101. l.Println("[INFO] [Nginx UI] Cleaned environment variables")
  102. }()
  103. provider, err := dnsproviders.NewDNSChallengeProviderByName(code)
  104. if err != nil {
  105. break
  106. }
  107. err = client.Challenge.SetDNS01Provider(provider)
  108. } else {
  109. errChan <- errors.Wrap(err, "environment configuration is empty")
  110. return
  111. }
  112. }
  113. if err != nil {
  114. errChan <- errors.Wrap(err, "challenge error")
  115. return
  116. }
  117. request := certificate.ObtainRequest{
  118. Domains: domain,
  119. Bundle: true,
  120. }
  121. l.Println("[INFO] [Nginx UI] Obtaining certificate")
  122. certificates, err := client.Certificate.Obtain(request)
  123. if err != nil {
  124. errChan <- errors.Wrap(err, "obtain certificate error")
  125. return
  126. }
  127. name := strings.Join(domain, "_")
  128. saveDir := nginx.GetConfPath("ssl/" + name + "_" + string(payload.KeyType))
  129. if _, err = os.Stat(saveDir); os.IsNotExist(err) {
  130. err = os.MkdirAll(saveDir, 0755)
  131. if err != nil {
  132. errChan <- errors.Wrap(err, "mkdir error")
  133. return
  134. }
  135. }
  136. // Each certificate comes back with the cert bytes, the bytes of the client's
  137. // private key, and a certificate URL. SAVE THESE TO DISK.
  138. l.Println("[INFO] [Nginx UI] Writing certificate to disk")
  139. err = os.WriteFile(filepath.Join(saveDir, "fullchain.cer"),
  140. certificates.Certificate, 0644)
  141. if err != nil {
  142. errChan <- errors.Wrap(err, "write fullchain.cer error")
  143. return
  144. }
  145. l.Println("[INFO] [Nginx UI] Writing certificate private key to disk")
  146. err = os.WriteFile(filepath.Join(saveDir, "private.key"),
  147. certificates.PrivateKey, 0644)
  148. if err != nil {
  149. errChan <- errors.Wrap(err, "write private.key error")
  150. return
  151. }
  152. l.Println("[INFO] [Nginx UI] Reloading nginx")
  153. nginx.Reload()
  154. l.Println("[INFO] [Nginx UI] Finished")
  155. // Wait log to be written
  156. time.Sleep(2 * time.Second)
  157. }