Browse Source

site create; bash completion

tags/v2.0.0
Robin Thoni 8 years ago
parent
commit
ad94bfa381

+ 1
- 5
TODO View File

@@ -1,5 +1 @@
1
-letsencrypt web root
2
-bash completion
3
-site create
4
-site remove
5
-letsencrypt command arguments
1
+site remove?

+ 0
- 1
apache/letsencrypt.conf View File

@@ -1 +0,0 @@
1
-Alias "/.well-known/acme-challenge/" "/tmp/acme-challenge/"

+ 6
- 0
apache/sitegen.conf View File

@@ -0,0 +1,6 @@
1
+Alias "/.well-known/acme-challenge/" "/tmp/acme-challenge/.well-known/acme-challenge/"
2
+<Directory /tmp/acme-challenge>
3
+  Options -Indexes -FollowSymLinks
4
+  AllowOverride All
5
+  Require all granted
6
+</Directory>

+ 1
- 12
bash/sitegen.completion View File

@@ -1,14 +1,3 @@
1 1
 #!/usr/bin/env bash
2 2
 
3
-_sitegen()
4
-{
5
-  cur=${COMP_WORDS[COMP_CWORD]}
6
-  if [ $COMP_CWORD -eq 2 ]
7
-  then
8
-    confs=$(env ls /etc/sitegen/ 2>/dev/null | sed 's/\.[^.]*$//' | sort | uniq | sed 's/^sitegen$//')
9
-    COMPREPLY=($(compgen -W "${confs}" "${cur}"))
10
-  else
11
-    COMPREPLY=()
12
-  fi
13
-}
14
-#complete -F _sitegen sitegen
3
+eval "$(register-python-argcomplete sitegen)"

+ 15
- 2
install View File

@@ -2,6 +2,8 @@
2 2
 
3 3
 dir="$(dirname $(readlink -f "${0}"))"
4 4
 
5
+pip3 install -r "${dir}/requirements.txt" &&
6
+
5 7
 if [ ! -e /etc/sitegen ]
6 8
 then
7 9
   rm -f /etc/sitegen &&
@@ -9,10 +11,21 @@ then
9 11
 fi &&
10 12
 
11 13
 cp "${dir}/sitegen.py" /usr/local/bin/sitegen &&
