Moduled Firegex, Merging pt1 (not finished and not working yet)

This commit is contained in:
DomySh
2022-07-21 20:25:39 +02:00
parent 3143f6e474
commit 63ba0e94e7
42 changed files with 2832 additions and 577 deletions

View File

@@ -2,7 +2,6 @@
@use "../../index.scss" as *;
.footer{
height: 150px;
margin-top: 50px;
background-color: $primary_color;
@extend .center-flex;

View File

@@ -1,12 +1,13 @@
import { Footer } from '@mantine/core';
import React from 'react';
import style from "./index.module.scss";
function Footer() {
return <div id="footer" className={style.footer}>
function FooterPage() {
return <Footer id="footer" height={70} className={style.footer}>
Made by Pwnzer0tt1
</div>
</Footer>
}
export default Footer;
export default FooterPage;

View File

@@ -1,17 +1,24 @@
@use "../../vars" as *;
@use "../../index.scss" as *;
.header{
width: 100%;
height: 140px;
background-color: $primary_color;
display: flex;
align-items: center;
justify-content: center;
}
.logo{
width: 200px;
margin-left: 40px;
height: 70%;
.divlogo{
width: 110px;
height: 100%;
cursor: pointer;
@extend .center-flex;
}
.navbtn{
@extend .center-flex;
width: 30px;
margin:0;
}

View File

@@ -1,12 +1,9 @@
import React, { useState } from 'react';
import { ActionIcon, Divider, Image, Menu, Tooltip, FloatingTooltip } from '@mantine/core';
import { ActionIcon, Divider, Image, Menu, Tooltip, FloatingTooltip, MediaQuery, Burger, Space, Header } from '@mantine/core';
import style from "./index.module.scss";
import { errorNotify, logout } from '../../js/utils';
import { BsPlusLg } from "react-icons/bs"
import { errorNotify, gatmainpath, logout } from '../../js/utils';
import { AiFillHome } from "react-icons/ai"
import { useNavigate, useParams } from 'react-router-dom';
import AddNewRegex from '../NFRegex/AddNewRegex';
import AddNewService from '../NFRegex/AddNewService';
import { useNavigate } from 'react-router-dom';
import { FaLock } from 'react-icons/fa';
import { MdOutlineSettingsBackupRestore } from 'react-icons/md';
import { ImExit } from 'react-icons/im';
@@ -14,7 +11,7 @@ import ResetPasswordModal from './ResetPasswordModal';
import ResetModal from './ResetModal';
function Header() {
function HeaderPage({navOpen, setNav, ...other}: { navOpen: boolean, setNav:React.Dispatch<React.SetStateAction<boolean>>}) {
const navigator = useNavigate()
@@ -26,19 +23,27 @@ function Header() {
})
}
const go_to_home = () => {
navigator(`/${gatmainpath()}`)
}
const [changePasswordModal, setChangePasswordModal] = useState(false);
const [resetFiregexModal, setResetFiregexModal] = useState(false);
const [tooltipAddOpened, setTooltipAddOpened] = useState(false);
const [tooltipHomeOpened, setTooltipHomeOpened] = useState(false);
const [tooltipLogoutOpened,setTooltipLogoutOpened] = useState(false);
const {srv} = useParams()
const [open, setOpen] = useState(false);
const closeModal = () => {setOpen(false);}
return <div id="header-page" className={style.header}>
<div style={{ width: 240, marginLeft: 'auto', marginRight: 'auto', padding:"40px", cursor: 'pointer' }}>
return <Header height={100} className={style.header} {...other}>
<Space w="lg" />
<MediaQuery largerThan="md" styles={{ display: 'none' }}><div>
<Burger
opened={navOpen}
className={style.navbtn}
onClick={() => setNav((o) => !o)}
size="sm"
mr="xl"
/>
</div></MediaQuery>
<div className={style.divlogo}>
<FloatingTooltip zIndex={0} label="Home" transition="pop" transitionDuration={200} openDelay={1000} transitionTimingFunction="ease" color="dark" position="right" >
<Image src="/header-logo.png" alt="Firegex logo" onClick={()=>navigator("/")}/>
</FloatingTooltip>
@@ -46,50 +51,34 @@ function Header() {
<div className="flex-spacer" />
<div style={{marginLeft:"20px"}}></div>
<Menu>
<Menu.Label>Firewall Access</Menu.Label>
<Menu.Item icon={<ImExit size={14} />} onClick={logout_action}>Logout</Menu.Item>
<Menu.Item color="red" icon={<FaLock size={14} />} onClick={() => setChangePasswordModal(true)}>Change Password</Menu.Item>
<Menu.Item 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>
<Tooltip zIndex={0} label="Home" position='bottom' transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color="teal" opened={tooltipHomeOpened} tooltipId="tooltip-home-id">
<Space w="md" />
<Tooltip label="Home" position='bottom' transition="pop" transitionDuration={200} transitionTimingFunction="ease" color="teal" opened={tooltipHomeOpened} tooltipId="tooltip-home-id">
<ActionIcon color="teal" style={{marginRight:"10px"}}
size="xl" radius="md" variant="filled"
onClick={()=>navigator("/")}
aria-describedby="tooltip-home-id"
onClick={go_to_home}
onFocus={() => setTooltipHomeOpened(false)} onBlur={() => setTooltipHomeOpened(false)}
onMouseEnter={() => setTooltipHomeOpened(true)} onMouseLeave={() => setTooltipHomeOpened(false)}>
<AiFillHome size="25px" />
</ActionIcon>
</Tooltip>
{ srv?
<Tooltip label="Add a new regex" zIndex={0} position='bottom' transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color="blue" opened={tooltipAddOpened} tooltipId="tooltip-add-id">
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
aria-describedby="tooltip-add-id"
onFocus={() => setTooltipAddOpened(false)} onBlur={() => setTooltipAddOpened(false)}
onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
</Tooltip>
: <Tooltip label="Add a new service" zIndex={0} position='bottom' transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color="blue" opened={tooltipAddOpened} tooltipId="tooltip-add-id">
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
aria-describedby="tooltip-add-id"
onFocus={() => setTooltipAddOpened(false)} onBlur={() => setTooltipAddOpened(false)}
onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
</Tooltip>
}
{srv?
<AddNewRegex opened={open} onClose={closeModal} service={srv} />:
<AddNewService opened={open} onClose={closeModal} />
}
<Tooltip label="Logout" position='bottom' transition="pop" transitionDuration={200} transitionTimingFunction="ease" color="blue" opened={tooltipLogoutOpened} tooltipId="tooltip-add-id">
<ActionIcon color="blue" onClick={logout_action} size="xl" radius="md" variant="filled"
onFocus={() => setTooltipLogoutOpened(false)} onBlur={() => setTooltipLogoutOpened(false)}
onMouseEnter={() => setTooltipLogoutOpened(true)} onMouseLeave={() => setTooltipLogoutOpened(false)}><ImExit size={23} style={{marginTop:"3px", marginLeft:"2px"}}/></ActionIcon>
</Tooltip>
<ResetPasswordModal opened={changePasswordModal} onClose={() => setChangePasswordModal(false)} />
<ResetModal opened={resetFiregexModal} onClose={() => setResetFiregexModal(false)} />
<div style={{marginLeft:"40px"}}></div>
</div>
<Space w="xl" />
</Header>
}
export default Header;
export default HeaderPage;

View File

@@ -1,23 +1,33 @@
import React from 'react';
import { Container, Space, Tabs } from '@mantine/core';
import Footer from './Footer';
import Header from './Header';
import React, { useState } from 'react';
import { Container, Space } from '@mantine/core';
import { AppShell } from '@mantine/core';
import NavBar from './NavBar';
import FooterPage from './Footer';
import HeaderPage from './Header';
function MainLayout({ children }:{ children:any }) {
const [opened, setOpened] = useState(false);
return <>
<Header/>
<Tabs grow variant="pills">
<Tabs.Tab label="Regex Prox"></Tabs.Tab>
<Tabs.Tab label="Port Hijacking"></Tabs.Tab>
<Tabs.Tab label="Netfilter regex"></Tabs.Tab>
</Tabs>
<Space h="xl" />
<Container size="lg" style={{minHeight:"57.5vh"}}>
{children}
</Container>
<Space h="xl" />
<Footer />
</>
<AppShell
padding="md"
fixed
navbar={<NavBar closeNav={()=>setOpened(false)} opened={opened} />}
header={<HeaderPage navOpen={opened} setNav={setOpened} />}
footer={<FooterPage />}
>
<Container size="lg">
{children}
</Container>
<Space h="lg" />
</AppShell>
</>
}
export default MainLayout;

View File

@@ -0,0 +1,101 @@
import { ServerResponse } from "../../js/models"
import { getapi, postapi } from "../../js/utils"
export type GeneralStats = {
services:number,
closed:number,
regexes:number
}
export type Service = {
name:string,
service_id:string,
status:string,
port:number,
proto: string,
ip_int: string,
n_packets:number,
n_regex:number,
}
export type ServiceAddForm = {
name:string,
port:number,
proto:string,
ip_int:string,
}
export type ServiceAddResponse = {
status: string,
service_id?: string,
}
export type RegexFilter = {
id:number,
service_id:string,
regex:string
is_blacklist:boolean,
is_case_sensitive:boolean,
mode:string //C S B => C->S S->C BOTH
n_packets:number,
active:boolean
}
export type RegexAddForm = {
service_id:string,
regex:string,
is_case_sensitive:boolean,
is_blacklist:boolean,
mode:string, // C->S S->C BOTH,
active: boolean
}
export const nfregex = {
stats: async () => {
return await getapi("nfregex/stats") as GeneralStats;
},
services: async () => {
return await getapi("nfregex/services") as Service[];
},
serviceinfo: async (service_id:string) => {
return await getapi(`nfregex/service/${service_id}`) as Service;
},
regexdelete: async (regex_id:number) => {
const { status } = await getapi(`nfregex/regex/${regex_id}/delete`) as ServerResponse;
return status === "ok"?undefined:status
},
regexenable: async (regex_id:number) => {
const { status } = await getapi(`nfregex/regex/${regex_id}/enable`) as ServerResponse;
return status === "ok"?undefined:status
},
regexdisable: async (regex_id:number) => {
const { status } = await getapi(`nfregex/regex/${regex_id}/disable`) as ServerResponse;
return status === "ok"?undefined:status
},
servicestart: async (service_id:string) => {
const { status } = await getapi(`nfregex/service/${service_id}/start`) as ServerResponse;
return status === "ok"?undefined:status
},
servicerename: async (service_id:string, name: string) => {
const { status } = await postapi(`nfregex/service/${service_id}/rename`,{ name }) as ServerResponse;
return status === "ok"?undefined:status
},
servicestop: async (service_id:string) => {
const { status } = await getapi(`nfregex/service/${service_id}/stop`) as ServerResponse;
return status === "ok"?undefined:status
},
servicesadd: async (data:ServiceAddForm) => {
return await postapi("nfregex/services/add",data) as ServiceAddResponse;
},
servicedelete: async (service_id:string) => {
const { status } = await getapi(`nfregex/service/${service_id}/delete`) as ServerResponse;
return status === "ok"?undefined:status
},
regexesadd: async (data:RegexAddForm) => {
const { status } = await postapi("nfregex/regexes/add",data) as ServerResponse;
return status === "ok"?undefined:status
},
serviceregexes: async (service_id:string) => {
return await getapi(`nfregex/service/${service_id}/regexes`) as RegexFilter[];
}
}

View File

@@ -0,0 +1,49 @@
import { Group, MantineColor, Navbar, ScrollArea, Text, ThemeIcon, Title, UnstyledButton } from "@mantine/core";
import React from "react";
import { IoMdGitNetwork } from "react-icons/io";
import { MdTransform } from "react-icons/md";
import { useNavigate } from "react-router-dom";
import { gatmainpath } from "../../js/utils";
import { GrDirections } from "react-icons/gr";
function NavBarButton({ navigate, closeNav, name, icon, color, disabled }:
{ navigate: string, closeNav: () => void, name:string, icon:any, color:MantineColor, disabled?:boolean }) {
const navigator = useNavigate()
return <UnstyledButton sx={(theme) => ({
display: 'block',
width: '100%',
padding: theme.spacing.xs,
borderRadius: theme.radius.sm,
opacity: disabled ? 0.4 : 1,
color: theme.colorScheme === 'dark' ? theme.colors.dark[0] : theme.black,
backgroundColor:(navigate===gatmainpath()?(theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0]):"transparent"),
'&:hover': {
backgroundColor:
theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0],
},
})} onClick={()=>{navigator(`/${navigate}`);closeNav()}} disabled={disabled}>
<Group>
<ThemeIcon color={color} variant="light">
{icon}
</ThemeIcon>
<Text size="sm">{name}</Text>
</Group>
</UnstyledButton>
}
export default function NavBar({ closeNav, opened }: {closeNav: () => void, opened: boolean}) {
return <Navbar p="md" hiddenBreakpoint="md" hidden={!opened} width={{ md: 300 }}>
<Navbar.Section px="xs" mt="xs">
<Title order={3}>[Fi]*regex 🔥</Title>
</Navbar.Section>
<hr style={{width:"100%"}}/>
<Navbar.Section grow component={ScrollArea} px="xs" mt="xs">
<NavBarButton navigate="nfregex" closeNav={closeNav} name="Netfilter Regex" color="blue" icon={<IoMdGitNetwork />} />
<NavBarButton navigate="regexproxy" closeNav={closeNav} name="TCP Proxy Regex Filter" color="lime" icon={<MdTransform />} />
<NavBarButton navigate="porthijack" closeNav={closeNav} name="Hijack Port to Proxy" color="red" icon={<GrDirections />} disabled/>
</Navbar.Section>
</Navbar>
}

View File

@@ -0,0 +1,124 @@
import { Button, Group, Space, TextInput, Notification, Switch, NativeSelect, Modal } from '@mantine/core';
import { useForm } from '@mantine/hooks';
import React, { useState } from 'react';
import { RegexAddForm } from '../js/models';
import { addregex, b64decode, b64encode, fireUpdateRequest, okNotify } from '../js/utils';
import { ImCross } from "react-icons/im"
import FilterTypeSelector from './FilterTypeSelector';
type RegexAddInfo = {
regex:string,
type:string,
mode:string,
is_case_insensitive:boolean,
deactive:boolean
}
function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=>void, service:string }) {
const form = useForm({
initialValues: {
regex:"",
type:"blacklist",
mode:"C -> S",
is_case_insensitive:false,
deactive:false
},
validationRules:{
regex: (value) => value !== "",
type: (value) => ["blacklist","whitelist"].includes(value),
mode: (value) => ['C -> S', 'S -> C', 'C <-> S'].includes(value)
}
})
const close = () =>{
onClose()
form.reset()
setError(null)
}
const [submitLoading, setSubmitLoading] = useState(false)
const [error, setError] = useState<string|null>(null)
const submitRequest = (values:RegexAddInfo) => {
setSubmitLoading(true)
const filter_mode = ({'C -> S':'C', 'S -> C':'S', 'C <-> S':'B'}[values.mode])
const request:RegexAddForm = {
is_blacklist:values.type !== "whitelist",
is_case_sensitive: !values.is_case_insensitive,
service_id: service,
mode: filter_mode?filter_mode:"B",
regex: b64encode(values.regex),
active: !values.deactive
}
setSubmitLoading(false)
addregex(request).then( res => {
if (!res){
setSubmitLoading(false)
close();
fireUpdateRequest();
okNotify(`Regex ${b64decode(request.regex)} has been added`, `Successfully added ${request.is_case_sensitive?"case sensitive":"case insensitive"} ${request.is_blacklist?"blacklist":"whitelist"} regex to ${request.service_id} service`)
}else if (res.toLowerCase() === "invalid regex"){
setSubmitLoading(false)
form.setFieldError("regex", "Invalid Regex")
}else{
setSubmitLoading(false)
setError("Error: [ "+res+" ]")
}
}).catch( err => {
setSubmitLoading(false)
setError("Request Failed! [ "+err+" ]")
})
}
return <Modal size="xl" title="Add a new regex filter" opened={opened} onClose={close} closeOnClickOutside={false} centered>
<form onSubmit={form.onSubmit(submitRequest)}>
<TextInput
label="Regex"
placeholder="[A-Z0-9]{31}="
{...form.getInputProps('regex')}
/>
<Space h="md" />
<Switch
label="Case insensitive"
{...form.getInputProps('is_case_insensitive', { type: 'checkbox' })}
/>
<Space h="md" />
<Switch
label="Deactivate"
{...form.getInputProps('deactive', { type: 'checkbox' })}
/>
<Space h="md" />
<NativeSelect
data={['C -> S', 'S -> C', 'C <-> S']}
label="Choose the source of the packets to filter"
variant="filled"
{...form.getInputProps('mode')}
/>
<Space h="md" />
<FilterTypeSelector
size="md"
color="gray"
{...form.getInputProps('type')}
/>
<Group position="right" mt="md">
<Button loading={submitLoading} type="submit">Add Filter</Button>
</Group>
<Space h="md" />
{error?<>
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error}
</Notification><Space h="md" /></>:null}
</form>
</Modal>
}
export default AddNewRegex;

View File

@@ -0,0 +1,120 @@
import { Button, Group, NumberInput, Space, TextInput, Notification, Modal, Switch } from '@mantine/core';
import { useForm } from '@mantine/hooks';
import React, { useState } from 'react';
import { addservice, fireUpdateRequest, okNotify, startservice } from '../js/utils';
import { ImCross } from "react-icons/im"
type ServiceAddForm = {
name:string,
port:number,
autostart: boolean,
chosenInternalPort: boolean,
internalPort: number
}
function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void }) {
const form = useForm({
initialValues: {
name:"",
port:8080,
internalPort:30001,
chosenInternalPort:false,
autostart: true
},
validationRules:{
name: (value) => value !== ""?true:false,
port: (value) => value>0 && value<65536,
internalPort: (value) => value>0 && value<65536,
}
})
const close = () =>{
onClose()
form.reset()
setError(null)
}
const [submitLoading, setSubmitLoading] = useState(false)
const [error, setError] = useState<string|null>(null)
const submitRequest = ({ name, port, autostart, chosenInternalPort, internalPort }:ServiceAddForm) =>{
setSubmitLoading(true)
addservice(chosenInternalPort?{ internalPort, name, port }:{ name, port }).then( res => {
if (res.status === "ok"){
setSubmitLoading(false)
close();
fireUpdateRequest();
if (autostart) startservice(res.id)
okNotify(`Service ${name} has been added`, `Successfully added ${res.id} with port ${port}`)
}else{
setSubmitLoading(false)
setError("Invalid request! [ "+res.status+" ]")
}
}).catch( err => {
setSubmitLoading(false)
setError("Request Failed! [ "+err+" ]")
})
}
return <Modal size="xl" title="Add a new service" opened={opened} onClose={close} closeOnClickOutside={false} centered>
<form onSubmit={form.onSubmit(submitRequest)}>
<TextInput
label="Service name"
placeholder="Challenge 01"
{...form.getInputProps('name')}
/>
<Space h="md" />
<NumberInput
placeholder="8080"
min={1}
max={65535}
label="Public Service port"
{...form.getInputProps('port')}
/>
{form.values.chosenInternalPort?<>
<Space h="md" />
<NumberInput
placeholder="8080"
min={1}
max={65535}
label="Internal Proxy Port"
{...form.getInputProps('internalPort')}
/>
<Space h="sm" />
</>:null}
<Space h="xl" />
<Switch
label="Auto-Start Service"
{...form.getInputProps('autostart', { type: 'checkbox' })}
/>
<Space h="md" />
<Switch
label="Choose internal port"
{...form.getInputProps('chosenInternalPort', { type: 'checkbox' })}
/>
<Group position="right" mt="md">
<Button loading={submitLoading} type="submit">Add Service</Button>
</Group>
<Space h="md" />
{error?<>
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error}
</Notification><Space h="md" /></>:null}
</form>
</Modal>
}
export default AddNewService;

