Skip to content

Commit 84bc33d

Browse files
authored
Merge pull request #1667 from jc21/develop
v2.9.13
2 parents 9ab5333 + 6392df3 commit 84bc33d

File tree

18 files changed

+353
-19
lines changed

18 files changed

+353
-19
lines changed

.version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.9.12
1+
2.9.13

Jenkinsfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ pipeline {
6868
-v "$(pwd)/global:/app/global" \\
6969
-w /app \\
7070
node:latest \\
71-
sh -c "yarn install && yarn eslint . && rm -rf node_modules"
71+
sh -c "ln -s /usr/bin/python3 /usr/bin/python && yarn install && yarn eslint . && rm -rf node_modules"
7272
'''
7373

7474
echo 'Docker Build ...'

README.md

Lines changed: 1 addition & 1 deletion
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.9.12-green.svg?style=for-the-badge">
4+
<img src="https://img.shields.io/badge/version-2.9.13-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>

backend/internal/certificate.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const _ = require('lodash');
22
const fs = require('fs');
3+
const https = require('https');
34
const tempWrite = require('temp-write');
45
const moment = require('moment');
56
const logger = require('../logger').ssl;
@@ -15,6 +16,7 @@ const letsencryptConfig = '/etc/letsencrypt.ini';
1516
const certbotCommand = 'certbot';
1617
const archiver = require('archiver');
1718
const path = require('path');
19+
const { isArray } = require('lodash');
1820

1921
function omissions() {
2022
return ['is_deleted'];
@@ -1124,6 +1126,94 @@ const internalCertificate = {
11241126
} else {
11251127
return Promise.resolve();
11261128
}
1129+
},
1130+
1131+
testHttpsChallenge: async (access, domains) => {
1132+
await access.can('certificates:list');
1133+
1134+
if (!isArray(domains)) {
1135+
throw new error.InternalValidationError('Domains must be an array of strings');
1136+
}
1137+
if (domains.length === 0) {
1138+
throw new error.InternalValidationError('No domains provided');
1139+
}
1140+
1141+
// Create a test challenge file
1142+
const testChallengeDir = '/data/letsencrypt-acme-challenge/.well-known/acme-challenge';
1143+
const testChallengeFile = testChallengeDir + '/test-challenge';
1144+
fs.mkdirSync(testChallengeDir, {recursive: true});
1145+
fs.writeFileSync(testChallengeFile, 'Success', {encoding: 'utf8'});
1146+
1147+
async function performTestForDomain (domain) {
1148+
logger.info('Testing http challenge for ' + domain);
1149+
const url = `http://${domain}/.well-known/acme-challenge/test-challenge`;
1150+
const formBody = `method=G&url=${encodeURI(url)}&bodytype=T&requestbody=&headername=User-Agent&headervalue=None&locationid=1&ch=false&cc=false`;
1151+
const options = {
1152+
method: 'POST',
1153+
headers: {
1154+
'Content-Type': 'application/x-www-form-urlencoded',
1155+
'Content-Length': Buffer.byteLength(formBody)
1156+
}
1157+
};
1158+
1159+
const result = await new Promise((resolve) => {
1160+
1161+
const req = https.request('https://www.site24x7.com/tools/restapi-tester', options, function (res) {
1162+
let responseBody = '';
1163+
1164+
res.on('data', (chunk) => responseBody = responseBody + chunk);
1165+
res.on('end', function () {
1166+
const parsedBody = JSON.parse(responseBody + '');
1167+
if (res.statusCode !== 200) {
1168+
logger.warn(`Failed to test HTTP challenge for domain ${domain}`, res);
1169+
resolve(undefined);
1170+
}
1171+
resolve(parsedBody);
1172+
});
1173+
});
1174+
1175+
// Make sure to write the request body.
1176+
req.write(formBody);
1177+
req.end();
1178+
req.on('error', function (e) { logger.warn(`Failed to test HTTP challenge for domain ${domain}`, e);
1179+
resolve(undefined); });
1180+
});
1181+
1182+
if (!result) {
1183+
// Some error occurred while trying to get the data
1184+
return 'failed';
1185+
} else if (`${result.responsecode}` === '200' && result.htmlresponse === 'Success') {
1186+
// Server exists and has responded with the correct data
1187+
return 'ok';
1188+
} else if (`${result.responsecode}` === '200') {
1189+
// Server exists but has responded with wrong data
1190+
logger.info(`HTTP challenge test failed for domain ${domain} because of invalid returned data:`, result.htmlresponse);
1191+
return 'wrong-data';
1192+
} else if (`${result.responsecode}` === '404') {
1193+
// Server exists but responded with a 404
1194+
logger.info(`HTTP challenge test failed for domain ${domain} because code 404 was returned`);
1195+
return '404';
1196+
} else if (`${result.responsecode}` === '0' || (typeof result.reason === 'string' && result.reason.toLowerCase() === 'host unavailable')) {
1197+
// Server does not exist at domain
1198+
logger.info(`HTTP challenge test failed for domain ${domain} the host was not found`);
1199+
return 'no-host';
1200+
} else {
1201+
// Other errors
1202+
logger.info(`HTTP challenge test failed for domain ${domain} because code ${result.responsecode} was returned`);
1203+
return `other:${result.responsecode}`;
1204+
}
1205+
}
1206+
1207+
const results = {};
1208+
1209+
for (const domain of domains){
1210+
results[domain] = await performTestForDomain(domain);
1211+
}
1212+
1213+
// Remove the test challenge file
1214+
fs.unlinkSync(testChallengeFile);
1215+
1216+
return results;
11271217
}
11281218
};
11291219

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
const migrate_name = 'stream_domain';
2+
const logger = require('../logger').migrate;
3+
const internalNginx = require('../internal/nginx');
4+
5+
async function regenerateDefaultHost(knex) {
6+
const row = await knex('setting').select('*').where('id', 'default-site').first();
7+
8+
if (!row) {
9+
return Promise.resolve();
10+
}
11+
12+
return internalNginx.deleteConfig('default')
13+
.then(() => {
14+
return internalNginx.generateConfig('default', row);
15+
})
16+
.then(() => {
17+
return internalNginx.test();
18+
})
19+
.then(() => {
20+
return internalNginx.reload();
21+
});
22+
}
23+
24+
/**
25+
* Migrate
26+
*
27+
* @see http://knexjs.org/#Schema
28+
*
29+
* @param {Object} knex
30+
* @param {Promise} Promise
31+
* @returns {Promise}
32+
*/
33+
exports.up = function (knex) {
34+
logger.info('[' + migrate_name + '] Migrating Up...');
35+
36+
return regenerateDefaultHost(knex);
37+
};
38+
39+
/**
40+
* Undo Migrate
41+
*
42+
* @param {Object} knex
43+
* @param {Promise} Promise
44+
* @returns {Promise}
45+
*/
46+
exports.down = function (knex) {
47+
logger.info('[' + migrate_name + '] Migrating Down...');
48+
49+
return regenerateDefaultHost(knex);
50+
};

backend/routes/api/nginx/certificates.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,32 @@ router
6868
.catch(next);
6969
});
7070

71+
/**
72+
* Test HTTP challenge for domains
73+
*
74+
* /api/nginx/certificates/test-http
75+
*/
76+
router
77+
.route('/test-http')
78+
.options((req, res) => {
79+
res.sendStatus(204);
80+
})
81+
.all(jwtdecode())
82+
83+
/**
84+
* GET /api/nginx/certificates/test-http
85+
*
86+
* Test HTTP challenge for domains
87+
*/
88+
.get((req, res, next) => {
89+
internalCertificate.testHttpsChallenge(res.locals.access, JSON.parse(req.query.domains))
90+
.then((result) => {
91+
res.status(200)
92+
.send(result);
93+
})
94+
.catch(next);
95+
});
96+
7197
/**
7298
* Specific certificate
7399
*
@@ -209,7 +235,6 @@ router
209235
.catch(next);
210236
});
211237

212-
213238
/**
214239
* Download LE Certs
215240
*

backend/schema/endpoints/certificates.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,17 @@
157157
"targetSchema": {
158158
"type": "boolean"
159159
}
160+
},
161+
{
162+
"title": "Test HTTP Challenge",
163+
"description": "Tests whether the HTTP challenge should work",
164+
"href": "/nginx/certificates/{definitions.identity.example}/test-http",
165+
"access": "private",
166+
"method": "GET",
167+
"rel": "info",
168+
"http_header": {
169+
"$ref": "../examples.json#/definitions/auth_header"
170+
}
160171
}
161172
]
162173
}

backend/templates/_location.conf

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
11
location {{ path }} {
2-
set $targetUri {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }};
3-
{% unless path contains "~" and path contains "(" and path contains ")" %}
4-
if ($request_uri != /){
5-
set $targetUri $targetUri$request_uri;
6-
}
7-
{% endunless %}
82
proxy_set_header Host $host;
93
proxy_set_header X-Forwarded-Scheme $scheme;
104
proxy_set_header X-Forwarded-Proto $scheme;
115
proxy_set_header X-Forwarded-For $remote_addr;
126
proxy_set_header X-Real-IP $remote_addr;
13-
proxy_pass $targetUri;
7+
proxy_pass {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }};
148

159
{% if access_list_id > 0 %}
1610
{% if access_list.items.length > 0 %}
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
set $targetUri $forward_scheme://$server:$port$request_uri;
21
add_header X-Served-By $host;
32
proxy_set_header Host $host;
43
proxy_set_header X-Forwarded-Scheme $scheme;
54
proxy_set_header X-Forwarded-Proto $scheme;
65
proxy_set_header X-Forwarded-For $remote_addr;
76
proxy_set_header X-Real-IP $remote_addr;
8-
proxy_pass $targetUri;
7+
proxy_pass $forward_scheme://$server:$port$request_uri;
98

frontend/js/app/api.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,16 @@ module.exports = {
685685
return fetch('post', 'nginx/certificates/' + id + '/renew', undefined, {timeout});
686686
},
687687

688+
/**
689+
* @param {Number} id
690+
* @returns {Promise}
691+
*/
692+
testHttpChallenge: function (domains) {
693+
return fetch('get', 'nginx/certificates/test-http?' + new URLSearchParams({
694+
domains: JSON.stringify(domains),
695+
}));
696+
},
697+
688698
/**
689699
* @param {Number} id
690700
* @returns {Promise}

0 commit comments

Comments
 (0)