Watch
1
0
Fork
You've already forked ovhfirewall
0
No description
  • JavaScript 54.1%
  • Python 43.2%
  • CSS 1.6%
  • HTML 0.5%
  • Shell 0.4%
  • Other 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-06-19 13:29:36 +02:00
backend firewall: treat source 'any'/blank as no source filter 2026-06-19 08:55:44 +02:00
deploy . 2026-06-16 18:47:06 +02:00
desktop . 2026-06-17 12:55:07 +02:00
frontend update 2026-06-19 13:29:36 +02:00
reference . 2026-06-16 18:47:06 +02:00
.gitignore fix: track tauri icons so Pi build can find them 2026-06-16 22:39:46 +02:00
deploy.sh . 2026-06-16 18:47:06 +02:00
README.md . 2026-06-16 18:47:06 +02:00

OVH Firewall Manager

Self-hosted app to manage OVH Network/Edge Firewall + view GAME anti-DDoS mitigation across multiple OVH accounts — instead of logging into the OVH dashboard. Built for a 2-owner setup (you + co-owner), 2 OVH accounts (FR + PL).

Raspberry Pi 5 (NVMe, 24/7)
  ├─ FastAPI backend  (127.0.0.1:8080)  ── python-ovh ──> OVH API (ovh-eu)
  │    └─ SQLite: users, OVH accounts (Fernet-encrypted creds), grants, audit
  ├─ React webapp     (served by FastAPI)
  ├─ nginx            (127.0.0.1:80, reverse proxy)
  └─ cloudflared      (tunnel ── public host fw.yourdomain.com)

Clients (all same UI, same backend):
  • Browser            → https://fw.yourdomain.com   (co-owner, phone)
  • Linux desktop app  → Tauri shell (you)
  • macOS desktop app  → Tauri shell (co-owner)
  • Auth: own login (argon2) + TOTP 2FA + rate-limiting. No passwords leave the Pi.
  • RBAC: per-user grants → which OVH account, read or write. Admin manages users/accounts.
  • Audit: every firewall change logged (who/what/when).
  • Secrets: OVH API keys encrypted at rest (Fernet); only the Pi holds them.

1. OVH credentials (per account)

