Browse Source

Flesh out cert handling a bit more

master
Chris Smith 5 years ago
parent
commit
0bbfd5f075
3 changed files with 66 additions and 35 deletions
  1. 21
    13
      certs/certificate_manager.go
  2. 23
    17
      dotege.go
  3. 22
    5
      model/model.go

+ 21
- 13
certs/certificate_manager.go View File

@@ -12,6 +12,7 @@ import (
12 12
 type CertificateManager struct {
13 13
 	logger      *zap.SugaredLogger
14 14
 	directories []string
15
+	certs       map[string]*foundCertificate
15 16
 }
16 17
 
17 18
 type foundCertificate struct {
@@ -29,6 +30,7 @@ func NewCertificateManager(logger *zap.SugaredLogger) *CertificateManager {
29 30
 	}
30 31
 }
31 32
 
33
+// AddDirectory adds a new directory to monitor
32 34
 func (c *CertificateManager) AddDirectory(directory string) {
33 35
 	c.directories = append(c.directories, directory)
34 36
 	go c.scanForFolders(directory)
@@ -63,29 +65,35 @@ func (c *CertificateManager) scanForCerts(vhost string, dir string) {
63 65
 			switch parts := strings.Split(base, "-"); parts[0] {
64 66
 			case "cert":
65 67
 				cert.cert = path.Join(dir, f.Name())
66
-				if f.ModTime().After(cert.modTime) {
67
-					cert.modTime = f.ModTime()
68
-				}
69 68
 			case "chain":
70 69
 				cert.chain = path.Join(dir, f.Name())
71
-				if f.ModTime().After(cert.modTime) {
72
-					cert.modTime = f.ModTime()
73
-				}
74 70
 			case "fullchain":
75 71
 				cert.fullChain = path.Join(dir, f.Name())
76
-				if f.ModTime().After(cert.modTime) {
77
-					cert.modTime = f.ModTime()
78
-				}
79 72
 			case "privkey":
80 73
 				cert.privateKey = path.Join(dir, f.Name())
81
-				if f.ModTime().After(cert.modTime) {
82
-					cert.modTime = f.ModTime()
83
-				}
74
+			default:
75
+				continue
76
+			}
77
+
78
+			if f.ModTime().After(cert.modTime) {
79
+				cert.modTime = f.ModTime()
84 80
 			}
85 81
 		}
86 82
 	}
87 83
 
84
+	c.maybeAddCert(vhost, cert)
85
+}
86
+
87
+func (c *CertificateManager) maybeAddCert(vhost string, cert foundCertificate) {
88 88
 	if len(cert.cert) > 0 && len(cert.chain) > 0 && len(cert.fullChain) > 0 && len(cert.privateKey) > 0 {
89
-		c.logger.Debugf("Found certificate files for %s in %s", vhost, dir)
89
+		if existing, ok := c.certs[vhost]; ok {
90
+			if cert.modTime.After(existing.modTime) {
91
+				c.logger.Debugf("Found newer certificate files for %s in %s", vhost, path.Dir(cert.cert))
92
+				c.certs[vhost] = &cert
93
+			}
94
+		} else {
95
+			c.logger.Debugf("Found new certificate files for %s in %s", vhost, path.Dir(cert.cert))
96
+			c.certs[vhost] = &cert
97
+		}
90 98
 	}
91 99
 }

+ 23
- 17
dotege.go View File

@@ -31,21 +31,25 @@ func monitorSignals() <-chan bool {
31 31
 }
32 32
 
