feat: complete snat with all shorewall columns (proto, port, ipsec, mark, user, switch, origdest, probability)
All checks were successful
Build containers when image tags change / build-if-image-changed (frontend, shorefront-frontend, shorefront frontend, frontend/Dockerfile, git.baumann.gr/adebaumann/shorefront-frontend, .frontend.image) (push) Successful in 1m14s
Build containers when image tags change / build-if-image-changed (backend, shorefront-backend, shorefront backend, backend/Dockerfile, git.baumann.gr/adebaumann/shorefront-backend, .backend.image) (push) Successful in 2m2s

This commit is contained in:
2026-03-01 11:27:36 +01:00
parent 36224cebcd
commit 02c8f71957
6 changed files with 93 additions and 6 deletions

View File

@@ -0,0 +1,34 @@
"""add missing shorewall snat columns
Revision ID: 0010
Revises: 0009
Create Date: 2026-03-01
"""
from alembic import op
import sqlalchemy as sa
revision = "0010"
down_revision = "0009"
branch_labels = None
depends_on = None
_NEW_COLS = [
("proto", sa.String(16)),
("port", sa.String(64)),
("ipsec", sa.String(128)),
("mark", sa.String(32)),
("user_group", sa.String(64)),
("switch_name", sa.String(32)),
("origdest", sa.String(128)),
("probability", sa.String(16)),
]
def upgrade() -> None:
for col_name, col_type in _NEW_COLS:
op.add_column("snat", sa.Column(col_name, col_type, server_default="''", nullable=False))
def downgrade() -> None:
for col_name, _ in reversed(_NEW_COLS):
op.drop_column("snat", col_name)

View File

@@ -124,6 +124,14 @@ class Snat(Base):
source_network: Mapped[str] = mapped_column(String(64), nullable=False)
out_interface: Mapped[str] = mapped_column(String(32), nullable=False)
to_address: Mapped[str] = mapped_column(String(64), default="")
proto: Mapped[str] = mapped_column(String(16), default="")
port: Mapped[str] = mapped_column(String(64), default="")
ipsec: Mapped[str] = mapped_column(String(128), default="")
mark: Mapped[str] = mapped_column(String(32), default="")
user_group: Mapped[str] = mapped_column(String(64), default="")
switch_name: Mapped[str] = mapped_column(String(32), default="")
origdest: Mapped[str] = mapped_column(String(128), default="")
probability: Mapped[str] = mapped_column(String(16), default="")
comment: Mapped[str] = mapped_column(Text, default="")
config: Mapped["Config"] = relationship("Config", back_populates="snat_entries")

View File

@@ -201,6 +201,14 @@ class SnatCreate(BaseModel):
source_network: str
out_interface: str
to_address: str = ""
proto: str = ""
port: str = ""
ipsec: str = ""
mark: str = ""
user_group: str = ""
switch_name: str = ""
origdest: str = ""
probability: str = ""
comment: str = ""
@@ -208,6 +216,14 @@ class SnatUpdate(BaseModel):
source_network: Optional[str] = None
out_interface: Optional[str] = None
to_address: Optional[str] = None
proto: Optional[str] = None
port: Optional[str] = None
ipsec: Optional[str] = None
mark: Optional[str] = None
user_group: Optional[str] = None
switch_name: Optional[str] = None
origdest: Optional[str] = None
probability: Optional[str] = None
comment: Optional[str] = None
@@ -217,6 +233,14 @@ class SnatOut(BaseModel):
source_network: str
out_interface: str
to_address: str
proto: str
port: str
ipsec: str
mark: str
user_group: str
switch_name: str
origdest: str
probability: str
comment: str
model_config = {"from_attributes": True}

View File

@@ -87,10 +87,22 @@ class ShorewallGenerator:
return "".join(lines)
def snat(self) -> str:
lines = [self._header("snat"), "#ACTION".ljust(24) + "SOURCE".ljust(24) + "DEST\n"]
lines = [
self._header("snat"),
"#ACTION".ljust(24) + "SOURCE".ljust(24) + "DEST".ljust(20)
+ "PROTO".ljust(10) + "PORT".ljust(16) + "IPSEC".ljust(16)
+ "MARK".ljust(12) + "USER/GROUP".ljust(16) + "SWITCH".ljust(16)
+ "ORIGDEST".ljust(20) + "PROBABILITY\n",
]
for m in self._config.snat_entries:
action = f"SNAT:{m.to_address}" if m.to_address else "MASQUERADE"
lines.append(self._col(action, m.source_network, m.out_interface, width=24))
lines.append(self._col(
action, m.source_network, m.out_interface,
m.proto or "-", m.port or "-", m.ipsec or "-",
m.mark or "-", m.user_group or "-", m.switch_name or "-",
m.origdest or "-", m.probability or "-",
width=16,
))
return "".join(lines)
def as_json(self) -> dict: