Next.js+React web interface for controlling HAProxy clusters (groups of servers), in conjunction with with https://gitgud.io/fatchan/haproxy-protection.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
98 lines
2.5 KiB
98 lines
2.5 KiB
const url = require('url');
|
|
|
|
const fMap = {
|
|
|
|
[process.env.HOSTS_MAP_NAME]: {
|
|
fname: 'Active Domains',
|
|
description: 'Domains that will be processed by the selected cluster',
|
|
columnNames: ['Domain', 'Backend'],
|
|
},
|
|
|
|
[process.env.DDOS_MAP_NAME]: {
|
|
fname: 'Protection Rules',
|
|
description: 'Rules for protection mode on domains and/or paths',
|
|
columnNames: ['Domain/Path', 'Mode'],
|
|
},
|
|
|
|
[process.env.BLOCKED_MAP_NAME]: {
|
|
fname: 'Blocked IPs/Subnets',
|
|
description: 'IPs/subnets that are outright blocked',
|
|
columnNames: ['IP/Subnet', ''],
|
|
},
|
|
|
|
[process.env.WHITELIST_MAP_NAME]: {
|
|
fname: 'Whitelisted IPs/Subnets',
|
|
description: 'IPs/subnets that bypass protection rules',
|
|
columnNames: ['IP/Subnet', ''],
|
|
},
|
|
|
|
[process.env.MAINTENANCE_MAP_NAME]: {
|
|
fname: 'Maintenance Mode',
|
|
description: 'Disable proxying and show maintenance page for selected domains',
|
|
columnNames: ['Domain', ''],
|
|
},
|
|
|
|
[process.env.BACKENDS_MAP_NAME]: {
|
|
fname: 'Domain Backend Mappings',
|
|
description: 'Which internal server haproxy uses for domains',
|
|
columnNames: ['Domain', 'Server Name'],
|
|
},
|
|
|
|
};
|
|
|
|
module.exports = {
|
|
|
|
fMap,
|
|
|
|
makeArrayIfSingle: (obj) => !Array.isArray(obj) ? [obj] : obj,
|
|
|
|
validClustersString: (string) => {
|
|
return !string.split(',').some(c => {
|
|
const cUrl = url.parse(c);
|
|
return (cUrl.protocol !== 'tcp:' || !cUrl.hostname)
|
|
});
|
|
},
|
|
|
|
extractMap: (item) => {
|
|
const match = item.match(/(?<index>\d+) \(\/etc\/haproxy\/(?<name>.+).map\)(?:.+entry_cnt=(?<count>\d+)$)?/);
|
|
if (match && match.groups) {
|
|
return {
|
|
...match.groups,
|
|
...fMap[match.groups.name],
|
|
}
|
|
}
|
|
},
|
|
|
|
getMapId: async (haproxy, name) => {
|
|
return haproxy.showMap()
|
|
.then(list => {
|
|
return list
|
|
.map(module.exports.extractMap)
|
|
.find(l => l && l.name == name);
|
|
});
|
|
},
|
|
|
|
deleteFromMap: async (haproxy, name, value) => {
|
|
//maybe in future, we should cache the map # or ID because they dont(afaik) change during runtime
|
|
const mapId = await module.exports.getMapId(haproxy, name);
|
|
if (!mapId) {
|
|
throw 'Invalid map';
|
|
}
|
|
return haproxy.delMap(mapId.index, value);
|
|
},
|
|
|
|
dynamicResponse: async(req, res, code, data) => {
|
|
const isRedirect = code === 302;
|
|
if (req.headers && req.headers['content-type'] === 'application/json') {
|
|
return res
|
|
.status(isRedirect ? 200 : code)
|
|
.json(data);
|
|
}
|
|
if (isRedirect) {
|
|
return res.redirect(data.redirect);
|
|
}
|
|
//TODO: pass through app (bind to middleware) and app.render an "error" page for nojs users?
|
|
return res.status(code).send(data);
|
|
},
|
|
|
|
};
|
|
|