151 lines
4.1 KiB
JavaScript
151 lines
4.1 KiB
JavaScript
const https = require('https');
|
|
const fs = require('fs');
|
|
const logger = require('../logger').ip_ranges;
|
|
const error = require('../lib/error');
|
|
const internalNginx = require('./nginx');
|
|
const { Liquid } = require('liquidjs');
|
|
|
|
const CLOUDFRONT_URL = 'https://ip-ranges.amazonaws.com/ip-ranges.json';
|
|
const CLOUDFARE_V4_URL = 'https://www.cloudflare.com/ips-v4';
|
|
const CLOUDFARE_V6_URL = 'https://www.cloudflare.com/ips-v6';
|
|
|
|
const regIpV4 = /^(\d+\.?){4}\/\d+/;
|
|
const regIpV6 = /^(([\da-fA-F]+)?:)+\/\d+/;
|
|
|
|
const internalIpRanges = {
|
|
|
|
interval_timeout: 1000 * 60 * 60 * 6, // 6 hours
|
|
interval: null,
|
|
interval_processing: false,
|
|
iteration_count: 0,
|
|
|
|
initTimer: () => {
|
|
logger.info('IP Ranges Renewal Timer initialized');
|
|
internalIpRanges.interval = setInterval(internalIpRanges.fetch, internalIpRanges.interval_timeout);
|
|
},
|
|
|
|
fetchUrl: (url) => {
|
|
return new Promise((resolve, reject) => {
|
|
logger.info('Fetching ' + url);
|
|
return https.get(url, (res) => {
|
|
res.setEncoding('utf8');
|
|
let raw_data = '';
|
|
res.on('data', (chunk) => {
|
|
raw_data += chunk;
|
|
});
|
|
|
|
res.on('end', () => {
|
|
resolve(raw_data);
|
|
});
|
|
}).on('error', (err) => {
|
|
reject(err);
|
|
});
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Triggered at startup and then later by a timer, this will fetch the ip ranges from services and apply them to nginx.
|
|
*/
|
|
fetch: () => {
|
|
if (!internalIpRanges.interval_processing) {
|
|
internalIpRanges.interval_processing = true;
|
|
logger.info('Fetching IP Ranges from online services...');
|
|
|
|
let ip_ranges = [];
|
|
|
|
return internalIpRanges.fetchUrl(CLOUDFRONT_URL)
|
|
.then((cloudfront_data) => {
|
|
let data = JSON.parse(cloudfront_data);
|
|
|
|
if (data && typeof data.prefixes !== 'undefined') {
|
|
data.prefixes.map((item) => {
|
|
if (item.service === 'CLOUDFRONT') {
|
|
ip_ranges.push(item.ip_prefix);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (data && typeof data.ipv6_prefixes !== 'undefined') {
|
|
data.ipv6_prefixes.map((item) => {
|
|
if (item.service === 'CLOUDFRONT') {
|
|
ip_ranges.push(item.ipv6_prefix);
|
|
}
|
|
});
|
|
}
|
|
})
|
|
.then(() => {
|
|
return internalIpRanges.fetchUrl(CLOUDFARE_V4_URL);
|
|
})
|
|
.then((cloudfare_data) => {
|
|
let items = cloudfare_data.split('\n').filter((line) => regIpV4.test(line));
|
|
ip_ranges = [... ip_ranges, ... items];
|
|
})
|
|
.then(() => {
|
|
return internalIpRanges.fetchUrl(CLOUDFARE_V6_URL);
|
|
})
|
|
.then((cloudfare_data) => {
|
|
let items = cloudfare_data.split('\n').filter((line) => regIpV6.test(line));
|
|
ip_ranges = [... ip_ranges, ... items];
|
|
})
|
|
.then(() => {
|
|
let clean_ip_ranges = [];
|
|
ip_ranges.map((range) => {
|
|
if (range) {
|
|
clean_ip_ranges.push(range);
|
|
}
|
|
});
|
|
|
|
return internalIpRanges.generateConfig(clean_ip_ranges)
|
|
.then(() => {
|
|
if (internalIpRanges.iteration_count) {
|
|
// Reload nginx
|
|
return internalNginx.reload();
|
|
}
|
|
});
|
|
})
|
|
.then(() => {
|
|
internalIpRanges.interval_processing = false;
|
|
internalIpRanges.iteration_count++;
|
|
})
|
|
.catch((err) => {
|
|
logger.error(err.message);
|
|
internalIpRanges.interval_processing = false;
|
|
});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @param {Array} ip_ranges
|
|
* @returns {Promise}
|
|
*/
|
|
generateConfig: (ip_ranges) => {
|
|
let renderEngine = new Liquid({
|
|
root: __dirname + '/../templates/'
|
|
});
|
|
|
|
return new Promise((resolve, reject) => {
|
|
let template = null;
|
|
let filename = '/etc/nginx/conf.d/include/ip_ranges.conf';
|
|
try {
|
|
template = fs.readFileSync(__dirname + '/../templates/ip_ranges.conf', {encoding: 'utf8'});
|
|
} catch (err) {
|
|
reject(new error.ConfigurationError(err.message));
|
|
return;
|
|
}
|
|
|
|
renderEngine
|
|
.parseAndRender(template, {ip_ranges: ip_ranges})
|
|
.then((config_text) => {
|
|
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
|
resolve(true);
|
|
})
|
|
.catch((err) => {
|
|
logger.warn('Could not write ' + filename + ':', err.message);
|
|
reject(new error.ConfigurationError(err.message));
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
module.exports = internalIpRanges;
|