Skip to content

Commit

Permalink
Merge pull request #654 from jc21/develop
Browse files Browse the repository at this point in the history
2.6.0 Release
  • Loading branch information
jc21 authored Oct 15, 2020
2 parents 28f7208 + c413b4a commit e3399e1
Show file tree
Hide file tree
Showing 38 changed files with 1,214 additions and 232 deletions.
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.5.0
2.6.0
1 change: 1 addition & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pipeline {
// See: https://github.com/yarnpkg/yarn/issues/3254
sh '''docker run --rm \\
-v "$(pwd)/backend:/app" \\
-v "$(pwd)/global:/app/global" \\
-w /app \\
node:latest \\
sh -c "yarn install && yarn eslint . && rm -rf node_modules"
Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<p align="center">
<img src="https://nginxproxymanager.com/github.png">
<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.0-green.svg?style=for-the-badge">
<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">
</a>
Expand Down Expand Up @@ -185,6 +185,26 @@ Special thanks to the following contributors:
<br /><sub><b>Jaap-Jan de Wit</b></sub>
</a>
</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>
</table>
<!-- markdownlint-enable -->
Expand Down
2 changes: 1 addition & 1 deletion backend/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
stack: typeof err.stack !== 'undefined' && err.stack ? err.stack.split('\n') : null,
previous: err.previous
Expand Down
2 changes: 1 addition & 1 deletion backend/config/sqlite-test-db.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"knex": {
"client": "sqlite3",
"connection": {
"filename": "/app/backend/config/mydb.sqlite"
"filename": "/app/config/mydb.sqlite"
},
"pool": {
"min": 0,
Expand Down
4 changes: 3 additions & 1 deletion backend/internal/access-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const internalAccessList = {
.insertAndFetch({
name: data.name,
satisfy_any: data.satisfy_any,
pass_auth: data.pass_auth,
owner_user_id: access.token.getUserId(1)
});
})
Expand Down Expand Up @@ -128,6 +129,7 @@ const internalAccessList = {
.patch({
name: data.name,
satisfy_any: data.satisfy_any,
pass_auth: data.pass_auth,
});
}
})
Expand Down Expand Up @@ -384,7 +386,7 @@ const internalAccessList = {
.orderBy('access_list.name', 'ASC');

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
Expand Down
121 changes: 91 additions & 30 deletions backend/internal/certificate.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const internalNginx = require('./nginx');
const internalHost = require('./host');
const certbot_command = '/usr/bin/certbot';
const le_config = '/etc/letsencrypt.ini';
const dns_plugins = require('../global/certbot-dns-plugins');

function omissions() {
return ['is_deleted'];
Expand Down Expand Up @@ -141,11 +142,11 @@ const internalCertificate = {
});
})
.then((in_use_result) => {
// Is CloudFlare, no config needed, so skip 3 and 5.
if (data.meta.cloudflare_use) {
// With DNS challenge no config is needed, so skip 3 and 5.
if (certificate.meta.dns_challenge) {
return internalNginx.reload().then(() => {
// 4. Request cert
return internalCertificate.requestLetsEncryptCloudFlareDnsSsl(certificate, data.meta.cloudflare_token);
return internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate);
})
.then(internalNginx.reload)
.then(() => {
Expand Down Expand Up @@ -772,35 +773,70 @@ const internalCertificate = {
},

/**
* @param {Object} certificate the certificate row
* @param {String} apiToken the cloudflare api token
* @param {Object} certificate the certificate row
* @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}
*/
requestLetsEncryptCloudFlareDnsSsl: (certificate, apiToken) => {
logger.info('Requesting Let\'sEncrypt certificates via Cloudflare DNS for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
requestLetsEncryptSslWithDnsChallenge: (certificate) => {
const dns_plugin = dns_plugins[certificate.meta.dns_provider];

if (!dns_plugin) {
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
}

logger.info(`Requesting Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);

const credentials_loc = '/etc/letsencrypt/credentials-' + certificate.id;
const credentials_cmd = '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;

let tokenLoc = '~/cloudflare-token';
let storeKey = 'echo "dns_cloudflare_api_token = ' + apiToken + '" > ' + tokenLoc;
// Whether the plugin has a --<name>-credentials argument
const has_config_arg = certificate.meta.dns_provider !== 'route53';

let cmd =
storeKey + ' && ' +
let main_cmd =
certbot_command + ' certonly --non-interactive ' +
'--cert-name "npm-' + certificate.id + '" ' +
'--agree-tos ' +
'--email "' + certificate.meta.letsencrypt_email + '" ' +
'--domains "' + certificate.domain_names.join(',') + '" ' +
'--dns-cloudflare --dns-cloudflare-credentials ' + tokenLoc +
(le_staging ? ' --staging' : '')
+ ' && rm ' + tokenLoc;
'--authenticator ' + dns_plugin.full_plugin_name + ' ' +
(
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' : '');

// Prepend the path to the credentials file as an environment variable
if (certificate.meta.dns_provider === 'route53') {
main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd;
}

const teardown_cmd = `rm '${credentials_loc}'`;

if (debug_mode) {
logger.info('Command:', cmd);
logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd} && ${teardown_cmd}`);
}

return utils.exec(cmd).then((result) => {
logger.info(result);
return result;
});
return utils.exec(credentials_cmd)
.then(() => {
return utils.exec(prepare_cmd)
.then(() => {
return utils.exec(main_cmd)
.then(async (result) => {
await utils.exec(teardown_cmd);
logger.info(result);
return result;
});
});
});
},


Expand All @@ -817,7 +853,7 @@ const internalCertificate = {
})
.then((certificate) => {
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)
.then(() => {
Expand Down Expand Up @@ -877,22 +913,47 @@ const internalCertificate = {
* @param {Object} certificate the certificate row
* @returns {Promise}
*/
renewLetsEncryptCloudFlareSsl: (certificate) => {
logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
renewLetsEncryptSslWithDnsChallenge: (certificate) => {
const dns_plugin = dns_plugins[certificate.meta.dns_provider];

let cmd = certbot_command + ' renew --non-interactive ' +
if (!dns_plugin) {
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
}

logger.info(`Renewing Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);

const credentials_loc = '/etc/letsencrypt/credentials-' + certificate.id;
const credentials_cmd = '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;

let main_cmd =
certbot_command + ' renew --non-interactive ' +
'--cert-name "npm-' + certificate.id + '" ' +
'--disable-hook-validation ' +
(le_staging ? '--staging' : '');
'--disable-hook-validation' +
(le_staging ? ' --staging' : '');

// Prepend the path to the credentials file as an environment variable
if (certificate.meta.dns_provider === 'route53') {
main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd;
}

const teardown_cmd = `rm '${credentials_loc}'`;

if (debug_mode) {
logger.info('Command:', cmd);
logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd} && ${teardown_cmd}`);
}

return utils.exec(cmd)
.then((result) => {
logger.info(result);
return result;
return utils.exec(credentials_cmd)
.then(() => {
return utils.exec(prepare_cmd)
.then(() => {
return utils.exec(main_cmd)
.then(async (result) => {
await utils.exec(teardown_cmd);
logger.info(result);
return result;
});
});
});
},

Expand Down
41 changes: 41 additions & 0 deletions backend/migrations/20201014143841_pass_auth.js
Original file line number Diff line number Diff line change
@@ -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');
});
};
4 changes: 4 additions & 0 deletions backend/models/access_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ class AccessList extends Model {
get satisfy() {
return this.satisfy_any ? 'satisfy any' : 'satisfy all';
}

get passauth() {
return this.pass_auth ? '' : 'proxy_set_header Authorization "";';
}
}

module.exports = AccessList;
2 changes: 2 additions & 0 deletions backend/routes/api/nginx/certificates.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ router
.post((req, res, next) => {
apiValidator({$ref: 'endpoints/certificates#/links/1/schema'}, req.body)
.then((payload) => {
req.setTimeout(900000); // 15 minutes timeout
return internalCertificate.create(res.locals.access, payload);
})
.then((result) => {
Expand Down Expand Up @@ -197,6 +198,7 @@ router
* Renew certificate
*/
.post((req, res, next) => {
req.setTimeout(900000); // 15 minutes timeout
internalCertificate.renew(res.locals.access, {
id: parseInt(req.params.certificate_id, 10)
})
Expand Down
9 changes: 9 additions & 0 deletions backend/schema/endpoints/access-lists.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
"satisfy_any": {
"type": "boolean"
},
"pass_auth": {
"type": "boolean"
},
"meta": {
"type": "object"
}
Expand Down Expand Up @@ -102,6 +105,9 @@
"satisfy_any": {
"$ref": "#/definitions/satisfy_any"
},
"pass_auth": {
"$ref": "#/definitions/pass_auth"
},
"items": {
"type": "array",
"minItems": 0,
Expand Down Expand Up @@ -167,6 +173,9 @@
"satisfy_any": {
"$ref": "#/definitions/satisfy_any"
},
"pass_auth": {
"$ref": "#/definitions/pass_auth"
},
"items": {
"type": "array",
"minItems": 0,
Expand Down
16 changes: 14 additions & 2 deletions backend/schema/endpoints/certificates.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,23 @@
"letsencrypt_agree": {
"type": "boolean"
},
"cloudflare_use": {
"dns_challenge": {
"type": "boolean"
},
"cloudflare_token": {
"dns_provider": {
"type": "string"
},
"dns_provider_credentials": {
"type": "string"
},
"propagation_seconds": {
"anyOf": [
{
"type": "integer",
"minimum": 0
}
]

}
}
}
Expand Down
Loading

0 comments on commit e3399e1

Please sign in to comment.