33 33
 func main() {
34
-	config := zap.NewDevelopmentConfig()
35
-	config.DisableCaller = true
36
-	config.DisableStacktrace = true
37
-	config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
38
-	config.OutputPaths = []string{"stdout"}
39
-	config.ErrorOutputPaths = []string{"stdout"}
40
-	logger, _ := config.Build()
34
+	zapConfig := zap.NewDevelopmentConfig()
35
+	zapConfig.DisableCaller = true
36
+	zapConfig.DisableStacktrace = true
37
+	zapConfig.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
38
+	zapConfig.OutputPaths = []string{"stdout"}
39
+	zapConfig.ErrorOutputPaths = []string{"stdout"}
40
+	logger, _ := zapConfig.Build()
41 41
 	sugar := logger.Sugar()
42 42
 	sugar.Info("Dotege is starting")
43 43
 
44 44
 	done := monitorSignals()
45 45
 	containerChan := make(chan model.Container, 1)
46 46
 	expiryChan := make(chan string, 1)
47
-	labelConfig := model.LabelConfig{
48
-		Hostnames: "com.chameth.vhost",
47
+	config := model.Config{
48
+		Labels: model.LabelConfig{
49
+			Hostnames: "com.chameth.vhost",
50
+		},
51
+		DefaultCertActions:     model.COMBINE | model.FLATTEN,
52
+		DefaultCertDestination: "/data/certs/",
49 53
 	}
50 54
 
51 55
 	cli, err := client.NewEnvClient()
@@ -54,12 +58,12 @@ func main() {
54 58
 	}
55 59
 
56 60
 	certMonitor := certs.NewCertificateManager(sugar)
57
-	certMonitor.AddDirectory("/certs/certs")
61
+	certMonitor.AddDirectory("/data/certrequests/certs")
58 62
 
59 63
 	templateGenerator := NewTemplateGenerator(sugar)
60 64
 	templateGenerator.AddTemplate(model.TemplateConfig{
61 65
 		Source:      "./templates/domains.txt.tpl",
62
-		Destination: "domains.txt",
66
+		Destination: "/data/certrequests/domains.txt",
63 67
 	})
64 68
 	templateGenerator.AddTemplate(model.TemplateConfig{
65 69
 		Source:      "./templates/haproxy.cfg.tpl",
@@ -85,7 +89,7 @@ func main() {
85 89
 			case <-timer.C:
86 90
 				templateGenerator.Generate(Context{
87 91
 					Containers: containers,
88
-					Hostnames:  getHostnames(containers, labelConfig),
92
+					Hostnames:  getHostnames(containers, config),
89 93
 				})
90 94
 			}
91 95
 		}
@@ -99,18 +103,20 @@ func main() {
99 103
 	}
100 104
 }
101 105
 
102
-func getHostnames(containers map[string]model.Container, config model.LabelConfig) (hostnames map[string]*model.Hostname) {
106
+func getHostnames(containers map[string]model.Container, config model.Config) (hostnames map[string]*model.Hostname) {
103 107
 	hostnames = make(map[string]*model.Hostname)
104 108
 	for _, container := range containers {
105
-		if label, ok := container.Labels[config.Hostnames]; ok {
109
+		if label, ok := container.Labels[config.Labels.Hostnames]; ok {
106 110
 			names := strings.Split(strings.Replace(label, ",", " ", -1), " ")
107 111
 			if hostname, ok := hostnames[names[0]]; ok {
108 112
 				hostname.Containers = append(hostname.Containers, container)
109 113
 			} else {
110 114
 				hostnames[names[0]] = &model.Hostname{
111
-					Name:         names[0],
112
-					Alternatives: make(map[string]bool),
113
-					Containers:   []model.Container{container},
115
+					Name:            names[0],
116
+					Alternatives:    make(map[string]bool),
117
+					Containers:      []model.Container{container},
118
+					CertActions:     config.DefaultCertActions,
119
+					CertDestination: config.DefaultCertDestination,
114 120
 				}
115 121
 			}
116 122
 			addAlternatives(hostnames[names[0]], names[1:])

+ 22
- 5
model/model.go View File

@@ -1,5 +1,18 @@
1 1
 package model
2 2
 
3
+// CertActions define what will be done with a certificate
4
+type CertActions uint8
5
+
6
+// constants defining CertActions
7
+const (
8
+	// COMBINE the full chain and private key into one file
9
+	COMBINE CertActions = 1 << iota
10
+	// FLATTEN the directory structure so all files are in one dir
11
+	FLATTEN
12
+	// CHMOD the files so they are world readable (potentially dangerous!)
13
+	CHMOD
14
+)
15
+
3 16
 // Container models a docker container that is running on the system.
4 17
 type Container struct {
5 18
 	Id     string
@@ -14,15 +27,19 @@ type LabelConfig struct {
14 27
 
15 28
 // Hostname describes a DNS name used for proxying, retrieving certificates, etc.
16 29
 type Hostname struct {
17
-	Name         string
18
-	Alternatives map[string]bool
19
-	Containers   []Container
30
+	Name            string
31
+	Alternatives    map[string]bool
32
+	Containers      []Container
33
+	CertActions     CertActions
34
+	CertDestination string
20 35
 }
21 36
 
22 37
 // Config is the user-definable configuration for Dotege.
23 38
 type Config struct {
24
-	Templates []TemplateConfig
25
-	Labels    LabelConfig
39
+	Templates              []TemplateConfig
40
+	Labels                 LabelConfig
41
+	DefaultCertActions     CertActions
42
+	DefaultCertDestination string
26 43
 }
27 44
 
28 45
 // TemplateConfig configures a single template for the generator.

Loading…
Cancel
Save