Skip to content

Repository files navigation

Sluice

A self-hosted rotating egress proxy. It routes outbound traffic through addresses you control, refuses internal destinations at dial time, and logs every request.

Sluice does not fetch, render or extract anything. It is what other things reach the internet through — an HTTP CONNECT and SOCKS5 endpoint that any client already knows how to use.

curl -x "http://$SLUICE_KEY:x@proxy:3128" https://api.ipify.org

The Sluice dashboard: 6-hour stats, a Success/Failed traffic chart, and the projects that own the API keys

What it does

Four kinds of egress. A direct connection, an upstream HTTP or SOCKS5 proxy, an SSH tunnel, or a local address bound on your own machine. Each is a route with its own health, and every route is probed against a real endpoint — a successful dial proves a socket bound, not that traffic from it comes back.

Rotation with stickiness. Settings ride in the proxy username, so a client that can only be told a proxy URL can still choose behaviour per request:

$SLUICE_KEY__rot.request      a different route each request
$SLUICE_KEY__sess.job42       the same route for everything tagged job42
$SLUICE_KEY__route.nyc1       pin one route

A default-deny guard at dial time. Loopback, RFC1918, link-local including cloud metadata, carrier-grade NAT and the node's own addresses are refused, whichever route or protocol a caller picked. The guard resolves DNS itself and dials the address it validated, so a name that resolves differently the second time does not get a second answer.

Local address management. Given a block routed to your host, Sluice puts addresses on the interface itself and keeps them in step with the routes table — adding what is missing, removing what is orphaned, repairing a wrong mask.

A dashboard and an audit log. Projects own API keys, each key carries its own rotation and its own allow-list exemption to the default-deny guard, and every request is recorded with its route, bytes and outcome.

Dashboard

A sidebar dashboard: live route health, a rotation self-test that runs real requests through the selection path, projects with per-key settings and allow-lists, a filterable audit log, and a sign-in that explains what a sluice is.

The Sluice sign-in, with the brand's control-the-flow diagram and feature cards

Running it

Sluice is one binary and one SQLite file.

docker run -p 3128:3128 -p 1080:1080 -p 8080:8080 \
  -v sluice-data:/data ghcr.io/jekyo/sluice:latest

The admin password is generated on first start and printed once. Set SLUICE_ADMIN_PASSWORD to choose your own.

On jekyo, jekyo up reads the jekyo.yaml in this repo.

Configuration

Variable Default
SLUICE_ADDR :8080 dashboard and API
SLUICE_PROXY_ADDR :3128 HTTP CONNECT
SLUICE_SOCKS_ADDR :1080 SOCKS5
SLUICE_DB /data/sluice.db
SLUICE_ADMIN_EMAIL admin@localhost
SLUICE_ADMIN_PASSWORD generated printed once at first start
SLUICE_ADDR_BLOCKS unset ranges usable as a source address
SLUICE_PROBE_URL https://api.ipify.org what health checks ask
SLUICE_LOG_RETENTION_DAYS 30

Local addresses

Binding an outbound socket to an address only works if the address is really on an interface. If your block is on-link — the usual case for a range on the same segment as your host — the upstream router resolves each address by ARP, so an address nothing answers for is a silent timeout rather than an error.

Declare the ranges Sluice may use:

SLUICE_ADDR_BLOCKS="185.235.78.0/24,gw=185.235.78.1,dev=enp1s0f1"

Semicolon-separated for several; only the CIDR is required. This is read from the environment at start and cannot be changed from the dashboard, because the process that acts on it holds NET_ADMIN over the host's network namespace.

Two things are worth knowing before you rely on this:

The mask matters and fails silently. An address added with the wrong prefix length sits on the interface looking correct while every connection from it times out. Sluice defaults to the mask your host's own address carries, which is right whenever the range is part of your host's subnet.

A new address is not usable the moment it exists. The upstream router has to learn it, which can take anywhere from seconds to a few minutes. The dashboard waits and tells you when it is actually reachable rather than reporting success the instant the row is written.

Under Kubernetes this needs host networking, and the address work runs in a small addrd sidecar holding NET_ADMIN so the proxy itself stays unprivileged and non-root. A capability does not survive execve for a non-root UID, so granting it to the proxy would not have worked anyway without running it as root.

Security

Sluice is an SSRF engine by design; the interesting question is what it refuses. See SECURITY.md for the threat model and how to report a vulnerability.

Contributing

See CONTRIBUTING.md.

License

MIT. See LICENSE.

About

Self-hosted rotating egress proxy: routes traffic through addresses you control, refuses internal destinations at dial time, logs every request.

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages