@timo972/ufw
Version:
ufw <-> nodejs app communication
333 lines (323 loc) • 9.99 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var child_process = require('child_process');
var path = require('path');
const ipv4Regex = /^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$/;
const ipv6Regex = /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/;
function isIPv4Valid(ip) {
return ipv4Regex.test(ip);
}
function isIPv6Valid(ip) {
return ipv6Regex.test(ip);
}
function isProtocolValid(proto) {
return proto === "any" || proto === "tcp" || proto === "udp";
}
function isPortValid(port) {
return port >= 0 && port < 65536;
}
exports.Protocol = void 0;
(function (Protocol) {
Protocol["TCP"] = "tcp";
Protocol["UDP"] = "udp";
Protocol["ANY"] = "any";
})(exports.Protocol || (exports.Protocol = {}));
/**
* @class Rule
* @author @Timo972
*/
class Rule {
_id;
_from;
_to;
_port;
_proto;
/**
* @param {number} _id WARNING: Do not pass something here!!! Only used internally.
*/
constructor(_id) {
this._id = _id;
}
/**
*
* @param {string} ip ipv4 or ipv6 address
* @returns {Rule}
*/
to(ip) {
if (!isIPv6Valid(ip) && !isIPv4Valid(ip))
throw new Error("invalid ip address");
this._to = ip;
return this;
}
/**
*
* @param {string} ip ipv4 or ipv6 address
* @returns {Rule}
*/
from(ip) {
if (!isIPv6Valid(ip) && !isIPv4Valid(ip))
throw new Error("invalid ip address");
this._from = ip;
return this;
}
/**
*
* @param {Protocol} protocol can be any, tcp, udp
* @returns {Rule}
*/
proto(proto) {
if (!isProtocolValid(proto))
throw new Error("invalid protocol");
this._proto = proto;
return this;
}
/**
*
* @param {number} port from 0-65535
* @returns {Rule}
*/
port(port) {
if (!isPortValid(port))
throw new Error("invalid port");
this._port = port;
return this;
}
/**
*
* @returns {number}
*/
getPort() {
return this._port;
}
/**
*
* @returns {Protocol}
*/
getProtocol() {
return this._proto ?? exports.Protocol.ANY;
}
/**
*
* @returns {string}
*/
getFrom() {
return this._from;
}
/**
*
* @returns {string}
*/
getTo() {
return this._to;
}
/**
* function returns undefined if you have created this rule
* @returns {number} rule id
*/
getId() {
return this._id;
}
toJSON() {
const { from, getFrom, getId, getPort, getProtocol, getTo, port, proto, to, toJSON, ...data } = this;
let formatted = {};
for (const key in data) {
const value = data[key];
if (!value)
continue;
const escapedKey = key.substring(1, key.length);
formatted[escapedKey] = value;
}
return JSON.stringify(formatted);
}
buildUfwCommand() {
let command = [];
if (this._proto === exports.Protocol.ANY) {
command.push("proto any");
}
if (this._proto === exports.Protocol.TCP) {
command.push("proto tcp");
}
if (this._proto === exports.Protocol.UDP) {
command.push("proto udp");
}
if (this._from &&
(isIPv4Valid(this._from) || isIPv6Valid(this._from) || this._from === "any")) {
command.push(`from ${this._from}`);
}
if (this._to &&
(isIPv4Valid(this._to) || isIPv6Valid(this._to) || this._to === "any")) {
command.push(`to ${this._to}`);
}
if (this._port && isPortValid(this._port))
command.push(`port ${this._port}`);
return command.join(" ");
}
}
var UFWOperation;
(function (UFWOperation) {
UFWOperation[UFWOperation["AddRule"] = 0] = "AddRule";
UFWOperation[UFWOperation["DeleteRule"] = 1] = "DeleteRule";
UFWOperation[UFWOperation["SetDefault"] = 2] = "SetDefault";
UFWOperation[UFWOperation["GetStatus"] = 3] = "GetStatus";
UFWOperation[UFWOperation["GetRules"] = 4] = "GetRules";
UFWOperation[UFWOperation["GetListening"] = 5] = "GetListening";
UFWOperation[UFWOperation["Enable"] = 6] = "Enable";
UFWOperation[UFWOperation["Disable"] = 7] = "Disable";
UFWOperation[UFWOperation["Reset"] = 8] = "Reset";
UFWOperation[UFWOperation["Reload"] = 9] = "Reload";
})(UFWOperation || (UFWOperation = {}));
class UFWRequest {
_operation;
data;
returnJson = false;
get operation() {
return this._operation;
}
set operation(op) {
this._operation = op;
if (this.operation == UFWOperation.GetListening ||
this.operation == UFWOperation.GetRules ||
this.operation == UFWOperation.GetStatus)
this.returnJson = true;
else
this.returnJson = false;
}
pass(data) {
this.data = JSON.stringify(data);
}
}
function execute(req) {
return new Promise((resolve, reject) => {
const data = `{"operation":${req.operation}, "data": ${req.data ?? '{}'}}`.replace(/"/g, '\\"');
console.log(data);
child_process.exec(`python3 ${path.join(__dirname, "..", "python", "ufw-bridge.py")} ${data}`, (error, stdout) => {
if (error) {
reject(error);
return;
}
console.log(stdout);
try {
if (req.returnJson)
resolve(JSON.parse(stdout));
else
resolve(stdout);
}
catch (e) {
reject(e);
}
});
});
}
async function addRule(command) {
const req = new UFWRequest();
req.pass({ command });
req.operation = UFWOperation.AddRule;
await execute(req);
return;
}
/**
*
* @param {Rule} rule
*/
async function allow(rule) {
if (!(rule instanceof Rule))
throw new Error("invalid rule");
return addRule(`allow ` + rule.buildUfwCommand());
}
/**
*
* @param {Rule} rule
*/
async function deny(rule) {
if (!(rule instanceof Rule))
throw new Error("invalid rule");
return addRule(`deny ` + rule.buildUfwCommand());
}
/**
*
* @param {Rule} rule
*/
async function limit(rule) {
if (!(rule instanceof Rule))
throw new Error("invalid rule");
return addRule(`limit ` + rule.buildUfwCommand());
}
/**
*
* @param {Rule} rule
*/
async function reject(rule) {
if (!(rule instanceof Rule))
throw new Error("invalid rule");
return addRule(`reject ` + rule.buildUfwCommand());
}
async function deleteRule(rule) {
if (!(rule instanceof Rule) && !(typeof rule === "number"))
throw new Error("invalid rule / id");
const id = rule instanceof Rule ? rule.getId() : rule;
if (!id)
throw new Error("invalid id");
const req = new UFWRequest();
req.pass({ id });
req.operation = UFWOperation.DeleteRule;
return execute(req);
}
async function getRules() {
const req = new UFWRequest();
req.operation = UFWOperation.GetRules;
return execute(req);
}
async function setDefaults(incoming, outgoing, routed) {
const req = new UFWRequest();
req.operation = UFWOperation.SetDefault;
req.pass({
incoming,
outgoing,
routed,
});
return execute(req);
}
async function enable() {
const req = new UFWRequest();
req.operation = UFWOperation.Enable;
return execute(req);
}
async function disable() {
const req = new UFWRequest();
req.operation = UFWOperation.Disable;
return execute(req);
}
async function getListening() {
const req = new UFWRequest();
req.operation = UFWOperation.GetListening;
return;
}
async function getStatus() {
const req = new UFWRequest();
req.operation = UFWOperation.GetStatus;
return execute(req);
}
async function reset() {
const req = new UFWRequest();
req.operation = UFWOperation.Reset;
return execute(req);
}
async function reload() {
const req = new UFWRequest();
req.operation = UFWOperation.Reload;
return execute(req);
}
exports.Rule = Rule;
exports.allow = allow;
exports.deleteRule = deleteRule;
exports.deny = deny;
exports.disable = disable;
exports.enable = enable;
exports.getListening = getListening;
exports.getRules = getRules;
exports.getStatus = getStatus;
exports.limit = limit;
exports.reject = reject;
exports.reload = reload;
exports.reset = reset;
exports.setDefaults = setDefaults;