-
Notifications
You must be signed in to change notification settings - Fork 0
Reaching it from outside
Argus listens on plain HTTP and has one token. That is fine on a LAN or a VPN and not fine on the open internet, so the question is how the phone gets to the machine.
This is worth stating first, because it is where the whole field landed and it is easy to mistake for a dodge.
A single-user tool should not implement identity. It should listen on loopback or on a private network, and let who you are be decided by a layer that does that for a living — Tailscale, Cloudflare Access, Teleport, Pomerium, oauth2-proxy, Authelia. Argus's own token then stops being the front door and becomes defence in depth, which is the right job for a shared secret.
Every comparable tool has arrived at some version of this:
- Jupyter prints exactly the link Argus prints, token and all, and its documentation is emphatic about not exposing it; it moved to a session cookie after first use, and had to add XSRF tokens to pay for it.
-
Syncthing binds to
127.0.0.1by default and refuses to be remotely reachable until you say so, with a password behind it and a reverse proxy in the instructions. - Home Assistant has real accounts and still does not tell you to forward a port: it sells you a relay instead. That is the commercial conclusion of the project with the most users.
-
code-server is a shell in a browser, like this, and its answer is a password in a file,
--auth nonebehind a proxy, and documentation pointing at SSH tunnels and Tailscale.
So the sections below are not five equal options. The first two are the answer; the rest are for when something about your situation rules them out.
Install it on the machine and on the phone and they are on the same private network wherever either of them is. Then:
tailscale serve --bg 8090 # https://<machine>.<tailnet>.ts.netserve puts a real certificate in front of it, which also unlocks the things HTTPS gates:
an installable PWA, the clipboard API, the in-app QR scanner. Nothing is exposed to the
internet — only devices on your tailnet can reach it.
tailscale funnel does expose it publicly. Don't, not with a shell behind it.
ssh -N -L 8090:127.0.0.1:8090 you@machine # then open http://127.0.0.1:8090Perfect from a laptop, awkward from a phone.
If you want a real hostname on the internet, put something in front that decides who before Argus is ever reached. Cloudflare Tunnel with Access is the least work: no open port at all — the machine dials out — and an identity check (email code, Google, whatever your organisation uses) in front of it.
cloudflared tunnel --url http://127.0.0.1:8090Then require an Access policy on that hostname. Teleport, Pomerium, oauth2-proxy and Authelia all do the same job if you would rather host it yourself; the shape is identical — the proxy authenticates, and Argus sees a request that has already been vouched for.
The reason this is better than putting Argus behind plain TLS with a password is not encryption, which both give you. It is that the thing between the internet and a shell becomes an identity system somebody maintains, instead of one shared secret. And Argus's token still sits behind it, so a hole in the proxy is not immediately a shell.
Do not simply forward a port to it. A public address with a shell behind it is found by scanners in hours, and from then on any future weakness in Argus, in uvicorn or in anything it imports is exploitable by everyone rather than by people already inside your network.
Caddy needs no WebSocket configuration:
argus.example.com {
reverse_proxy 127.0.0.1:8090
}nginx does, and it needs the read timeout raised or a quiet terminal drops every minute:
location / {
proxy_pass http://127.0.0.1:8090;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s;
}Serve it at the root of a name, not under a path: the app asks for /api/...
absolutely, so example.com/argus/ will not work. If the proxy is reachable from the
internet, put a second lock in front — basic auth, an identity provider, Cloudflare Access
— because Argus's own lock is a single token with no rate limiting behind it.
Everything above is about a browser reaching this machine. There is a case where nothing above can help, and it is common enough to be worth its own answer: the network only goes one way.
Measured on the pair this was written for — two boxes on the same wire, the same /25, each with the other's MAC in its ARP table, and one of them refusing everything inbound. From one side you can SSH to the other and not back. No configuration on the far side changes that, because the far side never gets to send the first packet.
So a machine can announce itself instead, opening the connection in the direction that already works:
# on the machine nobody can reach
report_to:
url: http://board.internal:8070
token: <the board's registration token>
name: gpu2 # what you want it called on the board
reach: http://gpu2.internal:8090 # where a browser should go, when it can
every: 10What it sends is exactly what GET /api/overview answers — hostname, uptime, load, memory,
the fullest disk, and the session names with which of them is ringing. No file, no path, no
token, no command. It is off unless you configure it, because a machine that phones a
board you did not set up is a surprise.
This is not a trick invented here: it is what Prometheus does in agent mode, and what every dial-out agent does — Tailscale, Cloudflare Tunnel, Teleport, a kubelet registering with its API server. Local pull, remote push.
The reply to that announcement is also the only channel back to such a machine, which is how
a board can ask it to start or stop something
— bounded by the same list, and off unless you say obey_board.
Note what this does not fix: it makes the machine visible on a board, not reachable by your
browser. reach: has to be an address your own device can open, which usually means the
board and the machines are on the same VPN.