Added reset button

This commit is contained in:
DomySh
2022-07-20 21:19:22 +02:00
parent 1266aebe0e
commit d4cc2f566c
7 changed files with 186 additions and 66 deletions

View File

@@ -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)

View File

@@ -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"]

View File

@@ -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()
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:

View File

@@ -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|string>(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 <Modal size="xl" title="Reset Firegex" opened={opened} onClose={close} closeOnClickOutside={false} centered>
<b>Resetting firegex will trigger the reloading of the firewall rules in nftables and the restarting
of all services filters</b> (technically the c++ filter processes).<br />
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.<br />
<b><Text color="red">Enabling the option below you will totaly reset firegex like you started it for the first time.</Text></b>
<form onSubmit={form.onSubmit(submitRequest)}>
<Space h="md" />
<Switch
label="Delete all data, including the firewall rules"
{...form.getInputProps('delete_data', { type: 'checkbox' })}
/>
<Space h="md" />
<Group position="right" mt="md">
<Button loading={loadingBtn} onClick={close} >Cancel</Button>
<Button loading={loadingBtn} type="submit" color="red">Reset</Button>
</Group>
</form>
<Space h="xl" />
{error?<>
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error}
</Notification><Space h="md" /></>:null}
</Modal>
}
export default ResetModal;

View File

@@ -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|string>(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 <Modal size="xl" title="Change Firewall Password" opened={opened} onClose={onClose} closeOnClickOutside={false} centered>
<form onSubmit={form.onSubmit(submitRequest)}>
<Space h="md" />
<PasswordInput
label="New Password"
placeholder="$3cr3t"
{...form.getInputProps('password')}
/>
<Space h="md" />
<Switch
label="Expire the login status to all connections"
{...form.getInputProps('expire', { type: 'checkbox' })}
/>
<Space h="md" />
<Group position="right" mt="md">
<Button loading={loadingBtn} type="submit">Change Password</Button>
</Group>
</form>
<Space h="xl" />
{error?<>
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error}
</Notification><Space h="md" /></>:null}
</Modal>
}
export default ResetPasswordModal;

View File

@@ -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|string>(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() {
<Menu>
<Menu.Label>Firewall Access</Menu.Label>
<Menu.Item icon={<ImExit size={14} />} onClick={logout_action}>Logout</Menu.Item>
<Divider />
<Menu.Item color="red" icon={<FaLock size={14} />} onClick={() => setChangePasswordModal(true)}>Change Password</Menu.Item>
<Divider />
<Menu.Label>Actions</Menu.Label>
<Menu.Item color="red" icon={<MdOutlineSettingsBackupRestore size={18} />} onClick={() => setResetFiregexModal(true)}>Reset Firegex</Menu.Item>
</Menu>
<div style={{marginLeft:"20px"}}></div>
{ location.pathname !== "/"?
@@ -143,31 +123,9 @@ function Header() {
<AddNewRegex opened={open} onClose={closeModal} service={srv} />:
<AddNewService opened={open} onClose={closeModal} />
}
<Modal size="xl" title="Change Firewall Password" opened={changePasswordModal} onClose={()=>setChangePasswordModal(false)} closeOnClickOutside={false} centered>
<ResetPasswordModal opened={changePasswordModal} onClose={() => setChangePasswordModal(false)} />
<ResetModal opened={resetFiregexModal} onClose={() => setResetFiregexModal(false)} />
<form onSubmit={form.onSubmit(submitRequest)}>
<Space h="md" />
<PasswordInput
label="New Password"
placeholder="$3cr3t"
{...form.getInputProps('password')}
/>
<Space h="md" />
<Switch
label="Expire the login status to all connections"
{...form.getInputProps('expire', { type: 'checkbox' })}
/>
<Space h="md" />
<Group position="right" mt="md">
<Button loading={loadingBtn} type="submit">Change Password</Button>
</Group>
</form>
<Space h="xl" />
{error?<>
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error}
</Notification><Space h="md" /></>:null}
</Modal>
<div style={{marginLeft:"40px"}}></div>
</div>
}

View File

@@ -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