From 1f734630b95228dc845a83536a5de19314dc193e Mon Sep 17 00:00:00 2001 From: Damien Duportal Date: Fri, 25 Oct 2019 17:32:04 +0200 Subject: [PATCH] Improve documentation of the router rules for API and dashboard --- docs/content/operations/.markdownlint.json | 4 + docs/content/operations/api.md | 58 ++++++++++--- docs/content/operations/dashboard.md | 99 ++++++++++++++++++++-- 3 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 docs/content/operations/.markdownlint.json diff --git a/docs/content/operations/.markdownlint.json b/docs/content/operations/.markdownlint.json new file mode 100644 index 000000000..3cd5e9a07 --- /dev/null +++ b/docs/content/operations/.markdownlint.json @@ -0,0 +1,4 @@ +{ + "extends": "../../.markdownlint.json", + "MD046": false +} diff --git a/docs/content/operations/api.md b/docs/content/operations/api.md index 24911b440..f1500293a 100644 --- a/docs/content/operations/api.md +++ b/docs/content/operations/api.md @@ -14,7 +14,7 @@ In production, it should be at least secured by authentication and authorization A good sane default (non exhaustive) set of recommendations would be to apply the following protection mechanisms: -* At the transport level: +* At the transport level: NOT publicly exposing the API's port, keeping it restricted to internal networks (as in the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege), applied to networks). @@ -23,13 +23,16 @@ would be to apply the following protection mechanisms: If you enable the API, a new special `service` named `api@internal` is created and can then be referenced in a router. -To enable the API handler: +To enable the API handler, use the following option on the +[static configuration](../getting-started/configuration-overview.md#the-static-configuration): ```toml tab="File (TOML)" +# Static Configuration [api] ``` ```yaml tab="File (YAML)" +# Static Configuration api: {} ``` @@ -37,11 +40,13 @@ api: {} --api=true ``` -And then you will be able to reference it like this: +And then define a routing configuration on Traefik itself with the +[dynamic configuration](../getting-started/configuration-overview.md#the-dynamic-configuration): ```yaml tab="Docker" +# Dynamic Configuration labels: - - "traefik.http.routers.api.rule=PathPrefix(`/api`) || PathPrefix(`/dashboard`)" + - "traefik.http.routers.api.rule=Host(`traefik.domain.com`) - "traefik.http.routers.api.service=api@internal" - "traefik.http.routers.api.middlewares=auth" - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" @@ -49,40 +54,42 @@ labels: ```json tab="Marathon" "labels": { - "traefik.http.routers.api.rule": "PathPrefix(`/api`) || PathPrefix(`/dashboard`)" - "traefik.http.routers.api.service": "api@internal" - "traefik.http.routers.api.middlewares": "auth" + "traefik.http.routers.api.rule": "Host(`traefik.domain.com`)", + "traefik.http.routers.api.service": "api@internal", + "traefik.http.routers.api.middlewares": "auth", "traefik.http.middlewares.auth.basicauth.users": "test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" } ``` ```yaml tab="Rancher" -# Declaring the user list +# Dynamic Configuration labels: - - "traefik.http.routers.api.rule=PathPrefix(`/api`) || PathPrefix(`/dashboard`)" + - "traefik.http.routers.api.rule=Host(`traefik.domain.com`) - "traefik.http.routers.api.service=api@internal" - "traefik.http.routers.api.middlewares=auth" - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" ``` ```toml tab="File (TOML)" +# Dynamic Configuration [http.routers.my-api] - rule="PathPrefix(`/api`) || PathPrefix(`/dashboard`)" + rule="Host(`traefik.domain.com`) service="api@internal" middlewares=["auth"] [http.middlewares.auth.basicAuth] users = [ - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", + "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", ] ``` ```yaml tab="File (YAML)" +# Dynamic Configuration http: routers: api: - rule: PathPrefix(`/api`) || PathPrefix(`/dashboard`) + rule: Host(`traefik.domain.com`) service: api@internal middlewares: - auth @@ -90,10 +97,32 @@ http: auth: basicAuth: users: - - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" + - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0" ``` +??? warning "The router's [rule](../../routing/routers#rule) must catch requests for the URI path `/api`" + Using an "Host" rule is recommended, by catching all the incoming traffic on this host domain to the API. + However, you can also use "path prefix" rule or any combination or rules. + + ```bash tab="Host Rule" + # Matches http://traefik.domain.com, http://traefik.domain.com/api + # or http://traefik.domain.com/hello + rule = "Host(`traefik.domain.com`)" + ``` + + ```bash tab="Path Prefix Rule" + # Matches http://api.traefik.domain.com/api or http://domain.com/api + # but does not match http://api.traefik.domain.com/hello + rule = "PathPrefix(`/api`)" + ``` + + ```bash tab="Combination of Rules" + # Matches http://traefik.domain.com/api or http://traefik.domain.com/dashboard + # but does not match http://traefik.domain.com/hello + rule = "Host(`traefik.domain.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))" + ``` + ### `insecure` Enable the API in `insecure` mode, which means that the API will be available directly on the entryPoint named `traefik`. @@ -135,6 +164,9 @@ api: --api.dashboard=true ``` +!!! warning "With Dashboard enabled, the router [rule](../../routing/routers#rule) must catch requests for both `/api` and `/dashboard`" + Please check the [Dashboard documentation](./dashboard.md#dashboard-router-rule) to learn more about this and to get examples. + ### `debug` _Optional, Default=false_ diff --git a/docs/content/operations/dashboard.md b/docs/content/operations/dashboard.md index 9dc77bb5e..169e136f2 100644 --- a/docs/content/operations/dashboard.md +++ b/docs/content/operations/dashboard.md @@ -28,7 +28,8 @@ There are 2 ways to configure and access the dashboard: This is the **recommended** method. -Start by enabling the dashboard by using the following option from [Traefik's API](./api.md): +Start by enabling the dashboard by using the following option from [Traefik's API](./api.md) +on the [static configuration](../getting-started/configuration-overview.md#the-static-configuration): ```toml tab="File (TOML)" [api] @@ -59,17 +60,103 @@ api: --api.dashboard=true ``` -Then specify a router associated to the service `api@internal` to allow: +Then define a routing configuration on Traefik itself, +with a router attached to the service `api@internal` in the +[dynamic configuration](../getting-started/configuration-overview.md#the-dynamic-configuration), +to allow defining: -- Defining one or more security features through [middlewares](../middlewares/overview.md) +- One or more security features through [middlewares](../middlewares/overview.md) like authentication ([basicAuth](../middlewares/basicauth.md) , [digestAuth](../middlewares/digestauth.md), [forwardAuth](../middlewares/forwardauth.md)) or [whitelisting](../middlewares/ipwhitelist.md). -- Defining your own [HTTP routing rule](../../routing/routers/#rule) for accessing the dashboard, +- A [router rule](#dashboard-router-rule) for accessing the dashboard, through Traefik itself (sometimes referred as "Traefik-ception"). -Please visit the ["Configuration" section of the API documentation](./api.md#configuration) -to learn about configuring a router with the service `api@internal` and enabling the security features. +??? example "Dashboard Dynamic Configuration Examples" + + ```yaml tab="Docker" + # Dynamic Configuration + labels: + - "traefik.http.routers.api.rule=Host(`traefik.domain.com`) + - "traefik.http.routers.api.service=api@internal" + - "traefik.http.routers.api.middlewares=auth" + - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" + ``` + + ```json tab="Marathon" + "labels": { + "traefik.http.routers.api.rule": "Host(`traefik.domain.com`)", + "traefik.http.routers.api.service": "api@internal", + "traefik.http.routers.api.middlewares": "auth", + "traefik.http.middlewares.auth.basicauth.users": "test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" + } + ``` + + ```yaml tab="Rancher" + # Dynamic Configuration + labels: + - "traefik.http.routers.api.rule=Host(`traefik.domain.com`) + - "traefik.http.routers.api.service=api@internal" + - "traefik.http.routers.api.middlewares=auth" + - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" + ``` + + ```toml tab="File (TOML)" + # Dynamic Configuration + [http.routers.my-api] + rule="Host(`traefik.domain.com`) + service="api@internal" + middlewares=["auth"] + + [http.middlewares.auth.basicAuth] + users = [ + "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", + "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + ] + ``` + + ```yaml tab="File (YAML)" + # Dynamic Configuration + http: + routers: + api: + rule: Host(`traefik.domain.com`) + service: api@internal + middlewares: + - auth + middlewares: + auth: + basicAuth: + users: + - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" + - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0" + ``` + +### Dashboard Router Rule + +As underlined in the [documentation for the `api.dashboard` option](./api.md#dashboard), +the [router rule](../routing/routers/index.md#rule) defined for Traefik must match +the path prefixes `/api` and `/dashboard`. + +We recommend to use a "Host Based rule" as ```Host(`traefik.domain.com`)``` to match everything on the host domain, +or to make sure that the defined rule captures both prefixes: + +```bash tab="Host Rule" +# Matches http://traefik.domain.com/api or http://traefik.domain.com/dashboard +rule = "Host(`traefik.domain.com`)" +``` + +```bash tab="Path Prefix Rule" +# Matches http://traefik.domain.com/api , http://domain.com/api or http://traefik.domain.com/dashboard +# but does not match http://traefik.domain.com/hello +rule = "PathPrefix(`/api`) || PathPrefix(`/dashboard`)" +``` + +```bash tab="Combination of Rules" +# Matches http://traefik.domain.com/api or http://traefik.domain.com/dashboard +# but does not match http://traefik.domain.com/hello +rule = "Host(`traefik.domain.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))" +``` ## Insecure Mode