roster-server 2.1.12 → 2.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -234,6 +234,10 @@ class Roster {
234
234
  this.disableWildcard = options.disableWildcard !== undefined
235
235
  ? parseBooleanFlag(options.disableWildcard, false)
236
236
  : parseBooleanFlag(process.env.ROSTER_DISABLE_WILDCARD, false);
237
+ const isBunRuntime = typeof Bun !== 'undefined' || process.release?.name === 'bun';
238
+ this.combineWildcardCerts = options.combineWildcardCerts !== undefined
239
+ ? parseBooleanFlag(options.combineWildcardCerts, false)
240
+ : parseBooleanFlag(process.env.ROSTER_COMBINE_WILDCARD_CERTS, isBunRuntime);
237
241
 
238
242
  const port = options.port === undefined ? 443 : options.port;
239
243
  if (port === 80 && !this.local) {
@@ -379,10 +383,21 @@ class Roster {
379
383
  if ((domain.match(/\./g) || []).length < 2) {
380
384
  primaryAltnames.push(`www.${domain}`);
381
385
  }
386
+ const shouldCombineWildcard = this.combineWildcardCerts && this.wildcardZones.has(domain) && this.dnsChallenge;
387
+ if (shouldCombineWildcard) {
388
+ primaryAltnames.push(`*.${domain}`);
389
+ }
382
390
  const primarySite = {
383
391
  subject: domain,
384
392
  altnames: primaryAltnames
385
393
  };
394
+ if (shouldCombineWildcard) {
395
+ const dns01 = { ...this.dnsChallenge };
396
+ if (dns01.propagationDelay === undefined) dns01.propagationDelay = 120000;
397
+ if (dns01.autoContinue === undefined) dns01.autoContinue = false;
398
+ if (dns01.dryRunDelay === undefined) dns01.dryRunDelay = dns01.propagationDelay;
399
+ primarySite.challenges = { 'dns-01': dns01 };
400
+ }
386
401
  const existingPrimarySite = Array.isArray(existingConfig.sites)
387
402
  ? existingConfig.sites.find(site => site.subject === domain)
388
403
  : null;
@@ -390,7 +405,7 @@ class Roster {
390
405
  sitesConfig.push(primarySite);
391
406
 
392
407
  // Wildcard cert is issued separately and uses dns-01 only.
393
- if (this.wildcardZones.has(domain) && this.dnsChallenge) {
408
+ if (!shouldCombineWildcard && this.wildcardZones.has(domain) && this.dnsChallenge) {
394
409
  const wildcardSubject = `*.${domain}`;
395
410
  const dns01 = { ...this.dnsChallenge };
396
411
  if (dns01.propagationDelay === undefined) {
@@ -853,8 +868,40 @@ class Roster {
853
868
  }
854
869
  return null;
855
870
  };
871
+ const issueAndReloadPemsForServername = async (servername) => {
872
+ const host = normalizeHostInput(servername).trim().toLowerCase();
873
+ if (!host) return null;
874
+
875
+ let pems = resolvePemsForServername(host);
876
+ if (pems) return pems;
877
+
878
+ try {
879
+ await greenlockRuntime.get({ servername: host });
880
+ } catch (error) {
881
+ log.warn(`⚠️ Greenlock issuance failed for ${host}: ${error?.message || error}`);
882
+ }
883
+
884
+ pems = resolvePemsForServername(host);
885
+ if (pems) return pems;
886
+
887
+ // For wildcard zones, try a valid subdomain bootstrap host so Greenlock can
888
+ // resolve the wildcard site without relying on invalid "*.domain" servername input.
889
+ const wildcardSubject = wildcardSubjectForHost(host);
890
+ const zone = wildcardSubject ? wildcardRoot(wildcardSubject) : null;
891
+ if (zone) {
892
+ const bootstrapHost = `bun-bootstrap.${zone}`;
893
+ try {
894
+ await greenlockRuntime.get({ servername: bootstrapHost });
895
+ } catch (error) {
896
+ log.warn(`⚠️ Greenlock wildcard bootstrap failed for ${bootstrapHost}: ${error?.message || error}`);
897
+ }
898
+ pems = resolvePemsForServername(host);
899
+ }
900
+
901
+ return pems;
902
+ };
856
903
  const ensureBunDefaultPems = async (primaryDomain) => {
857
- let pems = resolvePemsForServername(primaryDomain);
904
+ let pems = await issueAndReloadPemsForServername(primaryDomain);
858
905
  if (pems) return pems;
859
906
 
860
907
  const certSubject = primaryDomain.startsWith('*.') ? wildcardRoot(primaryDomain) : primaryDomain;
@@ -891,12 +938,12 @@ class Roster {
891
938
  key: defaultPems.key,
892
939
  cert: defaultPems.cert,
893
940
  SNICallback: (servername, callback) => {
894
- try {
895
- const pems = resolvePemsForServername(servername) || defaultPems;
896
- callback(null, tls.createSecureContext({ key: pems.key, cert: pems.cert }));
897
- } catch (error) {
898
- callback(error);
899
- }
941
+ issueAndReloadPemsForServername(servername)
942
+ .then((pems) => {
943
+ const selected = pems || defaultPems;
944
+ callback(null, tls.createSecureContext({ key: selected.key, cert: selected.cert }));
945
+ })
946
+ .catch(callback);
900
947
  }
901
948
  }, dispatcher);
902
949
  log.warn(`⚠️ Bun runtime detected: using file-based TLS with SNI for ${primaryDomain} on port ${portNum}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roster-server",
3
- "version": "2.1.12",
3
+ "version": "2.1.16",
4
4
  "description": "👾 RosterServer - A domain host router to host multiple HTTPS.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -313,6 +313,17 @@ describe('Roster', () => {
313
313
  else process.env.ROSTER_DISABLE_WILDCARD = previous;
314
314
  }
315
315
  });
316
+ it('enables combined wildcard certs from env var', () => {
317
+ const previous = process.env.ROSTER_COMBINE_WILDCARD_CERTS;
318
+ process.env.ROSTER_COMBINE_WILDCARD_CERTS = '1';
319
+ try {
320
+ const roster = new Roster({ local: false });
321
+ assert.strictEqual(roster.combineWildcardCerts, true);
322
+ } finally {
323
+ if (previous === undefined) delete process.env.ROSTER_COMBINE_WILDCARD_CERTS;
324
+ else process.env.ROSTER_COMBINE_WILDCARD_CERTS = previous;
325
+ }
326
+ });
316
327
  });
317
328
 
318
329
  describe('register (normal domain)', () => {
@@ -557,4 +568,38 @@ describe('Roster generateConfigJson', () => {
557
568
  fs.rmSync(tmpDir, { recursive: true, force: true });
558
569
  }
559
570
  });
571
+ it('can combine apex+www+wildcard in one cert with dns-01', () => {
572
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'roster-config-'));
573
+ try {
574
+ const roster = new Roster({
575
+ local: false,
576
+ greenlockStorePath: tmpDir,
577
+ combineWildcardCerts: true,
578
+ dnsChallenge: {
579
+ module: 'acme-dns-01-cli',
580
+ propagationDelay: 120000,
581
+ autoContinue: false,
582
+ dryRunDelay: 120000
583
+ }
584
+ });
585
+ roster.domains = ['tagnu.com', 'www.tagnu.com', '*.tagnu.com'];
586
+ roster.wildcardZones.add('tagnu.com');
587
+ roster.generateConfigJson();
588
+
589
+ const configPath = path.join(tmpDir, 'config.json');
590
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
591
+ const apexSite = config.sites.find((site) => site.subject === 'tagnu.com');
592
+ const wildcardSite = config.sites.find((site) => site.subject === '*.tagnu.com');
593
+
594
+ assert.ok(apexSite);
595
+ assert.deepStrictEqual(
596
+ apexSite.altnames.sort(),
597
+ ['tagnu.com', 'www.tagnu.com', '*.tagnu.com'].sort()
598
+ );
599
+ assert.ok(apexSite.challenges && apexSite.challenges['dns-01']);
600
+ assert.strictEqual(wildcardSite, undefined);
601
+ } finally {
602
+ fs.rmSync(tmpDir, { recursive: true, force: true });
603
+ }
604
+ });
560
605
  });
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const legacyCli = require('acme-dns-01-cli');
4
+ const log = require('lemonlog')('acme-dns-01');
4
5
 
5
6
  function toPromise(fn, context) {
6
7
  if (typeof fn !== 'function') {
@@ -73,10 +74,10 @@ module.exports.create = function create(config = {}) {
73
74
  }
74
75
 
75
76
  const ch = opts?.challenge || {};
76
- console.info('');
77
- console.info("[ACME dns-01 '" + (ch.altname || opts?.altname || 'unknown') + "' CHALLENGE]");
78
- console.info("You're about to receive the following DNS query:");
79
- console.info('');
77
+ log.info('');
78
+ log.info("[ACME dns-01 '" + (ch.altname || opts?.altname || 'unknown') + "' CHALLENGE]");
79
+ log.info("You're about to receive the following DNS query:");
80
+ log.info('');
80
81
  const dnsHost = String(ch.dnsHost || '');
81
82
  const dnsAuth = ch.dnsAuthorization || opts?.dnsAuthorization || null;
82
83
  if (dnsHost && dnsAuth) {
@@ -87,15 +88,15 @@ module.exports.create = function create(config = {}) {
87
88
  ? Math.max(0, dryRunDelay)
88
89
  : propagationDelay;
89
90
 
90
- console.info(
91
+ log.info(
91
92
  '\tTXT\t' +
92
93
  (ch.dnsHost || '_acme-challenge.<domain>') +
93
94
  '\t' +
94
95
  (ch.dnsAuthorization || '<dns-authorization-token>') +
95
96
  '\tTTL 60'
96
97
  );
97
- console.info('');
98
- console.info(
98
+ log.info('');
99
+ log.info(
99
100
  'Non-interactive mode (or autoContinue) detected. ' +
100
101
  'Set the TXT record now. Continuing automatically in ' +
101
102
  effectiveDelay +
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
 
3
+ var log = require("lemonlog")("greenlock-demo");
4
+
3
5
  require("./")
4
6
  .init(initialize)
5
7
  .serve(worker)
6
8
  .master(function() {
7
- console.log("Hello from master");
9
+ log.info("Hello from master");
8
10
  });
9
11
 
10
12
  function initialize() {
@@ -19,15 +21,15 @@ function initialize() {
19
21
  cluster: true,
20
22
 
21
23
  notify: function(ev, params) {
22
- console.info(ev, params);
24
+ log.info(ev, params);
23
25
  }
24
26
  };
25
27
  return config;
26
28
  }
27
29
 
28
30
  function worker(glx) {
29
- console.info();
30
- console.info("Hello from worker #" + glx.id());
31
+ log.info("");
32
+ log.info("Hello from worker #" + glx.id());
31
33
 
32
34
  glx.serveApp(function(req, res) {
33
35
  res.end("Hello, Encrypted World!");
@@ -2,6 +2,7 @@
2
2
 
3
3
  module.exports.create = function(opts) {
4
4
  var Greenlock = require("@root/greenlock");
5
+ var log = require("lemonlog")("greenlock-shim");
5
6
  //var Init = require("@root/greenlock/lib/init.js");
6
7
  var greenlock = opts.greenlock;
7
8
 
@@ -28,7 +29,7 @@ module.exports.create = function(opts) {
28
29
  greenlock._defaults.notify = opts.notify;
29
30
  }
30
31
  } catch (e) {
31
- console.error("Developer Error: notify not attached correctly");
32
+ log.error("Developer Error: notify not attached correctly");
32
33
  }
33
34
 
34
35
  // re-export as top-level function to simplify rpc with workers
@@ -38,23 +39,12 @@ module.exports.create = function(opts) {
38
39
 
39
40
  greenlock._find({}).then(function(sites) {
40
41
  if (sites.length <= 0) {
41
- console.warn("Warning: `find({})` returned 0 sites.");
42
- console.warn(" Does `" + greenlock.manager._modulename + "` implement `find({})`?");
43
- console.warn(" Did you add sites?");
44
- console.warn(" npx greenlock add --subject example.com --altnames example.com");
42
+ log.warn("Warning: `find({})` returned 0 sites.");
43
+ log.warn(" Does `" + greenlock.manager._modulename + "` implement `find({})`?");
44
+ log.warn(" Did you add sites?");
45
+ log.warn(" npx greenlock add --subject example.com --altnames example.com");
45
46
  return;
46
47
  }
47
- // console.info("Ready to Serve:");
48
-
49
- var max = 3;
50
- if (sites.length >= 1) {
51
- sites.slice(0, max).forEach(function(site) {
52
- // console.info("\t", site.altnames.join(" "));
53
- });
54
- }
55
- if (sites.length > max) {
56
- // console.info("and %d others", sites.length - max);
57
- }
58
48
  });
59
49
 
60
50
  return greenlock;
@@ -3,6 +3,7 @@
3
3
  var HttpMiddleware = module.exports;
4
4
  var servernameRe = /^[a-z0-9\.\-]+$/i;
5
5
  var challengePrefix = "/.well-known/acme-challenge/";
6
+ var log = require("lemonlog")("greenlock-http");
6
7
 
7
8
  HttpMiddleware.create = function(gl, defaultApp) {
8
9
  if (defaultApp && "function" !== typeof defaultApp) {
@@ -49,7 +50,7 @@ HttpMiddleware.create = function(gl, defaultApp) {
49
50
  return;
50
51
  }
51
52
  if (done) {
52
- console.error("Sanity check fail: `done` is in a quantum state of both true and false... huh?");
53
+ log.error("Invariant violation: challenge middleware reached `done=true` and response path simultaneously");
53
54
  return;
54
55
  }
55
56
  // HEADERS SENT DEBUG NOTE #4b
@@ -60,16 +61,16 @@ HttpMiddleware.create = function(gl, defaultApp) {
60
61
  // HEADERS SENT DEBUG NOTE #5
61
62
  // I really don't see how this can be possible.
62
63
  // Every case appears to be accounted for
63
- console.error();
64
- console.error("[warning] Developer Error:" + (err.code || err.context || ""), countA, countB);
65
- console.error(err.stack);
66
- console.error();
67
- console.error(
68
- "This is probably the error that happens routinely on http2 connections, but we're not sure why."
64
+ log.error(
65
+ "Unhandled ACME challenge middleware error",
66
+ {
67
+ code: err.code || null,
68
+ context: err.context || null,
69
+ catchCountA: countA,
70
+ catchCountB: countB
71
+ }
69
72
  );
70
- console.error("To track the status or help contribute,");
71
- console.error("visit: https://git.rootprojects.org/root/greenlock-express.js/issues/9");
72
- console.error();
73
+ log.error(err.stack || err.message || err);
73
74
  try {
74
75
  res.end("Internal Server Error [1003]: See logs for details.");
75
76
  } catch (e) {
@@ -114,7 +115,11 @@ function explainError(gl, err, ctx, hostname) {
114
115
  err.context = ctx;
115
116
  }
116
117
  // leaving this in the build for now because it will help with existing error reports
117
- console.error("[warning] network connection error:", (err.context || "") + " " + err.message);
118
+ log.error("Network connection error during ACME challenge handling", {
119
+ context: err.context || "",
120
+ servername: err.servername || hostname || "",
121
+ message: err.message
122
+ });
118
123
  (gl.notify || gl._notify)("error", err);
119
124
  return err;
120
125
  }
@@ -2,6 +2,7 @@
2
2
 
3
3
  var SanitizeHost = module.exports;
4
4
  var HttpMiddleware = require("./http-middleware.js");
5
+ var log = require("lemonlog")("greenlock-https");
5
6
 
6
7
  SanitizeHost.create = function(gl, app) {
7
8
  return function(req, res, next) {
@@ -61,8 +62,6 @@ SanitizeHost.create = function(gl, app) {
61
62
  // and such).
62
63
  // It was common for the log message to pop up as the first request
63
64
  // to the server, and that was confusing. So instead now we do nothing.
64
-
65
- //console.warn("no string for req.socket.servername," + " skipping fronting check for '" + safehost + "'");
66
65
  //gl._skip_fronting_check = true;
67
66
  }
68
67
  */
@@ -94,7 +93,7 @@ SanitizeHost._checkServername = function(safeHost, tlsSocket) {
94
93
  // domain fronting attacks allowed
95
94
  if (warnDomainFronting) {
96
95
  // https://github.com/nodejs/node/issues/24095
97
- console.warn(
96
+ log.warn(
98
97
  "Warning: node " +
99
98
  process.version +
100
99
  " is vulnerable to domain fronting attacks. Please use node v11.2.0 or greater."
@@ -111,7 +110,6 @@ SanitizeHost._checkServername = function(safeHost, tlsSocket) {
111
110
  // TODO optimize / cache?
112
111
  // *should* always have a string, right?
113
112
  // *should* always be lowercase already, right?
114
- //console.log(safeHost, cert.subject.CN, cert.subjectaltname);
115
113
  var isSubject = (cert.subject.CN || "").toLowerCase() === safeHost;
116
114
  if (isSubject) {
117
115
  return true;
@@ -129,7 +127,7 @@ SanitizeHost._checkServername = function(safeHost, tlsSocket) {
129
127
  } catch (e) {
130
128
  // not sure what else to do in this situation...
131
129
  if (warnUnexpectedError) {
132
- console.warn("Warning: encoutered error while performing domain fronting check: " + e.message);
130
+ log.warn("Encountered error while performing domain fronting check: " + e.message);
133
131
  warnUnexpectedError = false;
134
132
  }
135
133
  return true;
@@ -1,13 +1,15 @@
1
1
  "use strict";
2
2
 
3
+ var log = require("lemonlog")("greenlock-compat");
4
+
3
5
  function requireBluebird() {
4
6
  try {
5
7
  return require("bluebird");
6
8
  } catch (e) {
7
- console.error("");
8
- console.error("DON'T PANIC. You're running an old version of node with incomplete Promise support.");
9
- console.error("EASY FIX: `npm install --save bluebird`");
10
- console.error("");
9
+ log.error("");
10
+ log.error("DON'T PANIC. You're running an old version of node with incomplete Promise support.");
11
+ log.error("EASY FIX: `npm install --save bluebird`");
12
+ log.error("");
11
13
  throw e;
12
14
  }
13
15
  }
@@ -21,7 +23,9 @@ if ("function" !== typeof require("util").promisify) {
21
23
  }
22
24
 
23
25
  if (!console.debug) {
24
- console.debug = console.log;
26
+ console.debug = function() {
27
+ log.debug.apply(log, arguments);
28
+ };
25
29
  }
26
30
 
27
31
  var fs = require("fs");
@@ -3,6 +3,7 @@
3
3
  // this is the stuff that should run in the main foreground process,
4
4
  // whether it's single or master
5
5
 
6
+ var log = require("lemonlog")("greenlock-main");
6
7
  var major = parseInt(process.versions.node.split(".")[0], 10);
7
8
  var minor = parseInt(process.versions.node.split(".")[1], 10) || 0;
8
9
  var _hasSetSecureContext = false;
@@ -14,19 +15,19 @@ _hasSetSecureContext = major > 11 || (major === 11 && minor >= 2);
14
15
  // TODO document in issues
15
16
  if (!_hasSetSecureContext) {
16
17
  // TODO this isn't necessary if greenlock options are set with options.cert
17
- console.warn("Warning: node " + process.version + " is missing tlsSocket.setSecureContext().");
18
- console.warn(" The default certificate may not be set.");
18
+ log.warn("Warning: node " + process.version + " is missing tlsSocket.setSecureContext().");
19
+ log.warn(" The default certificate may not be set.");
19
20
  shouldUpgrade = true;
20
21
  }
21
22
 
22
23
  if (major < 11 || (11 === major && minor < 2)) {
23
24
  // https://github.com/nodejs/node/issues/24095
24
- console.warn("Warning: node " + process.version + " is missing tlsSocket.getCertificate().");
25
- console.warn(" This is necessary to guard against domain fronting attacks.");
25
+ log.warn("Warning: node " + process.version + " is missing tlsSocket.getCertificate().");
26
+ log.warn(" This is necessary to guard against domain fronting attacks.");
26
27
  shouldUpgrade = true;
27
28
  }
28
29
 
29
30
  if (shouldUpgrade) {
30
- console.warn("Warning: Please upgrade to node v11.2.0 or greater.");
31
- console.warn();
31
+ log.warn("Warning: Please upgrade to node v11.2.0 or greater.");
32
+ log.warn("");
32
33
  }
@@ -6,6 +6,7 @@ var Master = module.exports;
6
6
 
7
7
  var cluster = require("cluster");
8
8
  var os = require("os");
9
+ var log = require("lemonlog")("greenlock-master");
9
10
  var msgPrefix = "greenlock:";
10
11
 
11
12
  Master.create = function(opts) {
@@ -112,11 +113,11 @@ Master._spawnWorker = function(opts, greenlock) {
112
113
 
113
114
  // For now just kill all when any die
114
115
  if (signal) {
115
- console.error("worker was killed by signal:", signal);
116
+ log.error("worker was killed by signal:", signal);
116
117
  } else if (code !== 0) {
117
- console.error("worker exited with error code:", code);
118
+ log.error("worker exited with error code:", code);
118
119
  } else {
119
- console.error("worker unexpectedly quit without exit code or signal");
120
+ log.error("worker unexpectedly quit without exit code or signal");
120
121
  }
121
122
  process.exit(2);
122
123
 
@@ -155,8 +156,8 @@ Master._spawnWorker = function(opts, greenlock) {
155
156
  try {
156
157
  rpc();
157
158
  } catch (e) {
158
- console.error("Unexpected and uncaught greenlock." + msg._funcname + " error:");
159
- console.error(e);
159
+ log.error("Unexpected and uncaught greenlock." + msg._funcname + " error:");
160
+ log.error(e);
160
161
  }
161
162
  }
162
163
 
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ var log = require("lemonlog")("greenlock-postinstall");
3
4
 
4
5
  // BG WH \u001b[47m
5
6
  // BOLD \u001b[1m
@@ -68,7 +69,7 @@ setTimeout(function() {
68
69
  "================================================================================",
69
70
  ""
70
71
  ]).forEach(function(line) {
71
- console.info(line);
72
+ log.info(line);
72
73
  });
73
74
  }, 300);
74
75
 
@@ -7,6 +7,7 @@ var HttpMiddleware = require("./http-middleware.js");
7
7
  var HttpsMiddleware = require("./https-middleware.js");
8
8
  var sni = require("./sni.js");
9
9
  var cluster = require("cluster");
10
+ var log = require("lemonlog")("greenlock-servers");
10
11
 
11
12
  Servers.create = function(greenlock) {
12
13
  var servers = {};
@@ -21,7 +22,7 @@ Servers.create = function(greenlock) {
21
22
  servers.httpServer = function(defaultApp) {
22
23
  if (_httpServer) {
23
24
  if (defaultApp) {
24
- console.error("error: can only call httpServer(app) once");
25
+ log.error("Invalid API usage: `httpServer(app)` can only be called once");
25
26
  process.exit(1);
26
27
  }
27
28
  return _httpServer;
@@ -104,7 +105,7 @@ Servers.create = function(greenlock) {
104
105
  var plainAddr = "0.0.0.0";
105
106
  var plainPort = 80;
106
107
  plainServer.listen(plainPort, plainAddr, function() {
107
- console.info(
108
+ log.info(
108
109
  idstr + "Listening on",
109
110
  plainAddr + ":" + plainPort,
110
111
  "for ACME challenges, and redirecting to HTTPS"
@@ -116,7 +117,7 @@ Servers.create = function(greenlock) {
116
117
  var secureAddr = "0.0.0.0";
117
118
  var securePort = 443;
118
119
  secureServer.listen(securePort, secureAddr, function() {
119
- console.info(idstr + "Listening on", secureAddr + ":" + securePort, "for secure traffic");
120
+ log.info(idstr + "Listening on", secureAddr + ":" + securePort, "for secure traffic");
120
121
 
121
122
  plainServer.removeListener("error", startError);
122
123
  secureServer.removeListener("error", startError);
@@ -130,18 +131,21 @@ Servers.create = function(greenlock) {
130
131
  };
131
132
 
132
133
  function explainError(e) {
133
- console.error();
134
- console.error("Error: " + e.message);
134
+ log.error("Server startup error", {
135
+ code: e.code || e.errno || null,
136
+ address: e.address || null,
137
+ port: e.port || null,
138
+ message: e.message
139
+ });
135
140
  if ("EACCES" === e.errno) {
136
- console.error("You don't have prmission to access '" + e.address + ":" + e.port + "'.");
137
- console.error('You probably need to use "sudo" or "sudo setcap \'cap_net_bind_service=+ep\' $(which node)"');
141
+ log.error("Insufficient permission to bind " + e.address + ":" + e.port + ".");
142
+ log.error('You probably need to use "sudo" or "sudo setcap \'cap_net_bind_service=+ep\' $(which node)"');
138
143
  } else if ("EADDRINUSE" === e.errno) {
139
- console.error("'" + e.address + ":" + e.port + "' is already being used by some other program.");
140
- console.error("You probably need to stop that program or restart your computer.");
144
+ log.error("Address already in use: " + e.address + ":" + e.port + ".");
145
+ log.error("You probably need to stop that program or restart your computer.");
141
146
  } else {
142
- console.error(e.code + ": '" + e.address + ":" + e.port + "'");
147
+ log.error(e.code + ": '" + e.address + ":" + e.port + "'");
143
148
  }
144
- console.error();
145
149
  }
146
150
 
147
151
  function wrapDefaultSniCallback(greenlock, secureOpts) {
@@ -156,13 +160,8 @@ function wrapDefaultSniCallback(greenlock, secureOpts) {
156
160
  }
157
161
  */
158
162
  if (secureOpts.SNICallback) {
159
- console.warn();
160
- console.warn("[warning] Ignoring the given tlsOptions.SNICallback function.");
161
- console.warn();
162
- console.warn(" We're very open to implementing support for this,");
163
- console.warn(" we just don't understand the use case yet.");
164
- console.warn(" Please open an issue to discuss. We'd love to help.");
165
- console.warn();
163
+ log.warn("Ignoring user-provided tlsOptions.SNICallback; Greenlock-managed SNI callback is required");
164
+ log.warn("If you need custom SNI behavior, open an issue to discuss integration support");
166
165
  }
167
166
 
168
167
  // TODO greenlock.servername for workers
@@ -2,6 +2,7 @@
2
2
 
3
3
  var sni = module.exports;
4
4
  var tls = require("tls");
5
+ var log = require("lemonlog")("greenlock-sni");
5
6
  var servernameRe = /^[a-z0-9\.\-]+$/i;
6
7
 
7
8
  // a nice, round, irrational number - about every 6¼ hours
@@ -32,23 +33,21 @@ sni.create = function(greenlock, secureOpts) {
32
33
  // TODO _notify() or notify()?
33
34
  (greenlock.notify || greenlock._notify)(ev, args);
34
35
  } catch (e) {
35
- console.error(e);
36
- console.error(ev, args);
36
+ log.error(e);
37
+ log.error(ev, args);
37
38
  }
38
39
  }
39
40
 
40
41
  function getSecureContext(servername, cb) {
41
- //console.log("debug sni", servername);
42
42
  if ("string" !== typeof servername) {
43
43
  // this will never happen... right? but stranger things have...
44
- console.error("[sanity fail] non-string servername:", servername);
44
+ log.error("[sanity fail] non-string servername:", servername);
45
45
  cb(new Error("invalid servername"), null);
46
46
  return;
47
47
  }
48
48
 
49
49
  var secureContext = getCachedContext(servername);
50
50
  if (secureContext) {
51
- //console.log("debug sni got cached context", servername, getCachedMeta(servername));
52
51
  cb(null, secureContext);
53
52
  return;
54
53
  }
@@ -56,20 +55,19 @@ sni.create = function(greenlock, secureOpts) {
56
55
  getFreshContext(servername)
57
56
  .then(function(secureContext) {
58
57
  if (secureContext) {
59
- //console.log("debug sni got fresh context", servername, getCachedMeta(servername));
60
58
  cb(null, secureContext);
61
59
  return;
62
60
  }
63
61
 
64
62
  // Note: this does not replace tlsSocket.setSecureContext()
65
63
  // as it only works when SNI has been sent
66
- //console.log("debug sni got default context", servername, getCachedMeta(servername));
67
64
  if (!/PROD/.test(process.env.ENV) || /DEV|STAG/.test(process.env.ENV)) {
68
65
  // Change this once
69
66
  // A) the 'notify' message passing is verified fixed in cluster mode
70
67
  // B) we have a good way to let people know their server isn't configured
71
- console.debug("debug: ignoring servername " + JSON.stringify(servername));
72
- console.debug(" (it's probably either missing from your config, or a bot)");
68
+ log.debug("SNI servername not configured; falling back to default context", {
69
+ servername: servername
70
+ });
73
71
  notify("servername_unknown", {
74
72
  servername: servername
75
73
  });
@@ -81,7 +79,6 @@ sni.create = function(greenlock, secureOpts) {
81
79
  err.context = "sni_callback";
82
80
  }
83
81
  notify("error", err);
84
- //console.log("debug sni error", servername, err);
85
82
  cb(err);
86
83
  });
87
84
  }
@@ -125,8 +122,9 @@ sni.create = function(greenlock, secureOpts) {
125
122
  // Change this once
126
123
  // A) the 'notify' message passing is verified fixed in cluster mode
127
124
  // B) we have a good way to let people know their server isn't configured
128
- console.debug("debug: invalid servername " + JSON.stringify(servername));
129
- console.debug(" (it's probably just a bot trolling for vulnerable servers)");
125
+ log.debug("Invalid SNI servername received; skipping certificate lookup", {
126
+ servername: servername
127
+ });
130
128
  notify("servername_invalid", {
131
129
  servername: servername
132
130
  });