Mehrere Dienste hinter einem Port: Pfade statt Domainnamen

Hintergrund aus der Praxis: nach aussen ist nur ein Port frei, auf dem
Server laufen aber Checkmk und Grafana und noch ein Dienst. Die Frage
war, ob sich statt eines Domainnamens auch /irgendeinname nutzen laesst.

Ja - der Proxy Manager kennt dafuer "Custom locations", und die API
nimmt sie als locations-Array entgegen. Geprueft am Schema von v2.15.1
(components/proxy-host-object.json) und an der Vorlage _location.conf,
aus der nginx den location-Block erzeugt.

  proxy add --domain 10.0.0.5 --forward-port 5000 --cert self \
            --location /grafana=127.0.0.1:3000 \
            --location /sonstwas=127.0.0.1:8080

--location ist mehrfach angebbar, versteht /pfad=host:port, wahlweise
mit Schema (https://) und mit einem Pfad, der beim Ziel vorangestellt
wird (/alt=host:3000/basis). Ungueltige Angaben werden abgewiesen.
Ebenfalls geprueft: als --domain ist eine IP-Adresse zulaessig, ein
DNS-Name ist nicht noetig (das Muster im Schema laesst alles ausser
einer Handvoll Sonderzeichen zu).

Neu dazu "proxy show --domain X | --id N", das den Eintrag samt allen
Pfaden auflistet - noetig, weil --update den Eintrag vollstaendig
ersetzt und man die bestehenden Pfade wieder mitgeben muss.

Im Assistenten fragt die Oberflaeche nach dem Zertifikat, ob weitere
Dienste unter demselben Namen liegen sollen, und nimmt dann Pfad und
Ziel im Wechsel auf, bis ein Feld leer bleibt.

Dokumentiert als Abschnitt 10.3 in beiden Sprachfassungen, mit dem
Fallstrick, der sonst Stunden kostet: ein Dienst hinter einem Unterpfad
muss seinen Basispfad kennen. Fuer Grafana sind das root_url und
serve_from_sub_path; ohne die bleibt die Seite weiss, weil das
JavaScript unter /public statt /grafana/public gesucht wird. Checkmk
ist nicht betroffen, es liegt ohnehin unter /<site>/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-20 17:56:04 +02:00
co-authored by Claude Opus 5
parent fd96126199
commit e7eb1c3be9
3 changed files with 329 additions and 17 deletions
+99 -5
View File
@@ -1106,6 +1106,7 @@ HTTPS, enables HTTP/2 and websockets, and sets the recommended
| `proxy list` | existing proxy hosts |
| `proxy certs` | existing certificates with expiry date |
| `proxy add …` | create a proxy host (`--update` replaces an existing one) |
| `proxy show --domain X` | details including additional paths |
| `proxy del --id N` | delete a proxy host |
Options for `proxy add`:
@@ -1119,6 +1120,7 @@ Options for `proxy add`:
| `--cert self\|letsencrypt\|none` | `self` | certificate |
| `--update` | – | replace an existing entry |
| `--advanced TEXT` | see below | custom nginx block |
| `--location /P=HOST:PORT` | – | another service under a path, repeatable – see [10.3](#103-several-services-behind-one-port-paths-instead-of-names) |
| `--no-force-ssl`, `--no-http2`, `--no-websockets`, `--no-block-exploits` | – | switches |
#### The `advanced_config` block
@@ -1162,7 +1164,99 @@ Manager → ">> Proxy-Hosts anlegen/verwalten"**.
> created anew each time – otherwise dozens of entries with the same name pile
> up in the Proxy Manager.
### 10.3 If you do want the web interface
### 10.3 Several services behind one port (paths instead of names)
The typical situation: only **one port** is reachable from outside, but the
server runs several interfaces – Checkmk, Grafana, maybe something else.
Instead of needing a separate name per service, they can be told apart by
**path**:
```
https://10.0.0.5/cmk/ -> Checkmk (127.0.0.1:5000)
https://10.0.0.5/grafana -> Grafana (127.0.0.1:3000)
https://10.0.0.5/other -> another service
```
This is exactly what the Proxy Manager is for. In one call:
```bash
sudo ./scripts/cmk-manage.sh proxy add \
--domain 10.0.0.5 \
--forward-port 5000 \
--cert self \
--location /grafana=127.0.0.1:3000 \
--location /other=127.0.0.1:8080
```
* `--domain` may be an **IP address** – a DNS name is not required. (The
interface accepts anything except a handful of special characters.)
* `--forward-port` is the target for everything **not** covered by a path –
Checkmk here.
* `--location` can be given any number of times. Syntax `/path=host:port`,
optionally with a scheme (`https://host:port`) and with a path prefixed at
the target (`/alt=host:3000/base`).
Inspect the result with:
```bash
./scripts/cmk-manage.sh proxy show --domain 10.0.0.5
```
In the interface the wizard asks about the certificate, whether further
services should live under the same name, and then takes path and target in
turn until you leave a field empty.
> **The path list is absolute.** `proxy add --update` replaces the entry
> including all paths. So when adding a path later, pass the existing ones
> again – `proxy show` tells you which those are.
#### What the services themselves need to know
A service behind a sub-path otherwise generates links to `/` that lead nowhere.
**Checkmk** is unaffected – it lives under `/<site>/` anyway. Others need a
setting:
**Grafana** (`/etc/grafana/grafana.ini` or `GF_*` environment variables):
```ini
[server]
domain = 10.0.0.5
root_url = %(protocol)s://%(domain)s/grafana/
serve_from_sub_path = true
```
As a container:
```yaml
environment:
GF_SERVER_ROOT_URL: https://10.0.0.5/grafana/
GF_SERVER_SERVE_FROM_SUB_PATH: "true"
```
Without it Grafana loads but looks for its JavaScript under
`https://10.0.0.5/public/...` instead of `/grafana/public/...` – the page stays
blank. The same applies to any other service: it has to know its base path.
#### Why the Proxy Manager at all
All containers run on the host network, so the services listen directly on
their ports (Checkmk 5000, Grafana 3000, …). You expose **none** of them –
only 443. Behind it the Proxy Manager distributes by path and handles HTTPS
termination with one certificate for all services.
```
Internet / LAN
|
:443 (the only open port)
|
NGINX Proxy Manager
| | |
/cmk/ /grafana /other
| | |
:5000 :3000 :8080 (localhost only)
```
### 10.4 If you do want the web interface
1. open `http://<server>:81` – or whichever port is set in `NPM_ADMIN_PORT`
(see [chapter 12](#12-moving-the-admin-interface-to-a-different-port)); if
@@ -1170,7 +1264,7 @@ Manager → ">> Proxy-Hosts anlegen/verwalten"**.
[chapter 11](#11-configuring-the-proxy-manager-locally-terminal-browser)
2. log in with `NPM_ADMIN_EMAIL` / `NPM_ADMIN_PASSWORD`
### 10.4 Creating a proxy host in the browser
### 10.5 Creating a proxy host in the browser
**Hosts → Proxy Hosts → Add Proxy Host**
@@ -1193,7 +1287,7 @@ proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
```
### 10.5 Option A – Let's Encrypt (publicly reachable server)
### 10.6 Option A – Let's Encrypt (publicly reachable server)
Prerequisites: the DNS name resolves publicly to this server and **port 80 is
reachable from the internet** (HTTP-01 challenge).
@@ -1213,7 +1307,7 @@ project directory.
For an internal DNS zone without public access, use the **DNS challenge**
(SSL → *Use a DNS Challenge* → select provider, store the API token).
### 10.6 Option B – self-signed certificate (internal environment)
### 10.7 Option B – self-signed certificate (internal environment)
Creating one:
@@ -1366,7 +1460,7 @@ work reliably this way.
For exactly this case, use the Proxy Manager's REST interface. Create the
certificate as described in
[chapter 10.6](#106-option-b--self-signed-certificate-internal-environment), then:
[chapter 10.7](#107-option-b--self-signed-certificate-internal-environment), then:
```bash
NPM=http://127.0.0.1:81