knowledge/technology/applications/web/traefik.md
2024-02-12 21:55:34 +01:00

24 KiB

obj website repo
application https://traefik.io https://github.com/traefik/traefik

traefik

Traefik is an open-source Edge Router (Reverse Proxy) that makes publishing your services a fun and easy experience. It receives requests on behalf of your system and finds out which components are responsible for handling them.

Traefik is based on the concept of EntryPoints, Routers, Middlewares and Services.

  • EntryPoints: EntryPoints are the network entry points into Traefik. They define the port which will receive the packets, and whether to listen for TCP or UDP.
  • Routers: A router is in charge of connecting incoming requests to the services that can handle them.
  • Middlewares: Attached to the routers, middlewares can modify the requests or responses before they are sent to your service
  • Services: Services are responsible for configuring how to reach the actual services that will eventually handle the incoming requests.

Docker Compose

version: "3"

services:
  traefik:
    image: traefik:v2.10.4
    ports:
      - 80:80
      - 443:443
      - 28080:8080
    volumes:
      - ./conf:/etc/traefik
      - ./logs:/var/log/traefik/
      - /var/run/docker.sock:/var/run/docker.sock:ro
    restart: unless-stopped

Configuration

Configuration is done via the file traefik.yaml.

Entrypoints

You need to define some entrypoints which accept incoming traffik.

entryPoints:
  web:
    address: ":80"

  websecure:
    address: ":443"

Routers

A router is in charge of connecting incoming requests to the services that can handle them. In the process, routers may use pieces of middleware to update the request, or act before forwarding the request to the service.
Routers can handle HTTP, TCP and UDP traffik.

Example:

http:
  routers:
    my-router:
      rule: "Path(`/foo`)"
      service: service-foo

Entrypoints

If not specified, HTTP routers will accept requests from all defined entry points. If you want to limit the router scope to a set of entry points, set the entryPoints option.

Listen only to specific entrypoints:

http:
  routers:
    Router-1:
      # won't listen to entry point web
      entryPoints:
        - "websecure"
        - "other"
      rule: "Host(`example.com`)"
      service: "service-1"

Rules

Rules are a set of matchers configured with values, that determine if a particular request matches specific criteria. If the rule is verified, the router becomes active, calls middlewares, and then forwards the request to the service.

http:
  routers:
    Router:
      rule: "Host(`example.com`)"

The table below lists all the available matchers:

Rule Description
Headers(`key`, `value`) Check if there is a key key defined in the headers, with the value value
HeadersRegexp(`key`, `regexp`) Check if there is a key key defined in the headers, with a value that matches the regular expression regexp
Host(`example.com`, ...) Check if the request domain (host header value) targets one of the given domains.
HostRegexp(`example.com`, `{subdomain:[a-z]+}.example.com`, ...) Match the request domain
Method(`GET`, ...) Check if the request method is one of the given methods (GET, POST, PUT, DELETE, PATCH, HEAD)
Path(`/path`, `/articles/{cat:[a-z]+}/{id:[0-9]+}`, ...) Match exact request path
PathPrefix(/products/, /articles/{cat:[a-z]+}/{id:[0-9]+}) Match request prefix path
Query(`foo=bar`, `bar=baz`) Match Query String parameters. It accepts a sequence of key=value pairs.
ClientIP(`10.0.0.0/16`, `::1`) Match if the request client IP is one of the given IP/CIDR. It accepts IPv4, IPv6 and CIDR formats.

Middlewares

You can attach a list of middlewares to each HTTP router. The middlewares will take effect only if the rule matches, and before forwarding the request to the service.

http:
  routers:
    my-router:
      rule: "Path(`/foo`)"
      middlewares:
      - authentication
      service: service-foo

Service

Each request must eventually be handled by a service, which is why each router definition should include a service target, which is basically where the request will be passed along to.

In general, a service assigned to a router should have been defined, but there are exceptions for label-based providers.

TLS

