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.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  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. certs: make(map[string]*model.FoundCertificate),
  22. }
  23. }
  24. // AddDirectory adds a new directory to monitor
  25. func (c *CertificateMonitor) AddDirectory(directory string) {
  26. c.logger.Infof("Adding certificate directory %s", directory)
  27. c.directories = append(c.directories, directory)
  28. go c.scanForFolders(directory)
  29. }
  30. func (c *CertificateMonitor) scanForFolders(dir string) {
  31. c.logger.Debugf("Scanning folder %s for certificates", dir)
  32. dirs, err := ioutil.ReadDir(dir)
  33. if err != nil {
  34. c.logger.Errorf("Unable to read directory %s - %s", dir, err.Error())
  35. return
  36. }
  37. for _, d := range dirs {
  38. if d.IsDir() {
  39. c.scanForCerts(d.Name(), path.Join(dir, d.Name()))
  40. }
  41. }
  42. }
  43. func (c *CertificateMonitor) scanForCerts(vhost string, dir string) {
  44. c.logger.Debugf("Scanning folder %s for certificates", dir)
  45. files, err := ioutil.ReadDir(dir)
  46. if err != nil {
  47. c.logger.Errorf("Unable to read directory %s - %s", dir, err.Error())
  48. return
  49. }
  50. cert := model.FoundCertificate{
  51. Hostname: vhost,
  52. }
  53. for _, f := range files {
  54. ext := path.Ext(f.Name())
  55. base := path.Base(f.Name())
  56. base = base[:len(base)-len(ext)]
  57. if ext == ".pem" {
  58. prefix := strings.Split(base, "-")[0]
  59. added := maybeAddPart(&cert, prefix, path.Join(dir, f.Name()))
  60. if added && f.ModTime().After(cert.ModTime) {
  61. cert.ModTime = f.ModTime()
  62. }
  63. }
  64. }
  65. c.maybeAddCert(vhost, cert)
  66. }
  67. func maybeAddPart(cert *model.FoundCertificate, part string, path string) bool {
  68. switch part {
  69. case "cert":
  70. cert.Cert = path
  71. case "chain":
  72. cert.Chain = path
  73. case "fullchain":
  74. cert.FullChain = path
  75. case "privkey":
  76. cert.PrivateKey = path
  77. default:
  78. return false
  79. }
  80. return true
  81. }
  82. func (c *CertificateMonitor) maybeAddCert(vhost string, cert model.FoundCertificate) {
  83. if len(cert.Cert) > 0 && len(cert.Chain) > 0 && len(cert.FullChain) > 0 && len(cert.PrivateKey) > 0 {
  84. if existing, ok := c.certs[vhost]; ok {
  85. if cert.ModTime.After(existing.ModTime) {
  86. c.logger.Debugf("Found newer certificate files for %s in %s", vhost, path.Dir(cert.Cert))
  87. c.certs[vhost] = &cert
  88. c.channel <- cert
  89. }
  90. } else {
  91. c.logger.Debugf("Found new certificate files for %s in %s", vhost, path.Dir(cert.Cert))
  92. c.certs[vhost] = &cert
  93. c.channel <- cert
  94. }
  95. }
  96. }