Gives every browser on the machine ad and tracker blocking, including WebKitGTK ones like surf that have no extension mechanism and so cannot run uBlock Origin. 80,002 rules from the StevenBlack list load in 82ms and sit in 9MB resident. A proxy receives CONNECT doubleclick.net:443 before any TLS handshake, so a blocked host is refused without decrypting anything. Ads and trackers are third-party hosts, which is why host-level refusal captures nearly all of the weight while leaving traffic sealed: no certificate authority in the trust store, no CA private key on disk, and no visibility into a bank session this program merely relays. What that cannot do is cosmetic filtering and first-party ads, which need TLS interception. That is omitted deliberately, and the README records the reasoning and where the seam would be, rather than leaving it looking like an oversight. CONNECT to a blocked host 403, refused before the handshake CONNECT to anything else tunnelled bytes, untouched plain HTTP to a blocked host 204, so a beacon looks empty not failed plain HTTP otherwise forwarded and relayed Matching is by domain suffix, so a rule for doubleclick.net covers stats.g.doubleclick.net; lookups walk the labels of the requested host rather than the list. Hosts files, bare domain lists and the ||domain^ subset of Adblock syntax are accepted, while rules needing response inspection are skipped rather than half-applied. Allow rules win at any depth, and IP addresses are never blocked, since hosts files are full of them as addresses. Lists cache for a week and fall back to a stale copy when a refresh fails. Connections are capped at 96 with a Drop guard releasing the slot even on panic, and only the loopback interface is bound.
117 lines
4.4 KiB
Markdown
117 lines
4.4 KiB
Markdown
# furst-proxy
|
|
|
|
A filtering HTTP proxy. Point any browser at it and ad and tracker hosts stop
|
|
resolving — including in `surf` and other WebKitGTK browsers, which have no
|
|
extension mechanism and therefore no uBlock Origin.
|
|
|
|
80,000 rules, ~9MB resident, 82ms to start.
|
|
|
|
## It does not decrypt your traffic
|
|
|
|
A proxy receives `CONNECT doubleclick.net:443` **before** any TLS handshake
|
|
happens. That is enough to refuse the connection, so blocking needs no
|
|
certificate authority in your trust store and no visibility into the contents
|
|
of anything — your bank session is a sealed tunnel this program relays without
|
|
being able to read.
|
|
|
|
Ads and trackers are third-party hosts, so refusing hosts is most of the win.
|
|
What it cannot do, by construction:
|
|
|
|
- hide elements cosmetically (uBlock's `##` rules)
|
|
- block ads served from the site's own domain
|
|
- filter inside an HTTPS response
|
|
|
|
Those need TLS interception. That is a deliberate omission, not a missing
|
|
feature — see [the note below](#what-tls-interception-would-add).
|
|
|
|
## Use
|
|
|
|
```sh
|
|
furst-proxy --init # write ~/.config/furst/proxy.toml
|
|
furst-proxy --update # fetch the blocklists
|
|
furst-proxy # run in the foreground
|
|
|
|
furst-proxy --test doubleclick.net # would this be blocked?
|
|
furst-proxy --env # shell exports
|
|
furst-proxy --pac ~/.furst.pac # proxy auto-config file
|
|
```
|
|
|
|
Point a browser at `127.0.0.1:8228` for both HTTP and HTTPS:
|
|
|
|
```sh
|
|
eval "$(furst-proxy --env)" # anything honouring http_proxy
|
|
surf https://example.com/ # WebKitGTK reads the environment
|
|
```
|
|
|
|
For Firefox, set the manual proxy in *Settings → Network Settings*, or load the
|
|
PAC file. Visit **http://furst.proxy/** through the proxy for a status page:
|
|
rules loaded, requests, share blocked, bytes relayed.
|
|
|
|
## What happens to a request
|
|
|
|
| | |
|
|
|---|---|
|
|
| `CONNECT` to a blocked host | `403`, refused before the handshake |
|
|
| `CONNECT` to anything else | tunnelled bytes, untouched |
|
|
| plain HTTP to a blocked host | `204 No Content` |
|
|
| plain HTTP to anything else | forwarded, relayed |
|
|
|
|
Blocked plain-HTTP requests get `204` rather than an error, so a blocked script
|
|
or beacon looks like an empty answer instead of a failure the page retries.
|
|
|
|
## Configuration
|
|
|
|
`~/.config/furst/proxy.toml`:
|
|
|
|
```toml
|
|
port = 8228
|
|
|
|
lists = ["https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"]
|
|
|
|
# Webfont and analytics CDNs: a webfont is bytes, a layout pass and a repaint
|
|
# for no information, which is a bad trade on an old CPU.
|
|
block_heavy = true
|
|
|
|
block = ["extra.tracker.example"]
|
|
allow = ["fonts.gstatic.com"] # wins over everything
|
|
```
|
|
|
|
Lists are cached under `$XDG_CACHE_HOME/furst/lists` for a week; `--update`
|
|
refreshes them. If a refresh fails, the stale copy is used — degraded filtering
|
|
beats none. Hosts files, bare domain lists, and the `||domain^` subset of
|
|
Adblock syntax are all accepted; rules that need response inspection
|
|
(`$third-party`, paths, wildcards) are skipped rather than half-applied.
|
|
|
|
Matching is by domain suffix, so `doubleclick.net` also covers
|
|
`stats.g.doubleclick.net`. Lookups walk the labels of the requested host, so
|
|
cost is the number of dots in it, not the size of the list.
|
|
|
|
## What TLS interception would add
|
|
|
|
A `--mitm` mode would generate a local CA, mint a certificate per host, and
|
|
decrypt each connection so responses could be filtered. It would buy cosmetic
|
|
filtering and first-party ad blocking.
|
|
|
|
It was left out because the trade is poor here:
|
|
|
|
- every HTTPS page would be decrypted by this program, so a bug in it becomes a
|
|
bug in the confidentiality of everything you browse
|
|
- a CA private key on disk is a credential worth stealing
|
|
- certificate minting and validation is where proxies get subtly wrong, and
|
|
getting it wrong silently downgrades security rather than breaking loudly
|
|
- `furst-serve` already renders most pages without a browser engine, so the
|
|
remaining first-party ads are seen rarely
|
|
|
|
If you want it anyway, the seam is `handle()` in `proxy.rs`: after accepting
|
|
`CONNECT` for an unblocked host, terminate TLS with a minted certificate
|
|
instead of tunnelling.
|
|
|
|
## Limits
|
|
|
|
- Only the loopback interface is bound. This is a personal proxy, not a
|
|
network service; do not expose it.
|
|
- Plain HTTP is relayed with `Connection: close`, so no keep-alive on that
|
|
path. HTTPS tunnels are unaffected.
|
|
- At most 96 concurrent connections, so a runaway page cannot exhaust a 4GB
|
|
machine. Beyond that the proxy answers `503`.
|