{"id":29294658,"url":"https://github.com/fabriziosalmi/secure-proxy-manager","last_synced_at":"2026-06-04T23:00:56.901Z","repository":{"id":270329455,"uuid":"910025723","full_name":"fabriziosalmi/secure-proxy-manager","owner":"fabriziosalmi","description":"Secure proxy solution with filtering, real-time monitoring and a modern web UI.","archived":false,"fork":false,"pushed_at":"2026-06-04T12:59:49.000Z","size":37304,"stargazers_count":34,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-06-04T14:16:40.891Z","etag":null,"topics":["proxy","proxy-configuration","proxy-tool","squid","squid-proxy","squid-proxy-security","ui"],"latest_commit_sha":null,"homepage":"https://fabriziosalmi.github.io/secure-proxy-manager/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fabriziosalmi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"fabriziosalmi","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":null}},"created_at":"2024-12-30T10:05:07.000Z","updated_at":"2026-06-04T12:59:48.000Z","dependencies_parsed_at":null,"dependency_job_id":"36d13c85-d3b6-470a-8348-1d34a9fb95f2","html_url":"https://github.com/fabriziosalmi/secure-proxy-manager","commit_stats":null,"previous_names":["fabriziosalmi/secure-proxy","fabriziosalmi/secure-proxy-manager"],"tags_count":50,"template":false,"template_full_name":null,"purl":"pkg:github/fabriziosalmi/secure-proxy-manager","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabriziosalmi%2Fsecure-proxy-manager","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabriziosalmi%2Fsecure-proxy-manager/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabriziosalmi%2Fsecure-proxy-manager/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabriziosalmi%2Fsecure-proxy-manager/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fabriziosalmi","download_url":"https://codeload.github.com/fabriziosalmi/secure-proxy-manager/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabriziosalmi%2Fsecure-proxy-manager/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33923182,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-04T02:00:06.755Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["proxy","proxy-configuration","proxy-tool","squid","squid-proxy","squid-proxy-security","ui"],"created_at":"2025-07-06T13:07:22.999Z","updated_at":"2026-06-04T23:00:56.890Z","avatar_url":"https://github.com/fabriziosalmi.png","language":"Go","funding_links":["https://github.com/sponsors/fabriziosalmi"],"categories":[],"sub_categories":[],"readme":"# Secure Proxy Manager\n\nA self-hosted forward proxy with a web UI for filtering, inspecting, and logging\noutbound HTTP/HTTPS traffic on a network. It combines a Squid proxy, a custom\nWAF, a DNS sinkhole, and a management API into a single Docker Compose stack.\n\nUse it to give a home, lab, or small-office network one controllable egress\npoint: block domains and IPs, inspect requests against WAF rules, sinkhole\nmalware/ad domains at the DNS layer, and see what every client is reaching.\n\n![Dashboard](docs/screenshots/dashboard.png)\n\n## What it is\n\nThe stack is five containers (plus an optional sixth):\n\n| Service | Tech | Role |\n|---------|------|------|\n| `web` | React 19 + Vite, served by nginx | The management UI and HTTPS entry point. Terminates TLS, proxies the API and WebSocket. |\n| `backend` | Go (chi, modernc/sqlite, JWT) | REST API, log ingestion, settings, auth, background workers. ~20 MB RAM. |\n| `waf` | Go ICAP service | Inspects proxied requests/responses Squid hands off over ICAP. |\n| `proxy` | Squid (Ubuntu 22.04, `squid-openssl`) | The actual forward proxy on port 3128, with ICAP wired to the WAF. |\n| `dns` | dnsmasq | DNS resolver that sinkholes blacklisted domains at the network layer. |\n| `tailscale` | Tailscale (optional) | Sidecar for remote access over a private network. Enabled with a compose profile. |\n\nThe backend talks to Squid and dnsmasq through the Docker Engine API to apply\nblacklist and config changes without restarting the host.\n\n## Quick start\n\nRequirements: Docker 20.10+ with Compose v2, ~512 MB RAM, ~2 GB disk. Runs on\nx86_64 and ARM64.\n\n**One command (fresh VPS or server):**\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/fabriziosalmi/secure-proxy-manager/main/deploy/install.sh | sudo bash\n```\n\nThe installer checks for Docker, generates random admin credentials, builds the\nimages, and starts the stack. It prints the credentials at the end.\n\n**Manual:**\n\n```bash\ngit clone https://github.com/fabriziosalmi/secure-proxy-manager.git\ncd secure-proxy-manager\ncp .env.example .env\n# Set a strong BASIC_AUTH_PASSWORD in .env (the backend refuses to start with an\n# empty, common, or \u003c8-char password). Leave SECRET_KEY empty to auto-generate.\ndocker compose up -d --build\n```\n\nThen open \u003chttps://localhost:8443\u003e and accept the self-signed certificate. Log in\nwith the credentials from your `.env` (or the ones the installer printed). On\nfirst login a short wizard helps you pick a starting configuration.\n\nTo send traffic through the proxy, point a client at `http://\u003chost\u003e:3128`\n(Settings \u003e Client Setup generates per-OS instructions and a PAC file).\n\n## Ports\n\n| Port | Service | Notes |\n|------|---------|-------|\n| 443, 8443 | Web UI (HTTPS) | Main interface. 8443 is the same UI on an alternate port. |\n| 80, 8011 | HTTP | Redirects to HTTPS; also serves ACME challenges for Let's Encrypt. |\n| 3128 | Proxy | Point clients here. Only RFC1918/localhost sources are allowed by default. |\n| 5001 | Backend API | Bound to `127.0.0.1` on the host; the UI proxies it internally. |\n\n## Configuration\n\nAll configuration is environment-driven via `.env` (see `.env.example` for the\nfull list with comments). The essentials:\n\n| Variable | Default | Purpose |\n|----------|---------|---------|\n| `BASIC_AUTH_USERNAME` / `BASIC_AUTH_PASSWORD` | required | Admin login. The backend refuses to start if the password is empty, a common default, or shorter than 8 characters. |\n| `SECRET_KEY` | auto-generated | JWT signing secret. Leave empty to generate and persist one. If set, it must be unique, random, and 32+ chars; known/example values are rejected at startup. |\n| `LETSENCRYPT_DOMAIN` / `LETSENCRYPT_EMAIL` | empty | Set both to obtain a real certificate via certbot instead of the self-signed one. |\n| `CORS_ALLOWED_ORIGINS` | `https://localhost:8443` | Allowed UI origin(s). |\n| `DNS_UPSTREAM_1..3` | malware-blocking resolvers | Upstream DNS for dnsmasq. Override to use your own (e.g. Pi-hole). |\n| `PROXY_IP` | empty | Your LAN IP, to enable WPAD auto-discovery (`wpad.dat`) for browsers. |\n| `WAF_BLOCK_THRESHOLD` | `10` | Anomaly score at which a request is blocked (lower is stricter). |\n| `WAF_FAIL_OPEN` | `0` | If the WAF handler errors, block (`0`, fail-closed) or allow (`1`) the request. |\n\nMost runtime behaviour (WAF categories, blocklists, filtering toggles,\nnotifications) is managed from the UI and stored in the database, not in `.env`.\n\n## Features\n\n**Filtering**\n- Domain and IP blacklists and whitelists, managed in the UI or via the API,\n  importable from URLs or pasted content (with CIDR support for IPs).\n- DNS sinkholing of blacklisted domains via dnsmasq, so blocked lookups never\n  reach the proxy at all.\n- Direct-IP-access blocking and a method whitelist in Squid.\n\n**WAF**\n- 175 regex rules across 23 toggleable categories (SQLi, XSS, traversal, C2,\n  data-loss, etc.), each enableable/disableable at runtime.\n- 7 behavioural heuristics (entropy, beaconing, PII, sharding, morphing,\n  ghosting, sequence) plus DGA detection (bigram + entropy), typosquatting\n  detection (edit distance + homoglyphs), and a safe-URL fast-path cache.\n- Anti-evasion input normalization and anomaly scoring; fails closed on internal\n  errors by default.\n\n**HTTPS inspection (optional)**\n- HTTPS is tunnelled via `CONNECT` by default - the WAF sees connection metadata\n  (host, port), not request contents.\n- Enabling SSL-bump in Settings makes Squid intercept TLS with a locally\n  generated CA so the WAF can inspect decrypted requests. This requires\n  installing the generated CA (Settings \u003e download CA) on every client that\n  should trust it. The CA is generated per deployment at first boot.\n\n**Operations**\n- Live dashboard, per-client drill-down, searchable access logs, and an audit\n  log of configuration changes.\n- Prometheus metrics from the WAF at `:8080/metrics` (aggregate counters only).\n- Notifications to a custom webhook, Gotify, Telegram, or Microsoft Teams, with\n  retry and backoff.\n- WebSocket log streaming, JWT auth with a persistent revocation list, per-IP\n  rate limiting, and AES-256-GCM encryption of sensitive settings at rest.\n- Optional Let's Encrypt, WPAD/PAC client auto-config, and a Tailscale sidecar.\n\n## Using it\n\n**Point a client at the proxy.** Set the HTTP/HTTPS proxy to `http://\u003chost\u003e:3128`\non the device, or use the PAC file / WPAD auto-discovery from Settings \u003e Client\nSetup. Only RFC1918 and localhost sources are permitted by default.\n\n**Block a domain.** Blacklists \u003e Domains \u003e add `example.com`. The change is\nexported to Squid and dnsmasq within a couple of seconds. Whitelists take\nprecedence.\n\n**Inspect HTTPS.** Settings \u003e enable SSL inspection, download the generated CA,\nand install it as trusted on your clients. Without this, HTTPS requests are only\nfiltered by host/IP and DNS, not by WAF body rules.\n\n**Import a blocklist.** Blacklists \u003e Import supports popular public lists by URL\nor pasted content. Imports are size-bounded and fetched with an SSRF-safe client\nthat refuses private/internal targets.\n\n## Updating\n\n```bash\ncd secure-proxy-manager\ngit pull\ndocker compose up -d --build\n```\n\nYour `.env`, database, and blacklists live in bind-mounted volumes and are\npreserved across updates. The backend checks GitHub for newer releases and shows\na badge in the UI when one is available.\n\n## Backup and restore\n\nThe database (`data/`), config (`config/`), and `.env` hold all state.\n\n```bash\ndocker compose down\ncp data/proxy_manager.db proxy_manager.db.bak\ntar czf config.bak.tgz config/ .env\ndocker compose up -d\n```\n\nThe UI also offers a config export/import under Settings, and the database can be\nexported via the API.\n\n## Health and testing\n\n```bash\n# Service health\ncurl -skI https://localhost:8443/            # UI\ncurl -I http://127.0.0.1:5001/health         # backend API (localhost only)\n\n# Proxy a request\ncurl -x http://localhost:3128 -I http://example.com\n\n# End-to-end suite (service health, proxy egress, blocking, log pipeline)\nbash tests/ci-e2e.sh\n```\n\nGo unit tests: `cd backend-go \u0026\u0026 go test ./...` and `cd waf-go \u0026\u0026 go test ./...`.\nUI tests: `cd ui \u0026\u0026 npm test`. A local pre-commit check that runs TypeScript,\nESLint, Go vet/test, and the UI build is at `scripts/pre-commit-validate.sh`.\n\n## Troubleshooting\n\n- **Backend container won't start** - check `docker compose logs backend`. The\n  most common cause is an empty/weak `BASIC_AUTH_PASSWORD` or a known/short\n  `SECRET_KEY`; both are rejected by design.\n- **Can't reach the UI** - it is HTTPS on 8443 with a self-signed cert; accept\n  the certificate, or use `https://localhost:8443` (not `http://...:8011`).\n- **Clients aren't being filtered** - confirm the device's proxy is set to\n  `:3128` and the source IP is RFC1918/localhost (other sources are denied).\n- **HTTPS isn't inspected** - that is the default; enable SSL-bump and install\n  the CA to inspect request contents.\n\n## API\n\nThe backend exposes a REST API (72 routes) used by the UI. A machine-readable\nlisting is available at `GET /api/docs`. Authenticate with HTTP Basic auth, or\nexchange credentials at `POST /api/auth/login` for a JWT and send it as a Bearer\ntoken.\n\n## Architecture and deployment docs\n\n- [DEPLOYMENT.md](DEPLOYMENT.md) - step-by-step deployment, reverse-proxy and\n  TLS setup, resource tuning.\n- [SECURITY.md](SECURITY.md) - security model and how to report issues.\n- [BENCHMARKS.md](BENCHMARKS.md) - reproducible performance and detection\n  benchmarks.\n- [CHANGELOG.md](CHANGELOG.md) - release history.\n\n## Security notes\n\n- The SSL-bump CA is generated per deployment at first boot and never committed;\n  treat the private key under `config/` as sensitive and never share it.\n- Set strong admin credentials. The proxy only accepts RFC1918/localhost clients\n  by default - if you expose port 3128 publicly, put it behind authentication or\n  a private network (e.g. the Tailscale sidecar).\n- Sensitive settings (webhook URLs, tokens) are encrypted at rest; JWT secrets\n  and the encryption key are generated and persisted under `data/`.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md). Issues and pull requests are welcome.\n\n## License\n\n[MIT](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabriziosalmi%2Fsecure-proxy-manager","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffabriziosalmi%2Fsecure-proxy-manager","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabriziosalmi%2Fsecure-proxy-manager/lists"}