diff --git a/Dockerfile b/Dockerfile index c9238f39..a542b2b6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,7 +42,7 @@ RUN apk add --no-cache \ RUN update-alternatives --install /sbin/iptables iptables /sbin/iptables-legacy 10 --slave /sbin/iptables-restore iptables-restore /sbin/iptables-legacy-restore --slave /sbin/iptables-save iptables-save /sbin/iptables-legacy-save # Set Environment -ENV DEBUG=Server,WireGuard +ENV DEBUG=Server,WireGuard,Firewall # Run Web UI WORKDIR /app diff --git a/src/lib/Firewall.js b/src/lib/Firewall.js new file mode 100644 index 00000000..b7a6c0a6 --- /dev/null +++ b/src/lib/Firewall.js @@ -0,0 +1,153 @@ +'use strict'; + +const { existsSync } = require('fs'); +const path = require('path'); +const debug = require('debug')('Firewall'); + +const Util = require('./Util'); + +const WG_IPT_CHAIN_NAME = 'WGEASY'; +const WG_IPT_FILE_RULE_NAME = 'fw.rules'; + +const { WG_PATH } = require('../config'); + +module.exports = class Firewall { + + async init() { + try { + this.rulesFile = path.join(WG_PATH, WG_IPT_FILE_RULE_NAME); + const configFile = this.rulesFile; + if (existsSync(configFile)) { + await this.__restoreIptablesRules(); + } + // check otherwise create + await Util.exec(`iptables -L ${WG_IPT_CHAIN_NAME} -n`); + } catch (error) { + debug(`Create rules on chain "${WG_IPT_CHAIN_NAME}"...`); + await Util.exec(`iptables -N ${WG_IPT_CHAIN_NAME}`); + await this.__saveIptablesRules(); + debug('Rules created.'); + } + } + + async __saveIptablesRules() { + debug('Rules saving...'); + const configFile = this.rulesFile; + await Util.exec(`iptables-save > ${configFile}`); + debug('Rules saved.'); + } + + async __restoreIptablesRules() { + debug('Rules restoring...'); + const configFile = this.rulesFile; + await Util.exec(`iptables-restore < ${configFile}`); + debug('Rules restored.'); + } + + async getIptablesRules() { + // iptables list the rules WG_IPT_CHAIN_NAME chain + // + // $ iptables -L WGEASY -nv --line-numbers + // Chain WGEASY (0 references) + // num pkts bytes target prot opt in out source destination + // 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 + // 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}'`); + + const lines = stdout.split(/\r?\n/); + const rules = lines.slice(2).map((line) => { + const [num, target, protocol, source, destination] = line.trim().split(' '); + const targetToName = Util.getTargetName(target); + const protocolToName = Util.getProtocolName(protocol); + + return { + num, target: targetToName, source, destination, protocol: protocolToName, + }; + }).filter((rule) => rule !== null); + + return JSON.stringify(rules); + } + + async addIptablesRule(source, destination, protocol, target) { + debug('Rule adding...'); + // Validate target & protocol + if (!Util.isTarget(target) || !Util.isProtocol(protocol)) { + throw new Error('Invalid target or protocol.'); + } + + /* + Support command : + iptables -A CHAIN -s [IPv4 | IPv4/CIDR] [--sport PORT] -d [IPv4 | IPv4/CIDR] [--dport PORT] -p PROTOCOL -j TARGET + */ + + let iptablesCommand = `iptables -A ${WG_IPT_CHAIN_NAME}`; + + if (Util.isSupportedAddress(source)) { + const [address, port] = source.split(':'); + if (Util.isIPAddress(address) || Util.isCIDR(address)) { + iptablesCommand += ` -s ${address}`; + if (port) { + iptablesCommand += ` --sport ${port}`; + } + } else { + throw new Error('Invalid source address.'); + } + } else { + throw new Error('Invalid source format.'); + } + + 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 { + throw new Error('Invalid destination address.'); + } + } else { + throw new Error('Invalid destination format.'); + } + + iptablesCommand += ` -p ${protocol} -j ${target}`; + + await Util.exec(iptablesCommand); + await this.__saveIptablesRules(); + debug('Rule added'); + } + + async deleteIptablesRule(num) { + debug('Rule deleting...'); + await Util.exec(`iptables -D ${WG_IPT_CHAIN_NAME} ${num}`); + await this.__saveIptablesRules(); + debug('Rule deleted.'); + } + + async getInterfaces() { + // $ ip -brief address show + // lo UNKNOWN 127.0.0.1/8 ::1/128 + // eth0@if150 UP 172.17.0.3/16 + // $ ip -brief address show | awk '{print $1,$3,$4}' + // lo 127.0.0.1/8 ::1/128 + // eth0@if150 172.17.0.3/16 + const interfaces = await Util.exec("ip -brief a s | awk '{print $1,$3,$4}'"); + + const result = []; + if (typeof interfaces === 'string') { + for (const line of interfaces.split(/\r?\n/)) { + const [name, ipv4, ipv6] = line.split(' '); + result.push({ + name, ipv4: ipv4 || '', ipv6: ipv6 || '', + }); + } + } + + return JSON.stringify(result); + } + +}; diff --git a/src/lib/Server.js b/src/lib/Server.js index 7f06da50..2031484e 100644 --- a/src/lib/Server.js +++ b/src/lib/Server.js @@ -23,6 +23,7 @@ const { } = require('h3'); const WireGuard = require('../services/WireGuard'); +const Firewall = require('../services/Firewall'); const { PORT, @@ -259,10 +260,10 @@ module.exports = class Server { }; // backup_restore - const router3 = createRouter(); - app.use(router3); + const routerBackupRestore = createRouter(); + app.use(routerBackupRestore); - router3 + routerBackupRestore .get('/api/wireguard/backup', defineEventHandler(async (event) => { const config = await WireGuard.backupConfiguration(); setHeader(event, 'Content-Disposition', 'attachment; filename="wg0.json"'); @@ -275,6 +276,16 @@ module.exports = class Server { return { success: true }; })); + // firewall + const routerFirewall = createRouter(); + app.use(routerFirewall); + + routerFirewall + .get('/api/fw/interfaces', defineEventHandler(async (event) => { + setHeader(event, 'Content-Type', 'text/json'); + return Firewall.getInterfaces(); + })); + // Static assets const publicDir = '/app/www'; app.use( diff --git a/src/package-lock.json b/src/package-lock.json index f4c752ee..2b87a520 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -18,7 +18,8 @@ "devDependencies": { "eslint-config-athom": "^3.1.3", "nodemon": "^3.1.4", - "tailwindcss": "^3.4.7" + "tailwindcss": "^3.4.7", + "vue-template-compiler": "^2.7.16" }, "engines": { "node": ">=18" @@ -1305,6 +1306,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true + }, "node_modules/debug": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", @@ -2663,6 +2670,15 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -5023,6 +5039,16 @@ "license": "MIT", "peer": true }, + "node_modules/vue-template-compiler": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", + "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", + "dev": true, + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/src/package.json b/src/package.json index 6750366e..96c62d71 100644 --- a/src/package.json +++ b/src/package.json @@ -10,7 +10,8 @@ "serve": "DEBUG=Server,WireGuard npx nodemon server.js", "serve-with-password": "PASSWORD=wg npm run serve", "lint": "eslint .", - "buildcss": "npx tailwindcss -i ./www/src/css/app.css -o ./www/css/app.css" + "buildcss": "npx tailwindcss -i ./www/src/css/app.css -o ./www/css/app.css", + "buildfirewall": "vue-cli-service build --dest www/js/vendor --no-module --formats umd-min --target lib --name VueFirewall --filename vue-firewall --no-clean www/templates/Firewall.vue" }, "author": "Emile Nijssen", "license": "CC BY-NC-SA 4.0", @@ -24,7 +25,8 @@ "devDependencies": { "eslint-config-athom": "^3.1.3", "nodemon": "^3.1.4", - "tailwindcss": "^3.4.7" + "tailwindcss": "^3.4.7", + "vue-template-compiler": "^2.7.16" }, "nodemonConfig": { "ignore": [ diff --git a/src/server.js b/src/server.js index 6584b768..66887101 100644 --- a/src/server.js +++ b/src/server.js @@ -1,6 +1,7 @@ 'use strict'; require('./services/Server'); +require('./services/Firewall'); const WireGuard = require('./services/WireGuard'); diff --git a/src/services/Firewall.js b/src/services/Firewall.js new file mode 100644 index 00000000..ea3e04e0 --- /dev/null +++ b/src/services/Firewall.js @@ -0,0 +1,5 @@ +'use strict'; + +const Firewall = require('../lib/Firewall'); + +module.exports = new Firewall(); diff --git a/src/www/css/app.css b/src/www/css/app.css index 0cde16b7..98d7df7b 100644 --- a/src/www/css/app.css +++ b/src/www/css/app.css @@ -1567,10 +1567,6 @@ video { } @media not all and (min-width: 768px) { - .max-md\:flex { - display: flex; - } - .max-md\:hidden { display: none; } @@ -1720,6 +1716,10 @@ video { margin-right: 0.5rem; } + .md\:block { + display: block; + } + .md\:inline-block { display: inline-block; } diff --git a/src/www/index.html b/src/www/index.html index 878f5ca0..73dac4eb 100644 --- a/src/www/index.html +++ b/src/www/index.html @@ -88,7 +88,7 @@ -
+

{{$t("clients")}}

@@ -384,84 +384,7 @@
-
-
-
-

Firewall

-
-
- - - - - - - - - - - - - - - - - - - - - -
InterfaceIPv4 addressIPv6 address
eth0192.168.1.1
wg010.8.0.1
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
SourceDestinationProtocolAction
delete192.168.11.310.9.8.1udp
-
- -
-
- - - - - - - -
-
+
@@ -689,6 +612,7 @@ + diff --git a/src/www/js/api.js b/src/www/js/api.js index 9006f5ab..728b66c4 100644 --- a/src/www/js/api.js +++ b/src/www/js/api.js @@ -146,4 +146,11 @@ class API { }); } + async getInterfaces() { + return this.call({ + method: 'get', + path: '/fw/interfaces', + }); + } + } diff --git a/src/www/js/app.js b/src/www/js/app.js index 61bb7c03..27a61f13 100644 --- a/src/www/js/app.js +++ b/src/www/js/app.js @@ -46,6 +46,7 @@ new Vue({ el: '#app', components: { apexchart: VueApexCharts, + firewall: VueFirewall, }, i18n, data: { diff --git a/src/www/js/i18n.js b/src/www/js/i18n.js index b13412ee..d66f5fd8 100644 --- a/src/www/js/i18n.js +++ b/src/www/js/i18n.js @@ -34,6 +34,7 @@ const messages = { // eslint-disable-line no-unused-vars backup: 'Backup', titleRestoreConfig: 'Restore your configuration', titleBackupConfig: 'Backup your configuration', + firewall: 'Firewall', }, ua: { name: 'Ім`я', diff --git a/src/www/js/vendor/vue-firewall.umd.min.js b/src/www/js/vendor/vue-firewall.umd.min.js new file mode 100644 index 00000000..c5c79d30 --- /dev/null +++ b/src/www/js/vendor/vue-firewall.umd.min.js @@ -0,0 +1,2 @@ +(function(t,e){"object"===typeof exports&&"object"===typeof module?module.exports=e():"function"===typeof define&&define.amd?define([],e):"object"===typeof exports?exports["VueFirewall"]=e():t["VueFirewall"]=e()})("undefined"!==typeof self?self:this,(()=>(()=>{"use strict";var t={};(()=>{t.d=(e,r)=>{for(var a in r)t.o(r,a)&&!t.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:r[a]})}})(),(()=>{t.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e)})(),(()=>{t.p=""})();var e={};if(t.d(e,{default:()=>u}),"undefined"!==typeof window){var r=window.document.currentScript,a=r&&r.src.match(/(.+\/)[^/]+\.js(\?.*)?$/);a&&(t.p=a[1])}var s=function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"shadow-md rounded-lg bg-white dark:bg-neutral-700 overflow-hidden"},[r("div",{staticClass:"flex flex-row flex-auto items-center mb-4 p-3 px-5 border-b-2 border-gray-100 dark:border-neutral-600"},[r("div",{staticClass:"flex-grow"},[r("p",{staticClass:"text-2xl font-medium dark:text-neutral-200"},[t._v(t._s(t.$t("firewall")))])])]),r("table",{staticClass:"table-auto w-[70%] dark:text-neutral-200 mb-4"},[t._m(0),r("tbody",{staticClass:"text-center"},t._l(t.interfaces,(function(e,a){return r("tr",{key:a},[r("td",[t._v(t._s(e.name))]),r("td",[t._v(t._s(e.ipv4))]),r("td",[t._v(t._s(e.ipv6))])])})),0)]),t._m(1)])},o=[function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("thead",[r("tr",[r("th",[t._v("Interface")]),r("th",[t._v("IPv4 address")]),r("th",[t._v("IPv6 address")])])])},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("table",{staticClass:"table-auto border-t-2 dark:border-neutral-200 w-full dark:text-neutral-200"},[r("thead",{staticClass:"bg-gray-200 dark:bg-gray-600"},[r("tr",[r("th",[t._v("Action")]),r("th",{staticClass:"border-x-2 dark:border-neutral-200"},[t._v("Source")]),r("th",{staticClass:"border-x-2 dark:border-neutral-200"},[t._v("Destination")]),r("th",{staticClass:"border-x-2 dark:border-neutral-200"},[t._v("Protocol")]),r("th")])]),r("tbody",{staticClass:"text-center"},[r("tr",[r("td",{staticClass:"border border-gray-600 p-1"},[t._v("delete")]),r("td",{staticClass:"border border-gray-600 p-1"},[t._v("192.168.11.3")]),r("td",{staticClass:"border border-gray-600 p-1"},[t._v("10.9.8.1")]),r("td",{staticClass:"border border-gray-600 p-1"},[t._v("udp")]),r("td",{staticClass:"border border-gray-600 p-1"})]),r("tr",[r("td",{staticClass:"border border-gray-600 p-1"},[r("form",{attrs:{id:"fw"}},[r("input",{staticClass:"w-full outline-none bg-transparent text-center",attrs:{form:"fw",type:"submit",value:"send"}})])]),r("td",{staticClass:"border border-gray-600"},[r("input",{staticClass:"w-full outline-none bg-transparent text-center",attrs:{form:"fw",type:"text",name:"source"}})]),r("td",{staticClass:"border border-gray-600"},[r("input",{staticClass:"w-full outline-none bg-transparent text-center",attrs:{form:"fw",type:"text",name:"destination"}})]),r("td",{staticClass:"border border-gray-600"},[r("select",{staticClass:"bg-transparent",attrs:{form:"fw",name:"protocol"}},[r("option",{attrs:{value:"tcp"}},[t._v("tcp")]),r("option",{attrs:{value:"udp"}},[t._v("udp")])])]),r("td",{staticClass:"border border-gray-600 p-1"},[r("select",{staticClass:"bg-transparent",attrs:{form:"fw",name:"action"}},[r("option",{attrs:{value:"allow"}},[t._v("allow")]),r("option",{attrs:{value:"block"}},[t._v("block")])])])])])])}];const n={name:"Firewall",data(){let t=[];return{interfaces:t}},methods:{getInterfaces(){this.api.getInterfaces().then((t=>{this.interfaces=t})).catch((t=>{alert(t.message||t.toString())}))}},mounted(){this.api=new API,this.getInterfaces()}},i=n;function d(t,e,r,a,s,o,n,i){var d,l="function"===typeof t?t.options:t;if(e&&(l.render=e,l.staticRenderFns=r,l._compiled=!0),a&&(l.functional=!0),o&&(l._scopeId="data-v-"+o),n?(d=function(t){t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,t||"undefined"===typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),s&&s.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(n)},l._ssrRegister=d):s&&(d=i?function(){s.call(this,(l.functional?this.parent:this).$root.$options.shadowRoot)}:s),d)if(l.functional){l._injectStyles=d;var c=l.render;l.render=function(t,e){return d.call(e),c(t,e)}}else{var u=l.beforeCreate;l.beforeCreate=u?[].concat(u,d):[d]}return{exports:t,options:l}}var l=d(i,s,o,!1,null,null,null);const c=l.exports,u=c;return e=e["default"],e})())); +//# sourceMappingURL=vue-firewall.umd.min.js.map \ No newline at end of file diff --git a/src/www/js/vendor/vue-firewall.umd.min.js.map b/src/www/js/vendor/vue-firewall.umd.min.js.map new file mode 100644 index 00000000..18385e15 --- /dev/null +++ b/src/www/js/vendor/vue-firewall.umd.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"vue-firewall.umd.min.js","mappings":"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","sources":["webpack://VueFirewall/webpack/universalModuleDefinition","webpack://VueFirewall/webpack/bootstrap","webpack://VueFirewall/webpack/runtime/define property getters","webpack://VueFirewall/webpack/runtime/hasOwnProperty shorthand","webpack://VueFirewall/webpack/runtime/publicPath","webpack://VueFirewall/../../../.local/share/pnpm/global/5/.pnpm/@vue+cli-service@5.0.8_lodash@4.17.21_webpack-sources@3.2.3/node_modules/@vue/cli-service/lib/commands/build/setPublicPath.js","webpack://VueFirewall/./www/templates/Firewall.vue?a453","webpack://VueFirewall/www/templates/Firewall.vue","webpack://VueFirewall/./www/templates/Firewall.vue?6878","webpack://VueFirewall/../../../.local/share/pnpm/global/5/.pnpm/vue-loader@15.11.1_css-loader@6.11.0_webpack@5.93.0__lodash@4.17.21_webpack@5.93.0/node_modules/vue-loader/lib/runtime/componentNormalizer.js","webpack://VueFirewall/./www/templates/Firewall.vue","webpack://VueFirewall/../../../.local/share/pnpm/global/5/.pnpm/@vue+cli-service@5.0.8_lodash@4.17.21_webpack-sources@3.2.3/node_modules/@vue/cli-service/lib/commands/build/entry-lib.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"VueFirewall\"] = factory();\n\telse\n\t\troot[\"VueFirewall\"] = factory();\n})((typeof self !== 'undefined' ? self : this), () => {\nreturn ","// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","__webpack_require__.p = \"\";","/* eslint-disable no-var */\n// This file is imported into lib/wc client bundles.\n\nif (typeof window !== 'undefined') {\n var currentScript = window.document.currentScript\n if (process.env.NEED_CURRENTSCRIPT_POLYFILL) {\n var getCurrentScript = require('@soda/get-current-script')\n currentScript = getCurrentScript()\n\n // for backward compatibility, because previously we directly included the polyfill\n if (!('currentScript' in document)) {\n Object.defineProperty(document, 'currentScript', { get: getCurrentScript })\n }\n }\n\n var src = currentScript && currentScript.src.match(/(.+\\/)[^/]+\\.js(\\?.*)?$/)\n if (src) {\n __webpack_public_path__ = src[1] // eslint-disable-line\n }\n}\n\n// Indicate to webpack that this file can be concatenated\nexport default null\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"shadow-md rounded-lg bg-white dark:bg-neutral-700 overflow-hidden\"},[_c('div',{staticClass:\"flex flex-row flex-auto items-center mb-4 p-3 px-5 border-b-2 border-gray-100 dark:border-neutral-600\"},[_c('div',{staticClass:\"flex-grow\"},[_c('p',{staticClass:\"text-2xl font-medium dark:text-neutral-200\"},[_vm._v(_vm._s(_vm.$t(\"firewall\")))])])]),_c('table',{staticClass:\"table-auto w-[70%] dark:text-neutral-200 mb-4\"},[_vm._m(0),_c('tbody',{staticClass:\"text-center\"},_vm._l((_vm.interfaces),function(iface,index){return _c('tr',{key:index},[_c('td',[_vm._v(_vm._s(iface.name))]),_c('td',[_vm._v(_vm._s(iface.ipv4))]),_c('td',[_vm._v(_vm._s(iface.ipv6))])])}),0)]),_vm._m(1)])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('thead',[_c('tr',[_c('th',[_vm._v(\"Interface\")]),_c('th',[_vm._v(\"IPv4 address\")]),_c('th',[_vm._v(\"IPv6 address\")])])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('table',{staticClass:\"table-auto border-t-2 dark:border-neutral-200 w-full dark:text-neutral-200\"},[_c('thead',{staticClass:\"bg-gray-200 dark:bg-gray-600\"},[_c('tr',[_c('th',[_vm._v(\"Action\")]),_c('th',{staticClass:\"border-x-2 dark:border-neutral-200\"},[_vm._v(\"Source\")]),_c('th',{staticClass:\"border-x-2 dark:border-neutral-200\"},[_vm._v(\"Destination\")]),_c('th',{staticClass:\"border-x-2 dark:border-neutral-200\"},[_vm._v(\"Protocol\")]),_c('th')])]),_c('tbody',{staticClass:\"text-center\"},[_c('tr',[_c('td',{staticClass:\"border border-gray-600 p-1\"},[_vm._v(\"delete\")]),_c('td',{staticClass:\"border border-gray-600 p-1\"},[_vm._v(\"192.168.11.3\")]),_c('td',{staticClass:\"border border-gray-600 p-1\"},[_vm._v(\"10.9.8.1\")]),_c('td',{staticClass:\"border border-gray-600 p-1\"},[_vm._v(\"udp\")]),_c('td',{staticClass:\"border border-gray-600 p-1\"})]),_c('tr',[_c('td',{staticClass:\"border border-gray-600 p-1\"},[_c('form',{attrs:{\"id\":\"fw\"}},[_c('input',{staticClass:\"w-full outline-none bg-transparent text-center\",attrs:{\"form\":\"fw\",\"type\":\"submit\",\"value\":\"send\"}})])]),_c('td',{staticClass:\"border border-gray-600\"},[_c('input',{staticClass:\"w-full outline-none bg-transparent text-center\",attrs:{\"form\":\"fw\",\"type\":\"text\",\"name\":\"source\"}})]),_c('td',{staticClass:\"border border-gray-600\"},[_c('input',{staticClass:\"w-full outline-none bg-transparent text-center\",attrs:{\"form\":\"fw\",\"type\":\"text\",\"name\":\"destination\"}})]),_c('td',{staticClass:\"border border-gray-600\"},[_c('select',{staticClass:\"bg-transparent\",attrs:{\"form\":\"fw\",\"name\":\"protocol\"}},[_c('option',{attrs:{\"value\":\"tcp\"}},[_vm._v(\"tcp\")]),_c('option',{attrs:{\"value\":\"udp\"}},[_vm._v(\"udp\")])])]),_c('td',{staticClass:\"border border-gray-600 p-1\"},[_c('select',{staticClass:\"bg-transparent\",attrs:{\"form\":\"fw\",\"name\":\"action\"}},[_c('option',{attrs:{\"value\":\"allow\"}},[_vm._v(\"allow\")]),_c('option',{attrs:{\"value\":\"block\"}},[_vm._v(\"block\")])])])])])])}]\n\nexport { render, staticRenderFns }","\n\n\n","import mod from \"-!../../../../../.local/share/pnpm/global/5/.pnpm/vue-loader@15.11.1_css-loader@6.11.0_webpack@5.93.0__lodash@4.17.21_webpack@5.93.0/node_modules/vue-loader/lib/index.js??vue-loader-options!./Firewall.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../../.local/share/pnpm/global/5/.pnpm/vue-loader@15.11.1_css-loader@6.11.0_webpack@5.93.0__lodash@4.17.21_webpack@5.93.0/node_modules/vue-loader/lib/index.js??vue-loader-options!./Firewall.vue?vue&type=script&lang=js\"","/* globals __VUE_SSR_CONTEXT__ */\n\n// IMPORTANT: Do NOT use ES2015 features in this file (except for modules).\n// This module is a runtime utility for cleaner component module output and will\n// be included in the final webpack user bundle.\n\nexport default function normalizeComponent(\n scriptExports,\n render,\n staticRenderFns,\n functionalTemplate,\n injectStyles,\n scopeId,\n moduleIdentifier /* server only */,\n shadowMode /* vue-cli only */\n) {\n // Vue.extend constructor export interop\n var options =\n typeof scriptExports === 'function' ? scriptExports.options : scriptExports\n\n // render functions\n if (render) {\n options.render = render\n options.staticRenderFns = staticRenderFns\n options._compiled = true\n }\n\n // functional template\n if (functionalTemplate) {\n options.functional = true\n }\n\n // scopedId\n if (scopeId) {\n options._scopeId = 'data-v-' + scopeId\n }\n\n var hook\n if (moduleIdentifier) {\n // server build\n hook = function (context) {\n // 2.3 injection\n context =\n context || // cached call\n (this.$vnode && this.$vnode.ssrContext) || // stateful\n (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional\n // 2.2 with runInNewContext: true\n if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {\n context = __VUE_SSR_CONTEXT__\n }\n // inject component styles\n if (injectStyles) {\n injectStyles.call(this, context)\n }\n // register component module identifier for async chunk inferrence\n if (context && context._registeredComponents) {\n context._registeredComponents.add(moduleIdentifier)\n }\n }\n // used by ssr in case component is cached and beforeCreate\n // never gets called\n options._ssrRegister = hook\n } else if (injectStyles) {\n hook = shadowMode\n ? function () {\n injectStyles.call(\n this,\n (options.functional ? this.parent : this).$root.$options.shadowRoot\n )\n }\n : injectStyles\n }\n\n if (hook) {\n if (options.functional) {\n // for template-only hot-reload because in that case the render fn doesn't\n // go through the normalizer\n options._injectStyles = hook\n // register for functional component in vue file\n var originalRender = options.render\n options.render = function renderWithStyleInjection(h, context) {\n hook.call(context)\n return originalRender(h, context)\n }\n } else {\n // inject component registration as beforeCreate hook\n var existing = options.beforeCreate\n options.beforeCreate = existing ? [].concat(existing, hook) : [hook]\n }\n }\n\n return {\n exports: scriptExports,\n options: options\n }\n}\n","import { render, staticRenderFns } from \"./Firewall.vue?vue&type=template&id=8bb350ba\"\nimport script from \"./Firewall.vue?vue&type=script&lang=js\"\nexport * from \"./Firewall.vue?vue&type=script&lang=js\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../../.local/share/pnpm/global/5/.pnpm/vue-loader@15.11.1_css-loader@6.11.0_webpack@5.93.0__lodash@4.17.21_webpack@5.93.0/node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import './setPublicPath'\nimport mod from '~entry'\nexport default mod\nexport * from '~entry'\n"],"names":["root","factory","exports","module","define","amd","self","this","__webpack_require__","d","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","p","window","currentScript","document","src","match","render","_vm","_h","$createElement","_c","_self","staticClass","_v","_s","$t","_m","_l","iface","index","name","ipv4","ipv6","staticRenderFns","attrs","data","interfaces","methods","getInterfaces","api","then","catch","err","alert","message","toString","mounted","API","normalizeComponent","scriptExports","functionalTemplate","injectStyles","scopeId","moduleIdentifier","shadowMode","hook","options","_compiled","functional","_scopeId","context","$vnode","ssrContext","parent","__VUE_SSR_CONTEXT__","_registeredComponents","add","_ssrRegister","$root","$options","shadowRoot","_injectStyles","originalRender","h","existing","beforeCreate","concat","component"],"sourceRoot":""} \ No newline at end of file diff --git a/src/www/templates/Firewall.vue b/src/www/templates/Firewall.vue new file mode 100644 index 00000000..e5b524a5 --- /dev/null +++ b/src/www/templates/Firewall.vue @@ -0,0 +1,96 @@ + + +