From f4d81ad8e9535091df6704ad215425c643fffae0 Mon Sep 17 00:00:00 2001 From: "Adrian A. Baumann" Date: Sat, 28 Feb 2026 19:00:53 +0100 Subject: [PATCH] Instructions and plan --- .../2026-02-28-shorewall-manager-design.md | 210 ++++++++++++++++++ instructions.md | 149 +++++++++++++ 2 files changed, 359 insertions(+) create mode 100644 docs/plans/2026-02-28-shorewall-manager-design.md create mode 100644 instructions.md diff --git a/docs/plans/2026-02-28-shorewall-manager-design.md b/docs/plans/2026-02-28-shorewall-manager-design.md new file mode 100644 index 0000000..90e4453 --- /dev/null +++ b/docs/plans/2026-02-28-shorewall-manager-design.md @@ -0,0 +1,210 @@ +# Shorefront — Shorewall Configuration Manager: Design Document + +**Date:** 2026-02-28 +**Status:** Approved + +--- + +## Overview + +Shorefront is a production-ready web application for managing Shorewall firewall configurations. Users manage zones, interfaces, policies, rules, and masquerade/NAT entries through a browser UI backed by a PostgreSQL database, and can generate Shorewall config files on demand. + +--- + +## Architecture + +**Option A (selected):** Monorepo with separate Docker services. Frontend and backend are independently containerized. In production, Nginx serves the React bundle and reverse-proxies `/api` to the FastAPI backend. In development, the Vite dev server proxies `/api`. Clean separation between frontend, backend API, and the Shorewall generator module. + +**Deployment targets:** +- Local dev: Docker Compose +- Production: Kubernetes (Traefik ingress) with Helm charts + +--- + +## Project Structure + +``` +shorefront/ +├── backend/ +│ ├── Dockerfile +│ ├── requirements.txt +│ ├── alembic/ +│ │ ├── env.py +│ │ └── versions/ +│ │ └── 0001_initial.py # schema + seed data +│ └── app/ +│ ├── main.py # FastAPI app, CORS, lifespan +│ ├── database.py # SQLAlchemy engine + session +│ ├── models.py # ORM models +│ ├── schemas.py # Pydantic schemas +│ ├── auth.py # JWT logic, password hashing +│ ├── shorewall_generator.py # ShorewallGenerator class +│ └── api/ +│ ├── auth.py # /auth/* +│ ├── configs.py # CRUD + /generate +│ ├── zones.py +│ ├── interfaces.py +│ ├── policies.py +│ ├── rules.py +│ └── masq.py +├── frontend/ +│ ├── Dockerfile +│ ├── nginx.conf # prod: serve static + proxy /api +│ ├── vite.config.ts # dev: proxy /api → backend:8000 +│ ├── package.json +│ └── src/ +│ ├── main.tsx +│ ├── App.tsx +│ ├── api.ts # axios wrapper, withCredentials, 401 redirect +│ ├── routes/ +│ │ ├── Login.tsx +│ │ ├── ConfigList.tsx +│ │ └── ConfigDetail.tsx # tabbed: zones/interfaces/policies/rules/masq +│ └── components/ +│ ├── Layout.tsx # dark sidebar + top bar +│ ├── DataTable.tsx # reusable table w/ edit/delete +│ ├── EntityForm.tsx # reusable MUI Dialog form +│ └── GenerateModal.tsx # file preview tabs + ZIP download +├── helm/ +│ └── shorefront/ +│ ├── Chart.yaml +│ ├── values.yaml +│ ├── values-prod.yaml +│ └── templates/ +│ ├── _helpers.tpl +│ ├── namespace.yaml +│ ├── secret.yaml +│ ├── configmap.yaml +│ ├── pv.yaml # NFS PersistentVolume +│ ├── pvc.yaml +│ ├── postgres-deployment.yaml +│ ├── postgres-service.yaml +│ ├── backend-deployment.yaml +│ ├── backend-service.yaml +│ ├── frontend-deployment.yaml +│ ├── frontend-service.yaml +│ └── ingress.yaml # Traefik, networking.k8s.io/v1 +├── docker-compose.yml +└── README.md +``` + +--- + +## Data Model + +| Model | Key Fields | +|-------|-----------| +| `User` | `id`, `username`, `email`, `hashed_password`, `is_active` | +| `Config` | `id`, `name`, `description`, `is_active`, `created_at`, `updated_at`, `owner_id FK→User` | +| `Zone` | `id`, `config_id FK`, `name` (unique per config), `type` (ipv4/ipv6/firewall), `options` | +| `Interface` | `id`, `config_id FK`, `name`, `zone_id FK→Zone`, `options` | +| `Policy` | `id`, `config_id FK`, `src_zone_id FK`, `dst_zone_id FK`, `policy` (ACCEPT/DROP/REJECT/CONTINUE), `log_level`, `comment`, `position` | +| `Rule` | `id`, `config_id FK`, `action`, `src_zone_id FK`, `dst_zone_id FK`, `src_ip`, `dst_ip`, `proto`, `dport`, `sport`, `comment`, `position` | +| `Masq` | `id`, `config_id FK`, `source_network`, `out_interface`, `to_address`, `comment` | + +**Constraints:** Foreign keys enforced. Zone name unique per config. Position/order fields for policies and rules to preserve Shorewall file ordering semantics. + +--- + +## Seed Data (Initial Alembic Migration) + +- User: `admin` / `admin` (must change on first login) +- Config: `homelab` + - Zones: `fw` (firewall), `net` (ipv4), `loc` (ipv4) + - Interface: `eth0` → zone `net` + - Policies: `loc→net ACCEPT`, `net→all DROP info`, `all→all REJECT info` + - Masq: `192.168.1.0/24` via `eth0` + +--- + +## API Surface + +``` +POST /auth/register +POST /auth/login → sets httpOnly JWT cookie +POST /auth/logout → clears cookie +GET /auth/me + +GET /configs +POST /configs +GET /configs/{id} +PUT /configs/{id} +DELETE /configs/{id} +POST /configs/{id}/generate # ?format=zip for ZIP, JSON default + +GET /configs/{id}/zones +POST /configs/{id}/zones +GET /configs/{id}/zones/{zone_id} +PUT /configs/{id}/zones/{zone_id} +DELETE /configs/{id}/zones/{zone_id} +# same pattern for interfaces, policies, rules, masq +``` + +**Auth:** JWT in `httpOnly` cookie. `passlib` + `bcrypt` for password hashing. All `/configs/*` routes require valid JWT. + +--- + +## Shorewall Generator + +`app/shorewall_generator.py` — `ShorewallGenerator` class: + +```python +class ShorewallGenerator: + def __init__(self, config: Config): ... + def zones(self) -> str: ... + def interfaces(self) -> str: ... + def policy(self) -> str: ... + def rules(self) -> str: ... + def masq(self) -> str: ... + def as_json(self) -> dict: ... # { zones, interfaces, policy, rules, masq } + def as_zip(self) -> bytes: ... # in-memory ZIP +``` + +Each method produces a Shorewall-formatted string with a comment header: +``` +# zones — generated by shorefront | config: homelab | 2026-02-28T18:00:00Z +#ZONE TYPE OPTIONS +fw firewall +net ipv4 +``` + +The `/generate` endpoint returns JSON by default; appending `?format=zip` streams a ZIP archive. The frontend uses JSON for the in-browser preview modal and triggers a separate `?format=zip` request for the download button. + +--- + +## Frontend Design + +**Theme:** MUI with custom dark sidebar (`#1a1f2e`), light main content (`#f5f7fa`), white cards. + +**Pages:** +- **Login** — centered card, username/password +- **Config List** — data table with name, description, active badge, created date, edit/delete actions. FAB to create. +- **Config Detail** — breadcrumb, 5 tabs (Zones / Interfaces / Policies / Rules / Masq/NAT). Each tab: `DataTable` + "Add" button → `EntityForm` dialog. Top-right: "Generate Config" button → `GenerateModal`. +- **Generate Modal** — 5 tabs with monospace code blocks, copy-to-clipboard per file, Download ZIP button. + +**Key implementation:** +- `api.ts`: Axios instance, `baseURL=/api`, `withCredentials: true`, 401 interceptor → redirect to `/login` +- `DataTable.tsx`: Generic, accepts column defs + row data, reused across all tabs +- `EntityForm.tsx`: Generic MUI Dialog, field config passed per entity type +- React Router v6 with `` checking `GET /auth/me` + +--- + +## Infrastructure + +### Docker Compose (local dev) +- Services: `postgres`, `backend`, `frontend` +- Backend runs `alembic upgrade head` on startup +- Local named volume for postgres data +- Backend on port 8000, frontend (Nginx) on port 80 + +### Kubernetes / Helm +- **Namespace:** `shorefront` +- **Ingress:** Traefik (`ingressClassName: traefik`), standard `networking.k8s.io/v1` Ingress + - `shorefront.example.com/api/*` → backend service + - `shorefront.example.com/*` → frontend service + - Hostname configurable in `values.yaml` +- **Storage:** Static NFS PersistentVolume at `192.168.17.199:/mnt/user/kubernetesdata/shorefront`, `ReadWriteOnce`, bound via PVC to Postgres deployment +- **Secrets:** DB password + JWT secret key in `secret.yaml`, set via `values.yaml` (override at deploy time) +- **Migrations:** `alembic upgrade head` runs as an `initContainer` in the backend Deployment +- **values.yaml** configures: image tags, replicas, hostname, resource requests/limits, NFS server/path diff --git a/instructions.md b/instructions.md new file mode 100644 index 0000000..db39a1e --- /dev/null +++ b/instructions.md @@ -0,0 +1,149 @@ +Here’s a Claude Code prompt you can paste as-is and then tweak to taste: + +*** + +You are an expert full‑stack developer. +Build a small but production‑ready web application to manage Shorewall firewall configurations with a browser frontend and a database backend. The app should NOT be a toy example but a clean, modular implementation that I can extend. + +## High‑level requirements + +- Purpose: Let users manage Shorewall configurations (zones, interfaces, policies, rules, masq, nat, hosts, etc.) in a web UI and generate the corresponding Shorewall config files on demand. +- Stack: + - Backend: Python with FastAPI (preferred) or Django, using SQLAlchemy (or the framework’s ORM). + - Frontend: React + TypeScript with a simple component structure, using a UI library like MUI or Tailwind for basic styling. + - Database: PostgreSQL (but keep ORM models portable). +- Architecture: + - Clean separation: frontend, backend API, and a “shorewall generator” module that knows how to render the text files. + - Use a REST or JSON API between frontend and backend. + - Use Docker Compose to run web + db. + +## Domain model and database + +Design a minimal but sensible schema to model Shorewall configs: + +- Core entities: + - Config: top‑level object (name, description, created_at, updated_at, is_active). + - Zone: belongs to Config (name, type, options). + - Interface: belongs to Config (name, zone_id FK, options). + - Policy: belongs to Config (src_zone_id, dst_zone_id, policy (ACCEPT/DROP/REJECT), log_level, comment, position/order). + - Rule: belongs to Config (action, src_zone_id, dst_zone_id, src_ip/cidr, dst_ip/cidr, proto, dport, sport, comment, position/order). + - Masq/NAT entry: belongs to Config (source_network, out_interface, to_address, comment). +- Constraints: + - Foreign keys for relationships. + - Reasonable non‑null constraints. + - Basic uniqueness where it makes sense (e.g., zone name per config). + +Generate: +- ORM models. +- Database migration setup (e.g. Alembic) with an initial migration. + +## Shorewall file generation + +Implement a separate Python module that converts a Config from the database into Shorewall text files: + +- Target files (as functions or one class with multiple methods): + - zones + - interfaces + - policy + - rules + - masq +- Each generator should: + - Accept a Config ID (or ORM object) and query all related entities. + - Produce a string that matches Shorewall file syntax, with headers like `#ZONE TYPE`, `#ACTION SOURCE DEST`, etc. [wiki.calculate-linux](https://wiki.calculate-linux.org/shorewall) + - Include a small comment header at the top with config name and a generated timestamp. +- Implement one function that bundles all generated file contents into: + - Either a ZIP archive in memory, or + - A JSON object `{ zones: "...", interfaces: "...", policy: "...", rules: "...", masq: "..." }` + - We can later write this to `/etc/shorewall/` manually. + +## Backend API + +Use FastAPI (or Django REST) to implement these endpoints: + +- Auth (simple for now): + - POST /auth/register + - POST /auth/login + - Use JWT or simple session cookie; protect config management endpoints. +- Config management: + - GET /configs + - POST /configs + - GET /configs/{id} + - PUT /configs/{id} + - DELETE /configs/{id} +- Nested resources: + - CRUD for zones, interfaces, policies, rules, masq entries under a given config ID. +- Generation: + - POST /configs/{id}/generate + - Returns the generated Shorewall files as JSON or as a ZIP download. +- Implementation details: + - Pydantic models for request/response schemas. + - Proper error handling (404 if config not found, 400 on validation errors). + - Type hints everywhere. + +## Frontend + +Create a small React + TypeScript SPA: + +- Pages: + - Login/register page. + - Config list page. + - Config detail/edit page with tabs for: + - Zones + - Interfaces + - Policies + - Rules + - Masq/NAT +- Each tab: + - Shows a table of existing entries. + - Has forms to create/edit/delete entries. +- Generation UI: + - On the config detail page, add a “Generate Shorewall config” button that calls `/configs/{id}/generate`. + - Show the generated file contents in a modal dialog or separate view with tabs for zones/interfaces/policy/rules/masq. + - Add a button to download the ZIP (if you implement ZIP) or copy text. +- Implementation details: + - Use React Router for navigation. + - API client: + - Centralized `api.ts` that wraps fetch/axios calls with the correct base URL and auth headers. + - Minimal styling with MUI or Tailwind; keep it functional, not pretty. + +## Project structure and tooling + +Provide: + +- A clear project layout, e.g.: + + - backend/ + - app/main.py + - app/models.py + - app/schemas.py + - app/api/ + - app/services/ + - app/shorewall_generator.py + - alembic/… + - frontend/ + - src/App.tsx + - src/routes/… + - src/components/… + - src/api.ts + - docker-compose.yml + - README.md + +- Docker: + - `docker-compose.yml` to run: + - backend service + - frontend service + - postgres + - Provide Dockerfiles for backend and frontend. + +## What to output + +- All core backend files (FastAPI app, models, schemas, routers, shorewall generator). +- All core frontend files (entry, routes, main pages, key components). +- Database and migration setup. +- Dockerfiles and docker-compose.yml. +- A concise README with: + - How to start dev environment. + - How to create the first user. + - How to create a config and generate Shorewall files. + +Focus on correctness, a clean structure, and code that I can copy into a repo and run with minimal adjustments.