For each OVH account (yours, co-owner's), create API creds scoped tight:

Easiest: use the createToken page — it returns AK + AS + CK in one shot and lets you enter the exact method+path rows below: https://api.ovh.com/createToken/

Grant exactly these least-privilege rights (nothing broader — do not use /*):

Method Path Used for
GET /me "Test" button
GET /v2/networkDefense/vac/event DDoS alerts (VAC attack events)
GET /dedicated/server server list
GET /dedicated/server/* server detail
GET /ip IP block list
GET /ip/* all reads: firewall state, rules, mitigation, GAME
POST /ip/*/firewall enable firewall on an IP
POST /ip/*/firewall/* create rule
PUT /ip/*/firewall/* toggle firewall enabled
DELETE /ip/*/firewall/* remove firewall / delete rule / edit (delete half)
POST /ip/*/mitigation put an IP under mitigation
DELETE /ip/*/mitigation/* remove an IP from mitigation
POST /ip/*/game/* add GAME rule (/game/{ipOnGame}/rule)
PUT /ip/*/game/* toggle GAME firewall mode
DELETE /ip/*/game/* delete GAME rule
POST /ip/*/reverse set reverse DNS (PTR)
DELETE /ip/*/reverse/* remove reverse DNS
POST /ip/*/mitigationProfiles create auto-mitigation profile
PUT /ip/*/mitigationProfiles/* tune auto-mitigation timeout
DELETE /ip/*/mitigationProfiles/* remove auto-mitigation profile

Notes:

  • Set Validity: Unlimited — otherwise the key expires and the app stops working.
  • Rule edit needs no extra right: OVH rules are immutable, so the app does delete-then-recreate via the existing POST + DELETE /ip/*/firewall/*.
  • The * wildcard spans slashes, so it matches CIDR blocks like 1.2.3.0/24.
  • This key cannot reach reverse-DNS, IP move, or billing — firewall/anti-DDoS only.
  • Endpoint = ovh-eu for both FR and PL.

Co-owner generates their own creds for their own account and you add them in the app (Accounts tab). You never see each other's secrets in plaintext.

Instead of pasting keys, register one OVH Application and let each user self-connect by logging into their own OVH account:

  1. Create the app once: https://api.ovh.com/createApp/ → put the AppKey + AppSecret in backend .env as OVH_APP_KEY / OVH_APP_SECRET.
  2. Set PUBLIC_BASE_URL to how the app is reached (the tunnel host in prod).
  3. In the app: Accounts → Connect with OVH → enter a label → you're redirected to OVH's login, you approve the scoped rules, and the key is created + stored automatically. Co-owner does the same on their OVH account.

The scoped rules requested are defined in app/ovh_access.py (same least-privilege set as the table above). Co-owner's OVH password never touches this app.

API reference (verify exact firewall field names here): https://api.eu.ovhcloud.com/console/?section=%2Fip&branch=v1

2. Backend (on the Pi)

cd backend
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
cp .env.example .env
# generate the two secrets and paste into .env:
.venv/bin/python -c "from cryptography.fernet import Fernet; print('VAULT_KEY=',Fernet.generate_key().decode())"
.venv/bin/python -c "import secrets; print('SESSION_SECRET=',secrets.token_urlsafe(48))"
# set ADMIN_EMAIL in .env, then first run:
.venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8080 --proxy-headers --forwarded-allow-ips 127.0.0.1

On first start it prints a one-time admin password to the log. Log in, change it, enable 2FA. Then install the systemd unit (deploy/ovhfw.service) for 24/7.

3. Frontend build

cd frontend
npm install
npm run build      # outputs frontend/dist/ — FastAPI serves it automatically

Dev mode (hot reload, proxies API to :8080): npm run dev → http://localhost:5173

4. nginx + cloudflared

sudo cp deploy/nginx.conf /etc/nginx/sites-available/ovhfw
sudo ln -s /etc/nginx/sites-available/ovhfw /etc/nginx/sites-enabled/ovhfw
sudo nginx -t && sudo systemctl reload nginx

Cloudflare tunnel: see deploy/cloudflared-config.example.yml. After cloudflared tunnel route dns ovhfw fw.yourdomain.com, the app is reachable at https://fw.yourdomain.com — gated by the app login + 2FA.

5. Desktop apps

See desktop/README.md. Set the tunnel host in desktop/src-tauri/tauri.conf.json, then cargo tauri build on Linux (→ AppImage/deb) and on a Mac (→ dmg).


Updating (push new changes live)

Code changes are not auto-applied. After pulling/pushing changes to the Pi:

./deploy.sh        # git pull → build frontend → install deps → restart service
  • Frontend-only changes: FastAPI serves the rebuilt frontend/dist/ live — no restart needed; Vite hashes filenames so browsers fetch the new bundle on reload.
  • Backend changes: the restart in deploy.sh applies them.
  • DB schema changes: on every start the app auto-migrates — adds any new columns to existing tables (app/db.py:_auto_migrate). No data loss, no manual SQL. (This is why you can keep iterating locally without resetting data/app.db.)

Optional push-to-deploy: a GitHub Action can SSH to the Pi (via the tunnel or Tailscale) and run ./deploy.sh on every push — ask if you want it wired.

Scope (v1)

Feature State
Network/Edge Firewall: create on IP, rules create/edit/delete, enable/disable ✅ read+write
Anti-DDoS: mitigation add/remove, GAME rules ✅ read+write
Multi-account + RBAC grants ✅
Login + TOTP 2FA + audit log ✅

Per-server IP grouping (auto-list a server's IPs) is an easy follow-up.

Network/Edge Firewall rule fields

Verified against the OVH schema (reference/ovh_ip_schema.json). Up to 20 rules per IP, lower sequence evaluated first. Stateless — remember to allow return traffic.

Field Values Notes
sequence 0–19 order; lower first
action permit | deny
protocol tcp udp icmp ipv4 gre esp ah
source CIDR (e.g. 1.2.3.0/24) blank = any
source port 80 or 1000-2000 single → sourcePort; range → sourcePortRange {from,to}
dest port 80 or 1000-2000 single → destinationPort; range → destinationPortRange {from,to}
TCP option established | syn + fragments tcp only; sent as tcpOption {option,fragments}
L3 packet length from / to sent as l3PacketLength {from,to} (Range)

Port ranges are supported on both source and destination — type 1000-2000 in the port field and the app sends the …PortRange {from,to} variant; a bare number sends the single-port variant. Rules are immutable in OVH, so editing = delete + recreate.

GAME firewall rules use a fixed protocol enum (incl. counterStrike2, rust, …) plus a ports {from,to} range, under /ip/{ip}/game/{ipOnGame}/rule.

First-time firewall on a new IP

New OVH IPs (dedicated/VPS/GAME) ship with no firewall object. Flow in the app:

  1. Firewall tab → pick account + IP block → Enable firewall on an IP (auto-selects the IP for /32 blocks; dropdown for small blocks) → creates the firewall object.
  2. Add the first rule (works while the firewall is disabled).
  3. Enable the firewall — the toggle is gated until ≥1 rule exists.

Anti-DDoS note

OVH removed permanent mitigation mode — the permanent field and its PUT are deprecated. The app only adds/removes IPs under (auto-)mitigation; no permanent toggle.

Security checklist

  • .env never committed; VAULT_KEY backed up separately from the DB.
  • Backend bound to 127.0.0.1 only; tunnel is the sole ingress.
  • Every user has 2FA enabled (banner nags until they do).
  • OVH consumer keys use the least-privilege rights table above (firewall/anti-DDoS only), not /*, Validity = Unlimited.
  • DB + VAULT_KEY on the NVMe SSD; encrypted off-box backup of the DB.
  • Pi kept patched; ideally on its own VLAN.