Skip to content

Put an app behind a DedNets login

(Since v0.10.0)

A published app is public by default: anyone with the URL can open it. Turning on app authentication puts DedNets itself in front of it. A visitor has to be signed in to DedNets and be either you (the owner) or an account you explicitly allowed. Everyone else is stopped at the DedNets edge and never reaches your machine at all - no request is forwarded, so an unauthenticated visitor cannot so much as fingerprint what is running.

This is what you want for a private dashboard, an internal tool, a staging site, or anything that has no login of its own and should not be on the open internet.

Pick whichever fits how the service runs. All four end up in the same place.

Open the service in Services and use the Who can open this app switch under its hostname list. (Since v0.10.1 you can turn it on for a service that is already published — before that it could only be chosen while publishing.)

The block says who can open the app right now — Anyone with the link, or Only accounts you allow — and, since v0.11.2, lists both the hostnames it covers and the accounts it allows: you as the owner, plus everyone on the viewer list. Manage access adds and removes them.

The gate is per app, so it applies to every name of that service that goes through the DedNets edge. It reads n/a for a TCP/UDP app or one with no edge-served name, because our HTTP edge is not in those paths, and it is locked for an app published from the host’s own config or a container label — that is where its setting lives, and the Console will never turn a gate off that a daemon declared.

Turning the gate on applies immediately. Turning it off publishes the app to anyone who knows the address, so the Console asks you to confirm first (since v0.11.2) and names every hostname it is about to open; the accounts you allowed are kept and apply again if you turn the gate back on. If the host happens to be offline the change takes effect once it reconnects, and the Console says so rather than letting the switch appear to snap back.

You always have access to your own apps, and you are never on the list. Everyone else needs a DedNets account and an entry:

Terminal window
dednetsctl viewer add alice/homelab/dash -user bob
dednetsctl viewer list alice/homelab/dash
dednetsctl viewer rm alice/homelab/dash -user bob

or, in the Console, Manage access on the service’s access block → Allow.

Since v0.13.7 that dialog lists everyone who is allowed in a table — username and the account’s email, so you can tell two similar names apart — and the field above it searches accounts as you type, offering only the ones that are not on the list yet. Pick one and it is allowed straight away. Typing a username in full and pressing Enter still works. The search answers with usernames only, never with the email of an account you have not allowed.

viewer is deliberately not the same thing as sharing an app. A share also lets the other account build proxy ports to your app and re-publish it on their own gateway. A viewer may only open the website.

Removing someone applies on their next click: DedNets re-checks the list on every single request rather than trusting the sign-in they already have, so there is no session to wait out.

  1. They open the app’s normal address.
  2. DedNets sends them to a sign-in page that names the app and who publishes it, so they can see what they are signing in to before they type anything.
  3. They sign in with their DedNets account (password or emailed code, whichever your Console offers).
  4. They land back on the exact page they asked for.

If they are signed in but not on your list, they get a plain “you don’t have access” page naming the account they are signed in as, and a button to sign in as someone else. They never reach your service.

A visitor stays signed in to the app for 12 hours by default, per hostname. https://<your app>/.dednets/auth/logout clears that and sends them back to the app’s front page; it does not sign them out of DedNets itself.

Once a visitor is through the gate, DedNets tells your app who they are. Every proxied request carries:

Header Value Example
X-DedNets-Auth always dednets on an authenticated request dednets
X-DedNets-User the visitor’s DedNets username bob
X-DedNets-Email their verified account email; may be absent bob@example.com

These headers cannot be spoofed by a visitor. The DedNets edge deletes any client-supplied copy of all three on every request it proxies - including requests to apps that are not gated, where the headers are therefore guaranteed to be absent rather than attacker-controlled. If your app sees X-DedNets-User, DedNets put it there.

So you can skip your app’s own login screen entirely and trust the header.

app.use((req, res, next) => {
const user = req.get("x-dednets-user");
if (!user) return res.status(401).send("Not signed in through DedNets");
req.user = { name: user, email: req.get("x-dednets-email") || null };
next();
});
app.get("/", (req, res) => res.send(`Hello ${req.user.name}`));
from flask import Flask, request, abort, g
app = Flask(__name__)
@app.before_request
def dednets_user():
g.user = request.headers.get("X-DedNets-User")
g.email = request.headers.get("X-DedNets-Email")
if not g.user:
abort(401)
@app.get("/")
def index():
return f"Hello {g.user}"
func handler(w http.ResponseWriter, r *http.Request) {
user := r.Header.Get("X-DedNets-User")
if user == "" {
http.Error(w, "not signed in through DedNets", http.StatusUnauthorized)
return
}
email := r.Header.Get("X-DedNets-Email") // may be empty
fmt.Fprintf(w, "Hello %s <%s>", user, email)
}

App authentication is an HTTP gate that runs in the DedNets edge and, since v0.12.0, on your own web-enabled gateways. Three things go around it by design:

  • Gateway ports. A gateway forwards raw connections on your own machine and never touches our edge, so a gateway port reaches the app unauthenticated and with no identity header. Do not publish a gated app on a gateway port unless you meant to.
  • Proxy ports. A proxy port is a private mesh connection between your own hosts. There is no HTTP request for a gate to look at.
  • Custom domains served by your own entrypoint node. A custom domain on the DedNets edge front is gated normally, and so is one on the My Global Web Servers door (your web-enabled gateways run the same gate with the same headers). One served by your own entrypoint (front=node) is not, and DedNets refuses to combine the two: you cannot bind a node-front domain to a gated app, and you cannot gate an app that already has one. Use the edge or the global front for anything you want protected - see Use your own domain.

Raw TCP and UDP apps cannot be gated at all: there is no HTTP request to authenticate. -auth dednets on a tcp or udp app is refused rather than silently ignored.