Browse Source

update: add support ports

- tr lines BLOCK are bg-red
- add tool/script to build project (buildall.sh)
pull/1210/head
tetuaoro 2 years ago
parent
commit
5d7decb439
  1. 2
      buildall.sh
  2. 146
      src/lib/Firewall.js
  3. 30
      src/lib/Util.js
  4. 68
      src/package-lock.json
  5. 4
      src/www/css/app.css
  6. 2
      src/www/js/vendor/vue-firewall.umd.min.js
  7. 2
      src/www/js/vendor/vue-firewall.umd.min.js.map
  8. 10
      src/www/templates/Firewall.vue

2
buildall.sh

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# The buildall.sh script is designed to streamline the build process for the project. # The buildall.sh script is a tool to build the project and help developers.
# It allows you to run different build commands based on whether you are using "sudo" and supports multiple package managers. # It allows you to run different build commands based on whether you are using "sudo" and supports multiple package managers.
# If you need to run the build commands with "sudo[script]" (look at package.json scripts), provide the "with-sudo" argument : # If you need to run the build commands with "sudo[script]" (look at package.json scripts), provide the "with-sudo" argument :
# #

146
src/lib/Firewall.js

@ -46,51 +46,46 @@ module.exports = class Firewall {
async getIptablesRules() { async getIptablesRules() {
// iptables list the rules WG_IPT_CHAIN_NAME chain // iptables list the rules WG_IPT_CHAIN_NAME chain
<<<<<<< HEAD
<<<<<<< HEAD
// //
// $ iptables -L WGEASY -nv --line-numbers // $ iptables -L WGEASY -nv --line-numbers
// Chain WGEASY (0 references) // Chain WGEASY (0 references)
// num pkts bytes target prot opt in out source destination // num pkts bytes target prot opt in out source destination
<<<<<<< HEAD // 1 0 0 ACCEPT 6 -- * * 172.16.7.2 10.8.2.5 tcp spt:22565 dpt:53
// 1 0 0 ACCEPT 6 -- * * 172.16.7.2 10.8.2.5
// //
// $ iptables -L ${WG_IPT_CHAIN_NAME} -nv --line-numbers | awk '{print $1,$4,$5,$9,$10}' // $ iptables -L ${WG_IPT_CHAIN_NAME} -nv --line-numbers | awk '{print $1,$4,$5,$9,$10,$12,$13}'
// Chain (0 references) // Chain (0 references)
// num target prot source destination // num target prot source destination
// 1 ACCEPT 6 172.16.7.2 10.8.2.5 // 1 ACCEPT 6 172.16.7.2 10.8.2.5
======= const stdout = await Util.exec(`iptables -L ${WG_IPT_CHAIN_NAME} -nv --line-numbers | awk '{print $1,$4,$5,$9,$10,$12,$13}'`);
// $ iptables -L ${WG_IPT_CHAIN_NAME} -n -v
=======
//
// $ iptables -L WGEASY -nv --line-numbers
>>>>>>> b4f7165 (fix: restrict access to vue templates directory)
// Chain WGEASY (0 references)
// num pkts bytes target prot opt in out source destination
=======
>>>>>>> 2bac779 (fix: lint)
// 1 0 0 ACCEPT 6 -- * * 172.16.7.2 10.8.2.5
//
// $ iptables -L ${WG_IPT_CHAIN_NAME} -nv --line-numbers | awk '{print $1,$4,$5,$9,$10}'
// Chain (0 references)
// num target prot source destination
<<<<<<< HEAD
// 1 DROP 6 172.16.8.2 172.16.8.3
// 2 ACCEPT 6 172.16.9.2 172.16.8.3
>>>>>>> 9ec7359 (feat: firewall)
=======
// 1 ACCEPT 6 172.16.7.2 10.8.2.5
>>>>>>> b4f7165 (fix: restrict access to vue templates directory)
const stdout = await Util.exec(`iptables -L ${WG_IPT_CHAIN_NAME} -nv --line-numbers | awk '{print $1,$4,$5,$9,$10}'`);
const lines = stdout.split(/\r?\n/); const lines = stdout.split(/\r?\n/);
const rules = lines.slice(2).map((line) => { const rules = lines.slice(2).map((line) => {
const [num, target, protocol, source, destination] = line.trim().split(' '); const [num, target, protocol, source, destination, spt, dpt] = line.trim().split(' ');
const targetToName = Util.getTargetName(target); const targetToName = Util.getTargetName(target);
const protocolToName = Util.getProtocolName(protocol); const protocolToName = Util.getProtocolName(protocol);
let newSource = source;
let newDestination = destination;
if (spt) {
// eslint-disable-next-line no-unused-vars
const [_, sPort] = spt.split(':');
if (spt.startsWith('spt')) {
newSource += `:${sPort}`;
}
if (spt.startsWith('dpt')) {
newDestination += `:${sPort}`;
}
}
if (dpt) {
// eslint-disable-next-line no-unused-vars
const [_, dPort] = dpt.split(':');
newDestination += `:${dPort}`;
}
return { return {
num, target: targetToName, source, destination, protocol: protocolToName, num, target: targetToName, source: newSource, destination: newDestination, protocol: protocolToName,
}; };
}).filter((rule) => rule !== null); }).filter((rule) => rule !== null);
@ -99,8 +94,6 @@ module.exports = class Firewall {
async addIptablesRule(source, destination, protocol, target) { async addIptablesRule(source, destination, protocol, target) {
debug('Rule adding...'); debug('Rule adding...');
<<<<<<< HEAD
<<<<<<< HEAD
// Validate target & protocol // Validate target & protocol
if (!Util.isTarget(target) || !Util.isProtocol(protocol)) { if (!Util.isTarget(target) || !Util.isProtocol(protocol)) {
throw new Error('Invalid target or protocol.'); throw new Error('Invalid target or protocol.');
@ -108,93 +101,38 @@ module.exports = class Firewall {
/* /*
Support command : Support command :
iptables -A CHAIN -s [IPv4 | IPv4/CIDR] [--sport PORT] -d [IPv4 | IPv4/CIDR] [--dport PORT] -p PROTOCOL -j TARGET iptables -A CHAIN -s [IPv4 | IPv4/CIDR] -d [IPv4 | IPv4/CIDR] -p PROTOCOL [--sport PORT] [--dport PORT] -j TARGET
*/ */
let iptablesCommand = `iptables -A ${WG_IPT_CHAIN_NAME}`; let iptablesCommand = `iptables -A ${WG_IPT_CHAIN_NAME}`;
if (Util.isSupportedAddress(source)) { if (Util.isSupportedAddress(source) && Util.isSupportedAddress(destination)) {
const [address, port] = source.split(':'); const [sAddress, sPort] = source.split(':');
if (Util.isIPAddress(address) || Util.isCIDR(address)) { const [dAddress, dPort] = destination.split(':');
iptablesCommand += ` -s ${address}`; if (Util.isIPAddress(sAddress) || Util.isCIDR(sAddress)) {
if (port) { iptablesCommand += ` -s ${sAddress}`;
iptablesCommand += ` --sport ${port}`;
}
} else { } else {
throw new Error('Invalid source address.'); throw new Error('Invalid source address.');
} }
} else { if (Util.isIPAddress(dAddress) || Util.isCIDR(dAddress)) {
throw new Error('Invalid source format.'); iptablesCommand += ` -d ${dAddress}`;
}
if (Util.isSupportedAddress(destination)) {
const [address, port] = destination.split(':');
if (Util.isIPAddress(address) || Util.isCIDR(address)) {
iptablesCommand += ` -d ${address}`;
if (port) {
iptablesCommand += ` --dport ${port}`;
}
} else { } else {
throw new Error('Invalid destination address.'); throw new Error('Invalid destination address.');
} }
iptablesCommand += ` -p ${protocol}`;
if (sPort) {
iptablesCommand += ` --sport ${sPort}`;
}
if (dPort) {
iptablesCommand += ` --dport ${dPort}`;
}
} else { } else {
throw new Error('Invalid destination format.'); throw new Error('Invalid source/destination format.');
}
iptablesCommand += ` -p ${protocol} -j ${target}`;
await Util.exec(iptablesCommand);
=======
// Validate target
if (!Util.isValidIptablesTarget(target)) {
throw new Error('Invalid target.');
}
<<<<<<< HEAD
await Util.exec(`iptables -A ${WG_IPT_CHAIN_NAME} -s ${source} -d ${destination} -p ${protocol} -j ${target}`);
>>>>>>> 9ec7359 (feat: firewall)
=======
// Validate protocol
if (!Util.isValidIptablesProtocol(protocol)) {
throw new Error('Invalid protocol.');
=======
// Validate target & protocol
if (!Util.isValidIptablesTarget(target) || !Util.isValidIptablesProtocol(protocol)) {
throw new Error('Invalid target or protocol.');
}
<<<<<<< HEAD
// If empty source or destination
if (!source || !destination) {
throw new Error('Invalid source or destination address.')
>>>>>>> a20e416 (fix: source & destination are required)
}
=======
>>>>>>> 48f8be5 (fix: lint)
/*
Support command :
iptables -A CHAIN -s [IPv4 | IPv4/CIDR] -d [IPv4 | IPv4/CIDR] -p PROTOCOL -j TARGET
*/
let iptablesCommand = `iptables -A ${WG_IPT_CHAIN_NAME}`;
if (source && (Util.isIPAddress(source) || Util.isCIDR(source))) {
iptablesCommand += ` -s ${source}`;
} else {
throw new Error('Invalid source address.');
}
if (destination && (Util.isIPAddress(destination) || Util.isCIDR(destination))) {
iptablesCommand += ` -d ${destination}`;
} else {
throw new Error('Invalid destination address.');
} }
iptablesCommand += ` -p ${protocol} -j ${target}`; iptablesCommand += ` -j ${target}`;
await Util.exec(iptablesCommand); await Util.exec(iptablesCommand);
>>>>>>> 88b4ba1 (update: support @ip range with CIDR)
await this.__saveIptablesRules(); await this.__saveIptablesRules();
debug('Rule added'); debug('Rule added');
} }

30
src/lib/Util.js

@ -113,14 +113,40 @@ module.exports = class Util {
return ipRegex.test(ip); return ipRegex.test(ip);
} }
static isValidIptablesTarget(target) { static isTarget(target) {
return Object.values(Target).includes(target); return Object.values(Target).includes(target);
} }
static isValidIptablesProtocol(protocol) { static isProtocol(protocol) {
return Object.keys(Protocol).includes(protocol); return Object.keys(Protocol).includes(protocol);
} }
static isPort(port) {
return port >= 0 && port <= 65535;
}
static isRangeAddresses() {
/* implement */
}
static isRangePorts() {
/* implement */
}
/* Support addresses are :
192.168.1.1
192.168.1.1:53
192.168.1.0/24
192.168.1.0/24:53
*/
static isSupportedAddress(address) {
const parts = address.split(':');
if (parts.length > 2) return false;
const isAddress = this.isIPAddress(parts[0]) || this.isCIDR(parts[0]);
if (parts[1]) return this.isPort(parts[1]) && isAddress;
return isAddress;
}
static getProtocolName(protocolNumber) { static getProtocolName(protocolNumber) {
return ProtocolName[protocolNumber] || 'Unknown Protocol'; return ProtocolName[protocolNumber] || 'Unknown Protocol';
} }

68
src/package-lock.json

@ -747,11 +747,7 @@
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
"integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
"dev": true, "dev": true,
<<<<<<< HEAD
"license": "MIT", "license": "MIT",
"peer": true,
=======
>>>>>>> 88b4ba1 (update: support @ip range with CIDR)
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@ -1114,11 +1110,7 @@
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true, "dev": true,
<<<<<<< HEAD
"license": "MIT", "license": "MIT",
"peer": true,
=======
>>>>>>> 88b4ba1 (update: support @ip range with CIDR)
"dependencies": { "dependencies": {
"ansi-styles": "^4.1.0", "ansi-styles": "^4.1.0",
"supports-color": "^7.1.0" "supports-color": "^7.1.0"
@ -1712,11 +1704,7 @@
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true, "dev": true,
<<<<<<< HEAD
"license": "MIT", "license": "MIT",
"peer": true,
=======
>>>>>>> 88b4ba1 (update: support @ip range with CIDR)
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
@ -2434,28 +2422,8 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
<<<<<<< HEAD
"dev": true,
"license": "ISC",
"peer": true
=======
"dev": true
>>>>>>> 88b4ba1 (update: support @ip range with CIDR)
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true, "dev": true,
"hasInstallScript": true, "license": "ISC"
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
}, },
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
@ -2704,11 +2672,7 @@
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true, "dev": true,
<<<<<<< HEAD
"license": "MIT", "license": "MIT",
"peer": true,
=======
>>>>>>> 88b4ba1 (update: support @ip range with CIDR)
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@ -2842,11 +2806,7 @@
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dev": true, "dev": true,
<<<<<<< HEAD
"license": "ISC", "license": "ISC",
"peer": true,
=======
>>>>>>> 88b4ba1 (update: support @ip range with CIDR)
"dependencies": { "dependencies": {
"once": "^1.3.0", "once": "^1.3.0",
"wrappy": "1" "wrappy": "1"
@ -2856,13 +2816,8 @@
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
<<<<<<< HEAD
"dev": true, "dev": true,
"license": "ISC", "license": "ISC"
"peer": true
=======
"dev": true
>>>>>>> 88b4ba1 (update: support @ip range with CIDR)
}, },
"node_modules/internal-slot": { "node_modules/internal-slot": {
"version": "1.0.7", "version": "1.0.7",
@ -3968,11 +3923,7 @@
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true, "dev": true,
<<<<<<< HEAD
"license": "ISC", "license": "ISC",
"peer": true,
=======
>>>>>>> 88b4ba1 (update: support @ip range with CIDR)
"dependencies": { "dependencies": {
"wrappy": "1" "wrappy": "1"
} }
@ -4993,11 +4944,7 @@
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true, "dev": true,
<<<<<<< HEAD
"license": "MIT", "license": "MIT",
"peer": true,
=======
>>>>>>> 88b4ba1 (update: support @ip range with CIDR)
"engines": { "engines": {
"node": ">=8" "node": ">=8"
}, },
@ -5080,11 +5027,7 @@
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true, "dev": true,
<<<<<<< HEAD
"license": "MIT", "license": "MIT",
"peer": true,
=======
>>>>>>> 88b4ba1 (update: support @ip range with CIDR)
"dependencies": { "dependencies": {
"has-flag": "^4.0.0" "has-flag": "^4.0.0"
}, },
@ -5611,13 +5554,8 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
<<<<<<< HEAD
"dev": true, "dev": true,
"license": "ISC", "license": "ISC"
"peer": true
=======
"dev": true
>>>>>>> 88b4ba1 (update: support @ip range with CIDR)
}, },
"node_modules/y18n": { "node_modules/y18n": {
"version": "4.0.3", "version": "4.0.3",

4
src/www/css/app.css

@ -1154,6 +1154,10 @@ video {
background-color: rgb(254 226 226 / var(--tw-bg-opacity)); background-color: rgb(254 226 226 / var(--tw-bg-opacity));
} }
.bg-red-500\/50 {
background-color: rgb(239 68 68 / 0.5);
}
.bg-red-600 { .bg-red-600 {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(220 38 38 / var(--tw-bg-opacity)); background-color: rgb(220 38 38 / var(--tw-bg-opacity));

2
src/www/js/vendor/vue-firewall.umd.min.js

File diff suppressed because one or more lines are too long

2
src/www/js/vendor/vue-firewall.umd.min.js.map

File diff suppressed because one or more lines are too long

10
src/www/templates/Firewall.vue

@ -7,8 +7,8 @@ export default {
let interfaces = []; let interfaces = [];
let rules = []; let rules = [];
let newRule = { let newRule = {
source: '*', source: '',
destination: '*', destination: '',
protocol: 'TCP', protocol: 'TCP',
target: 'DROP', target: 'DROP',
}; };
@ -37,8 +37,8 @@ export default {
const { source, destination, protocol, target } = this.newRule; const { source, destination, protocol, target } = this.newRule;
this.api.addRule({ source, destination, protocol, target }).then(() => { this.api.addRule({ source, destination, protocol, target }).then(() => {
this.newRule = { this.newRule = {
source: '*', source: '',
destination: '*', destination: '',
protocol: 'TCP', protocol: 'TCP',
target: 'DROP', target: 'DROP',
}; };
@ -104,7 +104,7 @@ export default {
</tr> </tr>
</thead> </thead>
<tbody class="text-center"> <tbody class="text-center">
<tr v-for="(rule, index) in rules" :key="index"> <tr v-for="(rule, index) in rules" :key="index" :class="[rule.target == 'BLOCK' ? 'bg-red-500/50' : '']">
<td class="border border-neutral-500/50 p-1 hover:bg-red-800/50 transition"> <td class="border border-neutral-500/50 p-1 hover:bg-red-800/50 transition">
<button type="button" @click="deleteRule(rule.num)"> <button type="button" @click="deleteRule(rule.num)">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"

Loading…
Cancel
Save