|
|
|
@ -1,11 +1,11 @@
|
|
|
|
|
const _ = require('lodash');
|
|
|
|
|
const fs = require('fs');
|
|
|
|
|
const logger = require('../logger').nginx;
|
|
|
|
|
const utils = require('../lib/utils');
|
|
|
|
|
const error = require('../lib/error');
|
|
|
|
|
const { Liquid } = require('liquidjs');
|
|
|
|
|
const passthroughHostModel = require('../models/ssl_passthrough_host');
|
|
|
|
|
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
|
|
|
|
const _ = require('lodash');
|
|
|
|
|
const fs = require('fs');
|
|
|
|
|
const logger = require('../logger').nginx;
|
|
|
|
|
const utils = require('../lib/utils');
|
|
|
|
|
const error = require('../lib/error');
|
|
|
|
|
const { Liquid } = require('liquidjs');
|
|
|
|
|
const passthroughHostModel = require('../models/ssl_passthrough_host');
|
|
|
|
|
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
|
|
|
|
|
|
|
|
|
const internalNginx = {
|
|
|
|
|
|
|
|
|
@ -24,7 +24,7 @@ const internalNginx = {
|
|
|
|
|
* @returns {Promise}
|
|
|
|
|
*/
|
|
|
|
|
configure: (model, host_type, host) => {
|
|
|
|
|
let combined_meta = {};
|
|
|
|
|
let combined_meta = {};
|
|
|
|
|
const sslPassthroughEnabled = internalNginx.sslPassthroughEnabled();
|
|
|
|
|
|
|
|
|
|
return internalNginx.test()
|
|
|
|
@ -34,7 +34,7 @@ const internalNginx = {
|
|
|
|
|
return internalNginx.deleteConfig(host_type, host); // Don't throw errors, as the file may not exist at all
|
|
|
|
|
})
|
|
|
|
|
.then(() => {
|
|
|
|
|
if(host_type === 'ssl_passthrough_host' && !sslPassthroughEnabled){
|
|
|
|
|
if (host_type === 'ssl_passthrough_host' && !sslPassthroughEnabled){
|
|
|
|
|
// ssl passthrough is disabled
|
|
|
|
|
const meta = {
|
|
|
|
|
nginx_online: false,
|
|
|
|
@ -64,7 +64,7 @@ const internalNginx = {
|
|
|
|
|
nginx_err: null
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if(host_type === 'ssl_passthrough_host'){
|
|
|
|
|
if (host_type === 'ssl_passthrough_host'){
|
|
|
|
|
// If passthrough is disabled we have already marked the hosts as offline
|
|
|
|
|
if (sslPassthroughEnabled) {
|
|
|
|
|
return passthroughHostModel
|
|
|
|
@ -109,7 +109,7 @@ const internalNginx = {
|
|
|
|
|
nginx_err: valid_lines.join('\n')
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if(host_type === 'ssl_passthrough_host'){
|
|
|
|
|
if (host_type === 'ssl_passthrough_host'){
|
|
|
|
|
return passthroughHostModel
|
|
|
|
|
.query()
|
|
|
|
|
.where('is_deleted', 0)
|
|
|
|
@ -235,7 +235,7 @@ const internalNginx = {
|
|
|
|
|
* @param {Object} host
|
|
|
|
|
* @returns {Promise}
|
|
|
|
|
*/
|
|
|
|
|
generateConfig: (host_type, host) => {
|
|
|
|
|
generateConfig: async (host_type, host) => {
|
|
|
|
|
host_type = host_type.replace(new RegExp('-', 'g'), '_');
|
|
|
|
|
|
|
|
|
|
if (debug_mode) {
|
|
|
|
@ -248,90 +248,87 @@ const internalNginx = {
|
|
|
|
|
root: __dirname + '/../templates/'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return new Promise(async (resolve, reject) => {
|
|
|
|
|
let template = null;
|
|
|
|
|
let filename = internalNginx.getConfigName(host_type, host.id);
|
|
|
|
|
let template = null;
|
|
|
|
|
let filename = internalNginx.getConfigName(host_type, host.id);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
template = fs.readFileSync(__dirname + '/../templates/' + host_type + '.conf', {encoding: 'utf8'});
|
|
|
|
|
} catch (err) {
|
|
|
|
|
reject(new error.ConfigurationError(err.message));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
template = fs.readFileSync(__dirname + '/../templates/' + host_type + '.conf', {encoding: 'utf8'});
|
|
|
|
|
} catch (err) {
|
|
|
|
|
throw new error.ConfigurationError(err.message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let locationsPromise;
|
|
|
|
|
let origLocations;
|
|
|
|
|
|
|
|
|
|
// Manipulate the data a bit before sending it to the template
|
|
|
|
|
if (host_type === 'ssl_passthrough_host') {
|
|
|
|
|
if(internalNginx.sslPassthroughEnabled()){
|
|
|
|
|
const allHosts = await passthroughHostModel
|
|
|
|
|
.query()
|
|
|
|
|
.where('is_deleted', 0)
|
|
|
|
|
.groupBy('id')
|
|
|
|
|
.omit(['is_deleted']);
|
|
|
|
|
host = {
|
|
|
|
|
all_passthrough_hosts: allHosts.map((host) => {
|
|
|
|
|
// Replace dots in domain
|
|
|
|
|
host.forwarding_host = internalNginx.addIpv6Brackets(host.forwarding_host);
|
|
|
|
|
return host;
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
internalNginx.deleteConfig(host_type, host, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (host_type !== 'default') {
|
|
|
|
|
host.use_default_location = true;
|
|
|
|
|
if (typeof host.advanced_config !== 'undefined' && host.advanced_config) {
|
|
|
|
|
host.use_default_location = !internalNginx.advancedConfigHasDefaultLocation(host.advanced_config);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (host.locations) {
|
|
|
|
|
//logger.info ('host.locations = ' + JSON.stringify(host.locations, null, 2));
|
|
|
|
|
origLocations = [].concat(host.locations);
|
|
|
|
|
locationsPromise = internalNginx.renderLocations(host).then((renderedLocations) => {
|
|
|
|
|
host.locations = renderedLocations;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Allow someone who is using / custom location path to use it, and skip the default / location
|
|
|
|
|
_.map(host.locations, (location) => {
|
|
|
|
|
if (location.path === '/') {
|
|
|
|
|
host.use_default_location = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
let locationsPromise;
|
|
|
|
|
let origLocations;
|
|
|
|
|
|
|
|
|
|
// Manipulate the data a bit before sending it to the template
|
|
|
|
|
if (host_type === 'ssl_passthrough_host') {
|
|
|
|
|
if (internalNginx.sslPassthroughEnabled()){
|
|
|
|
|
const allHosts = await passthroughHostModel
|
|
|
|
|
.query()
|
|
|
|
|
.where('is_deleted', 0)
|
|
|
|
|
.groupBy('id')
|
|
|
|
|
.omit(['is_deleted']);
|
|
|
|
|
host = {
|
|
|
|
|
all_passthrough_hosts: allHosts.map((host) => {
|
|
|
|
|
// Replace dots in domain
|
|
|
|
|
host.forwarding_host = internalNginx.addIpv6Brackets(host.forwarding_host);
|
|
|
|
|
return host;
|
|
|
|
|
}),
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
locationsPromise = Promise.resolve();
|
|
|
|
|
internalNginx.deleteConfig(host_type, host, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (host_type !== 'default') {
|
|
|
|
|
host.use_default_location = true;
|
|
|
|
|
if (typeof host.advanced_config !== 'undefined' && host.advanced_config) {
|
|
|
|
|
host.use_default_location = !internalNginx.advancedConfigHasDefaultLocation(host.advanced_config);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the IPv6 setting for the host
|
|
|
|
|
host.ipv6 = internalNginx.ipv6Enabled();
|
|
|
|
|
|
|
|
|
|
locationsPromise.then(() => {
|
|
|
|
|
renderEngine
|
|
|
|
|
.parseAndRender(template, host)
|
|
|
|
|
.then((config_text) => {
|
|
|
|
|
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
|
|
|
|
|
|
|
|
|
if (debug_mode) {
|
|
|
|
|
logger.success('Wrote config:', filename, config_text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Restore locations array
|
|
|
|
|
host.locations = origLocations;
|
|
|
|
|
|
|
|
|
|
resolve(true);
|
|
|
|
|
})
|
|
|
|
|
.catch((err) => {
|
|
|
|
|
if (debug_mode) {
|
|
|
|
|
logger.warn('Could not write ' + filename + ':', err.message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reject(new error.ConfigurationError(err.message));
|
|
|
|
|
});
|
|
|
|
|
if (host.locations) {
|
|
|
|
|
//logger.info ('host.locations = ' + JSON.stringify(host.locations, null, 2));
|
|
|
|
|
origLocations = [].concat(host.locations);
|
|
|
|
|
locationsPromise = internalNginx.renderLocations(host).then((renderedLocations) => {
|
|
|
|
|
host.locations = renderedLocations;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Allow someone who is using / custom location path to use it, and skip the default / location
|
|
|
|
|
_.map(host.locations, (location) => {
|
|
|
|
|
if (location.path === '/') {
|
|
|
|
|
host.use_default_location = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
locationsPromise = Promise.resolve();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the IPv6 setting for the host
|
|
|
|
|
host.ipv6 = internalNginx.ipv6Enabled();
|
|
|
|
|
|
|
|
|
|
return locationsPromise.then(() => {
|
|
|
|
|
renderEngine
|
|
|
|
|
.parseAndRender(template, host)
|
|
|
|
|
.then((config_text) => {
|
|
|
|
|
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
|
|
|
|
|
|
|
|
|
if (debug_mode) {
|
|
|
|
|
logger.success('Wrote config:', filename, config_text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Restore locations array
|
|
|
|
|
host.locations = origLocations;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
})
|
|
|
|
|
.catch((err) => {
|
|
|
|
|
if (debug_mode) {
|
|
|
|
|
logger.warn('Could not write ' + filename + ':', err.message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new error.ConfigurationError(err.message);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
@ -514,12 +511,12 @@ const internalNginx = {
|
|
|
|
|
* Helper function to add brackets to an IP if it is IPv6
|
|
|
|
|
* @returns {string}
|
|
|
|
|
*/
|
|
|
|
|
addIpv6Brackets: function (ip) {
|
|
|
|
|
addIpv6Brackets: function (ip) {
|
|
|
|
|
// Only run check if ipv6 is enabled
|
|
|
|
|
if (internalNginx.ipv6Enabled()) {
|
|
|
|
|
const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/gi;
|
|
|
|
|
if(ipv6Regex.test(ip)){
|
|
|
|
|
return `[${ip}]`
|
|
|
|
|
if (ipv6Regex.test(ip)){
|
|
|
|
|
return `[${ip}]`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ip;
|
|
|
|
|