From f7f0a124f86e7f2d7feaae1fde7e04496edbab88 Mon Sep 17 00:00:00 2001 From: MarekWo Date: Mon, 3 Aug 2026 08:57:06 +0200 Subject: [PATCH] docs(https): explain how to require a login, not just that you can The guide has listed Access Lists under "Why bother" since it was written, but Step 5 covered only Force SSL, MC_TRUST_PROXY and MC_BIND_ADDRESS - all of which close the LAN route and none of which help an instance published to the internet. So the one step that matters most there was the one the reader had to invent. Adds the walkthrough, and draws the line between the two ideas the old text ran together: a login keeps strangers out, closing port 5000 stops the local network reaching the app around the proxy - and around the login. Notes that MC_BIND_ADDRESS binds the port to loopback rather than removing it, and that leaving it open is the right call when testers use the LAN address, as they do on this project's test server. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0187QpThw1Y8n7aYDjHtMHax --- docs/https-setup.md | 51 +++++++++++++++++++++++++++++++++++++++++++-- docs/whatsnew.md | 2 ++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/docs/https-setup.md b/docs/https-setup.md index 0e4bf64..7676f3f 100644 --- a/docs/https-setup.md +++ b/docs/https-setup.md @@ -18,7 +18,8 @@ changes. mc-webui as an app on a phone, and to browser notifications. - **A real hostname.** `https://mesh.example.com` instead of an IP and a port number. - **Optional password protection.** NPM's *Access Lists* can put HTTP authentication in - front of mc-webui, which has no login of its own. + front of mc-webui, which has no login of its own — see *Require a login* in Step 5. + This is what makes publishing the instance to the internet reasonable. ## How it fits together @@ -276,12 +277,44 @@ updates untouched. ## Step 5 — Tighten it up (optional) -Once HTTPS works, three optional steps make it the only way in. +Once HTTPS works, four optional steps make it the only way in. + +Two of them do different jobs, and it is worth being clear about which is which: +**a login** keeps strangers out of an instance published to the internet, while +**closing port 5000** keeps your own network from reaching the app around the proxy. +Publishing to the internet wants both. **Force HTTPS.** On the proxy host's SSL tab, enable **Force SSL** so plain HTTP requests redirect. Leave HSTS off until you are sure everything works — HSTS is remembered by browsers for months and is awkward to undo. +**Require a login.** mc-webui has no accounts of its own, so anyone who can reach it can +use it. If the instance is published to the internet, this is the step that matters most. +NPM calls it an **Access List**, and it adds the browser's own username/password prompt in +front of everything. + +1. **Access Lists** → **Add Access List** +2. On **Details**, give it a name (`mc-webui` will do). Leave **Satisfy Any** off — with it + on, satisfying *any one* condition is enough, which is not what you want when the + password is the only condition. Leave **Pass Auth to Upstream** off too; mc-webui does + not read the `Authorization` header, so there is nothing to pass on +3. On **Authorizations**, add a username and password. Add more than one if several people + use the instance — each gets their own +4. **Rules** is optional: it allows or denies whole IP addresses and ranges, on top of the + password +5. Save, then go to **Hosts → Proxy Hosts**, open your host's **⋮ → Edit**, and set + **Access List** to the one you just created. Save + +The host's **ACCESS** column should now name your list instead of *Publicly Accessible*. +Every path is covered — the interface, the API, and the websocket — so there is no way in +that skips the prompt. + +> **Check the Android app after turning this on.** [App version 1.3](android-app.md) or +> newer handles the prompt; older versions do not and will show the proxy's bare "401 +> Authorization Required" page instead. Either way, an app that was *already running* when +> you enabled the login will not notice — it keeps using the old credentials and quietly +> stops updating. Close it and open it again. + **Let the app see the real client.** In `.env`: ```ini @@ -304,6 +337,14 @@ The proxy is unaffected — it reaches the app over the internal Docker network. **after** HTTPS works, not before, or you will lock yourself out. To undo it, delete the line and run `docker compose up -d`. +This matters more than it looks once you have set up a login. Port 5000 bypasses the proxy +entirely, and with it the password, the certificate and the access log — so as long as it +answers on the network, the login only guards one of the two doors. Note the port is not +removed, only bound to the loopback address: anyone with a shell on the server can still +reach the app there without a password. If your testers still use `http://:5000` +on the LAN, as on this project's own test server, leave this one off and rely on the +login instead. + Apply either change with `docker compose up -d`. ## The Android app @@ -316,6 +357,10 @@ In practice: - Option C (self-signed) does **not** work in the app. Android's WebView does not trust certificates you added to the phone manually, and there is no way around that from inside the app. Use the app over `http://` on the LAN, or move to Option B. +- An **Access List** works from app version 1.3 on: the app asks for the username and + password on the first connection and remembers them per server address. Older versions + cannot answer the prompt at all. See *Servers that ask for a password* in + [android-app.md](android-app.md). ## Updating and removing @@ -347,6 +392,8 @@ unreachable from the network. The proxy's data directories are left in place; de | HTTPS works by hostname but not by IP address | The proxy refuses connections that carry no hostname. See "Browsing by IP address" above. | | After mounting `npm-default-site.conf`, *nothing* answers — not even port 81 | The mount has `:ro`. Remove it and run `docker compose up -d`. | | Locked out after setting `MC_BIND_ADDRESS` | On the server itself: remove the line from `.env` and run `docker compose up -d`. | +| The login prompt never appears, and the site opens as before | The Access List exists but is not attached to the proxy host. **Hosts → Proxy Hosts** — the **ACCESS** column must name your list, not *Publicly Accessible*. | +| The Android app shows "401 Authorization Required", or stops updating after you enable a login | App older than 1.3 cannot answer the prompt — update it. If it is 1.3 or newer, it was running when you turned the login on: close it and open it again. | | Proxy container keeps restarting | Check `docker compose logs npm`. A common cause is `./data/npm` sitting on a synced folder (Synology Drive, Dropbox) — the sync client corrupts its SQLite database. Move it with `NPM_DATA_DIR`. | ## See also diff --git a/docs/whatsnew.md b/docs/whatsnew.md index 09e96f3..370d441 100644 --- a/docs/whatsnew.md +++ b/docs/whatsnew.md @@ -12,6 +12,8 @@ For deep technical notes, see [architecture.md](architecture.md). For the full g ### Features +- **The HTTPS guide now explains how to put a password in front of mc-webui.** The guide always mentioned that Nginx Proxy Manager can do this — mc-webui has no accounts of its own — but never said how, which made the one step that actually matters for an instance published to the internet the one step you had to work out yourself. [https-setup.md](https-setup.md) now walks through creating an **Access List** and attaching it to your proxy host, and is clearer about a distinction that is easy to miss: requiring a login keeps strangers out, while closing port 5000 (`MC_BIND_ADDRESS=127.0.0.1`) stops your own network from reaching the app around the proxy — and therefore around the password. Publishing to the internet wants both; a server whose testers still use `http://
:5000` wants only the first. + - **The Android app can now sign in to a server that asks for a password.** Putting mc-webui behind a reverse proxy is what makes publishing it to the internet reasonable, and the usual next step is to make that proxy demand a username and password — an **Access List** in Nginx Proxy Manager, for instance. The app had no answer for that: it showed the proxy's bare "401 Authorization Required" page and stopped there, which meant the phone was the one thing you could not protect. It now asks for the login the first time it connects and remembers it per server address, so later launches go straight in; get it wrong and the same prompt returns with the username already filled in. This appears **only** when your server actually asks for a password — an instance without one, local `http://` or a proxy with no access control, behaves exactly as it always has and never shows this screen. One thing worth knowing: if you turn the password on, or change it, while the app is already open, the app cannot notice mid-session — close it and open it again. This is **app version 1.3** — install the new `.apk` over the one you have; it is signed with the same key, so it goes in as an update and keeps your server address. See [android-app.md](android-app.md). ---