View File

@@ -0,0 +1,13 @@
@use "../../vars" as *;
.box{
padding:30px;
margin:5px;
}
.regex_text{
padding: 10px;
background-color: $third_color;
border-radius: 15px;
}

View File

@@ -0,0 +1,114 @@
import { Grid, Text, Title, Badge, Space, ActionIcon, Tooltip } from '@mantine/core';
import React, { useState } from 'react';
import { RegexFilter } from '../../js/models';
import { activateregex, b64decode, deactivateregex, deleteregex, errorNotify, fireUpdateRequest, okNotify } from '../../js/utils';
import style from "./RegexView.module.scss";
import { BsTrashFill } from "react-icons/bs"
import YesNoModal from '../YesNoModal';
import FilterTypeSelector from '../FilterTypeSelector';
import { FaPause, FaPlay } from 'react-icons/fa';
function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
const mode_string = regexInfo.mode === "C"? "C -> S":
regexInfo.mode === "S"? "S -> C":
regexInfo.mode === "B"? "S <-> C": "🤔"
let regex_expr = b64decode(regexInfo.regex);
const [deleteModal, setDeleteModal] = useState(false);
const [deleteTooltipOpened, setDeleteTooltipOpened] = useState(false);
const [statusTooltipOpened, setStatusTooltipOpened] = useState(false);
const deleteRegex = () => {
deleteregex(regexInfo.id).then(res => {
if(!res){
okNotify(`Regex ${regex_expr} deleted successfully!`,`Regex '${regex_expr}' ID:${regexInfo.id} has been deleted!`)
fireUpdateRequest()
}else{
errorNotify(`Regex ${regex_expr} deleation failed!`,`Error: ${res}`)
}
}).catch( err => errorNotify(`Regex ${regex_expr} deleation failed!`,`Error: ${err}`))
}
const changeRegexStatus = () => {
(regexInfo.active?deactivateregex:activateregex)(regexInfo.id).then(res => {
if(!res){
okNotify(`Regex ${regex_expr} ${regexInfo.active?"deactivated":"activated"} successfully!`,`Regex '${regex_expr}' ID:${regexInfo.id} has been ${regexInfo.active?"deactivated":"activated"}!`)
fireUpdateRequest()
}else{
errorNotify(`Regex ${regex_expr} ${regexInfo.active?"deactivation":"activation"} failed!`,`Error: ${res}`)
}
}).catch( err => errorNotify(`Regex ${regex_expr} ${regexInfo.active?"deactivation":"activation"} failed!`,`Error: ${err}`))
}
return <div className={style.box}>
<Grid>
<Grid.Col span={2}>
<Title order={2} style={{color:"#FFF"}}>Regex:</Title>
</Grid.Col>
<Grid.Col span={8}>
<Text className={style.regex_text}> {regex_expr}</Text>
</Grid.Col>
<Grid.Col span={2} className='center-flex'>
<Space w="xs" />
<Tooltip label={regexInfo.active?"Deactivate":"Activate"} zIndex={0} transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color={regexInfo.active?"orange":"teal"} opened={statusTooltipOpened}>
<ActionIcon color={regexInfo.active?"orange":"teal"} onClick={changeRegexStatus} size="xl" radius="md" variant="filled"
onFocus={() => setStatusTooltipOpened(false)} onBlur={() => setStatusTooltipOpened(false)}
onMouseEnter={() => setStatusTooltipOpened(true)} onMouseLeave={() => setStatusTooltipOpened(false)}
>{regexInfo.active?<FaPause size="20px" />:<FaPlay size="20px" />}</ActionIcon>
</Tooltip>
<Space w="xs" />
<Tooltip label="Delete regex" zIndex={0} transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color="red" opened={deleteTooltipOpened} >
<ActionIcon color="red" onClick={()=>setDeleteModal(true)} size="xl" radius="md" variant="filled"
onFocus={() => setDeleteTooltipOpened(false)} onBlur={() => setDeleteTooltipOpened(false)}
onMouseEnter={() => setDeleteTooltipOpened(true)} onMouseLeave={() => setDeleteTooltipOpened(false)}
><BsTrashFill size={22} /></ActionIcon>
</Tooltip>
</Grid.Col>
<Grid.Col span={2} />
<Grid.Col className='center-flex-row' span={4}>
<Space h="xs" />
<FilterTypeSelector
size="md"
color="gray"
disabled
value={regexInfo.is_blacklist?"blacklist":"whitelist"}
/>
<Space h="md" />
<div className='center-flex'>
<Badge size="md" color="cyan" variant="filled">Service: {regexInfo.service_id}</Badge>
<Space w="xs" />
<Badge size="md" color={regexInfo.active?"lime":"red"} variant="filled">{regexInfo.active?"ACTIVE":"DISABLED"}</Badge>
<Space w="xs" />
<Badge size="md" color="gray" variant="filled">ID: {regexInfo.id}</Badge>
</div>
</Grid.Col>
<Grid.Col style={{width:"100%"}} span={6}>
<Space h="xs" />
<div className='center-flex-row'>
<Badge size="md" color={regexInfo.is_case_sensitive?"grape":"pink"} variant="filled">Case: {regexInfo.is_case_sensitive?"SENSIIVE":"INSENSITIVE"}</Badge>
<Space h="xs" />
<Badge size="md" color="yellow" variant="filled">Packets filtered: {regexInfo.n_packets}</Badge>
<Space h="xs" />
<Badge size="md" color="blue" variant="filled">Mode: {mode_string}</Badge>
</div>
</Grid.Col>
</Grid>
<YesNoModal
title='Are you sure to delete this regex?'
description={`You are going to delete the regex '${regex_expr}'.`}
onClose={()=>setDeleteModal(false)}
action={deleteRegex}
opened={deleteModal}
/>
</div>
}
export default RegexView;

