|
@@ -8,8 +8,10 @@ import (
|
8
|
8
|
"github.com/xenolf/lego/lego"
|
9
|
9
|
"go.uber.org/zap"
|
10
|
10
|
"go.uber.org/zap/zapcore"
|
|
11
|
+ "io/ioutil"
|
11
|
12
|
"os"
|
12
|
13
|
"os/signal"
|
|
14
|
+ "path"
|
13
|
15
|
"strings"
|
14
|
16
|
"syscall"
|
15
|
17
|
"time"
|
|
@@ -31,6 +33,12 @@ const (
|
31
|
33
|
envTemplateSourceDefault = "./templates/haproxy.cfg.tpl"
|
32
|
34
|
)
|
33
|
35
|
|
|
36
|
+var (
|
|
37
|
+ logger *zap.SugaredLogger
|
|
38
|
+ certificateManager *CertificateManager
|
|
39
|
+ config *model.Config
|
|
40
|
+)
|
|
41
|
+
|
34
|
42
|
func requiredVar(key string) (value string) {
|
35
|
43
|
value, ok := os.LookupEnv(key)
|
36
|
44
|
if !ok {
|
|
@@ -73,8 +81,8 @@ func createLogger() *zap.SugaredLogger {
|
73
|
81
|
return logger.Sugar()
|
74
|
82
|
}
|
75
|
83
|
|
76
|
|
-func createConfig() *model.Config {
|
77
|
|
- return &model.Config{
|
|
84
|
+func createConfig() {
|
|
85
|
+ config = &model.Config{
|
78
|
86
|
Templates: []model.TemplateConfig{
|
79
|
87
|
{
|
80
|
88
|
Source: optionalVar(envTemplateSourceKey, envTemplateSourceDefault),
|
|
@@ -97,7 +105,7 @@ func createConfig() *model.Config {
|
97
|
105
|
}
|
98
|
106
|
}
|
99
|
107
|
|
100
|
|
-func createTemplateGenerator(logger *zap.SugaredLogger, templates []model.TemplateConfig) *TemplateGenerator {
|
|
108
|
+func createTemplateGenerator(templates []model.TemplateConfig) *TemplateGenerator {
|
101
|
109
|
templateGenerator := NewTemplateGenerator(logger)
|
102
|
110
|
for _, template := range templates {
|
103
|
111
|
templateGenerator.AddTemplate(template)
|
|
@@ -105,21 +113,20 @@ func createTemplateGenerator(logger *zap.SugaredLogger, templates []model.Templa
|
105
|
113
|
return templateGenerator
|
106
|
114
|
}
|
107
|
115
|
|
108
|
|
-func createCertificateManager(logger *zap.SugaredLogger, config model.AcmeConfig) *CertificateManager {
|
109
|
|
- certificateManager := NewCertificateManager(logger, config.Endpoint, config.KeyType, config.DnsProvider, config.CacheLocation)
|
|
116
|
+func createCertificateManager(config model.AcmeConfig) {
|
|
117
|
+ certificateManager = NewCertificateManager(logger, config.Endpoint, config.KeyType, config.DnsProvider, config.CacheLocation)
|
110
|
118
|
err := certificateManager.Init(config.Email)
|
111
|
119
|
if err != nil {
|
112
|
120
|
panic(err)
|
113
|
121
|
}
|
114
|
|
- return certificateManager
|
115
|
122
|
}
|
116
|
123
|
|
117
|
124
|
func main() {
|
118
|
|
- logger := createLogger()
|
|
125
|
+ logger = createLogger()
|
119
|
126
|
logger.Info("Dotege is starting")
|
120
|
127
|
|
121
|
128
|
doneChan := monitorSignals()
|
122
|
|
- config := createConfig()
|
|
129
|
+ createConfig()
|
123
|
130
|
|
124
|
131
|
dockerStopChan := make(chan struct{})
|
125
|
132
|
dockerClient, err := client.NewEnvClient()
|
|
@@ -127,18 +134,18 @@ func main() {
|
127
|
134
|
panic(err)
|
128
|
135
|
}
|
129
|
136
|
|
130
|
|
- templateGenerator := createTemplateGenerator(logger, config.Templates)
|
131
|
|
- certificateManager := createCertificateManager(logger, config.Acme)
|
|
137
|
+ templateGenerator := createTemplateGenerator(config.Templates)
|
|
138
|
+ createCertificateManager(config.Acme)
|
132
|
139
|
|
133
|
140
|
jitterTimer := time.NewTimer(time.Minute)
|
134
|
141
|
redeployTimer := time.NewTicker(time.Hour * 24)
|
135
|
|
- containers := make(map[string]model.Container)
|
|
142
|
+ containers := make(map[string]*model.Container)
|
136
|
143
|
|
137
|
144
|
go func() {
|
138
|
|
- err := monitorContainers(dockerClient, dockerStopChan, func(container model.Container) {
|
|
145
|
+ err := monitorContainers(dockerClient, dockerStopChan, func(container *model.Container) {
|
139
|
146
|
containers[container.Name] = container
|
140
|
147
|
jitterTimer.Reset(100 * time.Millisecond)
|
141
|
|
- err, _ = certificateManager.GetCertificate(getHostnamesForContainer(container, *config))
|
|
148
|
+ deployCertForContainer(container)
|
142
|
149
|
}, func(name string) {
|
143
|
150
|
delete(containers, name)
|
144
|
151
|
jitterTimer.Reset(100 * time.Millisecond)
|
|
@@ -161,7 +168,7 @@ func main() {
|
161
|
168
|
case <-redeployTimer.C:
|
162
|
169
|
logger.Info("Performing periodic certificate refresh")
|
163
|
170
|
for _, container := range containers {
|
164
|
|
- err, _ = certificateManager.GetCertificate(getHostnamesForContainer(container, *config))
|
|
171
|
+ deployCertForContainer(container)
|
165
|
172
|
}
|
166
|
173
|
}
|
167
|
174
|
}
|
|
@@ -176,7 +183,7 @@ func main() {
|
176
|
183
|
}
|
177
|
184
|
}
|
178
|
185
|
|
179
|
|
-func getHostnamesForContainer(container model.Container, config model.Config) []string {
|
|
186
|
+func getHostnamesForContainer(container *model.Container) []string {
|
180
|
187
|
if label, ok := container.Labels[config.Labels.Hostnames]; ok {
|
181
|
188
|
return strings.Split(strings.Replace(label, ",", " ", -1), " ")
|
182
|
189
|
} else {
|
|
@@ -184,7 +191,7 @@ func getHostnamesForContainer(container model.Container, config model.Config) []
|
184
|
191
|
}
|
185
|
192
|
}
|
186
|
193
|
|
187
|
|
-func getHostnames(containers map[string]model.Container, config model.Config) (hostnames map[string]*model.Hostname) {
|
|
194
|
+func getHostnames(containers map[string]*model.Container, config model.Config) (hostnames map[string]*model.Hostname) {
|
188
|
195
|
hostnames = make(map[string]*model.Hostname)
|
189
|
196
|
for _, container := range containers {
|
190
|
197
|
if label, ok := container.Labels[config.Labels.Hostnames]; ok {
|
|
@@ -195,7 +202,7 @@ func getHostnames(containers map[string]model.Container, config model.Config) (h
|
195
|
202
|
hostnames[names[0]] = &model.Hostname{
|
196
|
203
|
Name: names[0],
|
197
|
204
|
Alternatives: make(map[string]bool),
|
198
|
|
- Containers: []model.Container{container},
|
|
205
|
+ Containers: []*model.Container{container},
|
199
|
206
|
CertActions: config.DefaultCertActions,
|
200
|
207
|
CertDestination: config.DefaultCertDestination,
|
201
|
208
|
}
|
|
@@ -216,3 +223,23 @@ func addAlternatives(hostname *model.Hostname, alternatives []string) {
|
216
|
223
|
hostname.Alternatives[alternative] = true
|
217
|
224
|
}
|
218
|
225
|
}
|
|
226
|
+
|
|
227
|
+func deployCertForContainer(container *model.Container) {
|
|
228
|
+ err, cert := certificateManager.GetCertificate(getHostnamesForContainer(container))
|
|
229
|
+ if err != nil {
|
|
230
|
+ logger.Warnf("Unable to generate certificate for %s: %s", container.Name, err.Error())
|
|
231
|
+ } else {
|
|
232
|
+ deployCert(cert)
|
|
233
|
+ }
|
|
234
|
+}
|
|
235
|
+
|
|
236
|
+func deployCert(certificate *SavedCertificate) {
|
|
237
|
+ target := path.Join(config.DefaultCertDestination, fmt.Sprintf("%s.pem", certificate.Domains[0]))
|
|
238
|
+
|
|
239
|
+ err := ioutil.WriteFile(target, append(certificate.Certificate, certificate.PrivateKey...), 0700)
|
|
240
|
+ if err != nil {
|
|
241
|
+ logger.Warnf("Unable to write certificate %s - %s", target, err.Error())
|
|
242
|
+ } else {
|
|
243
|
+ logger.Infof("Updated certificate file %s", target)
|
|
244
|
+ }
|
|
245
|
+}
|