From d4cc2f566ccdb4c8c27ac6f60e946e4b51b59ba4 Mon Sep 17 00:00:00 2001 From: DomySh Date: Wed, 20 Jul 2022 21:19:22 +0200 Subject: [PATCH] Added reset button --- backend/app.py | 24 +++++- backend/modules/firegex.py | 8 +- backend/modules/sqlite.py | 14 +++- frontend/src/components/Header/ResetModal.tsx | 64 ++++++++++++++++ .../components/Header/ResetPasswordModal.tsx | 62 ++++++++++++++++ frontend/src/components/Header/index.tsx | 74 ++++--------------- frontend/src/js/utils.tsx | 6 +- 7 files changed, 186 insertions(+), 66 deletions(-) create mode 100644 frontend/src/components/Header/ResetModal.tsx create mode 100644 frontend/src/components/Header/ResetPasswordModal.tsx diff --git a/backend/app.py b/backend/app.py index c20f57f..064e54b 100644 --- a/backend/app.py +++ b/backend/app.py @@ -11,6 +11,7 @@ from passlib.context import CryptContext from fastapi_socketio import SocketManager from modules import SQLite, FirewallManager from modules.firewall import STATUS +from modules.firegex import FiregexTables from utils import get_interfaces, ip_parse, refactor_name, gen_service_id ON_DOCKER = len(sys.argv) > 1 and sys.argv[1] == "DOCKER" @@ -69,7 +70,7 @@ async def check_login(token: str = Depends(oauth2_scheme)): try: payload = jwt.decode(token, JWT_SECRET(), algorithms=[settings.JWT_ALGORITHM]) logged_in: bool = payload.get("logged_in") - except JWTError: + except Exception: return False return logged_in @@ -374,6 +375,27 @@ async def get_ip_interfaces(auth: bool = Depends(is_loggined)): """Get a list of ip and ip6 interfaces""" return get_interfaces() +class ResetRequest(BaseModel): + delete:bool + +@app.post('/api/reset', response_model=StatusMessageModel) +async def reset_firegex(form: ResetRequest, auth: bool = Depends(is_loggined)): + """Reset firegex nftables rules and optionally all the database""" + if not form.delete: + db.backup() + await firewall.close() + FiregexTables().reset() + if form.delete: + db.delete() + db.init() + db.put("secret", secrets.token_hex(32)) + else: + db.restore() + await firewall.init() + await refresh_frontend() + + return {'status': 'ok'} + async def frontend_debug_proxy(path): httpc = httpx.AsyncClient() req = httpc.build_request("GET",f"http://127.0.0.1:{os.getenv('F_PORT','3000')}/"+path) diff --git a/backend/modules/firegex.py b/backend/modules/firegex.py index 2bd3038..31feb9f 100644 --- a/backend/modules/firegex.py +++ b/backend/modules/firegex.py @@ -39,6 +39,7 @@ class FiregexTables: else: raise Exception(err) def init(self): + self.reset() code, out, err = self.raw_cmd({"create":{"table":{"name":self.table_name,"family":"inet"}}}) if code == 0: self.cmd( @@ -61,10 +62,13 @@ class FiregexTables: "policy":"accept" }}} ) - self.reset() + def reset(self): - self.cmd({"flush":{"table":{"name":"firegex","family":"inet"}}}) + self.raw_cmd( + {"flush":{"table":{"name":"firegex","family":"inet"}}}, + {"delete":{"table":{"name":"firegex","family":"inet"}}}, + ) def list(self): return self.cmd({"list": {"ruleset": None}})["nftables"] diff --git a/backend/modules/sqlite.py b/backend/modules/sqlite.py index 66cae75..47362b4 100644 --- a/backend/modules/sqlite.py +++ b/backend/modules/sqlite.py @@ -50,18 +50,24 @@ class SQLite(): self.conn.row_factory = dict_factory def backup(self): - if self.conn: - with open(self.db_name, "rb") as f: - self.__backup = f.read() + with open(self.db_name, "rb") as f: + self.__backup = f.read() def restore(self): + were_active = True if self.conn else False + self.disconnect() if self.__backup: with open(self.db_name, "wb") as f: f.write(self.__backup) self.__backup = None - + if were_active: self.connect() + + def delete_backup(self): + self.__backup = None + def disconnect(self) -> None: if self.conn: self.conn.close() + self.conn = None def create_schema(self, tables = {}) -> None: if self.conn: diff --git a/frontend/src/components/Header/ResetModal.tsx b/frontend/src/components/Header/ResetModal.tsx new file mode 100644 index 0000000..4418070 --- /dev/null +++ b/frontend/src/components/Header/ResetModal.tsx @@ -0,0 +1,64 @@ +import { Button, Group, Modal, Notification, Space, Switch, Text } from "@mantine/core"; +import { useForm } from "@mantine/hooks"; +import React, { useState } from "react" +import { ImCross } from "react-icons/im"; +import { okNotify, resetfiregex } from "../../js/utils"; + +function ResetModal({ opened, onClose }:{ opened: boolean, onClose: () => void }) { + const form = useForm({ + initialValues: { + delete_data:false, + } + }) + + const [loadingBtn, setLoadingBtn] = useState(false) + const [error, setError] = useState(null) + + const close = () => { + setError(null) + setLoadingBtn(false) + form.reset() + onClose() + } + + const submitRequest = async ({ delete_data }:{ delete_data:boolean }) => { + setLoadingBtn(true) + await resetfiregex(delete_data).then(res => { + if(!res){ + okNotify("Firegex Resetted!","Firegex has been resetted!") + setError(null); + close() + form.reset() + }else{ + setError(res) + } + }).catch( err => setError(err.toString())) + setLoadingBtn(false) + } + return + Resetting firegex will trigger the reloading of the firewall rules in nftables and the restarting + of all services filters (technically the c++ filter processes).
+ This will only cause the stop of the filters for a second and then restore them, during this time the services will continue to be available without interruptions.
+ Enabling the option below you will totaly reset firegex like you started it for the first time. +
+ + + + + + + + + + {error?<> + } color="red" onClose={()=>{setError(null)}}> + Error: {error} + :null} +
+ +} + +export default ResetModal; \ No newline at end of file diff --git a/frontend/src/components/Header/ResetPasswordModal.tsx b/frontend/src/components/Header/ResetPasswordModal.tsx new file mode 100644 index 0000000..af025d8 --- /dev/null +++ b/frontend/src/components/Header/ResetPasswordModal.tsx @@ -0,0 +1,62 @@ +import { Button, Group, Modal, Notification, PasswordInput, Space, Switch } from "@mantine/core"; +import { useForm } from "@mantine/hooks"; +import React, { useState } from "react" +import { ImCross } from "react-icons/im"; +import { ChangePassword } from "../../js/models"; +import { changepassword, okNotify } from "../../js/utils"; + +function ResetPasswordModal({ opened, onClose }:{ opened: boolean, onClose: () => void }) { + const form = useForm({ + initialValues: { + password:"", + expire:true + }, + validationRules:{ + password: (value) => value !== "" + } + }) + const [loadingBtn, setLoadingBtn] = useState(false) + const [error, setError] = useState(null) + + const submitRequest = async (values:ChangePassword) => { + setLoadingBtn(true) + await changepassword(values).then(res => { + if(!res){ + okNotify("Password change done!","The password of the firewall has been changed!") + onClose() + form.reset() + }else{ + setError(res) + } + }).catch( err => setError(err.toString())) + setLoadingBtn(false) + } + return + +
+ + + + + + + + + + + {error?<> + } color="red" onClose={()=>{setError(null)}}> + Error: {error} + :null} +
+ +} + +export default ResetPasswordModal; \ No newline at end of file diff --git a/frontend/src/components/Header/index.tsx b/frontend/src/components/Header/index.tsx index d794750..601be0a 100755 --- a/frontend/src/components/Header/index.tsx +++ b/frontend/src/components/Header/index.tsx @@ -1,16 +1,19 @@ import React, { useState } from 'react'; -import { ActionIcon, Badge, Button, Divider, Group, Image, Menu, Modal, Notification, Space, Switch, Tooltip, FloatingTooltip, MediaQuery, PasswordInput } from '@mantine/core'; +import { ActionIcon, Badge, Divider, Image, Menu, Space, Tooltip, FloatingTooltip, MediaQuery } from '@mantine/core'; import style from "./index.module.scss"; -import { changepassword, errorNotify, eventUpdateName, generalstats, logout, okNotify } from '../../js/utils'; -import { ChangePassword, GeneralStats } from '../../js/models'; +import { errorNotify, eventUpdateName, generalstats, logout } from '../../js/utils'; +import { GeneralStats } from '../../js/models'; import { BsPlusLg } from "react-icons/bs" import { AiFillHome } from "react-icons/ai" import { useLocation, useNavigate, useParams } from 'react-router-dom'; import AddNewRegex from '../AddNewRegex'; import AddNewService from '../AddNewService'; import { FaLock } from 'react-icons/fa'; -import { ImCross, ImExit } from 'react-icons/im'; -import { useForm, useWindowEvent } from '@mantine/hooks'; +import { MdOutlineSettingsBackupRestore } from 'react-icons/md'; +import { ImExit } from 'react-icons/im'; +import { useWindowEvent } from '@mantine/hooks'; +import ResetPasswordModal from './ResetPasswordModal'; +import ResetModal from './ResetModal'; function Header() { @@ -38,37 +41,11 @@ function Header() { }) } - - const form = useForm({ - initialValues: { - password:"", - expire:true - }, - validationRules:{ - password: (value) => value !== "" - } - }) - - const [loadingBtn, setLoadingBtn] = useState(false) - const [error, setError] = useState(null) const [changePasswordModal, setChangePasswordModal] = useState(false); + const [resetFiregexModal, setResetFiregexModal] = useState(false); const [tooltipAddOpened, setTooltipAddOpened] = useState(false); const [tooltipHomeOpened, setTooltipHomeOpened] = useState(false); - const submitRequest = async (values:ChangePassword) => { - setLoadingBtn(true) - await changepassword(values).then(res => { - if(!res){ - okNotify("Password change done!","The password of the firewall has been changed!") - setChangePasswordModal(false) - form.reset() - }else{ - setError(res) - } - }).catch( err => setError(err.toString())) - setLoadingBtn(false) - } - const {srv} = useParams() const [open, setOpen] = useState(false); @@ -108,8 +85,11 @@ function Header() { Firewall Access } onClick={logout_action}>Logout - } onClick={() => setChangePasswordModal(true)}>Change Password + + Actions + } onClick={() => setResetFiregexModal(true)}>Reset Firegex +
{ location.pathname !== "/"? @@ -143,31 +123,9 @@ function Header() { : } - setChangePasswordModal(false)} closeOnClickOutside={false} centered> - -
- - - - - - - - - - - {error?<> - } color="red" onClose={()=>{setError(null)}}> - Error: {error} - :null} -
+ setChangePasswordModal(false)} /> + setResetFiregexModal(false)} /> +
} diff --git a/frontend/src/js/utils.tsx b/frontend/src/js/utils.tsx index 049e9ce..5abfda8 100755 --- a/frontend/src/js/utils.tsx +++ b/frontend/src/js/utils.tsx @@ -54,6 +54,11 @@ export function fireUpdateRequest(){ window.dispatchEvent(new Event(eventUpdateName)) } +export async function resetfiregex(delete_data:boolean = false){ + const { status } = await postapi("reset",{delete:delete_data}) as ServerResponse; + return (status === "ok"?undefined:status) +} + export async function getipinterfaces(){ return await getapi("interfaces") as IpInterface[]; } @@ -87,7 +92,6 @@ export async function setpassword(data:PasswordSend) { export async function changepassword(data:ChangePassword) { const { status, access_token } = await postapi("change-password",data) as ServerResponseToken; - console.log(access_token) if (access_token) window.localStorage.setItem("access_token", access_token); return status === "ok"?undefined:status