View File

@@ -0,0 +1,107 @@
import { Button, Group, NumberInput, Space, Notification, Modal, Center, Title } from '@mantine/core';
import { useForm } from '@mantine/hooks';
import React, { useEffect, useState } from 'react';
import { changeports, fireUpdateRequest, okNotify } from '../../js/utils';
import { ImCross } from "react-icons/im"
import { Service } from '../../js/models';
import { FaLongArrowAltDown } from 'react-icons/fa';
type InputForm = {
internalPort:number,
port:number
}
function ChangePortModal({ service, opened, onClose }:{ service:Service, opened:boolean, onClose:()=>void }) {
const form = useForm({
initialValues: {
internalPort: service.internal_port,
port: service.public_port
},
validationRules:{
internalPort: (value) => value>0 && value<65536,
port: (value) => value>0 && value<65536
}
})
useEffect(()=>{
form.setValues({internalPort: service.internal_port, port:service.public_port})
},[opened])
const close = () =>{
onClose()
form.reset()
setError(null)
}
const [submitLoading, setSubmitLoading] = useState(false)
const [error, setError] = useState<string|null>(null)
const submitRequest = (data:InputForm) =>{
setSubmitLoading(true)
changeports(service.id, data).then( res => {
if (!res){
setSubmitLoading(false)
close();
fireUpdateRequest();
okNotify(`Internal port on ${service.name} service has changed in ${data.internalPort}`, `Successfully changed internal port of service with id ${service.id}`)
}else{
setSubmitLoading(false)
setError("Invalid request! [ "+res+" ]")
}
}).catch( err => {
setSubmitLoading(false)
setError("Request Failed! [ "+err+" ]")
})
}
return <Modal size="xl" title="Change Ports" opened={opened} onClose={close} closeOnClickOutside={false} centered>
<form onSubmit={form.onSubmit(submitRequest)}>
<NumberInput
placeholder="30001"
min={1}
max={65535}
label="Internal Proxy Port"
{...form.getInputProps('internalPort')}
/>
<Space h="xl" />
<Center><FaLongArrowAltDown size={50}/></Center>
<NumberInput
placeholder="8080"
min={1}
max={65535}
label="Public Service Port"
{...form.getInputProps('port')}
/>
<Space h="xl" />
<Center><Title order={5}>The change of the ports will cause a temporarily shutdown of the service! </Title></Center>
<Space h="md" />
<Group position="right" mt="md">
<Button loading={submitLoading} disabled={
service.internal_port === form.values.internalPort && service.public_port === form.values.port
} type="submit">Change Port</Button>
</Group>
<Space h="md" />
{error?<>
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error}
</Notification><Space h="md" /></>:null}
</form>
</Modal>
}
export default ChangePortModal;

