mirror of
https://github.com/jc21/nginx-proxy-manager.git
synced 2024-08-30 18:22:48 +00:00
Refactor and integrate ddns resolution with nginx module
Refactored ddns resolver so that no patching is done. nginx.js will automatically resolve ddns addresses if needed. Added dedicated logger scope for ddns resovler.
This commit is contained in:
parent
5586709d03
commit
ec9eb0dd60
@ -9,7 +9,7 @@ async function appStart () {
|
||||
const apiValidator = require('./lib/validator/api');
|
||||
const internalCertificate = require('./internal/certificate');
|
||||
const internalIpRanges = require('./internal/ip_ranges');
|
||||
const ddnsResolver = require('./lib/ddns_resolver/ddns_resolver');
|
||||
const ddnsResolver = require('./lib/ddns_resolver');
|
||||
|
||||
return migrate.latest()
|
||||
.then(setup)
|
||||
|
@ -4,6 +4,7 @@ const logger = require('../logger').nginx;
|
||||
const config = require('../lib/config');
|
||||
const utils = require('../lib/utils');
|
||||
const error = require('../lib/error');
|
||||
const ddnsResolver = require('../lib/ddns_resolver');
|
||||
|
||||
const internalNginx = {
|
||||
|
||||
@ -131,6 +132,31 @@ const internalNginx = {
|
||||
return '/data/nginx/' + internalNginx.getFileFriendlyHostType(host_type) + '/' + host_id + '.conf';
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolves any ddns addresses that need to be resolved for clients in the host's access list.
|
||||
* @param {Object} host
|
||||
* @returns {Promise}
|
||||
*/
|
||||
resolveDDNSAddresses: (host) => {
|
||||
const promises = [];
|
||||
if (typeof host.access_list !== 'undefined' && typeof host.access_list.clients !== 'undefined') {
|
||||
for (const client of host.access_list.clients) {
|
||||
if (ddnsResolver.requiresResolution(client.address)) {
|
||||
const p = ddnsResolver.resolveAddress(client.address)
|
||||
.then((resolvedIP) => {
|
||||
client.address = `${resolvedIP}; # ${client.address}`;
|
||||
return Promise.resolve();
|
||||
});
|
||||
promises.push(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (promises.length) {
|
||||
return Promise.all(promises);
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
* Generates custom locations
|
||||
* @param {Object} host
|
||||
@ -201,6 +227,12 @@ const internalNginx = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve ddns addresses if needed
|
||||
let resolverPromise = Promise.resolve();
|
||||
if (host_type === 'proxy_host') {
|
||||
resolverPromise = internalNginx.resolveDDNSAddresses(host);
|
||||
}
|
||||
|
||||
let locationsPromise;
|
||||
let origLocations;
|
||||
|
||||
@ -215,9 +247,11 @@ const internalNginx = {
|
||||
if (host.locations) {
|
||||
//logger.info ('host.locations = ' + JSON.stringify(host.locations, null, 2));
|
||||
origLocations = [].concat(host.locations);
|
||||
locationsPromise = internalNginx.renderLocations(host).then((renderedLocations) => {
|
||||
locationsPromise = resolverPromise.then(() => {
|
||||
return 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) => {
|
||||
@ -227,7 +261,7 @@ const internalNginx = {
|
||||
});
|
||||
|
||||
} else {
|
||||
locationsPromise = Promise.resolve();
|
||||
locationsPromise = resolverPromise;
|
||||
}
|
||||
|
||||
// Set the IPv6 setting for the host
|
||||
|
@ -1,41 +1,7 @@
|
||||
const error = require('../error')
|
||||
const logger = require('../../logger').global;
|
||||
const internalAccessList = require('../../internal/access-list');
|
||||
const internalNginx = require('../../internal/nginx');
|
||||
const spawn = require('child_process').spawn;
|
||||
|
||||
const cmdHelper = {
|
||||
/**
|
||||
* Run the given command. Safer than using exec since args are passed as a list instead of in shell mode as a single string.
|
||||
* @param {string} cmd The command to run
|
||||
* @param {string} args The args to pass to the command
|
||||
* @returns Promise that resolves to stdout or an object with error code and stderr if there's an error
|
||||
*/
|
||||
run: (cmd, args) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
const proc = spawn(cmd, args);
|
||||
proc.stdout.on('data', (data) => {
|
||||
stdout += data;
|
||||
});
|
||||
proc.stderr.on('data', (data) => {
|
||||
stderr += data;
|
||||
});
|
||||
|
||||
proc.on('close', (exitCode) => {
|
||||
if (!exitCode) {
|
||||
resolve(stdout.trim());
|
||||
} else {
|
||||
reject({
|
||||
exitCode: exitCode,
|
||||
stderr: stderr
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
const error = require('./error')
|
||||
const logger = require('../logger').ddns;
|
||||
const internalAccessList = require('../internal/access-list');
|
||||
const utils = require('./utils');
|
||||
|
||||
const ddnsResolver = {
|
||||
/**
|
||||
@ -99,7 +65,7 @@ const ddnsResolver = {
|
||||
/** Private **/
|
||||
// Properties
|
||||
_initialized: false,
|
||||
_updateIntervalMs: 1000 * 60 * 60, // 1 hr default (overriden with $DDNS_UPDATE_INTERVAL env var)
|
||||
_updateIntervalMs: 60 * 60 * 1000, // 1 hr default (overriden with $DDNS_UPDATE_INTERVAL env var)
|
||||
/**
|
||||
* cache mapping host to (ip address, last updated time)
|
||||
*/
|
||||
@ -107,8 +73,6 @@ const ddnsResolver = {
|
||||
_interval: null, // reference to created interval id
|
||||
_processingDDNSUpdate: false,
|
||||
|
||||
_originalGenerateConfig: null, // Used for patching config generation to resolve hosts
|
||||
|
||||
// Methods
|
||||
|
||||
_initialize: () => {
|
||||
@ -126,16 +90,6 @@ const ddnsResolver = {
|
||||
logger.warn(`[DDNS] invalid value for update interval: '${process.env.DDNS_UPDATE_INTERVAL}'`);
|
||||
}
|
||||
}
|
||||
|
||||
// Patch nginx config generation if needed (check env var)
|
||||
if (typeof process.env.DDNS_UPDATE_PATCH !== 'undefined') {
|
||||
const enabled = Number(process.env.DDNS_UPDATE_PATCH.toLowerCase());
|
||||
if (!isNaN(enabled) && enabled) {
|
||||
logger.info('Patching nginx config generation');
|
||||
ddnsResolver._originalGenerateConfig = internalNginx.generateConfig;
|
||||
internalNginx.generateConfig = ddnsResolver._patchedGenerateConfig;
|
||||
}
|
||||
}
|
||||
ddnsResolver._initialized = true;
|
||||
},
|
||||
|
||||
@ -146,7 +100,7 @@ const ddnsResolver = {
|
||||
*/
|
||||
_queryHost: (host) => {
|
||||
logger.info('Looking up IP for ', host);
|
||||
return cmdHelper.run('getent', ['hosts', host])
|
||||
return utils.execSafe('getent', ['hosts', host])
|
||||
.then((result) => {
|
||||
if (result.length < 8) {
|
||||
logger.error('IP lookup returned invalid output: ', result);
|
||||
@ -162,35 +116,12 @@ const ddnsResolver = {
|
||||
});
|
||||
},
|
||||
|
||||
_patchedGenerateConfig: (host_type, host) => {
|
||||
const promises = [];
|
||||
if (host_type === 'proxy_host') {
|
||||
if (typeof host.access_list !== 'undefined' && typeof host.access_list.clients !== 'undefined') {
|
||||
for (const client of host.access_list.clients) {
|
||||
if (ddnsResolver.requiresResolution(client.address)) {
|
||||
const p = ddnsResolver.resolveAddress(client.address)
|
||||
.then((resolvedIP) => {
|
||||
client.address = `${resolvedIP}; # ${client.address}`;
|
||||
return Promise.resolve();
|
||||
});
|
||||
promises.push(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (promises.length) {
|
||||
return Promise.all(promises)
|
||||
.then(() => {
|
||||
return ddnsResolver._originalGenerateConfig(host_type, host);
|
||||
});
|
||||
}
|
||||
return ddnsResolver._originalGenerateConfig(host_type, host);
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggered by a timer, will check for and update ddns hosts in access list clients
|
||||
*/
|
||||
_checkForDDNSUpdates: () => {
|
||||
const internalNginx = require('../internal/nginx'); // Prevent circular import
|
||||
|
||||
logger.info('Checking for DDNS updates...');
|
||||
if (!ddnsResolver._processingDDNSUpdate) {
|
||||
ddnsResolver._processingDDNSUpdate = true;
|
@ -4,6 +4,7 @@ const execFile = require('child_process').execFile;
|
||||
const { Liquid } = require('liquidjs');
|
||||
const logger = require('../logger').global;
|
||||
const error = require('./error');
|
||||
const spawn = require('child_process').spawn;
|
||||
|
||||
module.exports = {
|
||||
|
||||
@ -26,6 +27,37 @@ module.exports = {
|
||||
return stdout;
|
||||
},
|
||||
|
||||
/**
|
||||
* Run the given command. Safer than using exec since args are passed as a list instead of in shell mode as a single string.
|
||||
* @param {string} cmd The command to run
|
||||
* @param {string} args The args to pass to the command
|
||||
* @returns Promise that resolves to stdout or an object with error code and stderr if there's an error
|
||||
*/
|
||||
execSafe: (cmd, args) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
const proc = spawn(cmd, args);
|
||||
proc.stdout.on('data', (data) => {
|
||||
stdout += data;
|
||||
});
|
||||
proc.stderr.on('data', (data) => {
|
||||
stderr += data;
|
||||
});
|
||||
|
||||
proc.on('close', (exitCode) => {
|
||||
if (!exitCode) {
|
||||
resolve(stdout.trim());
|
||||
} else {
|
||||
reject({
|
||||
exitCode: exitCode,
|
||||
stderr: stderr
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {String} cmd
|
||||
* @param {Array} args
|
||||
|
@ -10,5 +10,6 @@ module.exports = {
|
||||
certbot: new Signale({scope: 'Certbot '}),
|
||||
import: new Signale({scope: 'Importer '}),
|
||||
setup: new Signale({scope: 'Setup '}),
|
||||
ip_ranges: new Signale({scope: 'IP Ranges'})
|
||||
ip_ranges: new Signale({scope: 'IP Ranges'}),
|
||||
ddns: new Signale({scope: 'DDNS '})
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user