Docker template generator
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

monitor.go 2.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. package certs
  2. import (
  3. "github.com/csmith/dotege/model"
  4. "go.uber.org/zap"
  5. "io/ioutil"
  6. "path"
  7. "strings"
  8. )
  9. // CertificateMonitor handles scanning for new/updated certificates
  10. type CertificateMonitor struct {
  11. logger *zap.SugaredLogger
  12. channel chan<- model.FoundCertificate
  13. directories []string
  14. certs map[string]*model.FoundCertificate
  15. }
  16. // NewCertificateManager creates a new CertificateMonitor.
  17. func NewCertificateManager(logger *zap.SugaredLogger, channel chan<- model.FoundCertificate) *CertificateMonitor {
  18. return &CertificateMonitor{
  19. logger: logger,
  20. channel: channel,
  21. }
  22. }
  23. // AddDirectory adds a new directory to monitor
  24. func (c *CertificateMonitor) AddDirectory(directory string) {
  25. c.directories = append(c.directories, directory)
  26. go c.scanForFolders(directory)
  27. }
  28. func (c *CertificateMonitor) scanForFolders(dir string) {
  29. c.logger.Debugf("Scanning folder %s for certificates", dir)
  30. dirs, err := ioutil.ReadDir(dir)
  31. if err != nil {
  32. c.logger.Errorf("Unable to read directory %s - %s", dir, err.Error())
  33. return
  34. }
  35. for _, d := range dirs {
  36. if d.IsDir() {
  37. c.scanForCerts(d.Name(), path.Join(dir, d.Name()))
  38. }
  39. }
  40. }
  41. func (c *CertificateMonitor) scanForCerts(vhost string, dir string) {
  42. c.logger.Debugf("Scanning folder %s for certificates for %s", dir, vhost)
  43. files, err := ioutil.ReadDir(dir)
  44. if err != nil {
  45. c.logger.Errorf("Unable to read directory %s - %s", dir, err.Error())
  46. return
  47. }
  48. cert := model.FoundCertificate{}
  49. for _, f := range files {
  50. ext := path.Ext(f.Name())
  51. base := path.Base(f.Name())
  52. if ext == "pem" {
  53. prefix := strings.Split(base, "-")[0]
  54. added := maybeAddPart(&cert, prefix, path.Join(dir, f.Name()))
  55. if added && f.ModTime().After(cert.ModTime) {
  56. cert.ModTime = f.ModTime()
  57. }
  58. }
  59. }
  60. c.maybeAddCert(vhost, cert)
  61. }
  62. func maybeAddPart(cert *model.FoundCertificate, part string, path string) bool {
  63. switch part {
  64. case "cert":
  65. cert.Cert = path
  66. case "chain":
  67. cert.Chain = path
  68. case "fullchain":
  69. cert.FullChain = path
  70. case "privkey":
  71. cert.PrivateKey = path
  72. default:
  73. return false
  74. }
  75. return true
  76. }
  77. func (c *CertificateMonitor) maybeAddCert(vhost string, cert model.FoundCertificate) {
  78. if len(cert.Cert) > 0 && len(cert.Chain) > 0 && len(cert.FullChain) > 0 && len(cert.PrivateKey) > 0 {
  79. if existing, ok := c.certs[vhost]; ok {
  80. if cert.ModTime.After(existing.ModTime) {
  81. c.logger.Debugf("Found newer certificate files for %s in %s", vhost, path.Dir(cert.Cert))
  82. c.certs[vhost] = &cert
  83. c.channel <- cert
  84. }
  85. } else {
  86. c.logger.Debugf("Found new certificate files for %s in %s", vhost, path.Dir(cert.Cert))
  87. c.certs[vhost] = &cert
  88. c.channel <- cert
  89. }
  90. }
  91. }