View File

@@ -0,0 +1,69 @@
import { Button, Group, Space, TextInput, Notification, Modal } from '@mantine/core';
import { useForm } from '@mantine/hooks';
import React, { useEffect, useState } from 'react';
import { fireUpdateRequest, okNotify, renameservice } from '../../js/utils';
import { ImCross } from "react-icons/im"
import { Service } from '../../js/models';
function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>void, service:Service }) {
const form = useForm({
initialValues: { name:service.name },
validationRules:{ name: (value) => value !== "" }
})
const close = () =>{
onClose()
form.reset()
setError(null)
}
useEffect(()=> form.setFieldValue("name", service.name),[opened])
const [submitLoading, setSubmitLoading] = useState(false)
const [error, setError] = useState<string|null>(null)
const submitRequest = ({ name }:{ name:string }) => {
setSubmitLoading(true)
renameservice(service.id, name).then( res => {
if (!res){
setSubmitLoading(false)
close();
fireUpdateRequest();
okNotify(`Service ${service.name} has been renamed in ${ name }`, `Successfully renamed service on port ${service.public_port}`)
}else{
setSubmitLoading(false)
setError("Error: [ "+res+" ]")
}
}).catch( err => {
setSubmitLoading(false)
setError("Request Failed! [ "+err+" ]")
})
}
return <Modal size="xl" title={`Rename '${service.name}' service on port ${service.public_port}`} opened={opened} onClose={close} closeOnClickOutside={false} centered>
<form onSubmit={form.onSubmit(submitRequest)}>
<TextInput
label="Service Name"
placeholder="Awesome Service Name!"
{...form.getInputProps('name')}
/>
<Group position="right" mt="md">
<Button loading={submitLoading} type="submit">Rename</Button>
</Group>
<Space h="md" />
{error?<>
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error}
</Notification><Space h="md" /></>:null}
</form>
</Modal>
}
export default RenameForm;