When a TLS section is specified, it instructs Traefik that the current router is dedicated to HTTPS requests only (and that the router should ignore HTTP (non TLS) requests). Traefik will terminate the SSL connections (meaning that it will send decrypted data to the services).

http:
  routers:
    Router-1:
      rule: "Host(`foo-domain`) && Path(`/foo-path/`)"
      service: service-id
      # will terminate the TLS request
      tls: {}
  • certResolver
    If certResolver is defined, Traefik will try to generate certificates based on routers Host & HostSNI rules.
    http:
    routers:
      routerfoo:
        rule: "Host(`snitest.com`) && Path(`/foo`)"
        tls:
          certResolver: foo
    
  • domains
    You can set SANs (alternative domains) for each main domain. Every domain must have A/AAAA records pointing to Traefik. Each domain & SAN will lead to a certificate request.
    http:
    routers:
      routerbar:
        rule: "Host(`snitest.com`) && Path(`/bar`)"
        tls:
          certResolver: "bar"
          domains:
            - main: "snitest.com"
              sans:
                - "*.snitest.com"
    

Services

The Services are responsible for configuring how to reach the actual services that will eventually handle the incoming requests.

Example:

http:
  services:
    my-service:
      loadBalancer:
        servers:
        - url: "http://<private-ip-server-1>:<private-port-server-1>/"
        - url: "http://<private-ip-server-2>:<private-port-server-2>/"

tcp:
  services:
    my-service:
      loadBalancer:
        servers:
        - address: "<private-ip-server-1>:<private-port-server-1>"
        - address: "<private-ip-server-2>:<private-port-server-2>"

Load Balancing

Traefik can load balance across multiple servers with the loadBalancer.servers list.

http:
  services:
    my-service:
      loadBalancer:
        servers:
          - url: "http://server1:8080"
          - url: "http://server2:8080

Servers Transport

serversTransport allows to reference a ServersTransport configuration for the communication between Traefik and your servers.

http:
  services:
    Service01:
      loadBalancer:
        serversTransport: mytransport

Define a serverTransport configuration:

http:
  serversTransports:
    mytransport:

You can define root CAs used for verifying SSL between traefik and your servers:

http:
  serversTransports:
    mytransport:
      rootCAs:
        - foo.crt
        - bar.crt

Or skip SSL verification:

http:
  serversTransports:
    mytransport:
      insecureSkipVerify: true

TLS

Traefik supports HTTPS & TLS, which concerns roughly two parts of the configuration: routers, and the TLS connection (and its underlying certificates).

Traefik requires you to define "Certificate Resolvers" in the static configuration, which are responsible for retrieving certificates from an ACME server.

certificatesResolvers:
  myresolver:
    # Enable ACME (Let's Encrypt): automatic SSL.
    acme:
      email: "test@example.com"
      # File or key used for certificates storage.
      storage: "acme.json"

      # The certificates' duration in hours.
      # It defaults to 2160 (90 days) to follow Let's Encrypt certificates' duration.
      #
      # Optional
      # Default: 2160
      #
      certificatesDuration: 2160

      # Use a TLS-ALPN-01 ACME challenge.
      #
      # Optional (but recommended)
      #
      tlsChallenge:

      # Use a HTTP-01 ACME challenge.
      #
      # Optional
      #
      httpChallenge:

        # EntryPoint to use for the HTTP-01 challenges.
        #
        # Required
        #
        entryPoint: web

      # Use a DNS-01 ACME challenge rather than HTTP-01 challenge.
      # Note: mandatory for wildcard certificate generation.
      #
      # Optional
      #
      dnsChallenge:

        # DNS provider used.
        #
        # Required
        #
        provider: digitalocean

        # By default, the provider will verify the TXT DNS challenge record before letting ACME verify.
        # If delayBeforeCheck is greater than zero, this check is delayed for the configured duration in seconds.
        # Useful if internal networks block external DNS queries.
        #
        # Optional
        # Default: 0
        #
        delayBeforeCheck: 0

        # Use following DNS servers to resolve the FQDN authority.
        #
        # Optional
        # Default: empty
        #
        resolvers
         - "1.1.1.1:53"
         - "8.8.8.8:53"