14
+
15
+for typedir in "${dir}"/sitegen/hooks-available/*
16
+do
17
+    for hookfile in "${typedir}"/*
18
+    do
19
+        type=$(basename "${typedir}")
20
+        hook=$(basename "${hookfile}")
21
+        sitegen --hook-"${type}"-enable "${hook}"
22
+    done
23
+done &&
24
+
12 25
 cp "${dir}/bash/sitegen.completion" /etc/bash_completion.d/sitegen &&
13 26
 
14
-cp "${dir}/apache/letsencrypt.conf" /etc/apache2/conf-available/letsencrypt.conf &&
15
-a2enconf letsencrypt &&
27
+cp "${dir}/apache/sitegen.conf" /etc/apache2/conf-available/sitegen.conf &&
28
+a2enconf sitegen &&
16 29
 service apache2 reload &&
17 30
 
18 31
 if [ ! -e /etc/cron.d/sitegen-cert-renew ]

+ 1
- 0
requirements.txt View File

@@ -0,0 +1 @@
1
+argcomplete

+ 204
- 71
sitegen.py View File

@@ -4,6 +4,7 @@ import json
4 4
 import argparse
5 5
 import os
6 6
 import subprocess
7
+import argcomplete
7 8
 
8 9
 from os import path
9 10
 
@@ -26,6 +27,7 @@ class SiteGen:
26 27
     templatesDir = ""
27 28
     certRenewTime = ""
28 29
     letsencryptCommand = ""
30
+    letsencryptArgs = []
29 31
     letsencryptDir = ""
30 32
     certDir = ""
31 33
 
@@ -38,12 +40,18 @@ class SiteGen:
38 40
         self.templatesDir = path.join(self.confDir, "templates")
39 41
         self.certRenewTime = config["certRenewTime"]
40 42
         self.letsencryptCommand = config["letsencryptCommand"]
43
+        self.letsencryptArgs = config["letsencryptArgs"]
41 44
         self.letsencryptDir = config["letsencryptDir"]
42 45
         self.certDir = config["certDir"]
43 46
 
47
+    def make_dirs_p(self, folder):
48
+        if not path.isdir(folder):
49
+            os.makedirs(folder)
50
+
44 51
     def make_dirs(self):
45
-        if not path.isdir(self.certDir):
46
-            os.makedirs(self.certDir)
52
+        self.make_dirs_p(self.certDir)
53
+        self.make_dirs_p(self.siteConfDir)
54
+        self.make_dirs_p(self.siteDir)
47 55
 
48 56
     def get_hook_dir(self, hook_type, is_enabled):
49 57
         return path.join(self.hooksEnabledDir if is_enabled else self.hooksAvailableDir, hook_type)
@@ -79,12 +87,14 @@ class SiteGen:
79 87
                 path.abspath(path.join(self.certDir, domain + "-chain.crt"))]
80 88
 
81 89
     def execute(self, exe, args, get_output):
90
+        args = args.copy()
82 91
         args.insert(0, exe)
83 92
         proc = subprocess.Popen(args, stdout=(subprocess.PIPE if get_output else None))
84 93
         out = proc.communicate()
85 94
         return proc.returncode, out[0]
86 95
 
87 96
     def execute_hooks(self, hook_type, hook_event, args):
97
+        args = args.copy()
88 98
         args.insert(0, hook_event)
89 99
         for hook_name in self.get_hook_files(hook_type, True):
90 100
             self.execute(self.get_hook_file(hook_type, hook_name, True), args, False)
@@ -108,7 +118,7 @@ class SiteGen:
108 118
         res, out = self.execute("openssl", ["x509", "-noout", "-in", cert_files[0], "-checkend", str(checkend)], True)
109 119
         return res == 1
110 120
 
111
-    def get_all_domains(self):
121
+    def get_all_certs(self):
112 122
         files = os.listdir(self.certDir)
113 123
         files.sort()
114 124
         domains = []
@@ -117,13 +127,77 @@ class SiteGen:
117 127
                 domains.append(file[:-4])
118 128
         return domains
119 129
 
120
-    def cert_request(self, domain, logger):
130
+    def get_all_site_templates(self):
131
+        files = os.listdir(self.get_site_template_dir())
132
+        files.sort()
133
+        templates = []
134
+        for file in files:
135
+            if file.endswith(".conf"):
136
+                templates.append(file[:-5])
137
+        return templates
138
+
139
+    def get_all_sites(self):
140
+        files = os.listdir(self.siteConfDir)
141
+        files.sort()
142
+        templates = []
143
+        for file in files:
144
+            if file.endswith(".conf"):
145
+                templates.append(file[:-5])
146
+        return templates
147
+
148
+    def get_all_domains(self):
149
+        domains = list(set(self.get_all_sites() + self.get_all_certs()))
150
+        domains.sort()
151
+        return domains
152
+
153
+    def get_site_template_dir(self):
154
+        return path.join(self.confDir, "templates")
155
+
156
+    def get_site_conf_files(self, domain):
157
+        return [
158
+            path.join(self.siteConfDir, domain + ".conf"),
159
+            path.join(self.siteConfDir, domain + ".include")
160
+        ]
161
+
162
+    def get_site_template_conf_files(self, template):
163
+        return [
164
+            path.join(self.get_site_template_dir(), template + ".conf"),
165
+            path.join(self.get_site_template_dir(), template + ".include")
166
+        ]
167
+
168
+    def get_site_dir(self, domain):
169
+        return path.abspath(path.join(self.siteDir, domain))
170
+
171
+    def is_site_present(self, domain):
172
+        for file in self.get_site_conf_files(domain):
173
+            if path.isfile(file):
174
+                return True
175
+        return False
176
+
177
+    def is_site_template_present(self, template):
178
+        for file in self.get_site_template_conf_files(template):
179
+            if path.isfile(file):
180
+                return True
181
+        return False
182
+
183
+    def generate_site_conf_file(self, domain, template, outfile):
184
+        with open(template) as f:
185
+            content = f.read()
186
+        content = content.replace("%%HOST%%", domain).replace("%%ROOT%%", self.get_site_dir(domain))
187
+        with open(outfile, "w") as f:
188
+            f.write(content)
189
+
190
+    def cert_request(self, domain):
121 191
 
122 192
         cert_files = self.get_cert_files(domain)
123 193
         cert_files.insert(0, domain)
124 194
         self.execute_hooks("cert", "pre", cert_files)
125 195
 
126
-        res, out = self.execute(self.letsencryptCommand, [domain], False)
196
+        args = self.letsencryptArgs.copy()
197
+        args.append("-d")
198
+        args.append(domain)
199
+
200
+        res, out = self.execute(self.letsencryptCommand, args, False)
127 201
         if res != 0:
128 202
             raise SiteGenException("Certificate request failed with code %i" % res, res)
129 203
 
@@ -133,91 +207,158 @@ class SiteGen:
133 207
 
134 208
         self.execute_hooks("cert", "post", cert_files)
135 209
 
136
-    def certs_request(self, domains, logger):
210
+    def certs_request(self, domains):
137 211
         for domain in domains:
138
-            self.cert_request(domain, logger)
212
+            self.cert_request(domain)
139 213
 
140
-    def cert_check(self, domain, logger):
214
+    def cert_check(self, domain):
141 215
         if not self.is_cert_present(domain):
142 216
             raise SiteGenException("Certificate not present: %s" % domain, 1)
143 217
         if self.is_cert_gonna_expire(domain, self.certRenewTime):
144
-            logger("%s: %s" % (domain, self.get_cert_end_date(domain)))
218
+            print("%s: %s" % (domain, self.get_cert_end_date(domain)))
145 219
             return True
146 220
         return False
147 221
 
148
-    def certs_check(self, domains, logger):
222
+    def certs_check(self, domains):
149 223
         for domain in domains:
150
-            self.cert_check(domain, logger)
224
+            self.cert_check(domain)
151 225
 
152
-    def cert_enddate(self, domain, logger):
226
+    def cert_enddate(self, domain):
153 227
         if not self.is_cert_present(domain):
154 228
             raise SiteGenException("Certificate not present: %s" % domain, 1)
155
-        logger("%s: %s" % (domain, self.get_cert_end_date(domain)))
229
+        print("%s: %s" % (domain, self.get_cert_end_date(domain)))
156 230
 
157
-    def certs_enddate(self, domains, logger):
231
+    def certs_enddate(self, domains):
158 232
         for domain in domains:
159
-            self.cert_enddate(domain, logger)
233
+            self.cert_enddate(domain)
160 234
 
161
-    def cert_renew(self, domain, logger):
162
-        if self.cert_check(domain, logger):
163
-            self.cert_request(domain, logger)
235
+    def cert_renew(self, domain):
236
+        if self.cert_check(domain):
237
+            self.cert_request(domain)
164 238
 
165
-    def certs_renew(self, domains, logger):
239
+    def certs_renew(self, domains):
166 240
         for domain in domains:
167
-            self.cert_renew(domain, logger)
241
+            self.cert_renew(domain)
242
+
243
+    def site_create(self, domain, template):
244
+        if self.is_site_present(domain):
245
+            raise SiteGenException("Site is present", 1)
246
+        if not self.is_site_template_present(template):
247
+            raise SiteGenException("Template is not present", 1)
248
+
249
+        args = [domain, self.get_site_dir(domain)]
250
+        conf_files = self.get_site_template_conf_files(template)
251
+        site_files = self.get_site_conf_files(domain)
252
+        for f in conf_files:
253
+            args.append(f)
254
+        for f in site_files:
255
+            args.append(f)
256
+
257
+        self.execute_hooks("site", "pre", args)
168 258
 
169
-    def site_create(self, domain, logger):
170
-        pass
259
+        i = 0
260
+        while i < len(conf_files):
261
+            self.generate_site_conf_file(domain, conf_files[i], site_files[i])
262
+            i += 1
171 263
 
172
-    def site_remove(self, domain, logger):
173
-        pass
264
+        self.execute_hooks("site", "post", args)
174 265
 
175
-    def hook_enable(self, hook_type, hook_name, logger):
176
-        if not self.is_hook_present(hook_type, hook_name, False):
266
+    def hook_enable(self, hook_type, hook_name):
267
+        if hook_type is None or not self.is_hook_present(hook_type, hook_name, False):
177 268
             raise SiteGenException("Hook is not present", 1)
178 269
         if self.is_hook_enabled(hook_type, hook_name):
179 270
             raise SiteGenException("Hook is already enabled", 0)
180
-        logger("Enabling %s %s" % (hook_type, hook_name))
271
+        print("Enabling %s %s" % (hook_type, hook_name))
181 272
         hook_dir = self.get_hook_dir(hook_type, hook_name)
182
-        if not path.isdir(hook_dir):
183
-            os.makedirs(hook_dir)
273
+        self.make_dirs_p(hook_dir)
184 274
         hook_file_available = self.get_hook_file(hook_type, hook_name, False)
185 275
         hook_file_enabled = self.get_hook_file(hook_type, hook_name, True)
186 276
         hook_relative_file = path.relpath(hook_file_available, self.get_hook_dir(hook_type, True))
187 277
 
188 278
         os.symlink(hook_relative_file, hook_file_enabled)
189 279
 
190
-    def hook_disable(self, hook_type, hook_name, logger):
191
-        if not self.is_hook_present(hook_type, hook_name, False):
280
+    def hook_disable(self, hook_type, hook_name):
281
+        if hook_type is None or not self.is_hook_present(hook_type, hook_name, False):
192 282
             raise SiteGenException("Hook is not present", 1)
193 283
         if not self.is_hook_enabled(hook_type, hook_name):
194 284
             raise SiteGenException("Hook is not enabled", 0)
195
-        logger("Disabling %s %s" % (hook_type, hook_name))
285
+        print("Disabling %s %s" % (hook_type, hook_name))
196 286
         os.remove(self.get_hook_file(hook_type, hook_name, True))
197 287
 
198 288
 
289
+def parse_domain(domain):
290
+    site_template = "default"
291
+    if ":" in domain:
292
+        split = domain.split(":")
293
+        domain = split[0]
294
+        site_template = split[1]
295
+    return domain, site_template
296
+
297
+
298
+def parse_hook(hook):
299
+    if "." in hook:
300
+        split = hook.split(".")
301
+        return split[0], split[1]
302
+    return None, None
303
+
304
+
305
+def get_site_gen(prefix, **kwargs):
306
+    with open(kwargs.get("parsed_args").config, "r") as f:
307
+        config = json.load(f)
308
+    return SiteGen(config)
309
+
310
+
311
+def cert_completer(prefix, **kwargs):
312
+    site_gen = get_site_gen(prefix, **kwargs)
313
+    return site_gen.get_all_certs()
314
+
315
+
316
+def domain_completer(prefix, **kwargs):
317
+    site_gen = get_site_gen(prefix, **kwargs)
318
+    return site_gen.get_all_domains()
319
+
320
+
321
+def site_template_completer(prefix, **kwargs):
322
+    site_gen = get_site_gen(prefix, **kwargs)
323
+    return site_gen.get_all_site_templates()
324
+
325
+
326
+def site_create_completer(prefix, **kwargs):
327
+    if ":" in prefix:
328
+        domain, template = parse_domain(prefix)
329
+        templates = site_template_completer(prefix, **kwargs)
330
+        return [domain + ":" + elt for elt in templates]
331
+
332
+    return domain_completer(prefix, **kwargs)
333
+
334
+
335
+def hook_completer(prefix, **kwargs):
336
+    site_gen = get_site_gen(prefix, **kwargs)
337
+    return site_gen.get_all_certs()
338
+
339
+
199 340
 def main():
200 341
     parser = argparse.ArgumentParser(description='Manage apache websites and SSL certificates')
201 342
     parser.add_argument('--config', dest='config', default='/etc/sitegen/sitegen.json', help='Configuration file path')
202 343
 
203
-    parser.add_argument('--cert-request', metavar='cert_request', const='', nargs='?',
204
-                        help='Request/renew a certificate. Request/renew all certificates if no domain is specified')
205
-    parser.add_argument('--cert-check', metavar='cert_check', const='', nargs='?',
206
-                        help='Check if certificate needs to be renewed. Check all if no domain is specified')
207
-    parser.add_argument('--cert-renew', metavar='cert_renew', const='', nargs='?',
208
-                        help='Renew certificate if it needs to be. Renew all that needs to be if no domain is specified')
209
-    parser.add_argument('--cert-enddate', metavar='cert_enddate', const='', nargs='?',
210
-                        help='Print certificate enddate. Print all certificates enddate if no domain is specified')
344
+    parser.add_argument('--cert-request', metavar='domain', const='', nargs='?',
345
+                        help='Request/renew a certificate. Request/renew all certificates if no domain is specified').completer = domain_completer
346
+    parser.add_argument('--cert-check', metavar='domain', const='', nargs='?',
347
+                        help='Check if certificate needs to be renewed. Check all if no domain is specified').completer = cert_completer
348
+    parser.add_argument('--cert-renew', metavar='domain', const='', nargs='?',
349
+                        help='Renew certificate if it needs to be. Renew all that needs to be if no domain is specified').completer = cert_completer
350
+    parser.add_argument('--cert-enddate', metavar='enddate', const='', nargs='?',
351
+                        help='Print certificate enddate. Print all certificates enddate if no domain is specified').completer = cert_completer
211 352
 
212
-    parser.add_argument('--site-create', help='Create a site configuration', metavar='site_create')
213
-    parser.add_argument('--site-remove', help='Remove a site configuration', metavar='site_remove')
353
+    parser.add_argument('--site-create', help='Create a site configuration in the form domain[:template]',
354
+                        metavar='domain').completer = site_create_completer
214 355
 
215
-    parser.add_argument('--hook-site-enable', help='Enable a site hook', dest='site_hook_enable', metavar='hook')
216
-    parser.add_argument('--hook-site-disable', help='Disable a site hook', dest='site_hook_disable', metavar='hook')
217
-
218
-    parser.add_argument('--hook-cert-enable', help='Enable a certificate hook', dest='hook_cert_enable', metavar='hook')
219
-    parser.add_argument('--hook-cert-disable', help='Disable a certificate hook', dest='hook_cert_disable', metavar='hook')
356
+    parser.add_argument('--hook-enable', help='Enable a site hook in the form (site|cert):hook_name',
357
+                        dest='hook_enable', metavar='hook').completer = hook_completer
358
+    parser.add_argument('--hook-disable', help='Disable a site hook in the form (site|cert):hook_name',
359
+                        dest='hook_disable', metavar='hook').completer = hook_completer
220 360
 
361
+    argcomplete.autocomplete(parser)
221 362
     args = parser.parse_args()
222 363
 
223 364
     with open(args.config, "r") as f:
@@ -227,50 +368,42 @@ def main():
227 368
 
228 369
     site_gen.make_dirs()
229 370
 
230
-    logger = print
231
-
232 371
     try:
233 372
         if args.cert_request is not None:
234 373
             if args.cert_request == "":
235
-                site_gen.certs_request(site_gen.get_all_domains(), logger)
374
+                site_gen.certs_request(site_gen.get_all_certs())
236 375
             else:
237
-                site_gen.cert_request(args.cert_request, logger)
376
+                site_gen.cert_request(args.cert_request)
238 377
 
239 378
         elif args.cert_check is not None:
240 379
             if args.cert_check == "":
241
-                site_gen.certs_check(site_gen.get_all_domains(), logger)
380
+                site_gen.certs_check(site_gen.get_all_certs())
242 381
             else:
243
-                site_gen.cert_check(args.cert_check, logger)
382
+                site_gen.cert_check(args.cert_check)
244 383
 
245 384
         elif args.cert_renew is not None:
246 385
             if args.cert_renew == "":
247
-                site_gen.certs_renew(site_gen.get_all_domains(), logger)
386
+                site_gen.certs_renew(site_gen.get_all_certs())
248 387
             else:
249
-                site_gen.cert_renew(args.cert_renew, logger)
388
+                site_gen.cert_renew(args.cert_renew)
250 389
 
251 390
         elif args.cert_enddate is not None:
252 391
             if args.cert_enddate == "":
253
-                site_gen.certs_enddate(site_gen.get_all_domains(), logger)
392
+                site_gen.certs_enddate(site_gen.get_all_certs())
254 393
             else:
255
-                site_gen.cert_enddate(args.cert_enddate, logger)
394
+                site_gen.cert_enddate(args.cert_enddate)
256 395
 
257 396
         elif args.site_create is not None:
258
-            site_gen.site_create(args.site_create, logger)
259
-
260
-        elif args.site_remove is not None:
261
-            site_gen.site_remove(args.site_remove, logger)
262
-
263
-        elif args.site_hook_enable is not None:
264
-            site_gen.hook_enable("site", args.site_hook_enable, logger)
265
-
266
-        elif args.site_hook_disable is not None:
267
-            site_gen.hook_disable("site", args.site_hook_disable, logger)
397
+            domain, site_template = parse_domain(args.site_create)
398
+            site_gen.site_create(domain, site_template)
268 399
 
269
-        elif args.hook_cert_enable is not None:
270
-            site_gen.hook_enable("cert", args.hook_cert_enable, logger)
400
+        elif args.hook_enable is not None:
401
+            hook_type, hook_name = parse_hook(args.hook_enable)
402
+            site_gen.hook_enable(hook_type, hook_name)
271 403
 
272
-        elif args.hook_cert_disable is not None:
273
-            site_gen.hook_disable("cert", args.hook_cert_disable, logger)
404
+        elif args.hook_disable is not None:
405
+            hook_type, hook_name = parse_hook(args.hook_disable)
406
+            site_gen.hook_disable(hook_type, hook_name)
274 407
 
275 408
         else:
276 409
             parser.print_help()

+ 14
- 0
sitegen/hooks-available/cert/100-mkwebroot View File

@@ -0,0 +1,14 @@
1
+#! /usr/bin/env sh
2
+
3
+event="${1}"
4
+host="${2}"
5
+cert_file="${3}"
6
+key_file="${4}"
7
+chain_file="${5}"
8
+
9
+if [ "${event}" != "pre" ]
10
+then
11
+    exit 0
12
+fi
13
+
14
+mkdir -p /tmp/acme-challenge/.well-known/acme-challenge

+ 14
- 0
sitegen/hooks-available/cert/200-reload View File

@@ -0,0 +1,14 @@
1
+#! /usr/bin/env sh
2
+
3
+event="${1}"
4
+host="${2}"
5
+cert_file="${3}"
6
+key_file="${4}"
7
+chain_file="${5}"
8
+
9
+if [ "${event}" != "post" ]
10
+then
11
+    exit 0
12
+fi
13
+
14
+service apache2 reload

sitegen/hooks-available/site/050-sitegen-cert-request → sitegen/hooks-available/site/100-sitegen-cert-request View File


sitegen/hooks-available/site/100-chown → sitegen/hooks-available/site/200-chown View File


sitegen/hooks-available/site/200-a2ensite → sitegen/hooks-available/site/300-a2ensite View File


sitegen/hooks-available/site/300-reload → sitegen/hooks-available/site/400-reload View File


+ 3
- 5
sitegen/sitegen.json View File

@@ -7,11 +7,9 @@
7 7
   "letsencryptArgs": [
8 8
     "--agree-tos",
9 9
     "--renew-by-default",
10
-    "--standalone",
11
-    "--standalone-supported-challenges",
12
-    "http-01",
13
-    "--http-01-port",
14
-    "9999",
10
+    "--webroot",
11
+    "--webroot-path",
12
+    "/tmp/acme-challenge/",
15 13
     "--server",
16 14
     "https://acme-v01.api.letsencrypt.org/directory",
17 15
     "certonly"

+ 7
- 4
tests/fake-letsencrypt.sh View File

@@ -1,9 +1,11 @@
1 1
 #! /usr/bin/env sh
2 2
 
3 3
 dir="$(dirname $(readlink -f "${0}"))"
4
-host="${1}"
4
+arg="${1}"
5
+d="${2}"
6
+host="${3}"
5 7
 
6
-if [ "${host}" = "error.com" ]
8
+if [ "${host}" = "error.com" ] || [ "${arg}" != "Test." ] || [ "${d}" != "-d" ]
7 9
 then
8 10
     echo "Failed to get certificate" >&2
9 11
     exit 1
@@ -15,8 +17,9 @@ then
15 17
     exit 1
16 18
 fi &&
17 19
 
18
-leDir="${dir}/tests/etc/letsencrypt/live/${host}"
20
+leDir="${dir}/etc/letsencrypt/live/${host}"
19 21
 
22
+echo $leDir
20 23
 
21 24
 mkdir -p "${leDir}" &&
22
-scp serv3:/etc/letsencrypt/live/${host}/* tests/etc/letsencrypt/live/${host}/
25
+scp serv3:/etc/letsencrypt/live/${host}/* "${leDir}"

+ 3
- 0
tests/sitegen.json View File

@@ -4,6 +4,9 @@
4 4
   "confDir": "./sitegen/",
5 5
   "certRenewTime": 5356800,
6 6
   "letsencryptCommand": "./tests/fake-letsencrypt.sh",
7
+  "letsencryptArgs": [
8
+    "Test."
9
+  ],
7 10
   "letsencryptDir": "./tests/etc/letsencrypt/live/",
8 11
   "certDir": "./tests/etc/ssl/private/"
9 12
 }

Loading…
Cancel
Save