@ -4,8 +4,9 @@ var DAY = 24 * 60 * 60 * 1000;
//var MIN = 60 * 1000;
var ACME = require ( 'acme-v2/compat' ) . ACME ;
var LE = module . exports ;
LE . LE = LE ;
var Greenlock = module . exports ;
Greenlock . Greenlock = Greenlock ;
Greenlock . LE = Greenlock ;
// in-process cache, shared between all instances
var ipc = { } ;
@ -13,12 +14,12 @@ function _log(debug) {
if ( debug ) {
var args = Array . prototype . slice . call ( arguments ) ;
args . shift ( ) ;
args . unshift ( "[le /index.js]" ) ;
args . unshift ( "[g l/index.js]" ) ;
console . log . apply ( console , args ) ;
}
}
LE . defaults = {
Greenlock . defaults = {
productionServerUrl : 'https://acme-v01.api.letsencrypt.org/directory'
, stagingServerUrl : 'https://acme-staging.api.letsencrypt.org/directory'
@ -30,13 +31,13 @@ LE.defaults = {
} ;
// backwards compat
Object . keys ( LE . defaults ) . forEach ( function ( key ) {
LE [ key ] = LE . defaults [ key ] ;
Object . keys ( Greenlock . defaults ) . forEach ( function ( key ) {
Greenlock [ key ] = Greenlock . defaults [ key ] ;
} ) ;
// show all possible options
var u ; // undefined
LE . _ undefined = {
Greenlock . _ undefined = {
acme : u
, store : u
, challenge : u
@ -59,56 +60,51 @@ LE._undefined = {
, duplicate : u
, _ acmeUrls : u
} ;
LE . _ undefine = function ( le ) {
Object . keys ( LE . _ undefined ) . forEach ( function ( key ) {
if ( ! ( key in le ) ) {
le [ key ] = u ;
Greenlock . _ undefine = function ( g l) {
Object . keys ( Greenlock . _ undefined ) . forEach ( function ( key ) {
if ( ! ( key in g l) ) {
g l[ key ] = u ;
}
} ) ;
return le ;
return g l;
} ;
LE . create = function ( le ) {
Greenlock . create = function ( g l) {
var PromiseA = require ( 'bluebird' ) ;
le . store = le . store || require ( 'le-store-certbot' ) . create ( { debug : le . debug } ) ;
le . core = require ( './lib/core' ) ;
var log = le . log || _ log ;
g l. store = g l. store || require ( 'le-store-certbot' ) . create ( { debug : g l. debug } ) ;
g l. core = require ( './lib/core' ) ;
var log = g l. log || _ log ;
if ( ! le . challenges ) {
le . challenges = { } ;
if ( ! g l. challenges ) {
g l. challenges = { } ;
}
if ( ! le . challenges [ 'http-01' ] ) {
le . challenges [ 'http-01' ] = require ( 'le-challenge-fs' ) . create ( { debug : le . debug } ) ;
if ( ! g l. challenges [ 'http-01' ] ) {
g l. challenges [ 'http-01' ] = require ( 'le-challenge-fs' ) . create ( { debug : g l. debug } ) ;
}
/ *
if ( ! le . challenges [ 'tls-sni-01' ] ) {
le . challenges [ 'tls-sni-01' ] = require ( 'le-challenge-sni' ) . create ( { debug : le . debug } ) ;
}
* /
if ( ! le . challenges [ 'dns-01' ] ) {
if ( ! gl . challenges [ 'dns-01' ] ) {
try {
le . challenges [ 'dns-01' ] = require ( 'le-challenge-ddns' ) . create ( { debug : le . debug } ) ;
gl . challenges [ 'dns-01' ] = require ( 'le-challenge-ddns' ) . create ( { debug : gl . debug } ) ;
} catch ( e ) {
try {
le . challenges [ 'dns-01' ] = require ( 'le-challenge-dns' ) . create ( { debug : le . debug } ) ;
gl . challenges [ 'dns-01' ] = require ( 'le-challenge-dns' ) . create ( { debug : gl . debug } ) ;
} catch ( e ) {
// not yet implemented
}
}
}
le = LE . _ undefine ( le ) ;
le . acmeChallengePrefix = LE . acmeChallengePrefix ;
le . rsaKeySize = le . rsaKeySize || LE . rsaKeySize ;
le . challengeType = le . challengeType || LE . challengeType ;
le . _ ipc = ipc ;
le . _ communityPackage = le . _ communityPackage || 'greenlock.js' ;
le . agreeToTerms = le . agreeToTerms || function ( args , agreeCb ) {
agreeCb ( new Error ( "'agreeToTerms' was not supplied to LE and 'agreeTos' was not supplied to LE .register" ) ) ;
g l = Greenlock . _ undefine ( g l) ;
g l. acmeChallengePrefix = Greenlock . acmeChallengePrefix ;
g l. rsaKeySize = g l. rsaKeySize || Greenlock . rsaKeySize ;
g l. challengeType = g l. challengeType || Greenlock . challengeType ;
g l. _ ipc = ipc ;
g l. _ communityPackage = g l. _ communityPackage || 'greenlock.js' ;
g l. agreeToTerms = g l. agreeToTerms || function ( args , agreeCb ) {
agreeCb ( new Error ( "'agreeToTerms' was not supplied to Greenlock and 'agreeTos' was not supplied to Greenlock .register" ) ) ;
} ;
if ( ! le . renewWithin ) { le . renewWithin = 14 * DAY ; }
if ( ! g l. renewWithin ) { g l. renewWithin = 14 * DAY ; }
// renewBy has a default in le-sni-auto
@ -117,7 +113,7 @@ LE.create = function (le) {
// BEGIN VERSION MADNESS //
///////////////////////////
if ( ! le . version ) {
if ( ! g l. version ) {
//console.warn("Please specify version: 'v01' (Let's Encrypt v1) or 'draft-11' (Let's Encrypt v2 / ACME draft 11)");
console . warn ( "" ) ;
console . warn ( "" ) ;
@ -141,40 +137,40 @@ LE.create = function (le) {
console . warn ( "" ) ;
console . warn ( "" ) ;
console . warn ( "" ) ;
} else if ( 'v02' === le . version ) {
le . version = 'draft-11' ;
} else if ( 'v01' !== le . version && 'draft-11' !== le . version ) {
throw new Error ( "Unrecognized version '" + le . version + "'" ) ;
} else if ( 'v02' === g l. version ) {
g l. version = 'draft-11' ;
} else if ( 'v01' !== g l. version && 'draft-11' !== g l. version ) {
throw new Error ( "Unrecognized version '" + g l. version + "'" ) ;
}
if ( ! le . server ) {
if ( ! g l. server ) {
throw new Error ( "opts.server must specify an ACME directory URL, such as 'https://acme-staging-v02.api.letsencrypt.org/directory'" ) ;
}
if ( 'staging' === le . server ) {
le . server = 'https://acme-staging.api.letsencrypt.org/directory' ;
le . version = 'v01' ;
if ( 'staging' === g l. server ) {
g l. server = 'https://acme-staging.api.letsencrypt.org/directory' ;
g l. version = 'v01' ;
console . warn ( "" ) ;
console . warn ( "" ) ;
console . warn ( "=== WARNING ===" ) ;
console . warn ( "" ) ;
console . warn ( "Due to versioning issues the 'staging' option is deprecated. Please specify the full url and version." ) ;
console . warn ( "" ) ;
console . warn ( "\t--acme-url '" + le . server + "' \\" ) ;
console . warn ( "\t--acme-version '" + le . version + "' \\" ) ;
console . warn ( "\t--acme-url '" + g l. server + "' \\" ) ;
console . warn ( "\t--acme-version '" + g l. version + "' \\" ) ;
console . warn ( "" ) ;
console . warn ( "" ) ;
}
else if ( 'production' === le . server ) {
le . server = 'https://acme-v01.api.letsencrypt.org/directory' ;
le . version = 'v01' ;
else if ( 'production' === g l. server ) {
g l. server = 'https://acme-v01.api.letsencrypt.org/directory' ;
g l. version = 'v01' ;
console . warn ( "" ) ;
console . warn ( "" ) ;
console . warn ( "=== WARNING ===" ) ;
console . warn ( "" ) ;
console . warn ( "Due to versioning issues the 'production' option is deprecated. Please specify the full url and version." ) ;
console . warn ( "" ) ;
console . warn ( "\t--acme-url '" + le . server + "' \\" ) ;
console . warn ( "\t--acme-version '" + le . version + "' \\" ) ;
console . warn ( "\t--acme-url '" + g l. server + "' \\" ) ;
console . warn ( "\t--acme-version '" + g l. version + "' \\" ) ;
console . warn ( "" ) ;
console . warn ( "" ) ;
}
@ -202,23 +198,23 @@ LE.create = function (le) {
if ( - 1 !== [
'https://acme-v02.api.letsencrypt.org/directory'
, 'https://acme-staging-v02.api.letsencrypt.org/directory' ] . indexOf ( le . server )
, 'https://acme-staging-v02.api.letsencrypt.org/directory' ] . indexOf ( g l. server )
) {
if ( 'draft-11' !== le . version ) {
if ( 'draft-11' !== g l. version ) {
console . warn ( "Detected Let's Encrypt v02 URL. Changing version to draft-11." ) ;
le . version = 'draft-11' ;
g l. version = 'draft-11' ;
}
} else if ( - 1 !== [
'https://acme-v01.api.letsencrypt.org/directory'
, 'https://acme-staging.api.letsencrypt.org/directory' ] . indexOf ( le . server )
|| 'v01' === le . version
, 'https://acme-staging.api.letsencrypt.org/directory' ] . indexOf ( g l. server )
|| 'v01' === g l. version
) {
if ( 'v01' !== le . version ) {
if ( 'v01' !== g l. version ) {
console . warn ( "Detected Let's Encrypt v01 URL (deprecated). Changing version to v01." ) ;
le . version = 'v01' ;
g l. version = 'v01' ;
}
}
if ( 'v01' === le . version ) {
if ( 'v01' === g l. version ) {
ACME = loadLeV01 ( ) ;
}
/////////////////////////
@ -227,28 +223,28 @@ LE.create = function (le) {
le . acme = le . acme || ACME . create ( { debug : le . debug } ) ;
if ( le . acme . create ) {
le . acme = le . acme . create ( le ) ;
g l. acme = g l. acme || ACME . create ( { debug : g l. debug } ) ;
if ( g l. acme . create ) {
g l. acme = g l. acme . create ( g l) ;
}
le . acme = PromiseA . promisifyAll ( le . acme ) ;
le . _ acmeOpts = le . acme . getOptions ( ) ;
Object . keys ( le . _ acmeOpts ) . forEach ( function ( key ) {
if ( ! ( key in le ) ) {
le [ key ] = le . _ acmeOpts [ key ] ;
g l. acme = PromiseA . promisifyAll ( g l. acme ) ;
g l. _ acmeOpts = g l. acme . getOptions ( ) ;
Object . keys ( g l. _ acmeOpts ) . forEach ( function ( key ) {
if ( ! ( key in g l) ) {
g l[ key ] = g l. _ acmeOpts [ key ] ;
}
} ) ;
if ( le . store . create ) {
le . store = le . store . create ( le ) ;
if ( g l. store . create ) {
g l. store = g l. store . create ( g l) ;
}
le . store = PromiseA . promisifyAll ( le . store ) ;
le . store . accounts = PromiseA . promisifyAll ( le . store . accounts ) ;
le . store . certificates = PromiseA . promisifyAll ( le . store . certificates ) ;
le . _ storeOpts = le . store . getOptions ( ) ;
Object . keys ( le . _ storeOpts ) . forEach ( function ( key ) {
if ( ! ( key in le ) ) {
le [ key ] = le . _ storeOpts [ key ] ;
g l. store = PromiseA . promisifyAll ( g l. store ) ;
g l. store . accounts = PromiseA . promisifyAll ( g l. store . accounts ) ;
g l. store . certificates = PromiseA . promisifyAll ( g l. store . certificates ) ;
g l. _ storeOpts = g l. store . getOptions ( ) ;
Object . keys ( g l. _ storeOpts ) . forEach ( function ( key ) {
if ( ! ( key in g l) ) {
g l[ key ] = g l. _ storeOpts [ key ] ;
}
} ) ;
@ -256,118 +252,118 @@ LE.create = function (le) {
//
// Backwards compat for <= v2.1.7
//
if ( le . challenge ) {
console . warn ( "Deprecated use of le .challenge. Use le .challenges['" + LE . challengeType + "'] instead." ) ;
le . challenges [ le . challengeType ] = le . challenge ;
if ( g l. challenge ) {
console . warn ( "Deprecated use of g l.challenge. Use g l.challenges['" + Greenlock . challengeType + "'] instead." ) ;
g l. challenges [ g l. challengeType ] = g l. challenge ;
}
LE . challengeTypes . forEach ( function ( challengeType ) {
var challenger = le . challenges [ challengeType ] ;
Greenlock . challengeTypes . forEach ( function ( challengeType ) {
var challenger = g l. challenges [ challengeType ] ;
if ( ! challenger ) {
return ;
}
if ( challenger . create ) {
challenger = le . challenges [ challengeType ] = challenger . create ( le ) ;
challenger = g l. challenges [ challengeType ] = challenger . create ( g l) ;
}
challenger = le . challenges [ challengeType ] = PromiseA . promisifyAll ( challenger ) ;
le [ '_challengeOpts_' + challengeType ] = challenger . getOptions ( ) ;
Object . keys ( le [ '_challengeOpts_' + challengeType ] ) . forEach ( function ( key ) {
if ( ! ( key in le ) ) {
le [ key ] = le [ '_challengeOpts_' + challengeType ] [ key ] ;
challenger = g l. challenges [ challengeType ] = PromiseA . promisifyAll ( challenger ) ;
g l[ '_challengeOpts_' + challengeType ] = challenger . getOptions ( ) ;
Object . keys ( g l[ '_challengeOpts_' + challengeType ] ) . forEach ( function ( key ) {
if ( ! ( key in g l) ) {
g l[ key ] = g l[ '_challengeOpts_' + challengeType ] [ key ] ;
}
} ) ;
// TODO wrap these here and now with tplCopy?
if ( ! challenger . set || 5 !== challenger . set . length ) {
throw new Error ( "le .challenges[" + challengeType + "].set receives the wrong number of arguments."
throw new Error ( "g l.challenges[" + challengeType + "].set receives the wrong number of arguments."
+ " You must define setChallenge as function (opts, domain, token, keyAuthorization, cb) { }" ) ;
}
if ( challenger . get && 4 !== challenger . get . length ) {
throw new Error ( "le .challenges[" + challengeType + "].get receives the wrong number of arguments."
throw new Error ( "g l.challenges[" + challengeType + "].get receives the wrong number of arguments."
+ " You must define getChallenge as function (opts, domain, token, cb) { }" ) ;
}
if ( ! challenger . remove || 4 !== challenger . remove . length ) {
throw new Error ( "le .challenges[" + challengeType + "].remove receives the wrong number of arguments."
throw new Error ( "g l.challenges[" + challengeType + "].remove receives the wrong number of arguments."
+ " You must define removeChallenge as function (opts, domain, token, cb) { }" ) ;
}
/ *
if ( ! le . _ challengeWarn && ( ! challenger . loopback || 4 !== challenger . loopback . length ) ) {
le . _ challengeWarn = true ;
console . warn ( "le .challenges[" + challengeType + "].loopback should be defined as function (opts, domain, token, cb) { ... } and should prove (by external means) that the ACME server challenge '" + challengeType + "' will succeed" ) ;
if ( ! g l. _ challengeWarn && ( ! challenger . loopback || 4 !== challenger . loopback . length ) ) {
g l. _ challengeWarn = true ;
console . warn ( "g l.challenges[" + challengeType + "].loopback should be defined as function (opts, domain, token, cb) { ... } and should prove (by external means) that the ACME server challenge '" + challengeType + "' will succeed" ) ;
}
else if ( ! le . _ challengeWarn && ( ! challenger . test || 5 !== challenger . test . length ) ) {
le . _ challengeWarn = true ;
console . warn ( "le .challenges[" + challengeType + "].test should be defined as function (opts, domain, token, keyAuthorization, cb) { ... } and should prove (by external means) that the ACME server challenge '" + challengeType + "' will succeed" ) ;
else if ( ! g l. _ challengeWarn && ( ! challenger . test || 5 !== challenger . test . length ) ) {
g l. _ challengeWarn = true ;
console . warn ( "g l.challenges[" + challengeType + "].test should be defined as function (opts, domain, token, keyAuthorization, cb) { ... } and should prove (by external means) that the ACME server challenge '" + challengeType + "' will succeed" ) ;
}
* /
} ) ;
le . sni = le . sni || null ;
le . tlsOptions = le . tlsOptions || le . httpsOptions || { } ;
if ( ! le . tlsOptions . SNICallback ) {
if ( ! le . getCertificatesAsync && ! le . getCertificates ) {
if ( Array . isArray ( le . approveDomains ) ) {
le . approvedDomains = le . approveDomains ;
le . approveDomains = null ;
g l. sni = g l. sni || null ;
g l. tlsOptions = g l. tlsOptions || g l. httpsOptions || { } ;
if ( ! g l. tlsOptions . SNICallback ) {
if ( ! g l. getCertificatesAsync && ! g l. getCertificates ) {
if ( Array . isArray ( g l. approveDomains ) ) {
g l. approvedDomains = g l. approveDomains ;
g l. approveDomains = null ;
}
if ( ! le . approveDomains ) {
le . approvedDomains = le . approvedDomains || [ ] ;
le . approveDomains = function ( lexOpts , certs , cb ) {
if ( ! le . email ) {
if ( ! g l. approveDomains ) {
g l. approvedDomains = g l. approvedDomains || [ ] ;
g l. approveDomains = function ( lexOpts , certs , cb ) {
if ( ! g l. email ) {
throw new Error ( "le-sni-auto is not properly configured. Missing email" ) ;
}
if ( ! le . agreeTos ) {
if ( ! g l. agreeTos ) {
throw new Error ( "le-sni-auto is not properly configured. Missing agreeTos" ) ;
}
if ( ! le . approvedDomains . length ) {
if ( ! g l. approvedDomains . length ) {
throw new Error ( "le-sni-auto is not properly configured. Missing approveDomains(domain, certs, callback)" ) ;
}
if ( lexOpts . domains . every ( function ( domain ) {
return - 1 !== le . approvedDomains . indexOf ( domain ) ;
return - 1 !== g l. approvedDomains . indexOf ( domain ) ;
} ) ) {
lexOpts . domains = le . approvedDomains . slice ( 0 ) ;
lexOpts . email = le . email ;
lexOpts . agreeTos = le . agreeTos ;
lexOpts . domains = g l. approvedDomains . slice ( 0 ) ;
lexOpts . email = g l. email ;
lexOpts . agreeTos = g l. agreeTos ;
lexOpts . communityMember = lexOpts . communityMember ;
return cb ( null , { options : lexOpts , certs : certs } ) ;
}
log ( le . debug , 'unapproved domain' , lexOpts . domains , le . approvedDomains ) ;
log ( g l. debug , 'unapproved domain' , lexOpts . domains , g l. approvedDomains ) ;
cb ( new Error ( "unapproved domain" ) ) ;
} ;
}
le . getCertificates = function ( domain , certs , cb ) {
g l. getCertificates = function ( domain , certs , cb ) {
// certs come from current in-memory cache, not lookup
log ( le . debug , 'le .getCertificates called for' , domain , 'with certs for' , certs && certs . altnames || 'NONE' ) ;
log ( g l. debug , 'g l.getCertificates called for' , domain , 'with certs for' , certs && certs . altnames || 'NONE' ) ;
var opts = { domain : domain , domains : certs && certs . altnames || [ domain ] } ;
try {
le . approveDomains ( opts , certs , function ( _ err , results ) {
g l. approveDomains ( opts , certs , function ( _ err , results ) {
if ( _ err ) {
log ( le . debug , 'le .approveDomains called with error' , _ err ) ;
log ( g l. debug , 'g l.approveDomains called with error' , _ err ) ;
cb ( _ err ) ;
return ;
}
log ( le . debug , 'le .approveDomains called with certs for' , results . certs && results . certs . altnames || 'NONE' , 'and options:' ) ;
log ( le . debug , results . options ) ;
log ( g l. debug , 'g l.approveDomains called with certs for' , results . certs && results . certs . altnames || 'NONE' , 'and options:' ) ;
log ( g l. debug , results . options ) ;
var promise ;
if ( results . certs ) {
log ( le . debug , 'le renewing' ) ;
promise = le . core . certificates . renewAsync ( results . options , results . certs ) ;
log ( g l. debug , 'g l renewing' ) ;
promise = g l. core . certificates . renewAsync ( results . options , results . certs ) ;
}
else {
log ( le . debug , 'le getting from disk or registering new' ) ;
promise = le . core . certificates . getAsync ( results . options ) ;
log ( g l. debug , 'g l getting from disk or registering new' ) ;
promise = g l. core . certificates . getAsync ( results . options ) ;
}
return promise . then ( function ( certs ) { cb ( null , certs ) ; } , function ( e ) {
if ( le . debug ) { console . debug ( "Error" ) ; console . debug ( e ) ; }
if ( g l. debug ) { console . debug ( "Error" ) ; console . debug ( e ) ; }
cb ( e ) ;
} ) ;
} ) ;
@ -378,13 +374,13 @@ LE.create = function (le) {
}
} ;
}
le . sni = le . sni || require ( 'le-sni-auto' ) ;
if ( le . sni . create ) {
le . sni = le . sni . create ( le ) ;
g l. sni = g l. sni || require ( 'le-sni-auto' ) ;
if ( g l. sni . create ) {
g l. sni = g l. sni . create ( g l) ;
}
le . tlsOptions . SNICallback = function ( domain , cb ) {
g l. tlsOptions . SNICallback = function ( domain , cb ) {
try {
le . sni . sniCallback ( domain , cb ) ;
g l. sni . sniCallback ( domain , cb ) ;
} catch ( e ) {
console . error ( "[ERROR] Something went wrong in the SNICallback:" ) ;
console . error ( e ) ;
@ -395,29 +391,29 @@ LE.create = function (le) {
// We want to move to using tlsOptions instead of httpsOptions, but we also need to make
// sure anything that uses this object will still work if looking for httpsOptions.
le . httpsOptions = le . tlsOptions ;
g l. httpsOptions = g l. tlsOptions ;
if ( le . core . create ) {
le . core = le . core . create ( le ) ;
if ( g l. core . create ) {
g l. core = g l. core . create ( g l) ;
}
le . renew = function ( args , certs ) {
return le . core . certificates . renewAsync ( args , certs ) ;
g l. renew = function ( args , certs ) {
return g l. core . certificates . renewAsync ( args , certs ) ;
} ;
le . register = function ( args ) {
return le . core . certificates . getAsync ( args ) ;
g l. register = function ( args ) {
return g l. core . certificates . getAsync ( args ) ;
} ;
le . check = function ( args ) {
g l. check = function ( args ) {
// TODO must return email, domains, tos, pems
return le . core . certificates . checkAsync ( args ) ;
return g l. core . certificates . checkAsync ( args ) ;
} ;
le . middleware = le . middleware || require ( './lib/middleware' ) ;
if ( le . middleware . create ) {
le . middleware = le . middleware . create ( le ) ;
g l. middleware = g l. middleware || require ( './lib/middleware' ) ;
if ( g l. middleware . create ) {
g l. middleware = g l. middleware . create ( g l) ;
}
return le ;
return g l;
} ;