Middlewares

Attached to the routers, pieces of middleware are a means of tweaking the requests before they are sent to your service (or before the answer from the services are sent to the clients).

http:
  routers:
    router1:
      service: myService
      middlewares:
        - "foo-add-prefix"
      rule: "Host(`example.com`)"

  middlewares:
    foo-add-prefix:
      addPrefix:
        prefix: "/foo"

  services:
    service1:
      loadBalancer:
        servers:
          - url: "http://127.0.0.1:80"

HTTP Middlewares

Add Prefix

The AddPrefix middleware updates the path of a request before forwarding it. So http://domain/path would be http://domain/prefix/path if used.

http:
  middlewares:
    add-foo:
      addPrefix:
        prefix: "/prefix"
BasicAuth

The BasicAuth middleware grants access to services to authorized users only.

  • users
    The users option is an array of authorized users. Each user must be declared using the name:hashed-password format.
    http:
      middlewares:
        test-auth:
          basicAuth:
            users:
              - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
              - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"
    
  • usersFile
    The usersFile option is the path to an external file that contains the authorized users for the middleware.
    http:
    middlewares:
      test-auth:
        basicAuth:
          usersFile: "/path/to/my/usersfile"
    
  • headerField
    You can define a header field to store the authenticated user using the headerFieldoption.
    http:
    middlewares:
      my-auth:
        basicAuth:
          # ...
          headerField: "X-WebAuth-User"
    
  • removeHeader
    Set the removeHeader option to true to remove the authorization header before forwarding the request to your service. (Default value is false.)
Compress

Compress Responses before Sending them to the Client. The Compress middleware uses gzip compression.

  • excludedContentTypes specifies a list of content types to compare the Content-Type header of the incoming requests and responses before compressing. The responses with content types defined in excludedContentTypes are not compressed.
  • minResponseBodyBytes specifies the minimum amount of bytes a response body must have to be compressed. The default value is 1024, which should be a reasonable value for most cases.
http:
  middlewares:
    test-compress:
      compress:
        excludedContentTypes:
          - text/event-stream
        minResponseBodyBytes: 1200
ContentType

The Content-Type middleware - or rather its autoDetect option - specifies whether to let the Content-Type header, if it has not been defined by the backend, be automatically set to a value derived from the contents of the response.

http:
  middlewares:
    autodetect:
      contentType:
        autoDetect: true
DigestAuth

Adding Digest Authentication. The configuration options are the same as BasicAuth middleware.

http:
  middlewares:
    test-auth:
      digestAuth:
        users:
          - "test:traefik:a2688e031edb4be6a3797f3882655c05"
          - "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"
Errors

The Errors middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes.

http:
  middlewares:
    test-errors:
      errors:
        status:
          - "500"
          - "501"
          - "503"
          - "505-599"
        service: serviceError
        query: "/{status}.html"
  • status
    The status option defines which status or range of statuses should result in an error page.
    The status code ranges are inclusive (505-599 will trigger with every code between 505 and 599, 505 and 599 included).
  • service
    The service that will serve the new requested error page.
  • query
    The URL for the error page (hosted by service).
    There are multiple variables that can be placed in the query option to insert values in the URL.
    The table below lists all the available variables and their associated values.
Variable Value
{status} The response status code.
{url} The escaped request URL.
ForwardAuth

The ForwardAuth middleware delegates authentication to an external service. If the service answers with a 2XX code, access is granted, and the original request is performed. Otherwise, the response from the authentication server is returned.

http:
  middlewares:
    test-auth:
      forwardAuth:
        address: "https://example.com/auth"
Forward-Request Headers

The following request properties are provided to the forward-auth target endpoint as X-Forwarded- headers:

