From eef3ca0295950651f40e55d8baa0fa4c6bf17820 Mon Sep 17 00:00:00 2001 From: Damien Duportal Date: Mon, 9 Dec 2019 18:08:04 +0100 Subject: [PATCH] Improve documentation for ACME/Let's Encrypt --- docs/content/https/.markdownlint.json | 4 + docs/content/https/acme.md | 67 +++++++++++++- .../include-acme-multiple-domains-example.md | 88 +++++++++++++++++++ ...acme-multiple-domains-from-rule-example.md | 72 +++++++++++++++ .../include-acme-single-domain-example.md | 72 +++++++++++++++ docs/content/index.md | 2 +- docs/content/routing/providers/marathon.md | 2 +- docs/mkdocs.yml | 2 +- 8 files changed, 303 insertions(+), 6 deletions(-) create mode 100644 docs/content/https/.markdownlint.json create mode 100644 docs/content/https/include-acme-multiple-domains-example.md create mode 100644 docs/content/https/include-acme-multiple-domains-from-rule-example.md create mode 100644 docs/content/https/include-acme-single-domain-example.md diff --git a/docs/content/https/.markdownlint.json b/docs/content/https/.markdownlint.json new file mode 100644 index 000000000..3ad8a7f24 --- /dev/null +++ b/docs/content/https/.markdownlint.json @@ -0,0 +1,4 @@ +{ + "extends": "../../.markdownlint.json", + "MD041": false +} diff --git a/docs/content/https/acme.md b/docs/content/https/acme.md index 7602bc244..ef476df82 100644 --- a/docs/content/https/acme.md +++ b/docs/content/https/acme.md @@ -8,6 +8,45 @@ You can configure Traefik to use an ACME provider (like Let's Encrypt) for autom !!! warning "Let's Encrypt and Rate Limiting" Note that Let's Encrypt API has [rate limiting](https://letsencrypt.org/docs/rate-limits). + Use Let's Encrypt staging server with the [`caServer`](#caserver) configuration option + when experimenting to avoid hitting this limit too fast. + +## Certificate Resolvers + +Traefik requires you to define "Certificate Resolvers" in the [static configuration](../getting-started/configuration-overview.md#the-static-configuration), +which are responsible for retrieving certificates from an ACME server. + +Then, each ["router"](../routing/routers/index.md) is configured to enable TLS, +and is associated to a certificate resolver through the [`tls.certresolver` configuration option](../routing/routers/index.md#certresolver). + +Certificates are requested for domain names retrieved from the router's [dynamic configuration](../getting-started/configuration-overview.md#the-dynamic-configuration). + +You can read more about this retrieval mechanism in the following section: [ACME Domain Definition](#domain-definition). + +## Domain Definition + +Certificate resolvers request certificates for a set of the domain names +inferred from routers, with the following logic: + +- If the router has a [`tls.domains`](../routing/routers/index.md#domains) option set, + then the certificate resolver uses the `main` (and optionally `sans`) option of `tls.domains` to know the domain names for this router. + +- If no [`tls.domains`](../routing/routers/index.md#domains) option is set, + then the certificate resolver uses the [router's rule](../routing/routers/index.md#rule), + by checking the `Host()` matchers. + Please note that [multiple `Host()` matchers can be used](../routing/routers/index.md#certresolver)) for specifying multiple domain names for this router. + +Please note that: + +- When multiple domain names are inferred from a given router, + only **one** certificate is requested with the first domain name as the main domain, + and the other domains as ["SANs" (Subject Alternative Name)](https://en.wikipedia.org/wiki/Subject_Alternative_Name). + +- As [ACME V2 supports "wildcard domains"](#wildcard-domains), + any router can provide a [wildcard domain](https://en.wikipedia.org/wiki/Wildcard_certificate) name, as "main" domain or as "SAN" domain. + +Please check the [configuration examples below](#configuration-examples) for more details. + ## Configuration Examples ??? example "Enabling ACME" @@ -75,6 +114,26 @@ You can configure Traefik to use an ACME provider (like Let's Encrypt) for autom --8<-- "content/https/ref-acme.txt" ``` +??? example "Single Domain from Router's Rule Example" + + * A certificate for the domain `company.com` is requested: + + --8<-- "content/https/include-acme-single-domain-example.md" + +??? example "Multiple Domains from Router's Rule Example" + + * A certificate for the domains `company.com` (main) and `blog.company.org` + is requested: + + --8<-- "content/https/include-acme-multiple-domains-from-rule-example.md" + +??? example "Multiple Domains from Router's `tls.domain` Example" + + * A certificate for the domains `company.com` (main) and `*.company.org` (SAN) + is requested: + + --8<-- "content/https/include-acme-multiple-domains-example.md" + ## Automatic Renewals Traefik automatically tracks the expiry date of ACME certificates it generates. @@ -327,7 +386,9 @@ certificatesResolvers: [ACME V2](https://community.letsencrypt.org/t/acme-v2-and-wildcard-certificate-support-is-live/55579) supports wildcard certificates. As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/staging-endpoint-for-acme-v2/49605) wildcard certificates can only be generated through a [`DNS-01` challenge](#dnschallenge). -## `caServer` +## More Configuration + +### `caServer` ??? example "Using the Let's Encrypt staging server" @@ -353,7 +414,7 @@ As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/stagi # ... ``` -## `storage` +### `storage` The `storage` option sets the location where your ACME certificates are saved to. @@ -383,7 +444,7 @@ The value can refer to some kinds of storage: - a JSON file -### In a File +#### In a File ACME certificates can be stored in a JSON file that needs to have a `600` file mode . diff --git a/docs/content/https/include-acme-multiple-domains-example.md b/docs/content/https/include-acme-multiple-domains-example.md new file mode 100644 index 000000000..2fe5b156c --- /dev/null +++ b/docs/content/https/include-acme-multiple-domains-example.md @@ -0,0 +1,88 @@ + +```yaml tab="Docker" +## Dynamic configuration +labels: + - traefik.http.routers.blog.rule=Host(`company.com`) && Path(`/blog`) + - traefik.http.routers.blog.tls=true + - traefik.http.routers.blog.tls.certresolver=le + - traefik.http.routers.blog.tls.domains[0].main=company.org + - traefik.http.routers.blog.tls.domains[0].sans=*.company.org +``` + +```yaml tab="Docker (Swarm)" +## Dynamic configuration +deploy: + labels: + - traefik.http.routers.blog.rule=Host(`company.com`) && Path(`/blog`) + - traefik.http.services.blog-svc.loadbalancer.server.port=8080" + - traefik.http.routers.blog.tls=true + - traefik.http.routers.blog.tls.certresolver=le + - traefik.http.routers.blog.tls.domains[0].main=company.org + - traefik.http.routers.blog.tls.domains[0].sans=*.company.org +``` + +```yaml tab="Kubernetes" +--- +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: blogtls +spec: + entryPoints: + - websecure + routes: + - match: Host(`company.com`) && Path(`/blog`) + kind: Rule + services: + - name: blog + port: 8080 + tls: + certResolver: le +``` + +```json tab="Marathon" +labels: { + "traefik.http.routers.blog.rule": "Host(`company.com`) && Path(`/blog`)", + "traefik.http.routers.blog.tls": "true", + "traefik.http.routers.blog.tls.certresolver": "le", + "traefik.http.routers.blog.tls.domains[0].main": "company.com", + "traefik.http.routers.blog.tls.domains[0].sans": "*.company.com", + "traefik.http.services.blog-svc.loadbalancer.server.port": "8080" +} +``` + +```yaml tab="Rancher" +## Dynamic configuration +labels: + - traefik.http.routers.blog.rule=Host(`company.com`) && Path(`/blog`) + - traefik.http.routers.blog.tls=true + - traefik.http.routers.blog.tls.certresolver=le + - traefik.http.routers.blog.tls.domains[0].main=company.org + - traefik.http.routers.blog.tls.domains[0].sans=*.company.org +``` + +```toml tab="File (TOML)" +## Dynamic configuration +[http.routers] + [http.routers.blog] + rule = "Host(`company.com`) && Path(`/blog`)" + [http.routers.blog.tls] + certResolver = "le" # From static configuration + [[http.routers.blog.tls.domains]] + main = "company.org" + sans = ["*.company.org"] +``` + +```yaml tab="File (YAML)" +## Dynamic configuration +http: + routers: + blog: + rule: "Host(`company.com`) && Path(`/blog`)" + tls: + certResolver: le + domains: + - main: "company.org" + sans: + - "*.company.org" +``` diff --git a/docs/content/https/include-acme-multiple-domains-from-rule-example.md b/docs/content/https/include-acme-multiple-domains-from-rule-example.md new file mode 100644 index 000000000..f82cb8e0f --- /dev/null +++ b/docs/content/https/include-acme-multiple-domains-from-rule-example.md @@ -0,0 +1,72 @@ + +```yaml tab="Docker" +## Dynamic configuration +labels: + - traefik.http.routers.blog.rule=(Host(`company.com`) && Path(`/blog`)) || Host(`blog.company.org`) + - traefik.http.routers.blog.tls=true + - traefik.http.routers.blog.tls.certresolver=le +``` + +```yaml tab="Docker (Swarm)" +## Dynamic configuration +deploy: + labels: + - traefik.http.routers.blog.rule=(Host(`company.com`) && Path(`/blog`)) || Host(`blog.company.org`) + - traefik.http.services.blog-svc.loadbalancer.server.port=8080" + - traefik.http.routers.blog.tls=true + - traefik.http.routers.blog.tls.certresolver=le +``` + +```yaml tab="Kubernetes" +--- +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: blogtls +spec: + entryPoints: + - websecure + routes: + - match: (Host(`company.com`) && Path(`/blog`)) || Host(`blog.company.org`) + kind: Rule + services: + - name: blog + port: 8080 + tls: {} +``` + +```json tab="Marathon" +labels: { + "traefik.http.routers.blog.rule": "(Host(`company.com`) && Path(`/blog`)) || Host(`blog.company.org`)", + "traefik.http.routers.blog.tls": "true", + "traefik.http.routers.blog.tls.certresolver": "le", + "traefik.http.services.blog-svc.loadbalancer.server.port": "8080" +} +``` + +```yaml tab="Rancher" +## Dynamic configuration +labels: + - traefik.http.routers.blog.rule=(Host(`company.com`) && Path(`/blog`)) || Host(`blog.company.org`) + - traefik.http.routers.blog.tls=true + - traefik.http.routers.blog.tls.certresolver=le +``` + +```toml tab="File (TOML)" +## Dynamic configuration +[http.routers] + [http.routers.blog] + rule = "(Host(`company.com`) && Path(`/blog`)) || Host(`blog.company.org`)" + [http.routers.blog.tls] + certResolver = "le" # From static configuration +``` + +```yaml tab="File (YAML)" +## Dynamic configuration +http: + routers: + blog: + rule: "(Host(`company.com`) && Path(`/blog`)) || Host(`blog.company.org`)" + tls: + certResolver: le +``` diff --git a/docs/content/https/include-acme-single-domain-example.md b/docs/content/https/include-acme-single-domain-example.md new file mode 100644 index 000000000..f8e087b31 --- /dev/null +++ b/docs/content/https/include-acme-single-domain-example.md @@ -0,0 +1,72 @@ + +```yaml tab="Docker" +## Dynamic configuration +labels: + - traefik.http.routers.blog.rule=Host(`company.com`) && Path(`/blog`) + - traefik.http.routers.blog.tls=true + - traefik.http.routers.blog.tls.certresolver=le +``` + +```yaml tab="Docker (Swarm)" +## Dynamic configuration +deploy: + labels: + - traefik.http.routers.blog.rule=Host(`company.com`) && Path(`/blog`) + - traefik.http.services.blog-svc.loadbalancer.server.port=8080" + - traefik.http.routers.blog.tls=true + - traefik.http.routers.blog.tls.certresolver=le +``` + +```yaml tab="Kubernetes" +--- +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: blogtls +spec: + entryPoints: + - websecure + routes: + - match: Host(`company.com`) && Path(`/blog`) + kind: Rule + services: + - name: blog + port: 8080 + tls: {} +``` + +```json tab="Marathon" +labels: { + "traefik.http.routers.blog.rule": "Host(`company.com`) && Path(`/blog`)", + "traefik.http.routers.blog.tls": "true", + "traefik.http.routers.blog.tls.certresolver": "le", + "traefik.http.services.blog-svc.loadbalancer.server.port": "8080" +} +``` + +```yaml tab="Rancher" +## Dynamic configuration +labels: + - traefik.http.routers.blog.rule=Host(`company.com`) && Path(`/blog`) + - traefik.http.routers.blog.tls=true + - traefik.http.routers.blog.tls.certresolver=le +``` + +```toml tab="Single Domain" +## Dynamic configuration +[http.routers] + [http.routers.blog] + rule = "Host(`company.com`) && Path(`/blog`)" + [http.routers.blog.tls] + certResolver = "le" # From static configuration +``` + +```yaml tab="File (YAML)" +## Dynamic configuration +http: + routers: + blog: + rule: "Host(`company.com`) && Path(`/blog`)" + tls: + certResolver: le +``` diff --git a/docs/content/index.md b/docs/content/index.md index 6f7cdb268..b10303154 100644 --- a/docs/content/index.md +++ b/docs/content/index.md @@ -20,4 +20,4 @@ Developing Traefik, our main goal is to make it simple to use, and we're sure yo !!! info - If you're a business running critical services behind Traefik, know that [Containous](https://containo.us), the company that sponsors Traefik's development, can provide [commercial support](https://containo.us/services/#commercial-support) and develops an [Enterprise Edition](https://containo.us/traefikee/) of Traefik. + If you're a business running critical services behind Traefik, know that [Containous](https://containo.us), the company that sponsors Traefik's development, can provide [commercial support](https://info.containo.us/commercial-services) and develops an [Enterprise Edition](https://containo.us/traefikee/) of Traefik. diff --git a/docs/content/routing/providers/marathon.md b/docs/content/routing/providers/marathon.md index b272b42a0..7bc79a6b7 100644 --- a/docs/content/routing/providers/marathon.md +++ b/docs/content/routing/providers/marathon.md @@ -91,7 +91,7 @@ For example, to change the routing rule, you could add the label ```"traefik.htt See [tls](../routers/index.md#tls) for more information. ```json - "traefik.http.routers.myrouter>.tls": "true" + "traefik.http.routers.myrouter.tls": "true" ``` ??? info "`traefik.http.routers..tls.certresolver`" diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 6e30c5f20..46622f894 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -44,7 +44,7 @@ plugins: - search - exclude: glob: - - include-*.md + - "**/include-*.md" # https://squidfunk.github.io/mkdocs-material/extensions/admonition/ # https://facelessuser.github.io/pymdown-extensions/