Firewall refactor

This commit is contained in:
Domingo Dirutigliano
2023-09-28 20:45:58 +02:00
parent 99e4989cfe
commit 71edfc29c4
12 changed files with 212 additions and 166 deletions

View File

@@ -8,11 +8,15 @@ export const ModeSelector = (props:Omit<SegmentedControlProps, "data">) => (
data={[
{
value: RuleMode.IN,
label: 'Inbound',
label: 'IN',
},
{
value: RuleMode.FORWARD,
label: 'FWD',
},
{
value: RuleMode.OUT,
label: 'Outbound',
label: 'OUT',
}
]}
size={props.size?props.size:"xs"}

View File

@@ -13,6 +13,10 @@ export const ProtocolSelector = (props:Omit<SegmentedControlProps, "data">) => (
value: Protocol.UDP,
label: 'UDP',
},
{
value: Protocol.BOTH,
label: 'BOTH',
},
{
value: Protocol.ANY,
label: 'ANY',

View File

@@ -5,6 +5,7 @@ import { getapi, postapi } from "../../js/utils"
export enum Protocol {
TCP = "tcp",
UDP = "udp",
BOTH = "both",
ANY = "any"
}
@@ -15,16 +16,17 @@ export enum ActionType {
}
export enum RuleMode {
OUT = "O",
IN = "I",
OUT = "out",
IN = "in",
FORWARD = "forward"
}
export type Rule = {
active: boolean
name:string,
proto: Protocol,
ip_src: string,
ip_dst: string,
src: string,
dst: string,
port_src_from: number,
port_dst_from: number,
port_src_to: number,
@@ -48,6 +50,7 @@ export type FirewallSettings = {
keep_rules: boolean,
allow_loopback: boolean,
allow_established: boolean,
allow_icmp: boolean
}
@@ -74,16 +77,6 @@ export const firewall = {
disable: async() => {
return await getapi("firewall/disable") as ServerResponse;
},
rulenable: async (rule_id:number) => {
return await getapi(`firewall/rule/${rule_id}/enable`) as ServerResponse;
},
ruledisable: async (rule_id:number) => {
return await getapi(`firewall/rule/${rule_id}/disable`) as ServerResponse;
},
rulerename: async (rule_id:number, name: string) => {
const { status } = await postapi(`firewall/rule/${rule_id}/rename`,{ name }) as ServerResponse;
return status === "ok"?undefined:status
},
ruleset: async (data:RuleAddForm) => {
return await postapi("firewall/rules/set", data) as ServerResponseListed;
}

View File

@@ -14,19 +14,26 @@ interface ItemProps extends AutocompleteItem {
}
interface InterfaceInputProps extends Omit<SelectProps, "data">{
initialCustomInterfaces?:AutocompleteItem[]
initialCustomInterfaces?:AutocompleteItem[],
includeInterfaceNames?:boolean
}
export const InterfaceInput = (props:InterfaceInputProps) => {
const { initialCustomInterfaces, ...propeties } = props
export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames, ...props }:InterfaceInputProps) => {
const [customIpInterfaces, setCustomIpInterfaces] = useState<AutocompleteItem[]>(initialCustomInterfaces??[]);
const interfacesQuery = ipInterfacesQuery()
const interfaces = (!interfacesQuery.isLoading?
(interfacesQuery.data!.map(item => ({netint:item.name, value:item.addr, label:item.addr})) as AutocompleteItem[]):
[])
const getInterfaces = () => {
if (interfacesQuery.isLoading || !interfacesQuery.data) return []
if(includeInterfaceNames){
const result = interfacesQuery.data.map(item => ({netint:"IP", value:item.addr, label:item.addr})) as AutocompleteItem[]
interfacesQuery.data.map(item => item.name).filter((item, index, arr) => arr.indexOf(item) === index).forEach(item => result.push({netint:"INT", value:item, label:item}))
return result
}
return (interfacesQuery.data.map(item => ({netint:item.name, value:item.addr, label:item.addr})) as AutocompleteItem[])
}
const interfaces = getInterfaces()
return <Select
placeholder="10.1.1.1"
@@ -43,6 +50,6 @@ export const InterfaceInput = (props:InterfaceInputProps) => {
return item;
}}
style={props.style?{width:"100%", ...props.style}:{width:"100%"}}
{...propeties}
{...props}
/>
}

View File

@@ -5,7 +5,7 @@ import { FirewallSettings, firewall } from '../../components/Firewall/utils';
export function SettingsModal({ opened, onClose }:{ opened:boolean, onClose:()=>void }) {
const [settings, setSettings] = useState<FirewallSettings>({keep_rules:false, allow_established:true, allow_loopback:true})
const [settings, setSettings] = useState<FirewallSettings>({keep_rules:false, allow_established:true, allow_loopback:true, allow_icmp:true})
useEffect(()=>{
firewall.settings().then( res => {
@@ -39,6 +39,7 @@ export function SettingsModal({ opened, onClose }:{ opened:boolean, onClose:()=>
<Space h="md" />
<Switch label="Allow established connection (essential to allow opening connection) (Dangerous to disable)" checked={settings.allow_established} onChange={v => setSettings({...settings, allow_established:v.target.checked})}/>
<Space h="md" />
<Switch label="Allow icmp packets" checked={settings.allow_icmp} onChange={v => setSettings({...settings, allow_icmp:v.target.checked})}/>
<Group position="right" mt="md">
<Button loading={submitLoading} onClick={submitRequest}>Save Setting</Button>

View File

@@ -106,8 +106,8 @@ export const Firewall = () => {
active: true,
name: "Rule name",
proto: Protocol.TCP,
ip_src: "any",
ip_dst: "any",
src: "",
dst: "",
port_src_from: 1,
port_dst_from: 8080,
port_src_to: 65535,
@@ -140,18 +140,15 @@ export const Firewall = () => {
{(provided, snapshot) => {
const customInt = [
{ value: "0.0.0.0/0", netint: "ANY IPv4", label: "0.0.0.0/0" },
{ value: "::/0", netint: "ANY IPv6", label: "::/0" }
{ value: "::/0", netint: "ANY IPv6", label: "::/0" },
{ value: "", netint: "ANY", label: "ANY" }
]
const src_custom_int = customInt.map(v => v.value).includes(item.ip_src) || item.ip_dst == "any"?[]:[{ value: item.ip_src, netint: "SELECTED", label: item.ip_src }]
const dst_custom_int = customInt.map(v => v.value).includes(item.ip_dst) || item.ip_dst == "any"?[]:[{ value: item.ip_dst, netint: "SELECTED", label: item.ip_dst }]
const src_custom_int = customInt.map(v => v.value).includes(item.src)?[]:[{ value: item.src, netint: "SELECTED", label: item.src }]
const dst_custom_int = customInt.map(v => v.value).includes(item.dst)?[]:[{ value: item.dst, netint: "SELECTED", label: item.dst }]
const [srcPortEnabled, setSrcPortEnabled] = useState(item.port_src_from != 1 || item.port_src_to != 65535)
const [dstPortEnabled, setDstPortEnabled] = useState(item.port_dst_from != 1 || item.port_dst_to != 65535)
const [srcPortValue, setSrcPortValue] = useState(item.port_src_from==item.port_src_to?`${item.port_src_from}`:`${item.port_src_from}-${item.port_src_to}`)
const [dstPortValue, setDstPortValue] = useState(item.port_dst_from==item.port_dst_to?`${item.port_dst_from}`:`${item.port_dst_from}-${item.port_dst_to}`)
const [ipFilteringEnabled, setIpFilteringEnabled] = useState(!(item.ip_dst == "any" || item.ip_src == "any"))
const [srcIp, setSrcIp] = useState(item.ip_src!="any"?item.ip_src:"")
const [dstIp, setDstIp] = useState(item.ip_dst!="any"?item.ip_dst:"")
const port_range_setter = (rule:Rule, v:string, {src=false, dst=false}:{src?:boolean, dst?:boolean}) => {
const elements = v.split("-")
@@ -173,39 +170,23 @@ export const Firewall = () => {
const ip_setter = (rule:Rule, v:string|null, {src=false, dst=false}:{src?:boolean, dst?:boolean}) => {
const values = v?v:""
if (src){
rule.ip_src = values
setSrcIp(values)
rule.src = values
}
if (dst){
rule.ip_dst = values
setDstIp(values)
rule.dst = values
}
updateMe()
}
const set_filtering_ip = (value:boolean) => {
if (!value){
item.ip_src = "any"
item.ip_dst = "any"
}else{
item.ip_src = srcIp
item.ip_dst = dstIp
}
setIpFilteringEnabled(value)
updateMe()
}
const disable_style = { opacity:"0.4", cursor:"not-allowed" }
const proto_any = item.proto == Protocol.ANY
const disabletab = {
ips:!ipFilteringEnabled,
port_box: proto_any,
src_port: !srcPortEnabled || proto_any,
dst_port: !dstPortEnabled || proto_any
}
const additionalStyle = {
ips:disabletab.ips?disable_style:{},
port_box: disabletab.port_box?disable_style:{},
src_port: disabletab.src_port?disable_style:{},
dst_port: disabletab.dst_port?disable_style:{}
@@ -254,11 +235,9 @@ export const Firewall = () => {
<div style={{width:"100%"}}>
<InterfaceInput
initialCustomInterfaces={[...src_custom_int, ...customInt]}
value={srcIp}
value={item.src}
onChange={v => ip_setter(item, v, {src:true})}
disabled={disabletab.ips}
error={!disabletab.ips && !srcIp}
style={additionalStyle.ips}
includeInterfaceNames
/>
<Space h="sm" />
<div className="center-flex" style={{width:"100%"}}>
@@ -289,11 +268,9 @@ export const Firewall = () => {
<div style={{width:"100%"}}>
<InterfaceInput
initialCustomInterfaces={[...dst_custom_int, ...customInt]}
defaultValue={dstIp}
defaultValue={item.dst}
onChange={v => ip_setter(item, v, {dst:true})}
disabled={disabletab.ips}
error={!disabletab.ips && !dstIp}
style={additionalStyle.ips}
includeInterfaceNames
/>
<Space h="sm" />
<div className="center-flex" style={{width:"100%"}}>
@@ -338,14 +315,6 @@ export const Firewall = () => {
onChange={(value)=>{item.proto = value as Protocol;updateMe()}}
style={{width:"100%"}}
/>
<Space h="xs" />
<Button
size="xs" variant="light"
color={ipFilteringEnabled?"lime":"red"}
onClick={()=>set_filtering_ip(!ipFilteringEnabled)}
style={{width:"100%"}}
>{ipFilteringEnabled?"IP Filtering ON":"IP Filtering OFF"}</Button>
</div>
</div>
<Space h="md" />