1
0

autorenew.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package acme
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "log"
  7. "net/http"
  8. "net/mail"
  9. "os"
  10. "path/filepath"
  11. "time"
  12. "imuslab.com/zoraxy/mod/utils"
  13. )
  14. /*
  15. autorenew.go
  16. This script handle auto renew
  17. */
  18. type AutoRenewConfig struct {
  19. Enabled bool //Automatic renew is enabled
  20. Email string //Email for acme
  21. RenewAll bool //Renew all or selective renew with the slice below
  22. FilesToRenew []string //If RenewAll is false, renew these certificate files
  23. }
  24. type AutoRenewer struct {
  25. ConfigFilePath string
  26. CertFolder string
  27. RenewerConfig *AutoRenewConfig
  28. TickerstopChan chan bool
  29. }
  30. // Create an auto renew agent, require config filepath and auto scan & renew interval (seconds)
  31. // Set renew check interval to 0 for auto (1 day)
  32. func NewAutoRenewer(config string, certFolder string, renewCheckInterval int64) (*AutoRenewer, error) {
  33. if renewCheckInterval == 0 {
  34. renewCheckInterval = 86400 //1 day
  35. }
  36. ticker := time.NewTicker(time.Duration(renewCheckInterval) * time.Second)
  37. done := make(chan bool)
  38. //Load the config file. If not found, create one
  39. if !utils.FileExists(config) {
  40. //Create one
  41. os.MkdirAll(filepath.Dir(config), 0775)
  42. newConfig := AutoRenewConfig{
  43. RenewAll: true,
  44. FilesToRenew: []string{},
  45. }
  46. js, _ := json.MarshalIndent(newConfig, "", " ")
  47. err := os.WriteFile(config, js, 0775)
  48. if err != nil {
  49. return nil, errors.New("Failed to create acme auto renewer config: " + err.Error())
  50. }
  51. }
  52. renewerConfig := AutoRenewConfig{}
  53. content, err := os.ReadFile(config)
  54. if err != nil {
  55. return nil, errors.New("Failed to open acme auto renewer config: " + err.Error())
  56. }
  57. err = json.Unmarshal(content, &renewerConfig)
  58. if err != nil {
  59. return nil, errors.New("Malformed acme config file: " + err.Error())
  60. }
  61. //Create an Auto renew object
  62. thisRenewer := AutoRenewer{
  63. ConfigFilePath: config,
  64. CertFolder: certFolder,
  65. RenewerConfig: &renewerConfig,
  66. TickerstopChan: done,
  67. }
  68. //Check and renew certificate on startup
  69. thisRenewer.CheckAndRenewCertificates()
  70. //Start the ticker to check and renew every x seconds
  71. go func() {
  72. for {
  73. select {
  74. case <-done:
  75. return
  76. case <-ticker.C:
  77. log.Println("Check and renew certificates in progress")
  78. thisRenewer.CheckAndRenewCertificates()
  79. }
  80. }
  81. }()
  82. return &thisRenewer, nil
  83. }
  84. // Handle update auto renew domains
  85. // Set opr for different mode of operations
  86. // opr = setSelected -> Enter a list of file names (or matching rules) for auto renew
  87. // opr = setAuto -> Set to use auto detect certificates and renew
  88. func (a *AutoRenewer) HandleSetAutoRenewDomains(w http.ResponseWriter, r *http.Request) {
  89. opr, err := utils.GetPara(r, "opr")
  90. if err != nil {
  91. utils.SendErrorResponse(w, "Operation not set")
  92. return
  93. }
  94. if opr == "setSelected" {
  95. files, err := utils.PostPara(r, "domains")
  96. if err != nil {
  97. utils.SendErrorResponse(w, "Domains is not defined")
  98. return
  99. }
  100. //Parse it int array of string
  101. matchingRuleFiles := []string{}
  102. err = json.Unmarshal([]byte(files), &matchingRuleFiles)
  103. if err != nil {
  104. utils.SendErrorResponse(w, err.Error())
  105. return
  106. }
  107. //Update the configs
  108. a.RenewerConfig.RenewAll = false
  109. a.RenewerConfig.FilesToRenew = matchingRuleFiles
  110. a.saveRenewConfigToFile()
  111. utils.SendOK(w)
  112. } else if opr == "setAuto" {
  113. a.RenewerConfig.RenewAll = true
  114. a.saveRenewConfigToFile()
  115. utils.SendOK(w)
  116. }
  117. }
  118. // if auto renew all is true (aka auto scan), it will return []string{"*"}
  119. func (a *AutoRenewer) HandleLoadAutoRenewDomains(w http.ResponseWriter, r *http.Request) {
  120. results := []string{}
  121. if a.RenewerConfig.RenewAll {
  122. //Auto pick which cert to renew.
  123. results = append(results, "*")
  124. } else {
  125. //Manually set the files to renew
  126. results = a.RenewerConfig.FilesToRenew
  127. }
  128. js, _ := json.Marshal(results)
  129. utils.SendJSONResponse(w, string(js))
  130. }
  131. func (a *AutoRenewer) HandleRenewNow(w http.ResponseWriter, r *http.Request) {
  132. }
  133. func (a *AutoRenewer) HandleAutoRenewEnable(w http.ResponseWriter, r *http.Request) {
  134. val, err := utils.PostPara(r, "enable")
  135. if err != nil {
  136. js, _ := json.Marshal(a.RenewerConfig.Enabled)
  137. utils.SendJSONResponse(w, string(js))
  138. } else {
  139. if val == "true" {
  140. //Check if the email is not empty
  141. if a.RenewerConfig.Email == "" {
  142. utils.SendErrorResponse(w, "Email is not set")
  143. return
  144. }
  145. a.RenewerConfig.Enabled = true
  146. a.saveRenewConfigToFile()
  147. log.Println("[AutoRenew] ACME auto renew enabled")
  148. } else {
  149. a.RenewerConfig.Enabled = false
  150. a.saveRenewConfigToFile()
  151. log.Println("[AutoRenew] ACME auto renew disabled")
  152. }
  153. }
  154. }
  155. func (a *AutoRenewer) HandleACMEEmail(w http.ResponseWriter, r *http.Request) {
  156. email, err := utils.PostPara(r, "set")
  157. if err != nil {
  158. //Return the current email to user
  159. js, _ := json.Marshal(a.RenewerConfig.Email)
  160. utils.SendJSONResponse(w, string(js))
  161. } else {
  162. //Check if the email is valid
  163. _, err := mail.ParseAddress(email)
  164. if err != nil {
  165. utils.SendErrorResponse(w, err.Error())
  166. return
  167. }
  168. //Set the new config
  169. a.RenewerConfig.Email = email
  170. a.saveRenewConfigToFile()
  171. }
  172. }
  173. // Check and renew certificates. This check all the certificates in the
  174. // certificate folder and return a list of certs that is renewed in this call
  175. // Return string array with length 0 when no cert is expired
  176. func (a *AutoRenewer) CheckAndRenewCertificates() ([]string, error) {
  177. certFolder := a.CertFolder
  178. files, err := os.ReadDir(certFolder)
  179. if err != nil {
  180. log.Println("Unable to renew certificates: " + err.Error())
  181. return []string{}, err
  182. }
  183. fmt.Println("[ACME DEBUG] Cert found: ", files)
  184. return []string{}, nil
  185. }
  186. // Write the current renewer config to file
  187. func (a *AutoRenewer) saveRenewConfigToFile() error {
  188. js, _ := json.MarshalIndent(a.RenewerConfig, "", " ")
  189. return os.WriteFile(a.ConfigFilePath, js, 0775)
  190. }