feat: complete rules with all shorewall columns (origdest, rate, user, mark, connlimit, time, headers, switch, helper)
This commit is contained in:
35
backend/alembic/versions/0009_rules_add_missing_columns.py
Normal file
35
backend/alembic/versions/0009_rules_add_missing_columns.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
"""add missing shorewall rule columns
|
||||||
|
|
||||||
|
Revision ID: 0009
|
||||||
|
Revises: 0008
|
||||||
|
Create Date: 2026-03-01
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
revision = "0009"
|
||||||
|
down_revision = "0008"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
_NEW_COLS = [
|
||||||
|
("origdest", sa.String(128)),
|
||||||
|
("rate_limit", sa.String(64)),
|
||||||
|
("user_group", sa.String(64)),
|
||||||
|
("mark", sa.String(32)),
|
||||||
|
("connlimit", sa.String(32)),
|
||||||
|
("time", sa.String(128)),
|
||||||
|
("headers", sa.String(128)),
|
||||||
|
("switch_name", sa.String(32)),
|
||||||
|
("helper", sa.String(32)),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
for col_name, col_type in _NEW_COLS:
|
||||||
|
op.add_column("rules", sa.Column(col_name, col_type, server_default="''", nullable=False))
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
for col_name, _ in reversed(_NEW_COLS):
|
||||||
|
op.drop_column("rules", col_name)
|
||||||
@@ -99,6 +99,15 @@ class Rule(Base):
|
|||||||
proto: Mapped[str] = mapped_column(String(16), default="")
|
proto: Mapped[str] = mapped_column(String(16), default="")
|
||||||
dport: Mapped[str] = mapped_column(String(64), default="")
|
dport: Mapped[str] = mapped_column(String(64), default="")
|
||||||
sport: Mapped[str] = mapped_column(String(64), default="")
|
sport: Mapped[str] = mapped_column(String(64), default="")
|
||||||
|
origdest: Mapped[str] = mapped_column(String(128), default="")
|
||||||
|
rate_limit: Mapped[str] = mapped_column(String(64), default="")
|
||||||
|
user_group: Mapped[str] = mapped_column(String(64), default="")
|
||||||
|
mark: Mapped[str] = mapped_column(String(32), default="")
|
||||||
|
connlimit: Mapped[str] = mapped_column(String(32), default="")
|
||||||
|
time: Mapped[str] = mapped_column(String(128), default="")
|
||||||
|
headers: Mapped[str] = mapped_column(String(128), default="")
|
||||||
|
switch_name: Mapped[str] = mapped_column(String(32), default="")
|
||||||
|
helper: Mapped[str] = mapped_column(String(32), default="")
|
||||||
comment: Mapped[str] = mapped_column(Text, default="")
|
comment: Mapped[str] = mapped_column(Text, default="")
|
||||||
position: Mapped[int] = mapped_column(Integer, default=0)
|
position: Mapped[int] = mapped_column(Integer, default=0)
|
||||||
|
|
||||||
|
|||||||
@@ -135,6 +135,15 @@ class RuleCreate(BaseModel):
|
|||||||
proto: str = ""
|
proto: str = ""
|
||||||
dport: str = ""
|
dport: str = ""
|
||||||
sport: str = ""
|
sport: str = ""
|
||||||
|
origdest: str = ""
|
||||||
|
rate_limit: str = ""
|
||||||
|
user_group: str = ""
|
||||||
|
mark: str = ""
|
||||||
|
connlimit: str = ""
|
||||||
|
time: str = ""
|
||||||
|
headers: str = ""
|
||||||
|
switch_name: str = ""
|
||||||
|
helper: str = ""
|
||||||
comment: str = ""
|
comment: str = ""
|
||||||
position: int = 0
|
position: int = 0
|
||||||
|
|
||||||
@@ -148,6 +157,15 @@ class RuleUpdate(BaseModel):
|
|||||||
proto: Optional[str] = None
|
proto: Optional[str] = None
|
||||||
dport: Optional[str] = None
|
dport: Optional[str] = None
|
||||||
sport: Optional[str] = None
|
sport: Optional[str] = None
|
||||||
|
origdest: Optional[str] = None
|
||||||
|
rate_limit: Optional[str] = None
|
||||||
|
user_group: Optional[str] = None
|
||||||
|
mark: Optional[str] = None
|
||||||
|
connlimit: Optional[str] = None
|
||||||
|
time: Optional[str] = None
|
||||||
|
headers: Optional[str] = None
|
||||||
|
switch_name: Optional[str] = None
|
||||||
|
helper: Optional[str] = None
|
||||||
comment: Optional[str] = None
|
comment: Optional[str] = None
|
||||||
position: Optional[int] = None
|
position: Optional[int] = None
|
||||||
|
|
||||||
@@ -163,6 +181,15 @@ class RuleOut(BaseModel):
|
|||||||
proto: str
|
proto: str
|
||||||
dport: str
|
dport: str
|
||||||
sport: str
|
sport: str
|
||||||
|
origdest: str
|
||||||
|
rate_limit: str
|
||||||
|
user_group: str
|
||||||
|
mark: str
|
||||||
|
connlimit: str
|
||||||
|
time: str
|
||||||
|
headers: str
|
||||||
|
switch_name: str
|
||||||
|
helper: str
|
||||||
comment: str
|
comment: str
|
||||||
position: int
|
position: int
|
||||||
|
|
||||||
|
|||||||
@@ -53,13 +53,24 @@ class ShorewallGenerator:
|
|||||||
def rules(self) -> str:
|
def rules(self) -> str:
|
||||||
lines = [
|
lines = [
|
||||||
self._header("rules"),
|
self._header("rules"),
|
||||||
"#ACTION".ljust(16) + "SOURCE".ljust(24) + "DEST".ljust(24) + "PROTO".ljust(10) + "DPORT".ljust(10) + "SPORT\n",
|
"#ACTION".ljust(16) + "SOURCE".ljust(24) + "DEST".ljust(24)
|
||||||
|
+ "PROTO".ljust(10) + "DPORT".ljust(16) + "SPORT".ljust(16)
|
||||||
|
+ "ORIGDEST".ljust(20) + "RATE".ljust(16) + "USER".ljust(16)
|
||||||
|
+ "MARK".ljust(12) + "CONNLIMIT".ljust(14) + "TIME".ljust(20)
|
||||||
|
+ "HEADERS".ljust(16) + "SWITCH".ljust(16) + "HELPER\n",
|
||||||
"SECTION NEW\n",
|
"SECTION NEW\n",
|
||||||
]
|
]
|
||||||
for r in sorted(self._config.rules, key=lambda x: x.position):
|
for r in sorted(self._config.rules, key=lambda x: x.position):
|
||||||
src = (r.src_zone.name if r.src_zone else "all") + (f":{r.src_ip}" if r.src_ip else "")
|
src = (r.src_zone.name if r.src_zone else "all") + (f":{r.src_ip}" if r.src_ip else "")
|
||||||
dst = (r.dst_zone.name if r.dst_zone else "all") + (f":{r.dst_ip}" if r.dst_ip else "")
|
dst = (r.dst_zone.name if r.dst_zone else "all") + (f":{r.dst_ip}" if r.dst_ip else "")
|
||||||
lines.append(self._col(r.action, src, dst, r.proto or "-", r.dport or "-", r.sport or "-", width=16))
|
lines.append(self._col(
|
||||||
|
r.action, src, dst,
|
||||||
|
r.proto or "-", r.dport or "-", r.sport or "-",
|
||||||
|
r.origdest or "-", r.rate_limit or "-", r.user_group or "-",
|
||||||
|
r.mark or "-", r.connlimit or "-", r.time or "-",
|
||||||
|
r.headers or "-", r.switch_name or "-", r.helper or "-",
|
||||||
|
width=16,
|
||||||
|
))
|
||||||
return "".join(lines)
|
return "".join(lines)
|
||||||
|
|
||||||
def hosts(self) -> str:
|
def hosts(self) -> str:
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { zonesApi, interfacesApi, policiesApi, rulesApi, snatApi, hostsApi, para
|
|||||||
interface Zone { id: number; name: string; type: string; options: string }
|
interface Zone { id: number; name: string; type: string; options: string }
|
||||||
interface Iface { id: number; name: string; zone_id: number; options: string }
|
interface Iface { id: number; name: string; zone_id: number; options: string }
|
||||||
interface Policy { id: number; src_zone_id: number; dst_zone_id: number; policy: string; log_level: string; comment: string; position: number }
|
interface Policy { id: number; src_zone_id: number; dst_zone_id: number; policy: string; log_level: string; comment: string; position: number }
|
||||||
interface Rule { id: number; action: string; src_zone_id: number | null; dst_zone_id: number | null; src_ip: string; dst_ip: string; proto: string; dport: string; sport: string; comment: string; position: number }
|
interface Rule { id: number; action: string; src_zone_id: number | null; dst_zone_id: number | null; src_ip: string; dst_ip: string; proto: string; dport: string; sport: string; origdest: string; rate_limit: string; user_group: string; mark: string; connlimit: string; time: string; headers: string; switch_name: string; helper: string; comment: string; position: number }
|
||||||
interface Snat { id: number; source_network: string; out_interface: string; to_address: string; comment: string }
|
interface Snat { id: number; source_network: string; out_interface: string; to_address: string; comment: string }
|
||||||
interface Host { id: number; zone_id: number; interface: string; subnet: string; options: string }
|
interface Host { id: number; zone_id: number; interface: string; subnet: string; options: string }
|
||||||
interface Param { id: number; name: string; value: string }
|
interface Param { id: number; name: string; value: string }
|
||||||
@@ -147,17 +147,40 @@ export default function ConfigDetail() {
|
|||||||
},
|
},
|
||||||
{ key: 'proto' as const, label: 'Proto' },
|
{ key: 'proto' as const, label: 'Proto' },
|
||||||
{ key: 'dport' as const, label: 'DPort' },
|
{ key: 'dport' as const, label: 'DPort' },
|
||||||
|
{ key: 'origdest' as const, label: 'OrigDest' },
|
||||||
{ key: 'position' as const, label: 'Position' },
|
{ key: 'position' as const, label: 'Position' },
|
||||||
] as Column<AnyEntity>[],
|
] as Column<AnyEntity>[],
|
||||||
fields: [
|
fields: [
|
||||||
{ name: 'action', label: 'Action', required: true },
|
{ name: 'action', label: 'Action', required: true },
|
||||||
{ name: 'src_zone_id', label: 'Source Zone', type: 'select' as const, options: zoneOptions },
|
{ name: 'src_zone_id', label: 'Source Zone', type: 'select' as const, options: [{ value: '', label: 'all' }, ...zoneOptions] },
|
||||||
{ name: 'dst_zone_id', label: 'Dest Zone', type: 'select' as const, options: zoneOptions },
|
{ name: 'dst_zone_id', label: 'Dest Zone', type: 'select' as const, options: [{ value: '', label: 'all' }, ...zoneOptions] },
|
||||||
{ name: 'src_ip', label: 'Source IP/CIDR' },
|
{ name: 'src_ip', label: 'Source IP/CIDR' },
|
||||||
{ name: 'dst_ip', label: 'Dest IP/CIDR' },
|
{ name: 'dst_ip', label: 'Dest IP/CIDR' },
|
||||||
{ name: 'proto', label: 'Protocol' },
|
{ name: 'proto', label: 'Protocol', placeholder: 'e.g. tcp, udp, icmp' },
|
||||||
{ name: 'dport', label: 'Dest Port' },
|
{ name: 'dport', label: 'Dest Port(s)' },
|
||||||
{ name: 'sport', label: 'Source Port' },
|
{ name: 'sport', label: 'Source Port(s)' },
|
||||||
|
{ name: 'origdest', label: 'Original Dest', placeholder: 'e.g. 192.168.1.1' },
|
||||||
|
{ name: 'rate_limit', label: 'Rate Limit', placeholder: 'e.g. 10/sec:20' },
|
||||||
|
{ name: 'user_group', label: 'User/Group', placeholder: 'e.g. joe:wheel' },
|
||||||
|
{ name: 'mark', label: 'Mark', placeholder: 'e.g. 0x100/0xff0' },
|
||||||
|
{ name: 'connlimit', label: 'ConnLimit', placeholder: 'e.g. 10:24' },
|
||||||
|
{ name: 'time', label: 'Time', placeholder: 'e.g. timestart=09:00×top=17:00' },
|
||||||
|
{ name: 'headers', label: 'Headers (IPv6)', placeholder: 'e.g. auth,esp' },
|
||||||
|
{ name: 'switch_name', label: 'Switch', placeholder: 'e.g. vpn_enabled' },
|
||||||
|
{ name: 'helper', label: 'Helper', type: 'select' as const, options: [
|
||||||
|
{ value: '', label: '(none)' },
|
||||||
|
{ value: 'amanda', label: 'amanda' },
|
||||||
|
{ value: 'ftp', label: 'ftp' },
|
||||||
|
{ value: 'irc', label: 'irc' },
|
||||||
|
{ value: 'netbios-ns', label: 'netbios-ns' },
|
||||||
|
{ value: 'pptp', label: 'pptp' },
|
||||||
|
{ value: 'Q.931', label: 'Q.931' },
|
||||||
|
{ value: 'RAS', label: 'RAS' },
|
||||||
|
{ value: 'sane', label: 'sane' },
|
||||||
|
{ value: 'sip', label: 'sip' },
|
||||||
|
{ value: 'snmp', label: 'snmp' },
|
||||||
|
{ value: 'tftp', label: 'tftp' },
|
||||||
|
]},
|
||||||
{ name: 'comment', label: 'Comment' },
|
{ name: 'comment', label: 'Comment' },
|
||||||
{ name: 'position', label: 'Position', type: 'number' as const },
|
{ name: 'position', label: 'Position', type: 'number' as const },
|
||||||
] as FieldDef[],
|
] as FieldDef[],
|
||||||
|
|||||||
Reference in New Issue
Block a user