Browse Source

Support for wildcard domains

master
Chris Smith 5 years ago
parent
commit
232c3ec4f4
3 changed files with 58 additions and 3 deletions
  1. 4
    0
      README.adoc
  2. 15
    0
      config.go
  3. 39
    3
      dotege.go

+ 4
- 0
README.adoc View File

@@ -52,6 +52,10 @@ Location to write the templated configuration file to. Defaults to `/data/output
52 52
 Path to a template to use to generate configuration. Defaults to `./templates/haproxy.cfg.tpl`,
53 53
 which is a bundled basic template for generating HAProxy configurations.
54 54
 
55
+`DOTEGE_WILDCARD_DOMAINS`::
56
+A space or comma separated list of domains that should use wildcard certificates.
57
+Defaults to an empty list.
58
+
55 59
 === Docker labels
56 60
 
57 61
 Dotege operates by parsing labels applied to docker containers. It understands the following:

+ 15
- 0
config.go View File

@@ -5,6 +5,7 @@ import (
5 5
 	"github.com/xenolf/lego/certcrypto"
6 6
 	"github.com/xenolf/lego/lego"
7 7
 	"os"
8
+	"strings"
8 9
 )
9 10
 
10 11
 const (
@@ -25,6 +26,8 @@ const (
25 26
 	envTemplateDestinationDefault = "/data/output/haproxy.cfg"
26 27
 	envTemplateSourceKey          = "DOTEGE_TEMPLATE_SOURCE"
27 28
 	envTemplateSourceDefault      = "./templates/haproxy.cfg.tpl"
29
+	envWildcardDomainsKey         = "DOTEGE_WILDCARD_DOMAINS"
30
+	envWildcardDomainsDefault     = ""
28 31
 )
29 32
 
30 33
 // Config is the user-definable configuration for Dotege.
@@ -34,6 +37,7 @@ type Config struct {
34 37
 	Labels                 LabelConfig
35 38
 	DefaultCertDestination string
36 39
 	Acme                   AcmeConfig
40
+	WildCardDomains        []string
37 41
 }
38 42
 
39 43
 // TemplateConfig configures a single template for the generator.
@@ -114,5 +118,16 @@ func createConfig() *Config {
114 118
 		},
115 119
 		Signals:                createSignalConfig(),
116 120
 		DefaultCertDestination: optionalVar(envCertDestinationKey, envCertDestinationDefault),
121
+		WildCardDomains:        splitList(optionalVar(envWildcardDomainsKey, envWildcardDomainsDefault)),
117 122
 	}
118 123
 }
124
+
125
+func splitList(input string) (result []string) {
126
+	result = []string{}
127
+	for _, part := range strings.Split(strings.ReplaceAll(input, " ", ","), ",") {
128
+		if len(part) > 0 {
129
+			result = append(result, part)
130
+		}
131
+	}
132
+	return
133
+}

+ 39
- 3
dotege.go View File

@@ -175,17 +175,52 @@ func signalContainer() {
175 175
 
176 176
 func getHostnamesForContainer(container *Container) []string {
177 177
 	if label, ok := container.Labels[config.Labels.Hostnames]; ok {
178
-		return strings.Split(strings.Replace(label, ",", " ", -1), " ")
178
+		return applyWildcards(splitList(label), config.WildCardDomains)
179 179
 	} else {
180 180
 		return []string{}
181 181
 	}
182 182
 }
183 183
 
184
+func applyWildcards(domains []string, wildcards []string) (result []string) {
185
+	result = []string{}
186
+	required := make(map[string]bool)
187
+	for _, domain := range domains {
188
+		found := false
189
+		for _, wildcard := range wildcards {
190
+			if wildcardMatches(wildcard, domain) {
191
+				if !required["*."+wildcard] {
192
+					result = append(result, "*."+wildcard)
193
+					required["*."+wildcard] = true
194
+				}
195
+				found = true
196
+				break
197
+			}
198
+		}
199
+
200
+		if !found && !required[domain] {
201
+			result = append(result, domain)
202
+			required[domain] = true
203
+		}
204
+	}
205
+	return
206
+}
207
+
208
+func wildcardMatches(wildcard, domain string) bool {
209
+	if len(domain) <= len(wildcard) {
210
+		return false
211
+	}
212
+
213
+	pivot := len(domain) - len(wildcard) - 1
214
+	start := domain[:pivot]
215
+	end := domain[pivot+1:]
216
+	return domain[pivot] == '.' && end == wildcard && !strings.ContainsRune(start, '.')
217
+}
218
+
184 219
 func getHostnames(containers map[string]*Container) (hostnames map[string]*Hostname) {
185 220
 	hostnames = make(map[string]*Hostname)
186 221
 	for _, container := range containers {
187 222
 		if label, ok := container.Labels[config.Labels.Hostnames]; ok {
188
-			names := strings.Split(strings.Replace(label, ",", " ", -1), " ")
223
+			names := splitList(label)
189 224
 			if hostname, ok := hostnames[names[0]]; ok {
190 225
 				hostname.Containers = append(hostname.Containers, container)
191 226
 			} else {
@@ -230,7 +265,8 @@ func deployCertForContainer(container *Container) bool {
230 265
 }
231 266
 
232 267
 func deployCert(certificate *SavedCertificate) bool {
233
-	target := path.Join(config.DefaultCertDestination, fmt.Sprintf("%s.pem", certificate.Domains[0]))
268
+	name := fmt.Sprintf("%s.pem", strings.ReplaceAll("*", "_", certificate.Domains[0]))
269
+	target := path.Join(config.DefaultCertDestination, name)
234 270
 	content := append(certificate.Certificate, certificate.PrivateKey...)
235 271
 
236 272
 	buf, _ := ioutil.ReadFile(target)

Loading…
Cancel
Save