Skip to content

Commit e3399e1

Browse files
authored
Merge pull request #654 from jc21/develop
2.6.0 Release
2 parents 28f7208 + c413b4a commit e3399e1

38 files changed

+1214
-232
lines changed

.version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.5.0
1+
2.6.0

Jenkinsfile

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ pipeline {
6565
// See: https://github.com/yarnpkg/yarn/issues/3254
6666
sh '''docker run --rm \\
6767
-v "$(pwd)/backend:/app" \\
68+
-v "$(pwd)/global:/app/global" \\
6869
-w /app \\
6970
node:latest \\
7071
sh -c "yarn install && yarn eslint . && rm -rf node_modules"

README.md

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<p align="center">
22
<img src="https://nginxproxymanager.com/github.png">
33
<br><br>
4-
<img src="https://img.shields.io/badge/version-2.5.0-green.svg?style=for-the-badge">
4+
<img src="https://img.shields.io/badge/version-2.6.0-green.svg?style=for-the-badge">
55
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
66
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
77
</a>
@@ -185,6 +185,26 @@ Special thanks to the following contributors:
185185
<br /><sub><b>Jaap-Jan de Wit</b></sub>
186186
</a>
187187
</td>
188+
<td align="center">
189+
<a href="https://github.com/jmwebslave">
190+
<img src="https://avatars2.githubusercontent.com/u/6118262?s=460&u=7db409c47135b1e141c366bbb03ed9fae6ac2638&v=4" width="80px;" alt=""/>
191+
<br /><sub><b>James Morgan</b></sub>
192+
</a>
193+
</td>
194+
</tr>
195+
<tr>
196+
<td align="center">
197+
<a href="https://github.com/chaptergy">
198+
<img src="https://avatars2.githubusercontent.com/u/26956711?s=460&u=7d9adebabb6b4e7af7cb05d98d751087a372304b&v=4" width="80px;" alt=""/>
199+
<br /><sub><b>chaptergy</b></sub>
200+
</a>
201+
</td>
202+
<td align="center">
203+
<a href="https://github.com/Philip-Mooney">
204+
<img src="https://avatars0.githubusercontent.com/u/48624631?s=460&v=4" width="80px;" alt=""/>
205+
<br /><sub><b>Philip Mooney</b></sub>
206+
</a>
207+
</td>
188208
</tr>
189209
</table>
190210
<!-- markdownlint-enable -->

backend/app.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ app.use(function (err, req, res, next) {
6666
}
6767
};
6868

69-
if (process.env.NODE_ENV === 'development') {
69+
if (process.env.NODE_ENV === 'development' || (req.baseUrl + req.path).includes('nginx/certificates')) {
7070
payload.debug = {
7171
stack: typeof err.stack !== 'undefined' && err.stack ? err.stack.split('\n') : null,
7272
previous: err.previous

backend/config/sqlite-test-db.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"knex": {
55
"client": "sqlite3",
66
"connection": {
7-
"filename": "/app/backend/config/mydb.sqlite"
7+
"filename": "/app/config/mydb.sqlite"
88
},
99
"pool": {
1010
"min": 0,

backend/internal/access-list.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const internalAccessList = {
3131
.insertAndFetch({
3232
name: data.name,
3333
satisfy_any: data.satisfy_any,
34+
pass_auth: data.pass_auth,
3435
owner_user_id: access.token.getUserId(1)
3536
});
3637
})
@@ -128,6 +129,7 @@ const internalAccessList = {
128129
.patch({
129130
name: data.name,
130131
satisfy_any: data.satisfy_any,
132+
pass_auth: data.pass_auth,
131133
});
132134
}
133135
})
@@ -384,7 +386,7 @@ const internalAccessList = {
384386
.orderBy('access_list.name', 'ASC');
385387

386388
if (access_data.permission_visibility !== 'all') {
387-
query.andWhere('owner_user_id', access.token.getUserId(1));
389+
query.andWhere('access_list.owner_user_id', access.token.getUserId(1));
388390
}
389391

390392
// Query is used for searching

backend/internal/certificate.js

+91-30
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const internalNginx = require('./nginx');
1313
const internalHost = require('./host');
1414
const certbot_command = '/usr/bin/certbot';
1515
const le_config = '/etc/letsencrypt.ini';
16+
const dns_plugins = require('../global/certbot-dns-plugins');
1617

1718
function omissions() {
1819
return ['is_deleted'];
@@ -141,11 +142,11 @@ const internalCertificate = {
141142
});
142143
})
143144
.then((in_use_result) => {
144-
// Is CloudFlare, no config needed, so skip 3 and 5.
145-
if (data.meta.cloudflare_use) {
145+
// With DNS challenge no config is needed, so skip 3 and 5.
146+
if (certificate.meta.dns_challenge) {
146147
return internalNginx.reload().then(() => {
147148
// 4. Request cert
148-
return internalCertificate.requestLetsEncryptCloudFlareDnsSsl(certificate, data.meta.cloudflare_token);
149+
return internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate);
149150
})
150151
.then(internalNginx.reload)
151152
.then(() => {
@@ -772,35 +773,70 @@ const internalCertificate = {
772773
},
773774

774775
/**
775-
* @param {Object} certificate the certificate row
776-
* @param {String} apiToken the cloudflare api token
776+
* @param {Object} certificate the certificate row
777+
* @param {String} dns_provider the dns provider name (key used in `certbot-dns-plugins.js`)
778+
* @param {String | null} credentials the content of this providers credentials file
779+
* @param {String} propagation_seconds the cloudflare api token
777780
* @returns {Promise}
778781
*/
779-
requestLetsEncryptCloudFlareDnsSsl: (certificate, apiToken) => {
780-
logger.info('Requesting Let\'sEncrypt certificates via Cloudflare DNS for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
782+
requestLetsEncryptSslWithDnsChallenge: (certificate) => {
783+
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
784+
785+
if (!dns_plugin) {
786+
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
787+
}
788+
789+
logger.info(`Requesting Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
790+
791+
const credentials_loc = '/etc/letsencrypt/credentials-' + certificate.id;
792+
const credentials_cmd = 'echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'';
793+
const prepare_cmd = 'pip3 install ' + dns_plugin.package_name + '==' + dns_plugin.package_version;
781794

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

785-
let cmd =
786-
storeKey + ' && ' +
798+
let main_cmd =
787799
certbot_command + ' certonly --non-interactive ' +
788800
'--cert-name "npm-' + certificate.id + '" ' +
789801
'--agree-tos ' +
790802
'--email "' + certificate.meta.letsencrypt_email + '" ' +
791803
'--domains "' + certificate.domain_names.join(',') + '" ' +
792-
'--dns-cloudflare --dns-cloudflare-credentials ' + tokenLoc +
793-
(le_staging ? ' --staging' : '')
794-
+ ' && rm ' + tokenLoc;
804+
'--authenticator ' + dns_plugin.full_plugin_name + ' ' +
805+
(
806+
has_config_arg
807+
? '--' + dns_plugin.full_plugin_name + '-credentials "' + credentials_loc + '"'
808+
: ''
809+
) +
810+
(
811+
certificate.meta.propagation_seconds !== undefined
812+
? ' --' + dns_plugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds
813+
: ''
814+
) +
815+
(le_staging ? ' --staging' : '');
816+
817+
// Prepend the path to the credentials file as an environment variable
818+
if (certificate.meta.dns_provider === 'route53') {
819+
main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd;
820+
}
821+
822+
const teardown_cmd = `rm '${credentials_loc}'`;
795823

796824
if (debug_mode) {
797-
logger.info('Command:', cmd);
825+
logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd} && ${teardown_cmd}`);
798826
}
799827

800-
return utils.exec(cmd).then((result) => {
801-
logger.info(result);
802-
return result;
803-
});
828+
return utils.exec(credentials_cmd)
829+
.then(() => {
830+
return utils.exec(prepare_cmd)
831+
.then(() => {
832+
return utils.exec(main_cmd)
833+
.then(async (result) => {
834+
await utils.exec(teardown_cmd);
835+
logger.info(result);
836+
return result;
837+
});
838+
});
839+
});
804840
},
805841

806842

@@ -817,7 +853,7 @@ const internalCertificate = {
817853
})
818854
.then((certificate) => {
819855
if (certificate.provider === 'letsencrypt') {
820-
let renewMethod = certificate.meta.cloudflare_use ? internalCertificate.renewLetsEncryptCloudFlareSsl : internalCertificate.renewLetsEncryptSsl;
856+
let renewMethod = certificate.meta.dns_challenge ? internalCertificate.renewLetsEncryptSslWithDnsChallenge : internalCertificate.renewLetsEncryptSsl;
821857

822858
return renewMethod(certificate)
823859
.then(() => {
@@ -877,22 +913,47 @@ const internalCertificate = {
877913
* @param {Object} certificate the certificate row
878914
* @returns {Promise}
879915
*/
880-
renewLetsEncryptCloudFlareSsl: (certificate) => {
881-
logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
916+
renewLetsEncryptSslWithDnsChallenge: (certificate) => {
917+
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
882918

883-
let cmd = certbot_command + ' renew --non-interactive ' +
919+
if (!dns_plugin) {
920+
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
921+
}
922+
923+
logger.info(`Renewing Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
924+
925+
const credentials_loc = '/etc/letsencrypt/credentials-' + certificate.id;
926+
const credentials_cmd = 'echo \'' + certificate.meta.dns_provider_credentials.replace('\'', '\\\'') + '\' > \'' + credentials_loc + '\' && chmod 600 \'' + credentials_loc + '\'';
927+
const prepare_cmd = 'pip3 install ' + dns_plugin.package_name + '==' + dns_plugin.package_version;
928+
929+
let main_cmd =
930+
certbot_command + ' renew --non-interactive ' +
884931
'--cert-name "npm-' + certificate.id + '" ' +
885-
'--disable-hook-validation ' +
886-
(le_staging ? '--staging' : '');
932+
'--disable-hook-validation' +
933+
(le_staging ? ' --staging' : '');
934+
935+
// Prepend the path to the credentials file as an environment variable
936+
if (certificate.meta.dns_provider === 'route53') {
937+
main_cmd = 'AWS_CONFIG_FILE=\'' + credentials_loc + '\' ' + main_cmd;
938+
}
939+
940+
const teardown_cmd = `rm '${credentials_loc}'`;
887941

888942
if (debug_mode) {
889-
logger.info('Command:', cmd);
943+
logger.info('Command:', `${credentials_cmd} && ${prepare_cmd} && ${main_cmd} && ${teardown_cmd}`);
890944
}
891945

892-
return utils.exec(cmd)
893-
.then((result) => {
894-
logger.info(result);
895-
return result;
946+
return utils.exec(credentials_cmd)
947+
.then(() => {
948+
return utils.exec(prepare_cmd)
949+
.then(() => {
950+
return utils.exec(main_cmd)
951+
.then(async (result) => {
952+
await utils.exec(teardown_cmd);
953+
logger.info(result);
954+
return result;
955+
});
956+
});
896957
});
897958
},
898959

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const migrate_name = 'pass_auth';
2+
const logger = require('../logger').migrate;
3+
4+
/**
5+
* Migrate
6+
*
7+
* @see http://knexjs.org/#Schema
8+
*
9+
* @param {Object} knex
10+
* @param {Promise} Promise
11+
* @returns {Promise}
12+
*/
13+
exports.up = function (knex/*, Promise*/) {
14+
15+
logger.info('[' + migrate_name + '] Migrating Up...');
16+
17+
return knex.schema.table('access_list', function (access_list) {
18+
access_list.integer('pass_auth').notNull().defaultTo(1);
19+
})
20+
.then(() => {
21+
logger.info('[' + migrate_name + '] access_list Table altered');
22+
});
23+
};
24+
25+
/**
26+
* Undo Migrate
27+
*
28+
* @param {Object} knex
29+
* @param {Promise} Promise
30+
* @returns {Promise}
31+
*/
32+
exports.down = function (knex/*, Promise*/) {
33+
logger.info('[' + migrate_name + '] Migrating Down...');
34+
35+
return knex.schema.table('access_list', function (access_list) {
36+
access_list.dropColumn('pass_auth');
37+
})
38+
.then(() => {
39+
logger.info('[' + migrate_name + '] access_list pass_auth Column dropped');
40+
});
41+
};

backend/models/access_list.js

+4
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ class AccessList extends Model {
9393
get satisfy() {
9494
return this.satisfy_any ? 'satisfy any' : 'satisfy all';
9595
}
96+
97+
get passauth() {
98+
return this.pass_auth ? '' : 'proxy_set_header Authorization "";';
99+
}
96100
}
97101

98102
module.exports = AccessList;

backend/routes/api/nginx/certificates.js

+2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ router
5858
.post((req, res, next) => {
5959
apiValidator({$ref: 'endpoints/certificates#/links/1/schema'}, req.body)
6060
.then((payload) => {
61+
req.setTimeout(900000); // 15 minutes timeout
6162
return internalCertificate.create(res.locals.access, payload);
6263
})
6364
.then((result) => {
@@ -197,6 +198,7 @@ router
197198
* Renew certificate
198199
*/
199200
.post((req, res, next) => {
201+
req.setTimeout(900000); // 15 minutes timeout
200202
internalCertificate.renew(res.locals.access, {
201203
id: parseInt(req.params.certificate_id, 10)
202204
})

backend/schema/endpoints/access-lists.json

+9
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
"satisfy_any": {
4343
"type": "boolean"
4444
},
45+
"pass_auth": {
46+
"type": "boolean"
47+
},
4548
"meta": {
4649
"type": "object"
4750
}
@@ -102,6 +105,9 @@
102105
"satisfy_any": {
103106
"$ref": "#/definitions/satisfy_any"
104107
},
108+
"pass_auth": {
109+
"$ref": "#/definitions/pass_auth"
110+
},
105111
"items": {
106112
"type": "array",
107113
"minItems": 0,
@@ -167,6 +173,9 @@
167173
"satisfy_any": {
168174
"$ref": "#/definitions/satisfy_any"
169175
},
176+
"pass_auth": {
177+
"$ref": "#/definitions/pass_auth"
178+
},
170179
"items": {
171180
"type": "array",
172181
"minItems": 0,

backend/schema/endpoints/certificates.json

+14-2
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,23 @@
4242
"letsencrypt_agree": {
4343
"type": "boolean"
4444
},
45-
"cloudflare_use": {
45+
"dns_challenge": {
4646
"type": "boolean"
4747
},
48-
"cloudflare_token": {
48+
"dns_provider": {
4949
"type": "string"
50+
},
51+
"dns_provider_credentials": {
52+
"type": "string"
53+
},
54+
"propagation_seconds": {
55+
"anyOf": [
56+
{
57+
"type": "integer",
58+
"minimum": 0
59+
}
60+
]
61+
5062
}
5163
}
5264
}

0 commit comments

Comments
 (0)