Compare commits
51 Commits
Author | SHA1 | Date | |
---|---|---|---|
cf8812c932 | |||
5bc3e474a9 | |||
13eaa346bc | |||
d7437cc4a7 | |||
ddb3c6590c | |||
89d6773bda | |||
3651b9484f | |||
2200c950b7 | |||
14f84f01b5 | |||
cb014027bb | |||
32e5155783 | |||
a3159ad59e | |||
60a40197f1 | |||
7d693a4271 | |||
f192748bf9 | |||
96f401cba6 | |||
ffd2430160 | |||
190cd2d6bb | |||
7ba58bdbd3 | |||
08ab62108f | |||
1028de8158 | |||
301499dc52 | |||
5c2f13ed8e | |||
e30ad81f69 | |||
21f36f535f | |||
c14236823a | |||
551a9fe1c6 | |||
e3399e1035 | |||
c413b4af3f | |||
dbf5dec23b | |||
10f0eb17d7 | |||
e3b680c351 | |||
0df0545777 | |||
165bfc9f5f | |||
5830bd73b9 | |||
3c4ce839b9 | |||
ac9f052309 | |||
049e424957 | |||
07e78aec48 | |||
3fec135fe5 | |||
867fe1322b | |||
95208a50a7 | |||
514b13fcc2 | |||
4cbc1f5bbe | |||
64de36cdf2 | |||
093b48ad7b | |||
05f6a55a0b | |||
2523424f68 | |||
b81325d7bf | |||
3e10b7b2b1 | |||
e5cb750015 |
1
Jenkinsfile
vendored
1
Jenkinsfile
vendored
@ -65,6 +65,7 @@ pipeline {
|
|||||||
// See: https://github.com/yarnpkg/yarn/issues/3254
|
// See: https://github.com/yarnpkg/yarn/issues/3254
|
||||||
sh '''docker run --rm \\
|
sh '''docker run --rm \\
|
||||||
-v "$(pwd)/backend:/app" \\
|
-v "$(pwd)/backend:/app" \\
|
||||||
|
-v "$(pwd)/global:/app/global" \\
|
||||||
-w /app \\
|
-w /app \\
|
||||||
node:latest \\
|
node:latest \\
|
||||||
sh -c "yarn install && yarn eslint . && rm -rf node_modules"
|
sh -c "yarn install && yarn eslint . && rm -rf node_modules"
|
||||||
|
22
README.md
22
README.md
@ -1,7 +1,7 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://nginxproxymanager.com/github.png">
|
<img src="https://nginxproxymanager.com/github.png">
|
||||||
<br><br>
|
<br><br>
|
||||||
<img src="https://img.shields.io/badge/version-2.5.0-green.svg?style=for-the-badge">
|
<img src="https://img.shields.io/badge/version-2.6.2-green.svg?style=for-the-badge">
|
||||||
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
|
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
|
||||||
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
|
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
|
||||||
</a>
|
</a>
|
||||||
@ -185,6 +185,26 @@ Special thanks to the following contributors:
|
|||||||
<br /><sub><b>Jaap-Jan de Wit</b></sub>
|
<br /><sub><b>Jaap-Jan de Wit</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/jmwebslave">
|
||||||
|
<img src="https://avatars2.githubusercontent.com/u/6118262?s=460&u=7db409c47135b1e141c366bbb03ed9fae6ac2638&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>James Morgan</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/chaptergy">
|
||||||
|
<img src="https://avatars2.githubusercontent.com/u/26956711?s=460&u=7d9adebabb6b4e7af7cb05d98d751087a372304b&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>chaptergy</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/Philip-Mooney">
|
||||||
|
<img src="https://avatars0.githubusercontent.com/u/48624631?s=460&v=4" width="80px;" alt=""/>
|
||||||
|
<br /><sub><b>Philip Mooney</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<!-- markdownlint-enable -->
|
<!-- markdownlint-enable -->
|
||||||
|
@ -66,7 +66,7 @@ app.use(function (err, req, res, next) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development' || (req.baseUrl + req.path).includes('nginx/certificates')) {
|
||||||
payload.debug = {
|
payload.debug = {
|
||||||
stack: typeof err.stack !== 'undefined' && err.stack ? err.stack.split('\n') : null,
|
stack: typeof err.stack !== 'undefined' && err.stack ? err.stack.split('\n') : null,
|
||||||
previous: err.previous
|
previous: err.previous
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"knex": {
|
"knex": {
|
||||||
"client": "sqlite3",
|
"client": "sqlite3",
|
||||||
"connection": {
|
"connection": {
|
||||||
"filename": "/app/backend/config/mydb.sqlite"
|
"filename": "/app/config/mydb.sqlite"
|
||||||
},
|
},
|
||||||
"pool": {
|
"pool": {
|
||||||
"min": 0,
|
"min": 0,
|
||||||
|
@ -31,6 +31,7 @@ const internalAccessList = {
|
|||||||
.insertAndFetch({
|
.insertAndFetch({
|
||||||
name: data.name,
|
name: data.name,
|
||||||
satisfy_any: data.satisfy_any,
|
satisfy_any: data.satisfy_any,
|
||||||
|
pass_auth: data.pass_auth,
|
||||||
owner_user_id: access.token.getUserId(1)
|
owner_user_id: access.token.getUserId(1)
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@ -128,6 +129,7 @@ const internalAccessList = {
|
|||||||
.patch({
|
.patch({
|
||||||
name: data.name,
|
name: data.name,
|
||||||
satisfy_any: data.satisfy_any,
|
satisfy_any: data.satisfy_any,
|
||||||
|
pass_auth: data.pass_auth,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -384,7 +386,7 @@ const internalAccessList = {
|
|||||||
.orderBy('access_list.name', 'ASC');
|
.orderBy('access_list.name', 'ASC');
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
query.andWhere('owner_user_id', access.token.getUserId(1));
|
query.andWhere('access_list.owner_user_id', access.token.getUserId(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query is used for searching
|
// Query is used for searching
|
||||||
|
@ -13,6 +13,7 @@ const internalNginx = require('./nginx');
|
|||||||
const internalHost = require('./host');
|
const internalHost = require('./host');
|
||||||
const certbot_command = '/usr/bin/certbot';
|
const certbot_command = '/usr/bin/certbot';
|
||||||
const le_config = '/etc/letsencrypt.ini';
|
const le_config = '/etc/letsencrypt.ini';
|
||||||
|
const dns_plugins = require('../global/certbot-dns-plugins');
|
||||||
|
|
||||||
function omissions() {
|
function omissions() {
|
||||||
return ['is_deleted'];
|
return ['is_deleted'];
|
||||||
@ -141,11 +142,11 @@ const internalCertificate = {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then((in_use_result) => {
|
.then((in_use_result) => {
|
||||||
// Is CloudFlare, no config needed, so skip 3 and 5.
|
// With DNS challenge no config is needed, so skip 3 and 5.
|
||||||
if (data.meta.cloudflare_use) {
|
if (certificate.meta.dns_challenge) {
|
||||||
return internalNginx.reload().then(() => {
|
return internalNginx.reload().then(() => {
|
||||||
// 4. Request cert
|
// 4. Request cert
|
||||||
return internalCertificate.requestLetsEncryptCloudFlareDnsSsl(certificate, data.meta.cloudflare_token);
|
return internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate);
|
||||||
})
|
})
|
||||||
.then(internalNginx.reload)
|
.then(internalNginx.reload)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -607,12 +608,12 @@ const internalCertificate = {
|
|||||||
checkPrivateKey: (private_key) => {
|
checkPrivateKey: (private_key) => {
|
||||||
return tempWrite(private_key, '/tmp')
|
return tempWrite(private_key, '/tmp')
|
||||||
.then((filepath) => {
|
.then((filepath) => {
|
||||||
return utils.exec('openssl rsa -in ' + filepath + ' -check -noout')
|
let key_type = private_key.includes('-----BEGIN RSA') ? 'rsa' : 'ec';
|
||||||
|
return utils.exec('openssl ' + key_type + ' -in ' + filepath + ' -check -noout 2>&1 ')
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
if (!result.toLowerCase().includes('key ok')) {
|
if (!result.toLowerCase().includes('key ok') && !result.toLowerCase().includes('key valid') ) {
|
||||||
throw new error.ValidationError(result);
|
throw new error.ValidationError('Result Validation Error: ' + result);
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.unlinkSync(filepath);
|
fs.unlinkSync(filepath);
|
||||||
return true;
|
return true;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
@ -773,34 +774,71 @@ const internalCertificate = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} certificate the certificate row
|
* @param {Object} certificate the certificate row
|
||||||
* @param {String} apiToken the cloudflare api token
|
* @param {String} dns_provider the dns provider name (key used in `certbot-dns-plugins.js`)
|
||||||
|
* @param {String | null} credentials the content of this providers credentials file
|
||||||
|
* @param {String} propagation_seconds the cloudflare api token
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
requestLetsEncryptCloudFlareDnsSsl: (certificate, apiToken) => {
|
requestLetsEncryptSslWithDnsChallenge: (certificate) => {
|
||||||
logger.info('Requesting Let\'sEncrypt certificates via Cloudflare DNS for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
|
||||||
|
|
||||||
let tokenLoc = '~/cloudflare-token';
|
if (!dns_plugin) {
|
||||||
let storeKey = 'echo "dns_cloudflare_api_token = ' + apiToken + '" > ' + tokenLoc;
|
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
|
||||||
|
}
|
||||||
|
|
||||||
let cmd =
|
logger.info(`Requesting Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||||
storeKey + ' && ' +
|
|
||||||
|
const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
||||||
|
const credentials_cmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'';
|
||||||
|
const prepare_cmd = 'pip3 install ' + dns_plugin.package_name + '==' + dns_plugin.package_version + ' ' + dns_plugin.dependencies;
|
||||||
|
|
||||||
|
// Whether the plugin has a --<name>-credentials argument
|
||||||
|
const has_config_arg = certificate.meta.dns_provider !== 'route53';
|
||||||
|
|
||||||
|
let main_cmd =
|
||||||
certbot_command + ' certonly --non-interactive ' +
|
certbot_command + ' certonly --non-interactive ' +
|
||||||
'--cert-name "npm-' + certificate.id + '" ' +
|
'--cert-name "npm-' + certificate.id + '" ' +
|
||||||
'--agree-tos ' +
|
'--agree-tos ' +
|
||||||
'--email "' + certificate.meta.letsencrypt_email + '" ' +
|
'--email "' + certificate.meta.letsencrypt_email + '" ' +
|
||||||
'--domains "' + certificate.domain_names.join(',') + '" ' +
|
'--domains "' + certificate.domain_names.join(',') + '" ' +
|
||||||
'--dns-cloudflare --dns-cloudflare-credentials ' + tokenLoc +
|
'--authenticator ' + dns_plugin.full_plugin_name + ' ' +
|
||||||
(le_staging ? ' --staging' : '')
|
(
|
||||||
+ ' && rm ' + tokenLoc;
|
has_config_arg
|
||||||
|
? '--' + dns_plugin.full_plugin_name + '-credentials "' + credentials_loc + '"'
|
||||||
|
: ''
|
||||||
|
) +
|
||||||
|
(
|
||||||
|
certificate.meta.propagation_seconds !== undefined
|
||||||
|
? ' --' + dns_plugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds
|
||||||
|
: ''
|
||||||
|
) +
|
||||||
|
(le_staging ? ' --staging' : '');
|
||||||
|
|
||||||
if (debug_mode) {
|
// Prepend the path to the credentials file as an environment variable
|
||||||
logger.info('Command:', cmd);
|
if (certificate.meta.dns_provider === 'route53') {
|
||||||
|
main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
return utils.exec(cmd).then((result) => {
|
if (debug_mode) {
|
||||||
|
logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils.exec(credentials_cmd)
|
||||||
|
.then(() => {
|
||||||
|
return utils.exec(prepare_cmd)
|
||||||
|
.then(() => {
|
||||||
|
return utils.exec(main_cmd)
|
||||||
|
.then(async (result) => {
|
||||||
logger.info(result);
|
logger.info(result);
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
}).catch(async (err) => {
|
||||||
|
// Don't fail if file does not exist
|
||||||
|
const delete_credentials_cmd = `rm -f '${credentials_loc}' || true`;
|
||||||
|
await utils.exec(delete_credentials_cmd);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
@ -817,7 +855,7 @@ const internalCertificate = {
|
|||||||
})
|
})
|
||||||
.then((certificate) => {
|
.then((certificate) => {
|
||||||
if (certificate.provider === 'letsencrypt') {
|
if (certificate.provider === 'letsencrypt') {
|
||||||
let renewMethod = certificate.meta.cloudflare_use ? internalCertificate.renewLetsEncryptCloudFlareSsl : internalCertificate.renewLetsEncryptSsl;
|
let renewMethod = certificate.meta.dns_challenge ? internalCertificate.renewLetsEncryptSslWithDnsChallenge : internalCertificate.renewLetsEncryptSsl;
|
||||||
|
|
||||||
return renewMethod(certificate)
|
return renewMethod(certificate)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -877,20 +915,33 @@ const internalCertificate = {
|
|||||||
* @param {Object} certificate the certificate row
|
* @param {Object} certificate the certificate row
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
renewLetsEncryptCloudFlareSsl: (certificate) => {
|
renewLetsEncryptSslWithDnsChallenge: (certificate) => {
|
||||||
logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
|
||||||
|
|
||||||
let cmd = certbot_command + ' renew --non-interactive ' +
|
if (!dns_plugin) {
|
||||||
'--cert-name "npm-' + certificate.id + '" ' +
|
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
|
||||||
'--disable-hook-validation ' +
|
|
||||||
(le_staging ? '--staging' : '');
|
|
||||||
|
|
||||||
if (debug_mode) {
|
|
||||||
logger.info('Command:', cmd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return utils.exec(cmd)
|
logger.info(`Renewing Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||||
.then((result) => {
|
|
||||||
|
let main_cmd =
|
||||||
|
certbot_command + ' renew --non-interactive ' +
|
||||||
|
'--cert-name "npm-' + certificate.id + '" ' +
|
||||||
|
'--disable-hook-validation' +
|
||||||
|
(le_staging ? ' --staging' : '');
|
||||||
|
|
||||||
|
// Prepend the path to the credentials file as an environment variable
|
||||||
|
if (certificate.meta.dns_provider === 'route53') {
|
||||||
|
const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
||||||
|
main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug_mode) {
|
||||||
|
logger.info('Command:', main_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils.exec(main_cmd)
|
||||||
|
.then(async (result) => {
|
||||||
logger.info(result);
|
logger.info(result);
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
@ -904,20 +955,21 @@ const internalCertificate = {
|
|||||||
revokeLetsEncryptSsl: (certificate, throw_errors) => {
|
revokeLetsEncryptSsl: (certificate, throw_errors) => {
|
||||||
logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
|
||||||
|
|
||||||
let cmd = certbot_command + ' revoke --non-interactive ' +
|
const main_cmd = certbot_command + ' revoke --non-interactive ' +
|
||||||
'--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' +
|
'--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' +
|
||||||
'--delete-after-revoke ' +
|
'--delete-after-revoke ' +
|
||||||
(le_staging ? '--staging' : '');
|
(le_staging ? '--staging' : '');
|
||||||
|
|
||||||
|
// Don't fail command if file does not exist
|
||||||
|
const delete_credentials_cmd = `rm -f '/etc/letsencrypt/credentials/credentials-${certificate.id}' || true`;
|
||||||
|
|
||||||
if (debug_mode) {
|
if (debug_mode) {
|
||||||
logger.info('Command:', cmd);
|
logger.info('Command:', main_cmd + '; ' + delete_credentials_cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
return utils.exec(cmd)
|
return utils.exec(main_cmd)
|
||||||
.then((result) => {
|
.then(async (result) => {
|
||||||
if (debug_mode) {
|
await utils.exec(delete_credentials_cmd);
|
||||||
logger.info('Command:', cmd);
|
|
||||||
}
|
|
||||||
logger.info(result);
|
logger.info(result);
|
||||||
return result;
|
return result;
|
||||||
})
|
})
|
||||||
|
41
backend/migrations/20201014143841_pass_auth.js
Normal file
41
backend/migrations/20201014143841_pass_auth.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
const migrate_name = 'pass_auth';
|
||||||
|
const logger = require('../logger').migrate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate
|
||||||
|
*
|
||||||
|
* @see http://knexjs.org/#Schema
|
||||||
|
*
|
||||||
|
* @param {Object} knex
|
||||||
|
* @param {Promise} Promise
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
exports.up = function (knex/*, Promise*/) {
|
||||||
|
|
||||||
|
logger.info('[' + migrate_name + '] Migrating Up...');
|
||||||
|
|
||||||
|
return knex.schema.table('access_list', function (access_list) {
|
||||||
|
access_list.integer('pass_auth').notNull().defaultTo(1);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
logger.info('[' + migrate_name + '] access_list Table altered');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo Migrate
|
||||||
|
*
|
||||||
|
* @param {Object} knex
|
||||||
|
* @param {Promise} Promise
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
exports.down = function (knex/*, Promise*/) {
|
||||||
|
logger.info('[' + migrate_name + '] Migrating Down...');
|
||||||
|
|
||||||
|
return knex.schema.table('access_list', function (access_list) {
|
||||||
|
access_list.dropColumn('pass_auth');
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
logger.info('[' + migrate_name + '] access_list pass_auth Column dropped');
|
||||||
|
});
|
||||||
|
};
|
@ -93,6 +93,10 @@ class AccessList extends Model {
|
|||||||
get satisfy() {
|
get satisfy() {
|
||||||
return this.satisfy_any ? 'satisfy any' : 'satisfy all';
|
return this.satisfy_any ? 'satisfy any' : 'satisfy all';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get passauth() {
|
||||||
|
return this.pass_auth ? '' : 'proxy_set_header Authorization "";';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = AccessList;
|
module.exports = AccessList;
|
||||||
|
@ -58,6 +58,7 @@ router
|
|||||||
.post((req, res, next) => {
|
.post((req, res, next) => {
|
||||||
apiValidator({$ref: 'endpoints/certificates#/links/1/schema'}, req.body)
|
apiValidator({$ref: 'endpoints/certificates#/links/1/schema'}, req.body)
|
||||||
.then((payload) => {
|
.then((payload) => {
|
||||||
|
req.setTimeout(900000); // 15 minutes timeout
|
||||||
return internalCertificate.create(res.locals.access, payload);
|
return internalCertificate.create(res.locals.access, payload);
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
@ -197,6 +198,7 @@ router
|
|||||||
* Renew certificate
|
* Renew certificate
|
||||||
*/
|
*/
|
||||||
.post((req, res, next) => {
|
.post((req, res, next) => {
|
||||||
|
req.setTimeout(900000); // 15 minutes timeout
|
||||||
internalCertificate.renew(res.locals.access, {
|
internalCertificate.renew(res.locals.access, {
|
||||||
id: parseInt(req.params.certificate_id, 10)
|
id: parseInt(req.params.certificate_id, 10)
|
||||||
})
|
})
|
||||||
|
@ -42,6 +42,9 @@
|
|||||||
"satisfy_any": {
|
"satisfy_any": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"pass_auth": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"type": "object"
|
"type": "object"
|
||||||
}
|
}
|
||||||
@ -102,6 +105,9 @@
|
|||||||
"satisfy_any": {
|
"satisfy_any": {
|
||||||
"$ref": "#/definitions/satisfy_any"
|
"$ref": "#/definitions/satisfy_any"
|
||||||
},
|
},
|
||||||
|
"pass_auth": {
|
||||||
|
"$ref": "#/definitions/pass_auth"
|
||||||
|
},
|
||||||
"items": {
|
"items": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"minItems": 0,
|
"minItems": 0,
|
||||||
@ -167,6 +173,9 @@
|
|||||||
"satisfy_any": {
|
"satisfy_any": {
|
||||||
"$ref": "#/definitions/satisfy_any"
|
"$ref": "#/definitions/satisfy_any"
|
||||||
},
|
},
|
||||||
|
"pass_auth": {
|
||||||
|
"$ref": "#/definitions/pass_auth"
|
||||||
|
},
|
||||||
"items": {
|
"items": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"minItems": 0,
|
"minItems": 0,
|
||||||
|
@ -42,11 +42,23 @@
|
|||||||
"letsencrypt_agree": {
|
"letsencrypt_agree": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"cloudflare_use": {
|
"dns_challenge": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"cloudflare_token": {
|
"dns_provider": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"dns_provider_credentials": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"propagation_seconds": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,13 @@ const fs = require('fs');
|
|||||||
const NodeRSA = require('node-rsa');
|
const NodeRSA = require('node-rsa');
|
||||||
const config = require('config');
|
const config = require('config');
|
||||||
const logger = require('./logger').setup;
|
const logger = require('./logger').setup;
|
||||||
|
const certificateModel = require('./models/certificate');
|
||||||
const userModel = require('./models/user');
|
const userModel = require('./models/user');
|
||||||
const userPermissionModel = require('./models/user_permission');
|
const userPermissionModel = require('./models/user_permission');
|
||||||
|
const utils = require('./lib/utils');
|
||||||
const authModel = require('./models/auth');
|
const authModel = require('./models/auth');
|
||||||
const settingModel = require('./models/setting');
|
const settingModel = require('./models/setting');
|
||||||
|
const dns_plugins = require('./global/certbot-dns-plugins');
|
||||||
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -155,8 +158,53 @@ const setupDefaultSettings = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs all Certbot plugins which are required for an installed certificate
|
||||||
|
*
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
const setupCertbotPlugins = () => {
|
||||||
|
return certificateModel
|
||||||
|
.query()
|
||||||
|
.where('is_deleted', 0)
|
||||||
|
.andWhere('provider', 'letsencrypt')
|
||||||
|
.then((certificates) => {
|
||||||
|
if (certificates && certificates.length) {
|
||||||
|
let plugins = [];
|
||||||
|
let promises = [];
|
||||||
|
|
||||||
|
certificates.map(function (certificate) {
|
||||||
|
if (certificate.meta && certificate.meta.dns_challenge === true) {
|
||||||
|
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
|
||||||
|
const packages_to_install = `${dns_plugin.package_name}==${dns_plugin.package_version} ${dns_plugin.dependencies}`;
|
||||||
|
|
||||||
|
if (plugins.indexOf(packages_to_install) === -1) plugins.push(packages_to_install);
|
||||||
|
|
||||||
|
// Make sure credentials file exists
|
||||||
|
const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
||||||
|
const credentials_cmd = '[ -f \'' + credentials_loc + '\' ] || { mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'; }';
|
||||||
|
promises.push(utils.exec(credentials_cmd));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (plugins.length) {
|
||||||
|
const install_cmd = 'pip3 install ' + plugins.join(' ');
|
||||||
|
promises.push(utils.exec(install_cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (promises.length) {
|
||||||
|
return Promise.all(promises)
|
||||||
|
.then(() => {
|
||||||
|
logger.info('Added Certbot plugins ' + plugins.join(', '));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
return setupJwt()
|
return setupJwt()
|
||||||
.then(setupDefaultUser)
|
.then(setupDefaultUser)
|
||||||
.then(setupDefaultSettings);
|
.then(setupDefaultSettings)
|
||||||
|
.then(setupCertbotPlugins);
|
||||||
};
|
};
|
||||||
|
@ -27,6 +27,8 @@ server {
|
|||||||
# Authorization
|
# Authorization
|
||||||
auth_basic "Authorization required";
|
auth_basic "Authorization required";
|
||||||
auth_basic_user_file /data/access/{{ access_list_id }};
|
auth_basic_user_file /data/access/{{ access_list_id }};
|
||||||
|
|
||||||
|
{{ access_list.passauth }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
# Access Rules
|
# Access Rules
|
||||||
@ -35,7 +37,9 @@ server {
|
|||||||
{% endfor %}deny all;
|
{% endfor %}deny all;
|
||||||
|
|
||||||
# Access checks must...
|
# Access checks must...
|
||||||
|
{% if access_list.satisfy %}
|
||||||
{{ access_list.satisfy }};
|
{{ access_list.satisfy }};
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@ ENV NODE_ENV=production
|
|||||||
|
|
||||||
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
||||||
&& apk update \
|
&& apk update \
|
||||||
&& apk add python2 py-pip certbot jq \
|
&& apk add python3 certbot jq \
|
||||||
&& pip install certbot-dns-cloudflare \
|
&& python3 -m ensurepip \
|
||||||
&& rm -rf /var/cache/apk/*
|
&& rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
ENV NPM_BUILD_VERSION="${BUILD_VERSION}" NPM_BUILD_COMMIT="${BUILD_COMMIT}" NPM_BUILD_DATE="${BUILD_DATE}"
|
ENV NPM_BUILD_VERSION="${BUILD_VERSION}" NPM_BUILD_COMMIT="${BUILD_COMMIT}" NPM_BUILD_DATE="${BUILD_DATE}"
|
||||||
@ -34,6 +34,7 @@ EXPOSE 443
|
|||||||
COPY docker/rootfs /
|
COPY docker/rootfs /
|
||||||
ADD backend /app
|
ADD backend /app
|
||||||
ADD frontend/dist /app/frontend
|
ADD frontend/dist /app/frontend
|
||||||
|
COPY global /app/global
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN yarn install
|
RUN yarn install
|
||||||
|
@ -7,8 +7,8 @@ ENV S6_FIX_ATTRS_HIDDEN=1
|
|||||||
|
|
||||||
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
||||||
&& apk update \
|
&& apk update \
|
||||||
&& apk add python2 py-pip certbot jq \
|
&& apk add python3 certbot jq \
|
||||||
&& pip install certbot-dns-cloudflare \
|
&& python3 -m ensurepip \
|
||||||
&& rm -rf /var/cache/apk/*
|
&& rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
# Task
|
# Task
|
||||||
|
@ -11,6 +11,8 @@ services:
|
|||||||
- 3080:80
|
- 3080:80
|
||||||
- 3081:81
|
- 3081:81
|
||||||
- 3443:443
|
- 3443:443
|
||||||
|
networks:
|
||||||
|
- nginx_proxy_manager
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=development
|
- NODE_ENV=development
|
||||||
- FORCE_COLOR=1
|
- FORCE_COLOR=1
|
||||||
@ -19,13 +21,17 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- npm_data:/data
|
- npm_data:/data
|
||||||
- le_data:/etc/letsencrypt
|
- le_data:/etc/letsencrypt
|
||||||
- ..:/app
|
- ../backend:/app
|
||||||
|
- ../frontend:/app/frontend
|
||||||
|
- ../global:/app/global
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
working_dir: /app
|
working_dir: /app
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: jc21/mariadb-aria
|
image: jc21/mariadb-aria
|
||||||
|
networks:
|
||||||
|
- nginx_proxy_manager
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: "npm"
|
MYSQL_ROOT_PASSWORD: "npm"
|
||||||
MYSQL_DATABASE: "npm"
|
MYSQL_DATABASE: "npm"
|
||||||
@ -38,6 +44,8 @@ services:
|
|||||||
image: 'swaggerapi/swagger-ui:latest'
|
image: 'swaggerapi/swagger-ui:latest'
|
||||||
ports:
|
ports:
|
||||||
- 3001:80
|
- 3001:80
|
||||||
|
networks:
|
||||||
|
- nginx_proxy_manager
|
||||||
environment:
|
environment:
|
||||||
URL: "http://127.0.0.1:3081/api/schema"
|
URL: "http://127.0.0.1:3081/api/schema"
|
||||||
PORT: '80'
|
PORT: '80'
|
||||||
@ -48,3 +56,6 @@ volumes:
|
|||||||
npm_data:
|
npm_data:
|
||||||
le_data:
|
le_data:
|
||||||
db_data:
|
db_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
nginx_proxy_manager:
|
||||||
|
@ -17,6 +17,9 @@ server {
|
|||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_set_header X-Forwarded-For $remote_addr;
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
proxy_pass http://127.0.0.1:3000/;
|
proxy_pass http://127.0.0.1:3000/;
|
||||||
|
|
||||||
|
proxy_read_timeout 15m;
|
||||||
|
proxy_send_timeout 15m;
|
||||||
}
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
|
@ -1,196 +1,2 @@
|
|||||||
|
# This should be left blank is it is populated programatically
|
||||||
set_real_ip_from 144.220.0.0/16;
|
# by the application backend.
|
||||||
|
|
||||||
set_real_ip_from 52.124.128.0/17;
|
|
||||||
|
|
||||||
set_real_ip_from 54.230.0.0/16;
|
|
||||||
|
|
||||||
set_real_ip_from 54.239.128.0/18;
|
|
||||||
|
|
||||||
set_real_ip_from 52.82.128.0/19;
|
|
||||||
|
|
||||||
set_real_ip_from 99.84.0.0/16;
|
|
||||||
|
|
||||||
set_real_ip_from 204.246.172.0/24;
|
|
||||||
|
|
||||||
set_real_ip_from 205.251.192.0/19;
|
|
||||||
|
|
||||||
set_real_ip_from 54.239.192.0/19;
|
|
||||||
|
|
||||||
set_real_ip_from 70.132.0.0/18;
|
|
||||||
|
|
||||||
set_real_ip_from 13.32.0.0/15;
|
|
||||||
|
|
||||||
set_real_ip_from 13.224.0.0/14;
|
|
||||||
|
|
||||||
set_real_ip_from 13.35.0.0/16;
|
|
||||||
|
|
||||||
set_real_ip_from 204.246.164.0/22;
|
|
||||||
|
|
||||||
set_real_ip_from 204.246.168.0/22;
|
|
||||||
|
|
||||||
set_real_ip_from 71.152.0.0/17;
|
|
||||||
|
|
||||||
set_real_ip_from 216.137.32.0/19;
|
|
||||||
|
|
||||||
set_real_ip_from 205.251.249.0/24;
|
|
||||||
|
|
||||||
set_real_ip_from 99.86.0.0/16;
|
|
||||||
|
|
||||||
set_real_ip_from 52.46.0.0/18;
|
|
||||||
|
|
||||||
set_real_ip_from 52.84.0.0/15;
|
|
||||||
|
|
||||||
set_real_ip_from 204.246.173.0/24;
|
|
||||||
|
|
||||||
set_real_ip_from 130.176.0.0/16;
|
|
||||||
|
|
||||||
set_real_ip_from 64.252.64.0/18;
|
|
||||||
|
|
||||||
set_real_ip_from 204.246.174.0/23;
|
|
||||||
|
|
||||||
set_real_ip_from 64.252.128.0/18;
|
|
||||||
|
|
||||||
set_real_ip_from 205.251.254.0/24;
|
|
||||||
|
|
||||||
set_real_ip_from 143.204.0.0/16;
|
|
||||||
|
|
||||||
set_real_ip_from 205.251.252.0/23;
|
|
||||||
|
|
||||||
set_real_ip_from 204.246.176.0/20;
|
|
||||||
|
|
||||||
set_real_ip_from 13.249.0.0/16;
|
|
||||||
|
|
||||||
set_real_ip_from 54.240.128.0/18;
|
|
||||||
|
|
||||||
set_real_ip_from 205.251.250.0/23;
|
|
||||||
|
|
||||||
set_real_ip_from 52.222.128.0/17;
|
|
||||||
|
|
||||||
set_real_ip_from 54.182.0.0/16;
|
|
||||||
|
|
||||||
set_real_ip_from 54.192.0.0/16;
|
|
||||||
|
|
||||||
set_real_ip_from 13.124.199.0/24;
|
|
||||||
|
|
||||||
set_real_ip_from 34.226.14.0/24;
|
|
||||||
|
|
||||||
set_real_ip_from 52.15.127.128/26;
|
|
||||||
|
|
||||||
set_real_ip_from 35.158.136.0/24;
|
|
||||||
|
|
||||||
set_real_ip_from 52.57.254.0/24;
|
|
||||||
|
|
||||||
set_real_ip_from 18.216.170.128/25;
|
|
||||||
|
|
||||||
set_real_ip_from 13.52.204.0/23;
|
|
||||||
|
|
||||||
set_real_ip_from 13.54.63.128/26;
|
|
||||||
|
|
||||||
set_real_ip_from 13.59.250.0/26;
|
|
||||||
|
|
||||||
set_real_ip_from 13.210.67.128/26;
|
|
||||||
|
|
||||||
set_real_ip_from 35.167.191.128/26;
|
|
||||||
|
|
||||||
set_real_ip_from 52.47.139.0/24;
|
|
||||||
|
|
||||||
set_real_ip_from 52.199.127.192/26;
|
|
||||||
|
|
||||||
set_real_ip_from 52.212.248.0/26;
|
|
||||||
|
|
||||||
set_real_ip_from 52.66.194.128/26;
|
|
||||||
|
|
||||||
set_real_ip_from 13.113.203.0/24;
|
|
||||||
|
|
||||||
set_real_ip_from 99.79.168.0/23;
|
|
||||||
|
|
||||||
set_real_ip_from 34.195.252.0/24;
|
|
||||||
|
|
||||||
set_real_ip_from 35.162.63.192/26;
|
|
||||||
|
|
||||||
set_real_ip_from 34.223.12.224/27;
|
|
||||||
|
|
||||||
set_real_ip_from 52.56.127.0/25;
|
|
||||||
|
|
||||||
set_real_ip_from 34.223.80.192/26;
|
|
||||||
|
|
||||||
set_real_ip_from 13.228.69.0/24;
|
|
||||||
|
|
||||||
set_real_ip_from 34.216.51.0/25;
|
|
||||||
|
|
||||||
set_real_ip_from 3.231.2.0/25;
|
|
||||||
|
|
||||||
set_real_ip_from 54.233.255.128/26;
|
|
||||||
|
|
||||||
set_real_ip_from 18.200.212.0/23;
|
|
||||||
|
|
||||||
set_real_ip_from 52.52.191.128/26;
|
|
||||||
|
|
||||||
set_real_ip_from 3.234.232.224/27;
|
|
||||||
|
|
||||||
set_real_ip_from 52.78.247.128/26;
|
|
||||||
|
|
||||||
set_real_ip_from 52.220.191.0/26;
|
|
||||||
|
|
||||||
set_real_ip_from 34.232.163.208/29;
|
|
||||||
|
|
||||||
set_real_ip_from 2600:9000:eee::/48;
|
|
||||||
|
|
||||||
set_real_ip_from 2600:9000:4000::/36;
|
|
||||||
|
|
||||||
set_real_ip_from 2600:9000:3000::/36;
|
|
||||||
|
|
||||||
set_real_ip_from 2600:9000:f000::/36;
|
|
||||||
|
|
||||||
set_real_ip_from 2600:9000:fff::/48;
|
|
||||||
|
|
||||||
set_real_ip_from 2600:9000:2000::/36;
|
|
||||||
|
|
||||||
set_real_ip_from 2600:9000:1000::/36;
|
|
||||||
|
|
||||||
set_real_ip_from 2600:9000:ddd::/48;
|
|
||||||
|
|
||||||
set_real_ip_from 2600:9000:5300::/40;
|
|
||||||
|
|
||||||
set_real_ip_from 173.245.48.0/20;
|
|
||||||
|
|
||||||
set_real_ip_from 103.21.244.0/22;
|
|
||||||
|
|
||||||
set_real_ip_from 103.22.200.0/22;
|
|
||||||
|
|
||||||
set_real_ip_from 103.31.4.0/22;
|
|
||||||
|
|
||||||
set_real_ip_from 141.101.64.0/18;
|
|
||||||
|
|
||||||
set_real_ip_from 108.162.192.0/18;
|
|
||||||
|
|
||||||
set_real_ip_from 190.93.240.0/20;
|
|
||||||
|
|
||||||
set_real_ip_from 188.114.96.0/20;
|
|
||||||
|
|
||||||
set_real_ip_from 197.234.240.0/22;
|
|
||||||
|
|
||||||
set_real_ip_from 198.41.128.0/17;
|
|
||||||
|
|
||||||
set_real_ip_from 162.158.0.0/15;
|
|
||||||
|
|
||||||
set_real_ip_from 104.16.0.0/12;
|
|
||||||
|
|
||||||
set_real_ip_from 172.64.0.0/13;
|
|
||||||
|
|
||||||
set_real_ip_from 131.0.72.0/22;
|
|
||||||
|
|
||||||
set_real_ip_from 2400:cb00::/32;
|
|
||||||
|
|
||||||
set_real_ip_from 2606:4700::/32;
|
|
||||||
|
|
||||||
set_real_ip_from 2803:f800::/32;
|
|
||||||
|
|
||||||
set_real_ip_from 2405:b500::/32;
|
|
||||||
|
|
||||||
set_real_ip_from 2405:8100::/32;
|
|
||||||
|
|
||||||
set_real_ip_from 2a06:98c0::/29;
|
|
||||||
|
|
||||||
set_real_ip_from 2c0f:f248::/32;
|
|
||||||
|
@ -3,4 +3,6 @@ proxy_set_header Host $host;
|
|||||||
proxy_set_header X-Forwarded-Scheme $scheme;
|
proxy_set_header X-Forwarded-Scheme $scheme;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_set_header X-Forwarded-For $remote_addr;
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_pass $forward_scheme://$server:$port;
|
proxy_pass $forward_scheme://$server:$port;
|
||||||
|
|
||||||
|
@ -18,6 +18,9 @@ server {
|
|||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_set_header X-Forwarded-For $remote_addr;
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
proxy_pass http://127.0.0.1:3000/;
|
proxy_pass http://127.0.0.1:3000/;
|
||||||
|
|
||||||
|
proxy_read_timeout 15m;
|
||||||
|
proxy_send_timeout 15m;
|
||||||
}
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
|
@ -66,7 +66,7 @@ http {
|
|||||||
# NPM generated CDN ip ranges:
|
# NPM generated CDN ip ranges:
|
||||||
include conf.d/include/ip_ranges.conf;
|
include conf.d/include/ip_ranges.conf;
|
||||||
# always put the following 2 lines after ip subnets:
|
# always put the following 2 lines after ip subnets:
|
||||||
real_ip_header X-Forwarded-For;
|
real_ip_header X-Real-IP;
|
||||||
real_ip_recursive on;
|
real_ip_recursive on;
|
||||||
|
|
||||||
# Files generated by NPM
|
# Files generated by NPM
|
||||||
|
@ -5,7 +5,7 @@ mkdir -p /data/letsencrypt-acme-challenge
|
|||||||
cd /app || echo
|
cd /app || echo
|
||||||
|
|
||||||
if [ "$DEVELOPMENT" == "true" ]; then
|
if [ "$DEVELOPMENT" == "true" ]; then
|
||||||
cd /app/backend || exit 1
|
cd /app || exit 1
|
||||||
yarn install
|
yarn install
|
||||||
node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js
|
node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js
|
||||||
else
|
else
|
||||||
|
@ -434,7 +434,7 @@
|
|||||||
"neo-async": "^2.6.2",
|
"neo-async": "^2.6.2",
|
||||||
"nice-try": "^2.0.1",
|
"nice-try": "^2.0.1",
|
||||||
"no-case": "^3.0.3",
|
"no-case": "^3.0.3",
|
||||||
"node-forge": "^0.9.1",
|
"node-forge": "^0.10.0",
|
||||||
"node-libs-browser": "^2.2.1",
|
"node-libs-browser": "^2.2.1",
|
||||||
"node-releases": "^1.1.60",
|
"node-releases": "^1.1.60",
|
||||||
"nopt": "^4.0.3",
|
"nopt": "^4.0.3",
|
||||||
|
@ -6584,10 +6584,10 @@ node-forge@0.9.0:
|
|||||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579"
|
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579"
|
||||||
integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==
|
integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==
|
||||||
|
|
||||||
node-forge@^0.9.1:
|
node-forge@^0.10.0:
|
||||||
version "0.9.1"
|
version "0.10.0"
|
||||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.1.tgz#775368e6846558ab6676858a4d8c6e8d16c677b5"
|
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
|
||||||
integrity sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ==
|
integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==
|
||||||
|
|
||||||
node-libs-browser@^2.2.1:
|
node-libs-browser@^2.2.1:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
|
@ -53,7 +53,7 @@ function fetch(verb, path, data, options) {
|
|||||||
contentType: options.contentType || 'application/json; charset=UTF-8',
|
contentType: options.contentType || 'application/json; charset=UTF-8',
|
||||||
processData: options.processData || true,
|
processData: options.processData || true,
|
||||||
crossDomain: true,
|
crossDomain: true,
|
||||||
timeout: options.timeout ? options.timeout : 30000,
|
timeout: options.timeout ? options.timeout : 180000,
|
||||||
xhrFields: {
|
xhrFields: {
|
||||||
withCredentials: true
|
withCredentials: true
|
||||||
},
|
},
|
||||||
@ -139,7 +139,11 @@ function FileUpload(path, fd) {
|
|||||||
xhr.onreadystatechange = function () {
|
xhr.onreadystatechange = function () {
|
||||||
if (this.readyState === XMLHttpRequest.DONE) {
|
if (this.readyState === XMLHttpRequest.DONE) {
|
||||||
if (xhr.status !== 200 && xhr.status !== 201) {
|
if (xhr.status !== 200 && xhr.status !== 201) {
|
||||||
|
try {
|
||||||
|
reject(new Error('Upload failed: ' + JSON.parse(xhr.responseText).error.message));
|
||||||
|
} catch (err) {
|
||||||
reject(new Error('Upload failed: ' + xhr.status));
|
reject(new Error('Upload failed: ' + xhr.status));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
resolve(xhr.responseText);
|
resolve(xhr.responseText);
|
||||||
}
|
}
|
||||||
@ -587,7 +591,9 @@ module.exports = {
|
|||||||
* @param {Object} data
|
* @param {Object} data
|
||||||
*/
|
*/
|
||||||
create: function (data) {
|
create: function (data) {
|
||||||
return fetch('post', 'nginx/certificates', data);
|
|
||||||
|
const timeout = 180000 + (data && data.meta && data.meta.propagation_seconds ? Number(data.meta.propagation_seconds) * 1000 : 0);
|
||||||
|
return fetch('post', 'nginx/certificates', data, {timeout});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -630,8 +636,8 @@ module.exports = {
|
|||||||
* @param {Number} id
|
* @param {Number} id
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
renew: function (id) {
|
renew: function (id, timeout = 180000) {
|
||||||
return fetch('post', 'nginx/certificates/' + id + '/renew');
|
return fetch('post', 'nginx/certificates/' + id + '/renew', undefined, {timeout});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -31,6 +31,16 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-6 col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="custom-switch">
|
||||||
|
<input type="checkbox" class="custom-switch-input" name="pass_auth" value="1"<%- typeof pass_auth !== 'undefined' && pass_auth ? ' checked' : '' %>>
|
||||||
|
<span class="custom-switch-indicator"></span>
|
||||||
|
<span class="custom-switch-description"><%- i18n('access-lists', 'pass-auth') %></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -73,6 +73,7 @@ module.exports = Mn.View.extend({
|
|||||||
let data = {
|
let data = {
|
||||||
name: form_data.name,
|
name: form_data.name,
|
||||||
satisfy_any: !!form_data.satisfy_any,
|
satisfy_any: !!form_data.satisfy_any,
|
||||||
|
pass_auth: !!form_data.pass_auth,
|
||||||
items: items_data,
|
items: items_data,
|
||||||
clients: clients_data
|
clients: clients_data
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title"><%- i18n('certificates', 'form-title', {provider: provider}) %></h5>
|
<h5 class="modal-title"><%- i18n('certificates', 'form-title', {provider: provider}) %></h5>
|
||||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
<button type="button" class="close cancel non-loader-content" aria-label="Close" data-dismiss="modal"> </button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form>
|
<div class="alert alert-danger mb-0 rounded-0" id="le-error-info" role="alert"></div>
|
||||||
|
<div class="text-center loader-content">
|
||||||
|
<div class="loader mx-auto my-6"></div>
|
||||||
|
<p><%- i18n('ssl', 'processing-info') %></p>
|
||||||
|
</div>
|
||||||
|
<form class="non-loader-content">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<% if (provider === 'letsencrypt') { %>
|
<% if (provider === 'letsencrypt') { %>
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
@ -21,22 +26,97 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- CloudFlare -->
|
<!-- DNS challenge -->
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="custom-switch">
|
<label class="custom-switch">
|
||||||
<input type="checkbox" class="custom-switch-input" name="meta[cloudflare_use]" value="1">
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="custom-switch-input"
|
||||||
|
name="meta[dns_challenge]"
|
||||||
|
value="1"
|
||||||
|
<%- getUseDnsChallenge() ? 'checked' : '' %>
|
||||||
|
>
|
||||||
<span class="custom-switch-indicator"></span>
|
<span class="custom-switch-indicator"></span>
|
||||||
<span class="custom-switch-description"><%= i18n('ssl', 'use-cloudflare') %></span>
|
<span class="custom-switch-description"><%= i18n('ssl', 'dns-challenge') %></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12 cloudflare">
|
<div class="col-sm-12 col-md-12">
|
||||||
|
<fieldset class="form-fieldset dns-challenge">
|
||||||
|
<div class="text-red mb-4"><i class="fe fe-alert-triangle"></i> <%= i18n('ssl', 'certbot-warning') %></div>
|
||||||
|
|
||||||
|
<!-- Certbot DNS plugin selection -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">CloudFlare DNS API Token <span class="form-required">*</span></label>
|
<label class="form-label"><%- i18n('ssl', 'dns-provider') %> <span class="form-required">*</span></label>
|
||||||
<input type="text" name="meta[cloudflare_token]" class="form-control" id="cloudflare_token">
|
<select
|
||||||
|
name="meta[dns_provider]"
|
||||||
|
id="dns_provider"
|
||||||
|
class="form-control custom-select"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
value=""
|
||||||
|
disabled
|
||||||
|
hidden
|
||||||
|
<%- getDnsProvider() === null ? 'selected' : '' %>
|
||||||
|
>Please Choose...</option>
|
||||||
|
<% _.each(dns_plugins, function(plugin_info, plugin_name){ %>
|
||||||
|
<option
|
||||||
|
value="<%- plugin_name %>"
|
||||||
|
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
||||||
|
><%- plugin_info.display_name %></option>
|
||||||
|
<% }); %>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Certbot credentials file content -->
|
||||||
|
<div class="row credentials-file-content">
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label"><%- i18n('ssl', 'credentials-file-content') %> <span class="form-required">*</span></label>
|
||||||
|
<textarea
|
||||||
|
name="meta[dns_provider_credentials]"
|
||||||
|
class="form-control text-monospace"
|
||||||
|
id="dns_provider_credentials"
|
||||||
|
><%- getDnsProviderCredentials() %></textarea>
|
||||||
|
<div class="text-secondary small">
|
||||||
|
<i class="fe fe-info"></i>
|
||||||
|
<%= i18n('ssl', 'credentials-file-content-info') %>
|
||||||
|
</div>
|
||||||
|
<div class="text-red small">
|
||||||
|
<i class="fe fe-alert-triangle"></i>
|
||||||
|
<%= i18n('ssl', 'stored-as-plaintext-info') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- DNS propagation delay -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<label class="form-label"><%- i18n('ssl', 'propagation-seconds') %></label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
name="meta[propagation_seconds]"
|
||||||
|
class="form-control"
|
||||||
|
id="propagation_seconds"
|
||||||
|
value="<%- getPropagationSeconds() %>"
|
||||||
|
>
|
||||||
|
<div class="text-secondary small">
|
||||||
|
<i class="fe fe-info"></i>
|
||||||
|
<%= i18n('ssl', 'propagation-seconds-info') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -87,7 +167,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer non-loader-content">
|
||||||
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
|
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
|
||||||
<button type="button" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
|
<button type="button" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,6 +3,8 @@ const Mn = require('backbone.marionette');
|
|||||||
const App = require('../../main');
|
const App = require('../../main');
|
||||||
const CertificateModel = require('../../../models/certificate');
|
const CertificateModel = require('../../../models/certificate');
|
||||||
const template = require('./form.ejs');
|
const template = require('./form.ejs');
|
||||||
|
const i18n = require('../../i18n');
|
||||||
|
const dns_providers = require('../../../../../global/certbot-dns-plugins');
|
||||||
|
|
||||||
require('jquery-serializejson');
|
require('jquery-serializejson');
|
||||||
require('selectize');
|
require('selectize');
|
||||||
@ -14,6 +16,9 @@ module.exports = Mn.View.extend({
|
|||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
form: 'form',
|
form: 'form',
|
||||||
|
loader_content: '.loader-content',
|
||||||
|
non_loader_content: '.non-loader-content',
|
||||||
|
le_error_info: '#le-error-info',
|
||||||
domain_names: 'input[name="domain_names"]',
|
domain_names: 'input[name="domain_names"]',
|
||||||
buttons: '.modal-footer button',
|
buttons: '.modal-footer button',
|
||||||
cancel: 'button.cancel',
|
cancel: 'button.cancel',
|
||||||
@ -21,27 +26,49 @@ module.exports = Mn.View.extend({
|
|||||||
other_certificate: '#other_certificate',
|
other_certificate: '#other_certificate',
|
||||||
other_certificate_label: '#other_certificate_label',
|
other_certificate_label: '#other_certificate_label',
|
||||||
other_certificate_key: '#other_certificate_key',
|
other_certificate_key: '#other_certificate_key',
|
||||||
cloudflare_switch: 'input[name="meta[cloudflare_use]"]',
|
dns_challenge_switch: 'input[name="meta[dns_challenge]"]',
|
||||||
cloudflare_token: 'input[name="meta[cloudflare_token]"',
|
dns_challenge_content: '.dns-challenge',
|
||||||
cloudflare: '.cloudflare',
|
dns_provider: 'select[name="meta[dns_provider]"]',
|
||||||
|
credentials_file_content: '.credentials-file-content',
|
||||||
|
dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]',
|
||||||
|
propagation_seconds: 'input[name="meta[propagation_seconds]"]',
|
||||||
other_certificate_key_label: '#other_certificate_key_label',
|
other_certificate_key_label: '#other_certificate_key_label',
|
||||||
other_intermediate_certificate: '#other_intermediate_certificate',
|
other_intermediate_certificate: '#other_intermediate_certificate',
|
||||||
other_intermediate_certificate_label: '#other_intermediate_certificate_label'
|
other_intermediate_certificate_label: '#other_intermediate_certificate_label'
|
||||||
},
|
},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
'change @ui.cloudflare_switch': function() {
|
'change @ui.dns_challenge_switch': function () {
|
||||||
let checked = this.ui.cloudflare_switch.prop('checked');
|
const checked = this.ui.dns_challenge_switch.prop('checked');
|
||||||
if (checked) {
|
if (checked) {
|
||||||
this.ui.cloudflare_token.prop('required', 'required');
|
this.ui.dns_provider.prop('required', 'required');
|
||||||
this.ui.cloudflare.show();
|
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||||
|
if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){
|
||||||
|
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||||
|
}
|
||||||
|
this.ui.dns_challenge_content.show();
|
||||||
} else {
|
} else {
|
||||||
this.ui.cloudflare_token.prop('required', false);
|
this.ui.dns_provider.prop('required', false);
|
||||||
this.ui.cloudflare.hide();
|
this.ui.dns_provider_credentials.prop('required', false);
|
||||||
|
this.ui.dns_challenge_content.hide();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'change @ui.dns_provider': function () {
|
||||||
|
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||||
|
if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) {
|
||||||
|
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||||
|
this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials;
|
||||||
|
this.ui.credentials_file_content.show();
|
||||||
|
} else {
|
||||||
|
this.ui.dns_provider_credentials.prop('required', false);
|
||||||
|
this.ui.credentials_file_content.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
'click @ui.save': function (e) {
|
'click @ui.save': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
this.ui.le_error_info.hide();
|
||||||
|
|
||||||
if (!this.ui.form[0].checkValidity()) {
|
if (!this.ui.form[0].checkValidity()) {
|
||||||
$('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
|
$('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
|
||||||
@ -49,14 +76,15 @@ module.exports = Mn.View.extend({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let view = this;
|
|
||||||
let data = this.ui.form.serializeJSON();
|
let data = this.ui.form.serializeJSON();
|
||||||
data.provider = this.model.get('provider');
|
data.provider = this.model.get('provider');
|
||||||
|
let ssl_files = [];
|
||||||
|
|
||||||
|
if (data.provider === 'letsencrypt') {
|
||||||
|
if (typeof data.meta === 'undefined') data.meta = {};
|
||||||
|
|
||||||
let domain_err = false;
|
let domain_err = false;
|
||||||
if (!data.meta.cloudflare_use) {
|
if (!data.meta.dns_challenge) {
|
||||||
data.domain_names.split(',').map(function (name) {
|
data.domain_names.split(',').map(function (name) {
|
||||||
if (name.match(/\*/im)) {
|
if (name.match(/\*/im)) {
|
||||||
domain_err = true;
|
domain_err = true;
|
||||||
@ -65,26 +93,27 @@ module.exports = Mn.View.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (domain_err) {
|
if (domain_err) {
|
||||||
alert('Cannot request Let\'s Encrypt Certificate for wildcard domains when not using CloudFlare DNS');
|
alert(i18n('ssl', 'no-wildcard-without-dns'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manipulate
|
// Manipulate
|
||||||
if (typeof data.meta !== 'undefined' && typeof data.meta.letsencrypt_agree !== 'undefined') {
|
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1;
|
||||||
data.meta.letsencrypt_agree = !!data.meta.letsencrypt_agree;
|
data.meta.dns_challenge = data.meta.dns_challenge == 1;
|
||||||
}
|
|
||||||
if (typeof data.meta !== 'undefined' && typeof data.meta.cloudflare_use !== 'undefined') {
|
if(!data.meta.dns_challenge){
|
||||||
data.meta.cloudflare_use = !!data.meta.cloudflare_use;
|
data.meta.dns_provider = undefined;
|
||||||
|
data.meta.dns_provider_credentials = undefined;
|
||||||
|
data.meta.propagation_seconds = undefined;
|
||||||
|
} else {
|
||||||
|
if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof data.domain_names === 'string' && data.domain_names) {
|
if (typeof data.domain_names === 'string' && data.domain_names) {
|
||||||
data.domain_names = data.domain_names.split(',');
|
data.domain_names = data.domain_names.split(',');
|
||||||
}
|
}
|
||||||
|
} else if (data.provider === 'other' && !this.model.hasSslFiles()) {
|
||||||
let ssl_files = [];
|
|
||||||
|
|
||||||
// check files are attached
|
// check files are attached
|
||||||
if (this.model.get('provider') === 'other' && !this.model.hasSslFiles()) {
|
|
||||||
if (!this.ui.other_certificate[0].files.length || !this.ui.other_certificate[0].files[0].size) {
|
if (!this.ui.other_certificate[0].files.length || !this.ui.other_certificate[0].files[0].size) {
|
||||||
alert('Certificate file is not attached');
|
alert('Certificate file is not attached');
|
||||||
return;
|
return;
|
||||||
@ -116,19 +145,19 @@ module.exports = Mn.View.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
|
this.ui.loader_content.show();
|
||||||
this.ui.save.addClass('btn-loading');
|
this.ui.non_loader_content.hide();
|
||||||
|
|
||||||
// compile file data
|
// compile file data
|
||||||
let form_data = new FormData();
|
let form_data = new FormData();
|
||||||
if (view.model.get('provider') && ssl_files.length) {
|
if (data.provider === 'other' && ssl_files.length) {
|
||||||
ssl_files.map(function (file) {
|
ssl_files.map(function (file) {
|
||||||
form_data.append(file.name, file.file);
|
form_data.append(file.name, file.file);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
new Promise(resolve => {
|
new Promise(resolve => {
|
||||||
if (view.model.get('provider') === 'other') {
|
if (data.provider === 'other') {
|
||||||
resolve(App.Api.Nginx.Certificates.validate(form_data));
|
resolve(App.Api.Nginx.Certificates.validate(form_data));
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
@ -138,13 +167,13 @@ module.exports = Mn.View.extend({
|
|||||||
return App.Api.Nginx.Certificates.create(data);
|
return App.Api.Nginx.Certificates.create(data);
|
||||||
})
|
})
|
||||||
.then(result => {
|
.then(result => {
|
||||||
view.model.set(result);
|
this.model.set(result);
|
||||||
|
|
||||||
// Now upload the certs if we need to
|
// Now upload the certs if we need to
|
||||||
if (view.model.get('provider') === 'other') {
|
if (data.provider === 'other') {
|
||||||
return App.Api.Nginx.Certificates.upload(view.model.get('id'), form_data)
|
return App.Api.Nginx.Certificates.upload(this.model.get('id'), form_data)
|
||||||
.then(result => {
|
.then(result => {
|
||||||
view.model.set('meta', _.assign({}, view.model.get('meta'), result));
|
this.model.set('meta', _.assign({}, this.model.get('meta'), result));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -154,9 +183,17 @@ module.exports = Mn.View.extend({
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
alert(err.message);
|
let more_info = '';
|
||||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
if (err.code === 500 && err.debug) {
|
||||||
this.ui.save.removeClass('btn-loading');
|
try{
|
||||||
|
more_info = JSON.parse(err.debug).debug.stack.join("\n");
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
this.ui.le_error_info[0].innerHTML = `${err.message}${more_info !== '' ? `<pre class="mt-3">${more_info}</pre>`:''}`;
|
||||||
|
this.ui.le_error_info.show();
|
||||||
|
this.ui.le_error_info[0].scrollIntoView();
|
||||||
|
this.ui.loader_content.hide();
|
||||||
|
this.ui.non_loader_content.show();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'change @ui.other_certificate_key': function(e){
|
'change @ui.other_certificate_key': function(e){
|
||||||
@ -176,14 +213,22 @@ module.exports = Mn.View.extend({
|
|||||||
getLetsencryptEmail: function () {
|
getLetsencryptEmail: function () {
|
||||||
return typeof this.meta.letsencrypt_email !== 'undefined' ? this.meta.letsencrypt_email : App.Cache.User.get('email');
|
return typeof this.meta.letsencrypt_email !== 'undefined' ? this.meta.letsencrypt_email : App.Cache.User.get('email');
|
||||||
},
|
},
|
||||||
|
|
||||||
getLetsencryptAgree: function () {
|
getLetsencryptAgree: function () {
|
||||||
return typeof this.meta.letsencrypt_agree !== 'undefined' ? this.meta.letsencrypt_agree : false;
|
return typeof this.meta.letsencrypt_agree !== 'undefined' ? this.meta.letsencrypt_agree : false;
|
||||||
},
|
},
|
||||||
|
getUseDnsChallenge: function () {
|
||||||
getCloudflareUse: function () {
|
return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false;
|
||||||
return typeof this.meta.cloudflare_use !== 'undefined' ? this.meta.cloudflare_use : false;
|
},
|
||||||
}
|
getDnsProvider: function () {
|
||||||
|
return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null;
|
||||||
|
},
|
||||||
|
getDnsProviderCredentials: function () {
|
||||||
|
return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : '';
|
||||||
|
},
|
||||||
|
getPropagationSeconds: function () {
|
||||||
|
return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : '';
|
||||||
|
},
|
||||||
|
dns_plugins: dns_providers,
|
||||||
},
|
},
|
||||||
|
|
||||||
onRender: function () {
|
onRender: function () {
|
||||||
@ -199,7 +244,10 @@ module.exports = Mn.View.extend({
|
|||||||
},
|
},
|
||||||
createFilter: /^(?:[^.]+\.?)+[^.]$/
|
createFilter: /^(?:[^.]+\.?)+[^.]$/
|
||||||
});
|
});
|
||||||
this.ui.cloudflare.hide();
|
this.ui.dns_challenge_content.hide();
|
||||||
|
this.ui.credentials_file_content.hide();
|
||||||
|
this.ui.loader_content.hide();
|
||||||
|
this.ui.le_error_info.hide();
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize: function (options) {
|
initialize: function (options) {
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<%- i18n('ssl', provider) %><% if (meta.cloudflare_use) { %> - CloudFlare DNS<% } %>
|
<%- i18n('ssl', provider) %><% if (meta.dns_provider) { %> - <%- dns_providers[meta.dns_provider].display_name %><% } %>
|
||||||
</td>
|
</td>
|
||||||
<td class="<%- isExpired() ? 'text-danger' : '' %>">
|
<td class="<%- isExpired() ? 'text-danger' : '' %>">
|
||||||
<%- formatDbDate(expires_on, 'Do MMMM YYYY, h:mm a') %>
|
<%- formatDbDate(expires_on, 'Do MMMM YYYY, h:mm a') %>
|
||||||
|
@ -2,6 +2,7 @@ const Mn = require('backbone.marionette');
|
|||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const App = require('../../../main');
|
const App = require('../../../main');
|
||||||
const template = require('./item.ejs');
|
const template = require('./item.ejs');
|
||||||
|
const dns_providers = require('../../../../../../global/certbot-dns-plugins')
|
||||||
|
|
||||||
module.exports = Mn.View.extend({
|
module.exports = Mn.View.extend({
|
||||||
template: template,
|
template: template,
|
||||||
@ -35,7 +36,8 @@ module.exports = Mn.View.extend({
|
|||||||
canManage: App.Cache.User.canManage('certificates'),
|
canManage: App.Cache.User.canManage('certificates'),
|
||||||
isExpired: function () {
|
isExpired: function () {
|
||||||
return moment(this.expires_on).isBefore(moment());
|
return moment(this.expires_on).isBefore(moment());
|
||||||
}
|
},
|
||||||
|
dns_providers: dns_providers
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body has-tabs">
|
<div class="modal-body has-tabs">
|
||||||
|
<div class="alert alert-danger mb-0 rounded-0" id="le-error-info" role="alert"></div>
|
||||||
<form>
|
<form>
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
<li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> <%- i18n('all-hosts', 'details') %></a></li>
|
<li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> <%- i18n('all-hosts', 'details') %></a></li>
|
||||||
@ -73,22 +74,97 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- CloudFlare -->
|
<!-- DNS challenge -->
|
||||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="custom-switch">
|
<label class="custom-switch">
|
||||||
<input type="checkbox" class="custom-switch-input" name="meta[cloudflare_use]" value="1">
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="custom-switch-input"
|
||||||
|
name="meta[dns_challenge]"
|
||||||
|
value="1"
|
||||||
|
<%- getUseDnsChallenge() ? 'checked' : '' %>
|
||||||
|
>
|
||||||
<span class="custom-switch-indicator"></span>
|
<span class="custom-switch-indicator"></span>
|
||||||
<span class="custom-switch-description"><%= i18n('ssl', 'use-cloudflare') %></span>
|
<span class="custom-switch-description"><%= i18n('ssl', 'dns-challenge') %></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12 cloudflare letsencrypt">
|
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||||
|
<fieldset class="form-fieldset dns-challenge">
|
||||||
|
<div class="text-red mb-4"><i class="fe fe-alert-triangle"></i> <%= i18n('ssl', 'certbot-warning') %></div>
|
||||||
|
|
||||||
|
<!-- Certbot DNS plugin selection -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">CloudFlare DNS API Token <span class="form-required">*</span></label>
|
<label class="form-label"><%- i18n('ssl', 'dns-provider') %> <span class="form-required">*</span></label>
|
||||||
<input type="text" name="meta[cloudflare_token]" class="form-control" id="cloudflare_token">
|
<select
|
||||||
|
name="meta[dns_provider]"
|
||||||
|
id="dns_provider"
|
||||||
|
class="form-control custom-select"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
value=""
|
||||||
|
disabled
|
||||||
|
hidden
|
||||||
|
<%- getDnsProvider() === null ? 'selected' : '' %>
|
||||||
|
>Please Choose...</option>
|
||||||
|
<% _.each(dns_plugins, function(plugin_info, plugin_name){ %>
|
||||||
|
<option
|
||||||
|
value="<%- plugin_name %>"
|
||||||
|
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
||||||
|
><%- plugin_info.display_name %></option>
|
||||||
|
<% }); %>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Certbot credentials file content -->
|
||||||
|
<div class="row credentials-file-content">
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label"><%- i18n('ssl', 'credentials-file-content') %> <span class="form-required">*</span></label>
|
||||||
|
<textarea
|
||||||
|
name="meta[dns_provider_credentials]"
|
||||||
|
class="form-control text-monospace"
|
||||||
|
id="dns_provider_credentials"
|
||||||
|
><%- getDnsProviderCredentials() %></textarea>
|
||||||
|
<div class="text-secondary small">
|
||||||
|
<i class="fe fe-info"></i>
|
||||||
|
<%= i18n('ssl', 'credentials-file-content-info') %>
|
||||||
|
</div>
|
||||||
|
<div class="text-red small">
|
||||||
|
<i class="fe fe-alert-triangle"></i>
|
||||||
|
<%= i18n('ssl', 'stored-as-plaintext-info') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- DNS propagation delay -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<label class="form-label"><%- i18n('ssl', 'propagation-seconds') %></label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
name="meta[propagation_seconds]"
|
||||||
|
class="form-control"
|
||||||
|
id="propagation_seconds"
|
||||||
|
value="<%- getPropagationSeconds() %>"
|
||||||
|
>
|
||||||
|
<div class="text-secondary small">
|
||||||
|
<i class="fe fe-info"></i>
|
||||||
|
<%= i18n('ssl', 'propagation-seconds-info') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Lets encrypt -->
|
<!-- Lets encrypt -->
|
||||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||||
|
@ -4,6 +4,8 @@ const DeadHostModel = require('../../../models/dead-host');
|
|||||||
const template = require('./form.ejs');
|
const template = require('./form.ejs');
|
||||||
const certListItemTemplate = require('../certificates-list-item.ejs');
|
const certListItemTemplate = require('../certificates-list-item.ejs');
|
||||||
const Helpers = require('../../../lib/helpers');
|
const Helpers = require('../../../lib/helpers');
|
||||||
|
const i18n = require('../../i18n');
|
||||||
|
const dns_providers = require('../../../../../global/certbot-dns-plugins');
|
||||||
|
|
||||||
require('jquery-serializejson');
|
require('jquery-serializejson');
|
||||||
require('selectize');
|
require('selectize');
|
||||||
@ -18,14 +20,18 @@ module.exports = Mn.View.extend({
|
|||||||
buttons: '.modal-footer button',
|
buttons: '.modal-footer button',
|
||||||
cancel: 'button.cancel',
|
cancel: 'button.cancel',
|
||||||
save: 'button.save',
|
save: 'button.save',
|
||||||
|
le_error_info: '#le-error-info',
|
||||||
certificate_select: 'select[name="certificate_id"]',
|
certificate_select: 'select[name="certificate_id"]',
|
||||||
ssl_forced: 'input[name="ssl_forced"]',
|
ssl_forced: 'input[name="ssl_forced"]',
|
||||||
hsts_enabled: 'input[name="hsts_enabled"]',
|
hsts_enabled: 'input[name="hsts_enabled"]',
|
||||||
hsts_subdomains: 'input[name="hsts_subdomains"]',
|
hsts_subdomains: 'input[name="hsts_subdomains"]',
|
||||||
http2_support: 'input[name="http2_support"]',
|
http2_support: 'input[name="http2_support"]',
|
||||||
cloudflare_switch: 'input[name="meta[cloudflare_use]"]',
|
dns_challenge_switch: 'input[name="meta[dns_challenge]"]',
|
||||||
cloudflare_token: 'input[name="meta[cloudflare_token]"',
|
dns_challenge_content: '.dns-challenge',
|
||||||
cloudflare: '.cloudflare',
|
dns_provider: 'select[name="meta[dns_provider]"]',
|
||||||
|
credentials_file_content: '.credentials-file-content',
|
||||||
|
dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]',
|
||||||
|
propagation_seconds: 'input[name="meta[propagation_seconds]"]',
|
||||||
letsencrypt: '.letsencrypt'
|
letsencrypt: '.letsencrypt'
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -34,7 +40,7 @@ module.exports = Mn.View.extend({
|
|||||||
let id = this.ui.certificate_select.val();
|
let id = this.ui.certificate_select.val();
|
||||||
if (id === 'new') {
|
if (id === 'new') {
|
||||||
this.ui.letsencrypt.show().find('input').prop('disabled', false);
|
this.ui.letsencrypt.show().find('input').prop('disabled', false);
|
||||||
this.ui.cloudflare.hide();
|
this.ui.dns_challenge_content.hide();
|
||||||
} else {
|
} else {
|
||||||
this.ui.letsencrypt.hide().find('input').prop('disabled', true);
|
this.ui.letsencrypt.hide().find('input').prop('disabled', true);
|
||||||
}
|
}
|
||||||
@ -81,19 +87,37 @@ module.exports = Mn.View.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'change @ui.cloudflare_switch': function() {
|
'change @ui.dns_challenge_switch': function () {
|
||||||
let checked = this.ui.cloudflare_switch.prop('checked');
|
const checked = this.ui.dns_challenge_switch.prop('checked');
|
||||||
if (checked) {
|
if (checked) {
|
||||||
this.ui.cloudflare_token.prop('required', 'required');
|
this.ui.dns_provider.prop('required', 'required');
|
||||||
this.ui.cloudflare.show();
|
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||||
|
if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){
|
||||||
|
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||||
|
}
|
||||||
|
this.ui.dns_challenge_content.show();
|
||||||
} else {
|
} else {
|
||||||
this.ui.cloudflare_token.prop('required', false);
|
this.ui.dns_provider.prop('required', false);
|
||||||
this.ui.cloudflare.hide();
|
this.ui.dns_provider_credentials.prop('required', false);
|
||||||
|
this.ui.dns_challenge_content.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'change @ui.dns_provider': function () {
|
||||||
|
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||||
|
if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) {
|
||||||
|
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||||
|
this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials;
|
||||||
|
this.ui.credentials_file_content.show();
|
||||||
|
} else {
|
||||||
|
this.ui.dns_provider_credentials.prop('required', false);
|
||||||
|
this.ui.credentials_file_content.hide();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'click @ui.save': function (e) {
|
'click @ui.save': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
this.ui.le_error_info.hide();
|
||||||
|
|
||||||
if (!this.ui.form[0].checkValidity()) {
|
if (!this.ui.form[0].checkValidity()) {
|
||||||
$('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
|
$('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
|
||||||
@ -109,6 +133,18 @@ module.exports = Mn.View.extend({
|
|||||||
data.http2_support = !!data.http2_support;
|
data.http2_support = !!data.http2_support;
|
||||||
data.ssl_forced = !!data.ssl_forced;
|
data.ssl_forced = !!data.ssl_forced;
|
||||||
|
|
||||||
|
if (typeof data.meta === 'undefined') data.meta = {};
|
||||||
|
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1;
|
||||||
|
data.meta.dns_challenge = data.meta.dns_challenge == 1;
|
||||||
|
|
||||||
|
if(!data.meta.dns_challenge){
|
||||||
|
data.meta.dns_provider = undefined;
|
||||||
|
data.meta.dns_provider_credentials = undefined;
|
||||||
|
data.meta.propagation_seconds = undefined;
|
||||||
|
} else {
|
||||||
|
if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof data.domain_names === 'string' && data.domain_names) {
|
if (typeof data.domain_names === 'string' && data.domain_names) {
|
||||||
data.domain_names = data.domain_names.split(',');
|
data.domain_names = data.domain_names.split(',');
|
||||||
}
|
}
|
||||||
@ -116,7 +152,7 @@ module.exports = Mn.View.extend({
|
|||||||
// Check for any domain names containing wildcards, which are not allowed with letsencrypt
|
// Check for any domain names containing wildcards, which are not allowed with letsencrypt
|
||||||
if (data.certificate_id === 'new') {
|
if (data.certificate_id === 'new') {
|
||||||
let domain_err = false;
|
let domain_err = false;
|
||||||
if (!data.meta.cloudflare_use) {
|
if (!data.meta.dns_challenge) {
|
||||||
data.domain_names.map(function (name) {
|
data.domain_names.map(function (name) {
|
||||||
if (name.match(/\*/im)) {
|
if (name.match(/\*/im)) {
|
||||||
domain_err = true;
|
domain_err = true;
|
||||||
@ -125,12 +161,9 @@ module.exports = Mn.View.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (domain_err) {
|
if (domain_err) {
|
||||||
alert('Cannot request Let\'s Encrypt Certificate for wildcard domains without CloudFlare DNS.');
|
alert(i18n('ssl', 'no-wildcard-without-dns'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.meta.cloudflare_use = data.meta.cloudflare_use === '1';
|
|
||||||
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree === '1';
|
|
||||||
} else {
|
} else {
|
||||||
data.certificate_id = parseInt(data.certificate_id, 10);
|
data.certificate_id = parseInt(data.certificate_id, 10);
|
||||||
}
|
}
|
||||||
@ -159,7 +192,15 @@ module.exports = Mn.View.extend({
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
alert(err.message);
|
let more_info = '';
|
||||||
|
if(err.code === 500 && err.debug){
|
||||||
|
try{
|
||||||
|
more_info = JSON.parse(err.debug).debug.stack.join("\n");
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
this.ui.le_error_info[0].innerHTML = `${err.message}${more_info !== '' ? `<pre class="mt-3">${more_info}</pre>`:''}`;
|
||||||
|
this.ui.le_error_info.show();
|
||||||
|
this.ui.le_error_info[0].scrollIntoView();
|
||||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||||
this.ui.save.removeClass('btn-loading');
|
this.ui.save.removeClass('btn-loading');
|
||||||
});
|
});
|
||||||
@ -169,7 +210,20 @@ module.exports = Mn.View.extend({
|
|||||||
templateContext: {
|
templateContext: {
|
||||||
getLetsencryptEmail: function () {
|
getLetsencryptEmail: function () {
|
||||||
return App.Cache.User.get('email');
|
return App.Cache.User.get('email');
|
||||||
}
|
},
|
||||||
|
getUseDnsChallenge: function () {
|
||||||
|
return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false;
|
||||||
|
},
|
||||||
|
getDnsProvider: function () {
|
||||||
|
return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null;
|
||||||
|
},
|
||||||
|
getDnsProviderCredentials: function () {
|
||||||
|
return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : '';
|
||||||
|
},
|
||||||
|
getPropagationSeconds: function () {
|
||||||
|
return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : '';
|
||||||
|
},
|
||||||
|
dns_plugins: dns_providers,
|
||||||
},
|
},
|
||||||
|
|
||||||
onRender: function () {
|
onRender: function () {
|
||||||
@ -190,6 +244,9 @@ module.exports = Mn.View.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Certificates
|
// Certificates
|
||||||
|
this.ui.le_error_info.hide();
|
||||||
|
this.ui.dns_challenge_content.hide();
|
||||||
|
this.ui.credentials_file_content.hide();
|
||||||
this.ui.letsencrypt.hide();
|
this.ui.letsencrypt.hide();
|
||||||
this.ui.certificate_select.selectize({
|
this.ui.certificate_select.selectize({
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body has-tabs">
|
<div class="modal-body has-tabs">
|
||||||
|
<div class="alert alert-danger mb-0 rounded-0" id="le-error-info" role="alert"></div>
|
||||||
<form>
|
<form>
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
<li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> <%- i18n('all-hosts', 'details') %></a></li>
|
<li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> <%- i18n('all-hosts', 'details') %></a></li>
|
||||||
@ -141,22 +142,97 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- CloudFlare -->
|
<!-- DNS challenge -->
|
||||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="custom-switch">
|
<label class="custom-switch">
|
||||||
<input type="checkbox" class="custom-switch-input" name="meta[cloudflare_use]" value="1">
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="custom-switch-input"
|
||||||
|
name="meta[dns_challenge]"
|
||||||
|
value="1"
|
||||||
|
<%- getUseDnsChallenge() ? 'checked' : '' %>
|
||||||
|
>
|
||||||
<span class="custom-switch-indicator"></span>
|
<span class="custom-switch-indicator"></span>
|
||||||
<span class="custom-switch-description"><%= i18n('ssl', 'use-cloudflare') %></span>
|
<span class="custom-switch-description"><%= i18n('ssl', 'dns-challenge') %></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12 cloudflare letsencrypt">
|
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||||
|
<fieldset class="form-fieldset dns-challenge">
|
||||||
|
<div class="text-red mb-4"><i class="fe fe-alert-triangle"></i> <%= i18n('ssl', 'certbot-warning') %></div>
|
||||||
|
|
||||||
|
<!-- Certbot DNS plugin selection -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">CloudFlare DNS API Token <span class="form-required">*</span></label>
|
<label class="form-label"><%- i18n('ssl', 'dns-provider') %> <span class="form-required">*</span></label>
|
||||||
<input type="text" name="meta[cloudflare_token]" class="form-control" id="cloudflare_token">
|
<select
|
||||||
|
name="meta[dns_provider]"
|
||||||
|
id="dns_provider"
|
||||||
|
class="form-control custom-select"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
value=""
|
||||||
|
disabled
|
||||||
|
hidden
|
||||||
|
<%- getDnsProvider() === null ? 'selected' : '' %>
|
||||||
|
>Please Choose...</option>
|
||||||
|
<% _.each(dns_plugins, function(plugin_info, plugin_name){ %>
|
||||||
|
<option
|
||||||
|
value="<%- plugin_name %>"
|
||||||
|
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
||||||
|
><%- plugin_info.display_name %></option>
|
||||||
|
<% }); %>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Certbot credentials file content -->
|
||||||
|
<div class="row credentials-file-content">
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label"><%- i18n('ssl', 'credentials-file-content') %> <span class="form-required">*</span></label>
|
||||||
|
<textarea
|
||||||
|
name="meta[dns_provider_credentials]"
|
||||||
|
class="form-control text-monospace"
|
||||||
|
id="dns_provider_credentials"
|
||||||
|
><%- getDnsProviderCredentials() %></textarea>
|
||||||
|
<div class="text-secondary small">
|
||||||
|
<i class="fe fe-info"></i>
|
||||||
|
<%= i18n('ssl', 'credentials-file-content-info') %>
|
||||||
|
</div>
|
||||||
|
<div class="text-red small">
|
||||||
|
<i class="fe fe-alert-triangle"></i>
|
||||||
|
<%= i18n('ssl', 'stored-as-plaintext-info') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- DNS propagation delay -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<label class="form-label"><%- i18n('ssl', 'propagation-seconds') %></label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
name="meta[propagation_seconds]"
|
||||||
|
class="form-control"
|
||||||
|
id="propagation_seconds"
|
||||||
|
value="<%- getPropagationSeconds() %>"
|
||||||
|
>
|
||||||
|
<div class="text-secondary small">
|
||||||
|
<i class="fe fe-info"></i>
|
||||||
|
<%= i18n('ssl', 'propagation-seconds-info') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Lets encrypt -->
|
<!-- Lets encrypt -->
|
||||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||||
|
@ -7,6 +7,8 @@ const certListItemTemplate = require('../certificates-list-item.ejs');
|
|||||||
const accessListItemTemplate = require('./access-list-item.ejs');
|
const accessListItemTemplate = require('./access-list-item.ejs');
|
||||||
const CustomLocation = require('./location');
|
const CustomLocation = require('./location');
|
||||||
const Helpers = require('../../../lib/helpers');
|
const Helpers = require('../../../lib/helpers');
|
||||||
|
const i18n = require('../../i18n');
|
||||||
|
const dns_providers = require('../../../../../global/certbot-dns-plugins');
|
||||||
|
|
||||||
|
|
||||||
require('jquery-serializejson');
|
require('jquery-serializejson');
|
||||||
@ -26,16 +28,20 @@ module.exports = Mn.View.extend({
|
|||||||
cancel: 'button.cancel',
|
cancel: 'button.cancel',
|
||||||
save: 'button.save',
|
save: 'button.save',
|
||||||
add_location_btn: 'button.add_location',
|
add_location_btn: 'button.add_location',
|
||||||
locations_container:'.locations_container',
|
locations_container: '.locations_container',
|
||||||
|
le_error_info: '#le-error-info',
|
||||||
certificate_select: 'select[name="certificate_id"]',
|
certificate_select: 'select[name="certificate_id"]',
|
||||||
access_list_select: 'select[name="access_list_id"]',
|
access_list_select: 'select[name="access_list_id"]',
|
||||||
ssl_forced: 'input[name="ssl_forced"]',
|
ssl_forced: 'input[name="ssl_forced"]',
|
||||||
hsts_enabled: 'input[name="hsts_enabled"]',
|
hsts_enabled: 'input[name="hsts_enabled"]',
|
||||||
hsts_subdomains: 'input[name="hsts_subdomains"]',
|
hsts_subdomains: 'input[name="hsts_subdomains"]',
|
||||||
http2_support: 'input[name="http2_support"]',
|
http2_support: 'input[name="http2_support"]',
|
||||||
cloudflare_switch: 'input[name="meta[cloudflare_use]"]',
|
dns_challenge_switch: 'input[name="meta[dns_challenge]"]',
|
||||||
cloudflare_token: 'input[name="meta[cloudflare_token]"',
|
dns_challenge_content: '.dns-challenge',
|
||||||
cloudflare: '.cloudflare',
|
dns_provider: 'select[name="meta[dns_provider]"]',
|
||||||
|
credentials_file_content: '.credentials-file-content',
|
||||||
|
dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]',
|
||||||
|
propagation_seconds: 'input[name="meta[propagation_seconds]"]',
|
||||||
forward_scheme: 'select[name="forward_scheme"]',
|
forward_scheme: 'select[name="forward_scheme"]',
|
||||||
letsencrypt: '.letsencrypt'
|
letsencrypt: '.letsencrypt'
|
||||||
},
|
},
|
||||||
@ -49,7 +55,7 @@ module.exports = Mn.View.extend({
|
|||||||
let id = this.ui.certificate_select.val();
|
let id = this.ui.certificate_select.val();
|
||||||
if (id === 'new') {
|
if (id === 'new') {
|
||||||
this.ui.letsencrypt.show().find('input').prop('disabled', false);
|
this.ui.letsencrypt.show().find('input').prop('disabled', false);
|
||||||
this.ui.cloudflare.hide();
|
this.ui.dns_challenge_content.hide();
|
||||||
} else {
|
} else {
|
||||||
this.ui.letsencrypt.hide().find('input').prop('disabled', true);
|
this.ui.letsencrypt.hide().find('input').prop('disabled', true);
|
||||||
}
|
}
|
||||||
@ -95,14 +101,31 @@ module.exports = Mn.View.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'change @ui.cloudflare_switch': function() {
|
'change @ui.dns_challenge_switch': function () {
|
||||||
let checked = this.ui.cloudflare_switch.prop('checked');
|
const checked = this.ui.dns_challenge_switch.prop('checked');
|
||||||
if (checked) {
|
if (checked) {
|
||||||
this.ui.cloudflare_token.prop('required', 'required');
|
this.ui.dns_provider.prop('required', 'required');
|
||||||
this.ui.cloudflare.show();
|
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||||
|
if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){
|
||||||
|
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||||
|
}
|
||||||
|
this.ui.dns_challenge_content.show();
|
||||||
} else {
|
} else {
|
||||||
this.ui.cloudflare_token.prop('required', false);
|
this.ui.dns_provider.prop('required', false);
|
||||||
this.ui.cloudflare.hide();
|
this.ui.dns_provider_credentials.prop('required', false);
|
||||||
|
this.ui.dns_challenge_content.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'change @ui.dns_provider': function () {
|
||||||
|
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||||
|
if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) {
|
||||||
|
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||||
|
this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials;
|
||||||
|
this.ui.credentials_file_content.show();
|
||||||
|
} else {
|
||||||
|
this.ui.dns_provider_credentials.prop('required', false);
|
||||||
|
this.ui.credentials_file_content.hide();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -115,6 +138,7 @@ module.exports = Mn.View.extend({
|
|||||||
|
|
||||||
'click @ui.save': function (e) {
|
'click @ui.save': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
this.ui.le_error_info.hide();
|
||||||
|
|
||||||
if (!this.ui.form[0].checkValidity()) {
|
if (!this.ui.form[0].checkValidity()) {
|
||||||
$('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
|
$('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
|
||||||
@ -144,6 +168,18 @@ module.exports = Mn.View.extend({
|
|||||||
data.hsts_subdomains = !!data.hsts_subdomains;
|
data.hsts_subdomains = !!data.hsts_subdomains;
|
||||||
data.ssl_forced = !!data.ssl_forced;
|
data.ssl_forced = !!data.ssl_forced;
|
||||||
|
|
||||||
|
if (typeof data.meta === 'undefined') data.meta = {};
|
||||||
|
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1;
|
||||||
|
data.meta.dns_challenge = data.meta.dns_challenge == 1;
|
||||||
|
|
||||||
|
if(!data.meta.dns_challenge){
|
||||||
|
data.meta.dns_provider = undefined;
|
||||||
|
data.meta.dns_provider_credentials = undefined;
|
||||||
|
data.meta.propagation_seconds = undefined;
|
||||||
|
} else {
|
||||||
|
if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof data.domain_names === 'string' && data.domain_names) {
|
if (typeof data.domain_names === 'string' && data.domain_names) {
|
||||||
data.domain_names = data.domain_names.split(',');
|
data.domain_names = data.domain_names.split(',');
|
||||||
}
|
}
|
||||||
@ -151,7 +187,7 @@ module.exports = Mn.View.extend({
|
|||||||
// Check for any domain names containing wildcards, which are not allowed with letsencrypt
|
// Check for any domain names containing wildcards, which are not allowed with letsencrypt
|
||||||
if (data.certificate_id === 'new') {
|
if (data.certificate_id === 'new') {
|
||||||
let domain_err = false;
|
let domain_err = false;
|
||||||
if (!data.meta.cloudflare_use) {
|
if (!data.meta.dns_challenge) {
|
||||||
data.domain_names.map(function (name) {
|
data.domain_names.map(function (name) {
|
||||||
if (name.match(/\*/im)) {
|
if (name.match(/\*/im)) {
|
||||||
domain_err = true;
|
domain_err = true;
|
||||||
@ -160,12 +196,9 @@ module.exports = Mn.View.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (domain_err) {
|
if (domain_err) {
|
||||||
alert('Cannot request Let\'s Encrypt Certificate for wildcard domains without CloudFlare DNS.');
|
alert(i18n('ssl', 'no-wildcard-without-dns'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.meta.cloudflare_use = data.meta.cloudflare_use === '1';
|
|
||||||
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree === '1';
|
|
||||||
} else {
|
} else {
|
||||||
data.certificate_id = parseInt(data.certificate_id, 10);
|
data.certificate_id = parseInt(data.certificate_id, 10);
|
||||||
}
|
}
|
||||||
@ -194,7 +227,15 @@ module.exports = Mn.View.extend({
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
alert(err.message);
|
let more_info = '';
|
||||||
|
if(err.code === 500 && err.debug){
|
||||||
|
try{
|
||||||
|
more_info = JSON.parse(err.debug).debug.stack.join("\n");
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
this.ui.le_error_info[0].innerHTML = `${err.message}${more_info !== '' ? `<pre class="mt-3">${more_info}</pre>`:''}`;
|
||||||
|
this.ui.le_error_info.show();
|
||||||
|
this.ui.le_error_info[0].scrollIntoView();
|
||||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||||
this.ui.save.removeClass('btn-loading');
|
this.ui.save.removeClass('btn-loading');
|
||||||
});
|
});
|
||||||
@ -204,7 +245,20 @@ module.exports = Mn.View.extend({
|
|||||||
templateContext: {
|
templateContext: {
|
||||||
getLetsencryptEmail: function () {
|
getLetsencryptEmail: function () {
|
||||||
return App.Cache.User.get('email');
|
return App.Cache.User.get('email');
|
||||||
}
|
},
|
||||||
|
getUseDnsChallenge: function () {
|
||||||
|
return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false;
|
||||||
|
},
|
||||||
|
getDnsProvider: function () {
|
||||||
|
return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null;
|
||||||
|
},
|
||||||
|
getDnsProviderCredentials: function () {
|
||||||
|
return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : '';
|
||||||
|
},
|
||||||
|
getPropagationSeconds: function () {
|
||||||
|
return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : '';
|
||||||
|
},
|
||||||
|
dns_plugins: dns_providers,
|
||||||
},
|
},
|
||||||
|
|
||||||
onRender: function () {
|
onRender: function () {
|
||||||
@ -258,6 +312,9 @@ module.exports = Mn.View.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Certificates
|
// Certificates
|
||||||
|
this.ui.le_error_info.hide();
|
||||||
|
this.ui.dns_challenge_content.hide();
|
||||||
|
this.ui.credentials_file_content.hide();
|
||||||
this.ui.letsencrypt.hide();
|
this.ui.letsencrypt.hide();
|
||||||
this.ui.certificate_select.selectize({
|
this.ui.certificate_select.selectize({
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body has-tabs">
|
<div class="modal-body has-tabs">
|
||||||
|
<div class="alert alert-danger mb-0 rounded-0" id="le-error-info" role="alert"></div>
|
||||||
<form>
|
<form>
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
<ul class="nav nav-tabs" role="tablist">
|
||||||
<li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> <%- i18n('all-hosts', 'details') %></a></li>
|
<li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> <%- i18n('all-hosts', 'details') %></a></li>
|
||||||
@ -97,22 +98,97 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- CloudFlare -->
|
<!-- DNS challenge -->
|
||||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="custom-switch">
|
<label class="custom-switch">
|
||||||
<input type="checkbox" class="custom-switch-input" name="meta[cloudflare_use]" value="1">
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="custom-switch-input"
|
||||||
|
name="meta[dns_challenge]"
|
||||||
|
value="1"
|
||||||
|
<%- getUseDnsChallenge() ? 'checked' : '' %>
|
||||||
|
>
|
||||||
<span class="custom-switch-indicator"></span>
|
<span class="custom-switch-indicator"></span>
|
||||||
<span class="custom-switch-description"><%= i18n('ssl', 'use-cloudflare') %></span>
|
<span class="custom-switch-description"><%= i18n('ssl', 'dns-challenge') %></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-12 cloudflare letsencrypt">
|
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||||
|
<fieldset class="form-fieldset dns-challenge">
|
||||||
|
<div class="text-red mb-4"><i class="fe fe-alert-triangle"></i> <%= i18n('ssl', 'certbot-warning') %></div>
|
||||||
|
|
||||||
|
<!-- Certbot DNS plugin selection -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">CloudFlare DNS API Token <span class="form-required">*</span></label>
|
<label class="form-label"><%- i18n('ssl', 'dns-provider') %> <span class="form-required">*</span></label>
|
||||||
<input type="text" name="meta[cloudflare_token]" class="form-control" id="cloudflare_token">
|
<select
|
||||||
|
name="meta[dns_provider]"
|
||||||
|
id="dns_provider"
|
||||||
|
class="form-control custom-select"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
value=""
|
||||||
|
disabled
|
||||||
|
hidden
|
||||||
|
<%- getDnsProvider() === null ? 'selected' : '' %>
|
||||||
|
>Please Choose...</option>
|
||||||
|
<% _.each(dns_plugins, function(plugin_info, plugin_name){ %>
|
||||||
|
<option
|
||||||
|
value="<%- plugin_name %>"
|
||||||
|
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
||||||
|
><%- plugin_info.display_name %></option>
|
||||||
|
<% }); %>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Certbot credentials file content -->
|
||||||
|
<div class="row credentials-file-content">
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label"><%- i18n('ssl', 'credentials-file-content') %> <span class="form-required">*</span></label>
|
||||||
|
<textarea
|
||||||
|
name="meta[dns_provider_credentials]"
|
||||||
|
class="form-control text-monospace"
|
||||||
|
id="dns_provider_credentials"
|
||||||
|
><%- getDnsProviderCredentials() %></textarea>
|
||||||
|
<div class="text-secondary small">
|
||||||
|
<i class="fe fe-info"></i>
|
||||||
|
<%= i18n('ssl', 'credentials-file-content-info') %>
|
||||||
|
</div>
|
||||||
|
<div class="text-red small">
|
||||||
|
<i class="fe fe-alert-triangle"></i>
|
||||||
|
<%= i18n('ssl', 'stored-as-plaintext-info') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- DNS propagation delay -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12 col-md-12">
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<label class="form-label"><%- i18n('ssl', 'propagation-seconds') %></label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
name="meta[propagation_seconds]"
|
||||||
|
class="form-control"
|
||||||
|
id="propagation_seconds"
|
||||||
|
value="<%- getPropagationSeconds() %>"
|
||||||
|
>
|
||||||
|
<div class="text-secondary small">
|
||||||
|
<i class="fe fe-info"></i>
|
||||||
|
<%= i18n('ssl', 'propagation-seconds-info') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Lets encrypt -->
|
<!-- Lets encrypt -->
|
||||||
<div class="col-sm-12 col-md-12 letsencrypt">
|
<div class="col-sm-12 col-md-12 letsencrypt">
|
||||||
|
@ -4,6 +4,9 @@ const RedirectionHostModel = require('../../../models/redirection-host');
|
|||||||
const template = require('./form.ejs');
|
const template = require('./form.ejs');
|
||||||
const certListItemTemplate = require('../certificates-list-item.ejs');
|
const certListItemTemplate = require('../certificates-list-item.ejs');
|
||||||
const Helpers = require('../../../lib/helpers');
|
const Helpers = require('../../../lib/helpers');
|
||||||
|
const i18n = require('../../i18n');
|
||||||
|
const dns_providers = require('../../../../../global/certbot-dns-plugins');
|
||||||
|
|
||||||
|
|
||||||
require('jquery-serializejson');
|
require('jquery-serializejson');
|
||||||
require('selectize');
|
require('selectize');
|
||||||
@ -18,14 +21,18 @@ module.exports = Mn.View.extend({
|
|||||||
buttons: '.modal-footer button',
|
buttons: '.modal-footer button',
|
||||||
cancel: 'button.cancel',
|
cancel: 'button.cancel',
|
||||||
save: 'button.save',
|
save: 'button.save',
|
||||||
|
le_error_info: '#le-error-info',
|
||||||
certificate_select: 'select[name="certificate_id"]',
|
certificate_select: 'select[name="certificate_id"]',
|
||||||
ssl_forced: 'input[name="ssl_forced"]',
|
ssl_forced: 'input[name="ssl_forced"]',
|
||||||
hsts_enabled: 'input[name="hsts_enabled"]',
|
hsts_enabled: 'input[name="hsts_enabled"]',
|
||||||
hsts_subdomains: 'input[name="hsts_subdomains"]',
|
hsts_subdomains: 'input[name="hsts_subdomains"]',
|
||||||
http2_support: 'input[name="http2_support"]',
|
http2_support: 'input[name="http2_support"]',
|
||||||
cloudflare_switch: 'input[name="meta[cloudflare_use]"]',
|
dns_challenge_switch: 'input[name="meta[dns_challenge]"]',
|
||||||
cloudflare_token: 'input[name="meta[cloudflare_token]"',
|
dns_challenge_content: '.dns-challenge',
|
||||||
cloudflare: '.cloudflare',
|
dns_provider: 'select[name="meta[dns_provider]"]',
|
||||||
|
credentials_file_content: '.credentials-file-content',
|
||||||
|
dns_provider_credentials: 'textarea[name="meta[dns_provider_credentials]"]',
|
||||||
|
propagation_seconds: 'input[name="meta[propagation_seconds]"]',
|
||||||
letsencrypt: '.letsencrypt'
|
letsencrypt: '.letsencrypt'
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -34,7 +41,7 @@ module.exports = Mn.View.extend({
|
|||||||
let id = this.ui.certificate_select.val();
|
let id = this.ui.certificate_select.val();
|
||||||
if (id === 'new') {
|
if (id === 'new') {
|
||||||
this.ui.letsencrypt.show().find('input').prop('disabled', false);
|
this.ui.letsencrypt.show().find('input').prop('disabled', false);
|
||||||
this.ui.cloudflare.hide();
|
this.ui.dns_challenge_content.hide();
|
||||||
} else {
|
} else {
|
||||||
this.ui.letsencrypt.hide().find('input').prop('disabled', true);
|
this.ui.letsencrypt.hide().find('input').prop('disabled', true);
|
||||||
}
|
}
|
||||||
@ -80,19 +87,37 @@ module.exports = Mn.View.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'change @ui.cloudflare_switch': function() {
|
'change @ui.dns_challenge_switch': function () {
|
||||||
let checked = this.ui.cloudflare_switch.prop('checked');
|
const checked = this.ui.dns_challenge_switch.prop('checked');
|
||||||
if (checked) {
|
if (checked) {
|
||||||
this.ui.cloudflare_token.prop('required', 'required');
|
this.ui.dns_provider.prop('required', 'required');
|
||||||
this.ui.cloudflare.show();
|
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||||
|
if(selected_provider != '' && dns_providers[selected_provider].credentials !== false){
|
||||||
|
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||||
|
}
|
||||||
|
this.ui.dns_challenge_content.show();
|
||||||
} else {
|
} else {
|
||||||
this.ui.cloudflare_token.prop('required', false);
|
this.ui.dns_provider.prop('required', false);
|
||||||
this.ui.cloudflare.hide();
|
this.ui.dns_provider_credentials.prop('required', false);
|
||||||
|
this.ui.dns_challenge_content.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'change @ui.dns_provider': function () {
|
||||||
|
const selected_provider = this.ui.dns_provider[0].options[this.ui.dns_provider[0].selectedIndex].value;
|
||||||
|
if (selected_provider != '' && dns_providers[selected_provider].credentials !== false) {
|
||||||
|
this.ui.dns_provider_credentials.prop('required', 'required');
|
||||||
|
this.ui.dns_provider_credentials[0].value = dns_providers[selected_provider].credentials;
|
||||||
|
this.ui.credentials_file_content.show();
|
||||||
|
} else {
|
||||||
|
this.ui.dns_provider_credentials.prop('required', false);
|
||||||
|
this.ui.credentials_file_content.hide();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'click @ui.save': function (e) {
|
'click @ui.save': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
this.ui.le_error_info.hide();
|
||||||
|
|
||||||
if (!this.ui.form[0].checkValidity()) {
|
if (!this.ui.form[0].checkValidity()) {
|
||||||
$('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
|
$('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
|
||||||
@ -110,6 +135,18 @@ module.exports = Mn.View.extend({
|
|||||||
data.hsts_subdomains = !!data.hsts_subdomains;
|
data.hsts_subdomains = !!data.hsts_subdomains;
|
||||||
data.ssl_forced = !!data.ssl_forced;
|
data.ssl_forced = !!data.ssl_forced;
|
||||||
|
|
||||||
|
if (typeof data.meta === 'undefined') data.meta = {};
|
||||||
|
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1;
|
||||||
|
data.meta.dns_challenge = data.meta.dns_challenge == 1;
|
||||||
|
|
||||||
|
if(!data.meta.dns_challenge){
|
||||||
|
data.meta.dns_provider = undefined;
|
||||||
|
data.meta.dns_provider_credentials = undefined;
|
||||||
|
data.meta.propagation_seconds = undefined;
|
||||||
|
} else {
|
||||||
|
if(data.meta.propagation_seconds === '') data.meta.propagation_seconds = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof data.domain_names === 'string' && data.domain_names) {
|
if (typeof data.domain_names === 'string' && data.domain_names) {
|
||||||
data.domain_names = data.domain_names.split(',');
|
data.domain_names = data.domain_names.split(',');
|
||||||
}
|
}
|
||||||
@ -117,7 +154,7 @@ module.exports = Mn.View.extend({
|
|||||||
// Check for any domain names containing wildcards, which are not allowed with letsencrypt
|
// Check for any domain names containing wildcards, which are not allowed with letsencrypt
|
||||||
if (data.certificate_id === 'new') {
|
if (data.certificate_id === 'new') {
|
||||||
let domain_err = false;
|
let domain_err = false;
|
||||||
if (!data.meta.cloudflare_use) {
|
if (!data.meta.dns_challenge) {
|
||||||
data.domain_names.map(function (name) {
|
data.domain_names.map(function (name) {
|
||||||
if (name.match(/\*/im)) {
|
if (name.match(/\*/im)) {
|
||||||
domain_err = true;
|
domain_err = true;
|
||||||
@ -126,12 +163,9 @@ module.exports = Mn.View.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (domain_err) {
|
if (domain_err) {
|
||||||
alert('Cannot request Let\'s Encrypt Certificate for wildcard domains without CloudFlare DNS.');
|
alert(i18n('ssl', 'no-wildcard-without-dns'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.meta.cloudflare_use = data.meta.cloudflare_use === '1';
|
|
||||||
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree === '1';
|
|
||||||
} else {
|
} else {
|
||||||
data.certificate_id = parseInt(data.certificate_id, 10);
|
data.certificate_id = parseInt(data.certificate_id, 10);
|
||||||
}
|
}
|
||||||
@ -160,7 +194,15 @@ module.exports = Mn.View.extend({
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
alert(err.message);
|
let more_info = '';
|
||||||
|
if(err.code === 500 && err.debug){
|
||||||
|
try{
|
||||||
|
more_info = JSON.parse(err.debug).debug.stack.join("\n");
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
this.ui.le_error_info[0].innerHTML = `${err.message}${more_info !== '' ? `<pre class="mt-3">${more_info}</pre>`:''}`;
|
||||||
|
this.ui.le_error_info.show();
|
||||||
|
this.ui.le_error_info[0].scrollIntoView();
|
||||||
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
|
||||||
this.ui.save.removeClass('btn-loading');
|
this.ui.save.removeClass('btn-loading');
|
||||||
});
|
});
|
||||||
@ -170,7 +212,20 @@ module.exports = Mn.View.extend({
|
|||||||
templateContext: {
|
templateContext: {
|
||||||
getLetsencryptEmail: function () {
|
getLetsencryptEmail: function () {
|
||||||
return App.Cache.User.get('email');
|
return App.Cache.User.get('email');
|
||||||
}
|
},
|
||||||
|
getUseDnsChallenge: function () {
|
||||||
|
return typeof this.meta.dns_challenge !== 'undefined' ? this.meta.dns_challenge : false;
|
||||||
|
},
|
||||||
|
getDnsProvider: function () {
|
||||||
|
return typeof this.meta.dns_provider !== 'undefined' && this.meta.dns_provider != '' ? this.meta.dns_provider : null;
|
||||||
|
},
|
||||||
|
getDnsProviderCredentials: function () {
|
||||||
|
return typeof this.meta.dns_provider_credentials !== 'undefined' ? this.meta.dns_provider_credentials : '';
|
||||||
|
},
|
||||||
|
getPropagationSeconds: function () {
|
||||||
|
return typeof this.meta.propagation_seconds !== 'undefined' ? this.meta.propagation_seconds : '';
|
||||||
|
},
|
||||||
|
dns_plugins: dns_providers,
|
||||||
},
|
},
|
||||||
|
|
||||||
onRender: function () {
|
onRender: function () {
|
||||||
@ -191,6 +246,9 @@ module.exports = Mn.View.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Certificates
|
// Certificates
|
||||||
|
this.ui.le_error_info.hide();
|
||||||
|
this.ui.dns_challenge_content.hide();
|
||||||
|
this.ui.credentials_file_content.hide();
|
||||||
this.ui.letsencrypt.hide();
|
this.ui.letsencrypt.hide();
|
||||||
this.ui.certificate_select.selectize({
|
this.ui.certificate_select.selectize({
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
|
@ -102,7 +102,17 @@
|
|||||||
"letsencrypt-agree": "I Agree to the <a href=\"{url}\" target=\"_blank\">Let's Encrypt Terms of Service</a>",
|
"letsencrypt-agree": "I Agree to the <a href=\"{url}\" target=\"_blank\">Let's Encrypt Terms of Service</a>",
|
||||||
"delete-ssl": "The SSL certificates attached will NOT be removed, they will need to be removed manually.",
|
"delete-ssl": "The SSL certificates attached will NOT be removed, they will need to be removed manually.",
|
||||||
"hosts-warning": "These domains must be already configured to point to this installation",
|
"hosts-warning": "These domains must be already configured to point to this installation",
|
||||||
"use-cloudflare": "Use CloudFlare DNS verification"
|
"no-wildcard-without-dns": "Cannot request Let's Encrypt Certificate for wildcard domains when not using DNS challenge",
|
||||||
|
"dns-challenge": "Use a DNS Challenge",
|
||||||
|
"certbot-warning": "This section requires some knowledge about Certbot and its DNS plugins. Please consult the respective plugins documentation.",
|
||||||
|
"dns-provider": "DNS Provider",
|
||||||
|
"please-choose": "Please Choose...",
|
||||||
|
"credentials-file-content": "Credentials File Content",
|
||||||
|
"credentials-file-content-info": "This plugin requires a configuration file containing an API token or other credentials to your provider",
|
||||||
|
"stored-as-plaintext-info": "This data will be stored as plaintext in the database and in a file!",
|
||||||
|
"propagation-seconds": "Propagation Seconds",
|
||||||
|
"propagation-seconds-info": "Leave empty to use the plugins default value. Number of seconds to wait for DNS propagation.",
|
||||||
|
"processing-info": "Processing... This might take a few minutes."
|
||||||
},
|
},
|
||||||
"proxy-hosts": {
|
"proxy-hosts": {
|
||||||
"title": "Proxy Hosts",
|
"title": "Proxy Hosts",
|
||||||
@ -196,7 +206,8 @@
|
|||||||
"authorization": "Authorization",
|
"authorization": "Authorization",
|
||||||
"access": "Access",
|
"access": "Access",
|
||||||
"satisfy": "Satisfy",
|
"satisfy": "Satisfy",
|
||||||
"satisfy-any": "Satisfy Any"
|
"satisfy-any": "Satisfy Any",
|
||||||
|
"pass-auth": "Pass Auth to Host"
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
"title": "Users",
|
"title": "Users",
|
||||||
|
275
global/certbot-dns-plugins.js
Normal file
275
global/certbot-dns-plugins.js
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
/**
|
||||||
|
* This file contains info about available Certbot DNS plugins.
|
||||||
|
* This only works for plugins which use the standard argument structure, so:
|
||||||
|
* --authenticator <plugin-name> --<plugin-name>-credentials <FILE> --<plugin-name>-propagation-seconds <number>
|
||||||
|
*
|
||||||
|
* File Structure:
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* cloudflare: {
|
||||||
|
* display_name: "Name displayed to the user",
|
||||||
|
* package_name: "Package name in PyPi repo",
|
||||||
|
* package_version: "Package version in PyPi repo",
|
||||||
|
* dependencies: "Additional dependencies, space separated (as you would pass it to pip install)",
|
||||||
|
* credentials: `Template of the credentials file`,
|
||||||
|
* full_plugin_name: "The full plugin name as used in the commandline with certbot, including prefixes, e.g. 'certbot-dns-njalla:dns-njalla'",
|
||||||
|
* },
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
cloudflare: {
|
||||||
|
display_name: 'Cloudflare',
|
||||||
|
package_name: 'certbot-dns-cloudflare',
|
||||||
|
package_version: '1.8.0',
|
||||||
|
dependencies: 'cloudflare',
|
||||||
|
credentials: `# Cloudflare API token
|
||||||
|
dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567`,
|
||||||
|
full_plugin_name: 'dns-cloudflare',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
cloudxns: {
|
||||||
|
display_name: 'CloudXNS',
|
||||||
|
package_name: 'certbot-dns-cloudxns',
|
||||||
|
package_version: '1.8.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `dns_cloudxns_api_key = 1234567890abcdef1234567890abcdef
|
||||||
|
dns_cloudxns_secret_key = 1122334455667788`,
|
||||||
|
full_plugin_name: 'dns-cloudxns',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
corenetworks: {
|
||||||
|
display_name: 'Core Networks',
|
||||||
|
package_name: 'certbot-dns-corenetworks',
|
||||||
|
package_version: '0.1.4',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `certbot_dns_corenetworks:dns_corenetworks_username = asaHB12r
|
||||||
|
certbot_dns_corenetworks:dns_corenetworks_password = secure_password`,
|
||||||
|
full_plugin_name: 'certbot-dns-corenetworks:dns-corenetworks',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
cpanel: {
|
||||||
|
display_name: 'cPanel',
|
||||||
|
package_name: 'certbot-dns-cpanel',
|
||||||
|
package_version: '0.2.2',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `certbot_dns_cpanel:cpanel_url = https://cpanel.example.com:2083
|
||||||
|
certbot_dns_cpanel:cpanel_username = user
|
||||||
|
certbot_dns_cpanel:cpanel_password = hunter2`,
|
||||||
|
full_plugin_name: 'certbot-dns-cpanel:cpanel',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
digitalocean: {
|
||||||
|
display_name: 'DigitalOcean',
|
||||||
|
package_name: 'certbot-dns-digitalocean',
|
||||||
|
package_version: '1.8.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: 'dns_digitalocean_token = 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff',
|
||||||
|
full_plugin_name: 'dns-digitalocean',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
directadmin: {
|
||||||
|
display_name: 'DirectAdmin',
|
||||||
|
package_name: 'certbot-dns-directadmin',
|
||||||
|
package_version: '0.0.20',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `directadmin_url = https://my.directadminserver.com:2222
|
||||||
|
directadmin_username = username
|
||||||
|
directadmin_password = aSuperStrongPassword`,
|
||||||
|
full_plugin_name: 'certbot-dns-directadmin:directadmin',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
dnsimple: {
|
||||||
|
display_name: 'DNSimple',
|
||||||
|
package_name: 'certbot-dns-dnsimple',
|
||||||
|
package_version: '1.8.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: 'dns_dnsimple_token = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw',
|
||||||
|
full_plugin_name: 'dns-dnsimple',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
dnsmadeeasy: {
|
||||||
|
display_name: 'DNS Made Easy',
|
||||||
|
package_name: 'certbot-dns-dnsmadeeasy',
|
||||||
|
package_version: '1.8.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `dns_dnsmadeeasy_api_key = 1c1a3c91-4770-4ce7-96f4-54c0eb0e457a
|
||||||
|
dns_dnsmadeeasy_secret_key = c9b5625f-9834-4ff8-baba-4ed5f32cae55`,
|
||||||
|
full_plugin_name: 'dns-dnsmadeeasy',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
dnspod: {
|
||||||
|
display_name: 'DNSPod',
|
||||||
|
package_name: 'certbot-dns-dnspod',
|
||||||
|
package_version: '0.1.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `certbot_dns_dnspod:dns_dnspod_email = "DNSPOD-API-REQUIRES-A-VALID-EMAIL"
|
||||||
|
certbot_dns_dnspod:dns_dnspod_api_token = "DNSPOD-API-TOKEN"`,
|
||||||
|
full_plugin_name: 'certbot-dns-dnspod:dns-dnspod',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
google: {
|
||||||
|
display_name: 'Google',
|
||||||
|
package_name: 'certbot-dns-google',
|
||||||
|
package_version: '1.8.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `{
|
||||||
|
"type": "service_account",
|
||||||
|
...
|
||||||
|
}`,
|
||||||
|
full_plugin_name: 'dns-google',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
hetzner: {
|
||||||
|
display_name: 'Hetzner',
|
||||||
|
package_name: 'certbot-dns-hetzner',
|
||||||
|
package_version: '1.0.4',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: 'certbot_dns_hetzner:dns_hetzner_api_token = 0123456789abcdef0123456789abcdef',
|
||||||
|
full_plugin_name: 'certbot-dns-hetzner:dns-hetzner',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
inwx: {
|
||||||
|
display_name: 'INWX',
|
||||||
|
package_name: 'certbot-dns-inwx',
|
||||||
|
package_version: '2.1.2',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `certbot_dns_inwx:dns_inwx_url = https://api.domrobot.com/xmlrpc/
|
||||||
|
certbot_dns_inwx:dns_inwx_username = your_username
|
||||||
|
certbot_dns_inwx:dns_inwx_password = your_password
|
||||||
|
certbot_dns_inwx:dns_inwx_shared_secret = your_shared_secret optional`,
|
||||||
|
full_plugin_name: 'certbot-dns-inwx:dns-inwx',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
ispconfig: {
|
||||||
|
display_name: 'ISPConfig',
|
||||||
|
package_name: 'certbot-dns-ispconfig',
|
||||||
|
package_version: '0.2.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `certbot_dns_ispconfig:dns_ispconfig_username = myremoteuser
|
||||||
|
certbot_dns_ispconfig:dns_ispconfig_password = verysecureremoteuserpassword
|
||||||
|
certbot_dns_ispconfig:dns_ispconfig_endpoint = https://localhost:8080`,
|
||||||
|
full_plugin_name: 'certbot-dns-ispconfig:dns-ispconfig',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
isset: {
|
||||||
|
display_name: 'Isset',
|
||||||
|
package_name: 'certbot-dns-isset',
|
||||||
|
package_version: '0.0.3',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `certbot_dns_isset:dns_isset_endpoint="https://customer.isset.net/api"
|
||||||
|
certbot_dns_isset:dns_isset_token="<token>"`,
|
||||||
|
full_plugin_name: 'certbot-dns-isset:dns-isset',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
linode: {
|
||||||
|
display_name: 'Linode',
|
||||||
|
package_name: 'certbot-dns-linode',
|
||||||
|
package_version: '1.8.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `dns_linode_key = 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ64
|
||||||
|
dns_linode_version = [<blank>|3|4]`,
|
||||||
|
full_plugin_name: 'dns-linode',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
luadns: {
|
||||||
|
display_name: 'LuaDNS',
|
||||||
|
package_name: 'certbot-dns-luadns',
|
||||||
|
package_version: '1.8.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `dns_luadns_email = user@example.com
|
||||||
|
dns_luadns_token = 0123456789abcdef0123456789abcdef`,
|
||||||
|
full_plugin_name: 'dns-luadns',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
netcup: {
|
||||||
|
display_name: 'netcup',
|
||||||
|
package_name: 'certbot-dns-netcup',
|
||||||
|
package_version: '1.0.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `dns_netcup_customer_id = 123456
|
||||||
|
dns_netcup_api_key = 0123456789abcdef0123456789abcdef01234567
|
||||||
|
dns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123`,
|
||||||
|
full_plugin_name: 'certbot-dns-netcup:dns-netcup',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
njalla: {
|
||||||
|
display_name: 'Njalla',
|
||||||
|
package_name: 'certbot-dns-njalla',
|
||||||
|
package_version: '1.0.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: 'certbot_dns_njalla:dns_njalla_token = 0123456789abcdef0123456789abcdef01234567',
|
||||||
|
full_plugin_name: 'certbot-dns-njalla:dns-njalla',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
nsone: {
|
||||||
|
display_name: 'NS1',
|
||||||
|
package_name: 'certbot-dns-nsone',
|
||||||
|
package_version: '1.8.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: 'dns_nsone_api_key = MDAwMDAwMDAwMDAwMDAw',
|
||||||
|
full_plugin_name: 'dns-nsone',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
ovh: {
|
||||||
|
display_name: 'OVH',
|
||||||
|
package_name: 'certbot-dns-ovh',
|
||||||
|
package_version: '1.8.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `dns_ovh_endpoint = ovh-eu
|
||||||
|
dns_ovh_application_key = MDAwMDAwMDAwMDAw
|
||||||
|
dns_ovh_application_secret = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
|
||||||
|
dns_ovh_consumer_key = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw`,
|
||||||
|
full_plugin_name: 'dns-ovh',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
powerdns: {
|
||||||
|
display_name: 'PowerDNS',
|
||||||
|
package_name: 'certbot-dns-powerdns',
|
||||||
|
package_version: '0.2.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `certbot_dns_powerdns:dns_powerdns_api_url = https://api.mypowerdns.example.org
|
||||||
|
certbot_dns_powerdns:dns_powerdns_api_key = AbCbASsd!@34`,
|
||||||
|
full_plugin_name: 'certbot-dns-powerdns:dns-powerdns',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
rfc2136: {
|
||||||
|
display_name: 'RFC 2136',
|
||||||
|
package_name: 'certbot-dns-rfc2136',
|
||||||
|
package_version: '1.8.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `# Target DNS server
|
||||||
|
dns_rfc2136_server = 192.0.2.1
|
||||||
|
# Target DNS port
|
||||||
|
dns_rfc2136_port = 53
|
||||||
|
# TSIG key name
|
||||||
|
dns_rfc2136_name = keyname.
|
||||||
|
# TSIG key secret
|
||||||
|
dns_rfc2136_secret = 4q4wM/2I180UXoMyN4INVhJNi8V9BCV+jMw2mXgZw/CSuxUT8C7NKKFs AmKd7ak51vWKgSl12ib86oQRPkpDjg==
|
||||||
|
# TSIG key algorithm
|
||||||
|
dns_rfc2136_algorithm = HMAC-SHA512`,
|
||||||
|
full_plugin_name: 'dns-rfc2136',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
route53: {
|
||||||
|
display_name: 'Route 53 (Amazon)',
|
||||||
|
package_name: 'certbot-dns-route53',
|
||||||
|
package_version: '1.8.0',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: `[default]
|
||||||
|
aws_access_key_id=AKIAIOSFODNN7EXAMPLE
|
||||||
|
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`,
|
||||||
|
full_plugin_name: 'dns-route53',
|
||||||
|
},
|
||||||
|
//####################################################//
|
||||||
|
vultr: {
|
||||||
|
display_name: 'Vultr',
|
||||||
|
package_name: 'certbot-dns-vultr',
|
||||||
|
package_version: '1.0.3',
|
||||||
|
dependencies: '',
|
||||||
|
credentials: 'certbot_dns_vultr:dns_vultr_key = YOUR_VULTR_API_KEY',
|
||||||
|
full_plugin_name: 'certbot-dns-vultr:dns-vultr',
|
||||||
|
},
|
||||||
|
};
|
@ -10,7 +10,7 @@ if hash docker 2>/dev/null; then
|
|||||||
docker pull "${DOCKER_IMAGE}"
|
docker pull "${DOCKER_IMAGE}"
|
||||||
cd "${DIR}/.."
|
cd "${DIR}/.."
|
||||||
echo -e "${BLUE}❯ ${CYAN}Building Frontend ...${RESET}"
|
echo -e "${BLUE}❯ ${CYAN}Building Frontend ...${RESET}"
|
||||||
docker run --rm -e CI=true -v "$(pwd)/frontend:/app/frontend" -w /app/frontend "$DOCKER_IMAGE" sh -c "yarn install && yarn build && yarn build && chown -R $(id -u):$(id -g) /app/frontend"
|
docker run --rm -e CI=true -v "$(pwd)/frontend:/app/frontend" -v "$(pwd)/global:/app/global" -w /app/frontend "$DOCKER_IMAGE" sh -c "yarn install && yarn build && yarn build && chown -R $(id -u):$(id -g) /app/frontend"
|
||||||
echo -e "${BLUE}❯ ${GREEN}Building Frontend Complete${RESET}"
|
echo -e "${BLUE}❯ ${GREEN}Building Frontend Complete${RESET}"
|
||||||
else
|
else
|
||||||
echo -e "${RED}❯ docker command is not available${RESET}"
|
echo -e "${RED}❯ docker command is not available${RESET}"
|
||||||
|
@ -7,7 +7,7 @@ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|||||||
if hash docker-compose 2>/dev/null; then
|
if hash docker-compose 2>/dev/null; then
|
||||||
cd "${DIR}/.."
|
cd "${DIR}/.."
|
||||||
echo -e "${BLUE}❯ ${CYAN}Testing Dev Stack ...${RESET}"
|
echo -e "${BLUE}❯ ${CYAN}Testing Dev Stack ...${RESET}"
|
||||||
docker-compose exec -T npm bash -c "cd /app/backend && task test"
|
docker-compose exec -T npm bash -c "cd /app && task test"
|
||||||
else
|
else
|
||||||
echo -e "${RED}❯ docker-compose command is not available${RESET}"
|
echo -e "${RED}❯ docker-compose command is not available${RESET}"
|
||||||
fi
|
fi
|
||||||
|
Reference in New Issue
Block a user