Property Forward-Request Header
HTTP Method X-Forwarded-Method
Protocol X-Forwarded-Proto
Host X-Forwarded-Host
Request URI X-Forwarded-Uri
Source IP-Address X-Forwarded-For
  • address:
    The address option defines the authentication server address.
    http:
    middlewares:
      test-auth:
        forwardAuth:
          address: "https://example.com/auth"
    
  • trustForwardHeader
    Set the trustForwardHeader option to true to trust all X-Forwarded-* headers.
    http:
    middlewares:
      test-auth:
        forwardAuth:
          address: "https://example.com/auth"
          trustForwardHeader: true
    
  • authResponseHeaders
    The authResponseHeaders option is the list of headers to copy from the authentication server response and set on forwarded request, replacing any existing conflicting headers.
    http:
    middlewares:
      test-auth:
        forwardAuth:
          address: "https://example.com/auth"
          authResponseHeaders:
            - "X-Auth-User"
            - "X-Secret"
    
  • authResponseHeadersRegex
    The authResponseHeadersRegex option is the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex.
    http:
    middlewares:
      test-auth:
        forwardAuth:
          address: "https://example.com/auth"
          authResponseHeadersRegex: "^X-"
    
  • authRequestHeaders
    The authRequestHeaders option is the list of the headers to copy from the request to the authentication server. It allows filtering headers that should not be passed to the authentication server. If not set or empty then all request headers are passed.
    http:
    middlewares:
      test-auth:
        forwardAuth:
          address: "https://example.com/auth"
          authRequestHeaders:
            - "Accept"
            - "X-CustomHeader"
    
  • tls
    Defines the TLS configuration used for the secure connection to the authentication server.
    http:
    middlewares:
      test-auth:
        forwardAuth:
          address: "https://example.com/auth"
          tls:
            # CA used for SSL
            ca: "path/to/local.crt"
            # Skip SSL
            insecureSkipVerify: true
    
Headers

Managing Request/Response headers

http:
  middlewares:
    testHeader:
      headers:
        customRequestHeaders:
          X-Script-Name: "test"
        customResponseHeaders:
          X-Custom-Response-Header: "value"
  • Add/Remove Headers
    http:
    middlewares:
      testHeader:
        headers:
          customRequestHeaders:
            X-Script-Name: "test" # Adds
            X-Custom-Request-Header: "" # Removes
          customResponseHeaders:
            X-Custom-Response-Header: "" # Removes
    
  • CORS Headers
    http:
    middlewares:
      testHeader:
        headers:
          accessControlAllowMethods:
            - GET
            - OPTIONS
            - PUT
          accessControlAllowHeaders: "*"
          accessControlAllowOriginList:
            - https://foo.bar.org
            - https://example.org
          accessControlMaxAge: 100
          addVaryHeader: true
    
IPWhiteList

Limiting Clients to Specific IPs
IPWhitelist accepts / refuses requests based on the client IP.

http:
  middlewares:
    test-ipwhitelist:
      ipWhiteList:
        sourceRange:
          - "127.0.0.1/32"
          - "192.168.1.7"
Ratelimit

The RateLimit middleware ensures that services will receive a fair amount of requests, and allows one to define what fair is.

It is based on a token bucket implementation. In this analogy, the average parameter (defined below) is the rate at which the bucket refills, and the burst is the size (volume) of the bucket.

http:
  middlewares:
    test-ratelimit:
      rateLimit:
        average: 100
        burst: 50
  • average
    average is the maximum rate, by default in requests per second, allowed from a given source.
    It defaults to 0, which means no rate limiting.
    The rate is actually defined by dividing average by period.
  • period
    period, in combination with average, defines the actual maximum rate.
    It defaults to 1 second.
  • burst
    burst is the maximum number of requests allowed to go through in the same arbitrarily small period of time. It defaults to 1.

You can exclude IPs from rate limiting:

http:
  middlewares:
    test-ratelimit:
      rateLimit:
        sourceCriterion:
          ipStrategy:
            excludedIPs:
              - "127.0.0.1/32"
              - "192.168.1.7"
RedirectScheme

The RedirectScheme middleware redirects the request if the request scheme is different from the configured scheme.