View File

@@ -0,0 +1,18 @@
@use "../../index.scss" as *;
.row{
width: 95%;
padding: 30px 0px;
border-radius: 20px;
margin: 10px;
@extend .center-flex;
}
.name{
font-size: 2.3em;
font-weight: bolder;
margin-right: 10px;
margin-bottom: 13px;
color:#FFF;
}

View File

@@ -0,0 +1,234 @@
import { ActionIcon, Badge, Divider, Grid, MediaQuery, Menu, Space, Title, Tooltip } from '@mantine/core';
import React, { useState } from 'react';
import { FaPause, FaPlay, FaStop } from 'react-icons/fa';
import { Service } from '../../js/models';
import { MdOutlineArrowForwardIos } from "react-icons/md"
import style from "./ServiceRow.module.scss";
import YesNoModal from '../YesNoModal';
import { deleteservice, errorNotify, fireUpdateRequest, okNotify, pauseservice, regenport, startservice, stopservice } from '../../js/utils';
import { BsArrowRepeat, BsTrashFill } from 'react-icons/bs';
import { TbNumbers } from 'react-icons/tb';
import { BiRename } from 'react-icons/bi'
import ChangePortModal from './ChangePortModal';
import RenameForm from './RenameForm';
//"status":"stop"/"wait"/"active"/"pause",
function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void }) {
let status_color = "gray";
switch(service.status){
case "stop": status_color = "red"; break;
case "wait": status_color = "yellow"; break;
case "active": status_color = "teal"; break;
case "pause": status_color = "cyan"; break;
}
const [stopModal, setStopModal] = useState(false);
const [buttonLoading, setButtonLoading] = useState(false)
const [tooltipStopOpened, setTooltipStopOpened] = useState(false);
const [deleteModal, setDeleteModal] = useState(false)
const [changePortModal, setChangePortModal] = useState(false)
const [choosePortModal, setChoosePortModal] = useState(false)
const [renameModal, setRenameModal] = useState(false)
const stopService = async () => {
setButtonLoading(true)
await stopservice(service.id).then(res => {
if(!res){
okNotify(`Service ${service.id} stopped successfully!`,`The service ${service.name} has been stopped!`)
fireUpdateRequest();
}else{
errorNotify(`An error as occurred during the stopping of the service ${service.id}`,`Error: ${res}`)
}
}).catch(err => {
errorNotify(`An error as occurred during the stopping of the service ${service.id}`,`Error: ${err}`)
})
setButtonLoading(false);
}
const startService = async () => {
setButtonLoading(true)
await startservice(service.id).then(res => {
if(!res){
okNotify(`Service ${service.id} started successfully!`,`The service ${service.name} has been started!`)
fireUpdateRequest();
}else{
errorNotify(`An error as occurred during the starting of the service ${service.id}`,`Error: ${res}`)
}
}).catch(err => {
errorNotify(`An error as occurred during the starting of the service ${service.id}`,`Error: ${err}`)
})
setButtonLoading(false)
}
const pauseService = async () => {
setButtonLoading(true)
await pauseservice(service.id).then(res => {
if(!res){
okNotify(`Service ${service.id} paused successfully!`,`The service ${service.name} has been paused (Transparent mode)!`)
fireUpdateRequest();
}else{
errorNotify(`An error as occurred during the pausing of the service ${service.id}`,`Error: ${res}`)
}
}).catch(err => {
errorNotify(`An error as occurred during the pausing of the service ${service.id}`,`Error: ${err}`)
})
setButtonLoading(false)
}
const deleteService = () => {
deleteservice(service.id).then(res => {
if (!res){
okNotify("Service delete complete!",`The service ${service.id} has been deleted!`)
fireUpdateRequest();
}else
errorNotify("An error occurred while deleting a service",`Error: ${res}`)
}).catch(err => {
errorNotify("An error occurred while deleting a service",`Error: ${err}`)
})
}
const changePort = () => {
regenport(service.id).then(res => {
if (!res){
okNotify("Service port regeneration completed!",`The service ${service.id} has changed the internal port!`)
fireUpdateRequest();
}else
errorNotify("An error occurred while changing the internal service port",`Error: ${res}`)
}).catch(err => {
errorNotify("An error occurred while changing the internal service port",`Error: ${err}`)
})
}
return <>
<Grid className={style.row} justify="flex-end" style={{width:"100%"}}>
<Grid.Col md={4} xs={12}>
<MediaQuery smallerThan="md" styles={{ display: 'none' }}><div>
<div className="center-flex-row">
<div className="center-flex"><Title className={style.name}>{service.name}</Title> <Badge size="xl" gradient={{ from: 'indigo', to: 'cyan' }} variant="gradient">:{service.public_port}</Badge></div>
<Badge color={status_color} size="xl" radius="md">{service.internal_port} {"->"} {service.public_port}</Badge>
</div>
</div></MediaQuery>
<MediaQuery largerThan="md" styles={{ display: 'none' }}><div>
<div className="center-flex">
<div className="center-flex"><Title className={style.name}>{service.name}</Title> <Badge size="xl" gradient={{ from: 'indigo', to: 'cyan' }} variant="gradient">:{service.public_port}</Badge></div>
<Space w="xl" />
<Badge color={status_color} size="xl" radius="md">{service.internal_port} {"->"} {service.public_port}</Badge>
</div>
</div></MediaQuery>
<MediaQuery largerThan="md" styles={{ display: 'none' }}>
<Space h="xl" />
</MediaQuery>
</Grid.Col>
<Grid.Col className="center-flex" md={8} xs={12}>
<MediaQuery smallerThan="md" styles={{ display: 'none' }}>
<div className='flex-spacer' />
</MediaQuery>
<MediaQuery largerThan="md" styles={{ display: 'none' }}>
<><Space w="xl" /><Space w="xl" /></>
</MediaQuery>
<div className="center-flex-row">
<Badge style={{marginBottom:"20px"}} color={status_color} radius="sm" size="xl" variant="filled">Status: <u>{service.status}</u></Badge>
<Badge style={{marginBottom:"8px"}}color="violet" radius="sm" size="lg" variant="filled">Regex: {service.n_regex}</Badge>
<Badge color="yellow" radius="sm" size="lg" variant="filled">Connections Blocked: {service.n_packets}</Badge>
</div>
<MediaQuery largerThan="md" styles={{ display: 'none' }}>
<div className='flex-spacer' />
</MediaQuery>
<MediaQuery smallerThan="md" styles={{ display: 'none' }}>
<><Space w="xl" /><Space w="xl" /></>
</MediaQuery>
<div className="center-flex">
<Menu>
<Menu.Label><b>Rename service</b></Menu.Label>
<Menu.Item icon={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
<Divider />
<Menu.Label><b>Change ports</b></Menu.Label>
<Menu.Item icon={<TbNumbers size={18} />} onClick={()=>setChoosePortModal(true)}>Change port</Menu.Item>
<Menu.Item icon={<BsArrowRepeat size={18} />} onClick={()=>setChangePortModal(true)}>Regen proxy port</Menu.Item>
<Divider />
<Menu.Label><b>Danger zone</b></Menu.Label>
<Menu.Item color="red" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
</Menu>
<Space w="md"/>
{["pause","wait"].includes(service.status)?
<Tooltip label="Stop service" zIndex={0} transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color="orange" opened={tooltipStopOpened} tooltipId="tooltip-stop-id">
<ActionIcon color="yellow" loading={buttonLoading}
onClick={()=>setStopModal(true)} size="xl" radius="md" variant="filled"
disabled={service.status === "stop"}
aria-describedby="tooltip-stop-id"
onFocus={() => setTooltipStopOpened(false)} onBlur={() => setTooltipStopOpened(false)}
onMouseEnter={() => setTooltipStopOpened(true)} onMouseLeave={() => setTooltipStopOpened(false)}>
<FaStop size="20px" />
</ActionIcon>
</Tooltip>:
<Tooltip label={service.status === "stop"?"Start in pause mode":"Pause service"} zIndex={0} transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color={service.status === "stop"?"cyan":"red"}>
<ActionIcon color={service.status === "stop"?"cyan":"red"} loading={buttonLoading}
onClick={pauseService} size="xl" radius="md" variant="filled"
/*disabled={service.status === "stop"}*/>
<FaPause size="20px" />
</ActionIcon>
</Tooltip>
}
<Space w="md"/>
<Tooltip label="Start service" transition="pop" zIndex={0} transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color="teal">
<ActionIcon color="teal" size="xl" radius="md" onClick={startService} loading={buttonLoading}
variant="filled" disabled={!["stop","pause"].includes(service.status)?true:false}>
<FaPlay size="20px" />
</ActionIcon>
</Tooltip>
</div>
<Space w="xl" /><Space w="xl" />
{onClick?<div>
<MdOutlineArrowForwardIos onClick={onClick} style={{cursor:"pointer"}} size={45} />
<Space w="xl" />
</div>:null}
<MediaQuery largerThan="md" styles={{ display: 'none' }}>
<><Space w="xl" /><Space w="xl" /></>
</MediaQuery>
</Grid.Col>
</Grid>
<YesNoModal
title='Are you sure to stop this service?'
description={`You are going to delete the service '${service.id}', causing the firewall to stop. This will cause the shutdown of your service! ⚠️`}
onClose={()=>{setStopModal(false);}}
action={stopService}
opened={stopModal}
/>
<hr style={{width:"100%"}}/>
<YesNoModal
title='Are you sure to delete this service?'
description={`You are going to delete the service '${service.id}', causing the stopping of the firewall and deleting all the regex associated. This will cause the shutdown of your service! ⚠️`}
onClose={()=>setDeleteModal(false) }
action={deleteService}
opened={deleteModal}
/>
<YesNoModal
title='Are you sure to change the proxy internal port?'
description={`You are going to change the proxy port '${service.internal_port}'. This will cause the shutdown of your service temporarily! ⚠️`}
onClose={()=>setChangePortModal(false)}
action={changePort}
opened={changePortModal}
/>
<ChangePortModal
service={service}
onClose={()=> setChoosePortModal(false)}
opened={choosePortModal}
/>
<RenameForm
onClose={()=>setRenameModal(false)}
opened={renameModal}
service={service}
/>
</>
}
export default ServiceRow;

View File

@@ -0,0 +1,116 @@
import { ServerResponse } from "../../js/models"
import { getapi, postapi } from "../../js/utils"
export type GeneralStats = {
services:number,
closed:number,
regexes:number
}
export type Service = {
id:string,
name:string,
status:string,
public_port:number,
internal_port:number,
n_packets:number,
n_regex:number,
}
export type ServiceAddForm = {
name:string,
port:number,
internalPort?:number
}
export type ServerResponseWithID = {
status:string,
id:string
}
export type ChangePort = {
port?: number,
internalPort?: number
}
export type RegexFilter = {
id:number,
service_id:string,
regex:string
is_blacklist:boolean,
is_case_sensitive:boolean,
mode:string //C S B => C->S S->C BOTH
n_packets:number,
active:boolean
}
export type RegexAddForm = {
service_id:string,
regex:string,
is_case_sensitive:boolean,
is_blacklist:boolean,
mode:string, // C->S S->C BOTH,
active: boolean
}
export const regexproxy = {
stats: async () => {
return await getapi("regexproxy/stats") as GeneralStats;
},
services: async() => {
return await getapi("regexproxy/services") as Service[];
},
serviceinfo: async (service_id:string) => {
return await getapi(`regexproxy/service/${service_id}`) as Service;
},
regexdelete: async (regex_id:number) => {
const { status } = await getapi(`regexproxy/regex/${regex_id}/delete`) as ServerResponse;
return status === "ok"?undefined:status
},
regexenable: async (regex_id:number) => {
const { status } = await getapi(`regexproxy/regex/${regex_id}/enable`) as ServerResponse;
return status === "ok"?undefined:status
},
regexdisable: async (regex_id:number) => {
const { status } = await getapi(`regexproxy/regex/${regex_id}/disable`) as ServerResponse;
return status === "ok"?undefined:status
},
servicestart: async (service_id:string) => {
const { status } = await getapi(`regexproxy/service/${service_id}/start`) as ServerResponse;
return status === "ok"?undefined:status
},
servicestop: async (service_id:string) => {
const { status } = await getapi(`regexproxy/service/${service_id}/stop`) as ServerResponse;
return status === "ok"?undefined:status
},
servicepause: async (service_id:string) => {
const { status } = await getapi(`regexproxy/service/${service_id}/pause`) as ServerResponse;
return status === "ok"?undefined:status
},
serviceregenport: async (service_id:string) => {
const { status } = await getapi(`regexproxy/service/${service_id}/regen-port`) as ServerResponse;
return status === "ok"?undefined:status
},
servicechangeport: async (service_id:string, data:ChangePort) => {
const { status } = await postapi(`regexproxy/service/${service_id}/change-ports`,data) as ServerResponse;
return status === "ok"?undefined:status
},
servicesadd: async (data:ServiceAddForm) => {
return await postapi("regexproxy/services/add",data) as ServerResponseWithID;
},
servicerename: async (service_id:string, name: string) => {
const { status } = await postapi(`regexproxy/service/${service_id}/rename`,{ name }) as ServerResponse;
return status === "ok"?undefined:status
},
servicedelete: async (service_id:string) => {
const { status } = await getapi(`regexproxy/service/${service_id}/delete`) as ServerResponse;
return status === "ok"?undefined:status
},
regexesadd: async (data:RegexAddForm) => {
const { status } = await postapi("regexproxy/regexes/add",data) as ServerResponse;
return status === "ok"?undefined:status
},
serviceregexes: async (service_id:string) => {
return await getapi(`regexproxy/service/${service_id}/regexes`) as RegexFilter[];
}
}