http:
  middlewares:
    test-redirectscheme:
      redirectScheme:
        scheme: https
        permanent: true
RedirectRegex

The RedirectRegex redirects a request using regex matching and replacement.

http:
  middlewares:
    test-redirectregex:
      redirectRegex:
        regex: "^http://localhost/(.*)"
        replacement: "http://mydomain/${1}"
ReplacePath

The ReplacePath middleware will:

  • replace the actual path with the specified one.
  • store the original path in a X-Replaced-Path header.
http:
  middlewares:
    test-replacepath:
      replacePath:
        path: "/foo"
ReplacePathRegex

The ReplacePathRegex middleware will:

  • replace the matching path with the specified one.
  • store the original path in a X-Replaced-Path header.
http:
  middlewares:
    test-replacepathregex:
      replacePathRegex:
        regex: "^/foo/(.*)"
        replacement: "/bar/$1"
Retry

The Retry middleware reissues requests a given number of times to a backend server if that server does not reply. As soon as the server answers, the middleware stops retrying, regardless of the response status. The Retry middleware has an optional configuration to enable an exponential backoff.

http:
  middlewares:
    test-retry:
      retry:
        attempts: 4
        initialInterval: 100ms
StripPrefix

Remove the specified prefixes from the URL path.

http:
  middlewares:
    test-stripprefix:
      stripPrefix:
        prefixes:
          - "/foobar"
          - "/fiibar"
StripPrefixRegex

Remove the matching prefixes from the URL path.

http:
  middlewares:
    test-stripprefixregex:
      stripPrefixRegex:
        regex:
          - "/foo/[a-z0-9]+/[0-9]+/"

Access log

Access logs allow you to keep track of what request are made to your server.

To enable the access logs:

accessLog: {}
  • filePath:
    By default access logs are written to the standard output. To write the logs into a log file, use the filePath option.

    accessLog:
      filePath: "/path/to/access.log"
    
  • format:
    By default, logs are written using the Common Log Format (CLF). To write logs in JSON, use json in the format option. If the given format is unsupported, the default (CLF) is used instead.

    Common Log Format

<remote_IP_address> - <client_user_name_if_available> [] "<request_method> <request_path> <request_protocol>" <HTTP_status> "<request_referrer>" "<request_user_agent>" <number_of_requests_received_since_Traefik_started> "<Traefik_router_name>" "<Traefik_server_URL>" <request_duration_in_ms>m

  • bufferingSize
    To write the logs in an asynchronous fashion, specify a bufferingSize option. This option represents the number of log lines Traefik will keep in memory before writing them to the selected output. In some cases, this option can greatly help performances.
    # Configuring a buffer of 100 lines
    accessLog:
      filePath: "/path/to/access.log"
      bufferingSize: 100
    

Filtering

To filter logs, you can specify a set of filters which are logically "OR-connected". Thus, specifying multiple filters will keep more access logs than specifying only one.
The available filters are:

  • statusCodes, to limit the access logs to requests with a status codes in the specified range
  • retryAttempts, to keep the access logs when at least one retry has happened
  • minDuration, to keep access logs when requests take longer than the specified duration (provided in seconds or as a valid duration format)
# Configuring Multiple Filters
accessLog:
  filePath: "/path/to/access.log"
  format: json
  filters:
    statusCodes:
      - "200"
      - "300-302"
    retryAttempts: true
    minDuration: "10ms"
Limiting the Fields/Including Headers

You can decide to limit the logged fields/headers to a given list with the fields.names and fields.headers options.

Each field can be set to:

  • keep to keep the value
  • drop to drop the value
  • redact to replace the value with "redacted"

The defaultMode for fields.names is keep.

The defaultMode for fields.headers is drop.

# Limiting the Logs to Specific Fields
accessLog:
  filePath: "/path/to/access.log"
  format: json
  fields:
    defaultMode: keep
    names:
      ClientUsername: drop
    headers:
      defaultMode: keep
      names:
        User-Agent: redact
        Authorization: drop
        Content-Type: keep