diff --git a/docs/content/https/acme.md b/docs/content/https/acme.md index dddd74fbc..ef2a27310 100644 --- a/docs/content/https/acme.md +++ b/docs/content/https/acme.md @@ -282,7 +282,7 @@ For example, `CF_API_EMAIL_FILE=/run/secrets/traefik_cf-api-email` could be used |-------------------------------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------| | [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/acme-dns) | | [Alibaba Cloud](https://www.alibabacloud.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/alidns) | -| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/auroradns) | +| [Auroradns](https://www.pcextreme.com/dns-health-checks) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/auroradns) | | [Autodns](https://www.internetx.com/domains/autodns/) | `autodns` | `AUTODNS_API_USER`, `AUTODNS_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/autodns) | | [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | [Additional configuration](https://go-acme.github.io/lego/dns/azure) | | [Bindman](https://github.com/labbsr0x/bindman-dns-webhook) | `bindman` | `BINDMAN_MANAGER_ADDRESS` | [Additional configuration](https://go-acme.github.io/lego/dns/bindman) | diff --git a/docs/content/middlewares/headers.md b/docs/content/middlewares/headers.md index cd9072a0c..c9740d917 100644 --- a/docs/content/middlewares/headers.md +++ b/docs/content/middlewares/headers.md @@ -311,7 +311,7 @@ This value can contains a list of allowed origins. More information including how to use the settings can be found on: - [Mozilla.org](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin) -- [w3](https://www.w3.org/TR/cors/#access-control-allow-origin-response-header) +- [w3](https://fetch.spec.whatwg.org/#http-access-control-allow-origin) - [IETF](https://tools.ietf.org/html/rfc6454#section-7.1) Traefik no longer supports the null value, as it is [no longer recommended as a return value](https://w3c.github.io/webappsec-cors-for-developers/#avoid-returning-access-control-allow-origin-null). diff --git a/docs/content/migration/v1-to-v2.md b/docs/content/migration/v1-to-v2.md index 2a7006add..ea680a746 100644 --- a/docs/content/migration/v1-to-v2.md +++ b/docs/content/migration/v1-to-v2.md @@ -97,7 +97,7 @@ Then any router can refer to an instance of the wanted middleware. ```yaml tab="Docker" labels: - - "traefik.http.routers.router0.rule=Host(`example.com`) && PathPrefix(`/test`)" + - "traefik.http.routers.router0.rule=Host(`test.localhost`) && PathPrefix(`/test`)" - "traefik.http.routers.router0.middlewares=auth" - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" ``` diff --git a/docs/content/observability/access-logs.md b/docs/content/observability/access-logs.md index c09a8ebf4..0c54d485d 100644 --- a/docs/content/observability/access-logs.md +++ b/docs/content/observability/access-logs.md @@ -111,9 +111,9 @@ accessLog: --accesslog.filters.minduration=10ms ``` -### Limiting the Fields +### Limiting the Fields/Including Headers -You can decide to limit the logged fields/headers to a given list with the `fields.names` and `fields.header` options +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: @@ -121,7 +121,7 @@ Each field can be set to: - `drop` to drop the value - `redact` to replace the value with "redacted" -The `defaultMode` for `fields.header` is `drop`. +The `defaultMode` for `fields.headers` is `drop`. ```toml tab="File (TOML)" # Limiting the Logs to Specific Fields diff --git a/docs/content/providers/docker.md b/docs/content/providers/docker.md index 968bb11a8..35c1ed0b4 100644 --- a/docs/content/providers/docker.md +++ b/docs/content/providers/docker.md @@ -154,7 +154,7 @@ You can specify which Docker API Endpoint to use with the directive [`endpoint`] - Authentication with Client Certificates as described in ["Protect the Docker daemon socket."](https://docs.docker.com/engine/security/https/) - Authorize and filter requests to restrict possible actions with [the TecnativaDocker Socket Proxy](https://github.com/Tecnativa/docker-socket-proxy). - - Authorization with the [Docker Authorization Plugin Mechanism](https://docs.docker.com/engine/extend/plugins_authorization/) + - Authorization with the [Docker Authorization Plugin Mechanism](https://web.archive.org/web/20190920092526/https://docs.docker.com/engine/extend/plugins_authorization/) - Accounting at networking level, by exposing the socket only inside a Docker private network, only available for Traefik. - Accounting at container level, by exposing the socket on a another container than Traefik's. With Swarm mode, it allows scheduling of Traefik on worker nodes, with only the "socket exposer" container on the manager nodes. diff --git a/docs/content/routing/providers/docker.md b/docs/content/routing/providers/docker.md index 46fa35f66..e19aafaa5 100644 --- a/docs/content/routing/providers/docker.md +++ b/docs/content/routing/providers/docker.md @@ -291,7 +291,7 @@ you'd add the label `traefik.http.services..loadbalancer.pa See [health check](../services/index.md#health-check) for more information. ```yaml - - "traefik.http.services.myservice.loadbalancer.healthcheck.interval=10" + - "traefik.http.services.myservice.loadbalancer.healthcheck.interval=10s" ``` ??? info "`traefik.http.services..loadbalancer.healthcheck.path`" diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index f07d50573..7201fa994 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -108,24 +108,24 @@ The Kubernetes Ingress Controller, The Custom Resource Way. name: myingressroute namespace: default - spec: - entryPoints: - - web + spec: + entryPoints: + - web - routes: - - match: Host(`foo`) && PathPrefix(`/bar`) - kind: Rule - services: - - name: whoami - port: 80 + routes: + - match: Host(`foo`) && PathPrefix(`/bar`) + kind: Rule + services: + - name: whoami + port: 80 --- apiVersion: traefik.containo.us/v1alpha1 - kind: IngressRouteTCP - metadata: - name: ingressroute.tcp + kind: IngressRouteTCP + metadata: + name: ingressroute.tcp namespace: default - + spec: entryPoints: - tcpep @@ -135,22 +135,22 @@ The Kubernetes Ingress Controller, The Custom Resource Way. services: - name: whoamitcp port: 8080 - + --- apiVersion: traefik.containo.us/v1alpha1 - kind: IngressRouteUDP - metadata: - name: ingressroute.udp - namespace: default - - spec: - entryPoints: - - fooudp - routes: - - kind: Rule - services: - - name: whoamiudp - port: 8080 + kind: IngressRouteUDP + metadata: + name: ingressroute.udp + namespace: default + + spec: + entryPoints: + - fooudp + routes: + - kind: Rule + services: + - name: whoamiudp + port: 8080 ``` ```yaml tab="Whoami" diff --git a/docs/content/routing/providers/kubernetes-ingress.md b/docs/content/routing/providers/kubernetes-ingress.md index 95622b002..3fcd8af7f 100644 --- a/docs/content/routing/providers/kubernetes-ingress.md +++ b/docs/content/routing/providers/kubernetes-ingress.md @@ -202,7 +202,7 @@ which in turn will create the resulting routers, services, handlers, etc. See [middlewares](../routers/index.md#middlewares) and [middlewares overview](../../middlewares/overview.md) for more information. ```yaml - traefik.ingress.kubernetes.io/router.middlewares: auth@file,prefix@kuberntescrd,cb@file + traefik.ingress.kubernetes.io/router.middlewares: auth@file,prefix@kubernetescrd,cb@file ``` ??? info "`traefik.ingress.kubernetes.io/router.priority`" diff --git a/pkg/middlewares/forwardedheaders/forwarded_header.go b/pkg/middlewares/forwardedheaders/forwarded_header.go index 4d796c55d..4bbae8a95 100644 --- a/pkg/middlewares/forwardedheaders/forwarded_header.go +++ b/pkg/middlewares/forwardedheaders/forwarded_header.go @@ -132,18 +132,18 @@ func (x *XForwarded) rewrite(outreq *http.Request) { xfProto := outreq.Header.Get(xForwardedProto) if xfProto == "" { - if outreq.TLS != nil { - outreq.Header.Set(xForwardedProto, "https") + if isWebsocketRequest(outreq) { + if outreq.TLS != nil { + outreq.Header.Set(xForwardedProto, "wss") + } else { + outreq.Header.Set(xForwardedProto, "ws") + } } else { - outreq.Header.Set(xForwardedProto, "http") - } - } - - if isWebsocketRequest(outreq) { - if outreq.Header.Get(xForwardedProto) == "https" || outreq.Header.Get(xForwardedProto) == "wss" { - outreq.Header.Set(xForwardedProto, "wss") - } else { - outreq.Header.Set(xForwardedProto, "ws") + if outreq.TLS != nil { + outreq.Header.Set(xForwardedProto, "https") + } else { + outreq.Header.Set(xForwardedProto, "http") + } } } diff --git a/pkg/provider/docker/config.go b/pkg/provider/docker/config.go index dc7eb4f95..1f35e7897 100644 --- a/pkg/provider/docker/config.go +++ b/pkg/provider/docker/config.go @@ -345,7 +345,23 @@ func (p Provider) getIPAddress(ctx context.Context, container dockerData) string logger.Warnf("Unable to get IP address for container %s : Failed to inspect container ID %s, error: %s", container.Name, connectedContainer, err) return "" } - return p.getIPAddress(ctx, parseContainer(containerInspected)) + + // Check connected container for traefik.docker.network, falling back to + // the network specified on the current container. + containerParsed := parseContainer(containerInspected) + extraConf, err := p.getConfiguration(containerParsed) + + if err != nil { + logger.Warnf("Unable to get IP address for container %s : failed to get extra configuration for container %s: %s", container.Name, containerInspected.Name, err) + return "" + } + + if extraConf.Docker.Network == "" { + extraConf.Docker.Network = container.ExtraConf.Docker.Network + } + + containerParsed.ExtraConf = extraConf + return p.getIPAddress(ctx, containerParsed) } for _, network := range container.NetworkSettings.Networks { diff --git a/pkg/udp/conn.go b/pkg/udp/conn.go index 0fec8ce04..9aef31f2a 100644 --- a/pkg/udp/conn.go +++ b/pkg/udp/conn.go @@ -128,9 +128,11 @@ func (l *Listener) Shutdown(graceTimeout time.Duration) error { // we find that session, and otherwise we create a new one. // We then send the data the session's readLoop. func (l *Listener) readLoop() { - buf := make([]byte, receiveMTU) - for { + // Allocating a new buffer for every read avoids + // overwriting data in c.msgs in case the next packet is received + // before c.msgs is emptied via Read() + buf := make([]byte, receiveMTU) n, raddr, err := l.pConn.ReadFrom(buf) if err != nil { return @@ -177,7 +179,7 @@ func (l *Listener) newConn(rAddr net.Addr) *Conn { readCh: make(chan []byte), sizeCh: make(chan int), doneCh: make(chan struct{}), - ticker: time.NewTicker(timeoutTicker), + timeout: timeoutTicker, } } @@ -194,7 +196,7 @@ type Conn struct { muActivity sync.RWMutex lastActivity time.Time // the last time the session saw either read or write activity - ticker *time.Ticker // for timeouts + timeout time.Duration // for timeouts doneOnce sync.Once doneCh chan struct{} } @@ -204,12 +206,15 @@ type Conn struct { // that is to say it waits on readCh to receive the slice of bytes that the Read operation wants to read onto. // The Read operation receives the signal that the data has been written to the slice of bytes through the sizeCh. func (c *Conn) readLoop() { + ticker := time.NewTicker(c.timeout) + defer ticker.Stop() + for { if len(c.msgs) == 0 { select { case msg := <-c.receiveCh: c.msgs = append(c.msgs, msg) - case <-c.ticker.C: + case <-ticker.C: c.muActivity.RLock() deadline := c.lastActivity.Add(connTimeout) c.muActivity.RUnlock() @@ -229,7 +234,7 @@ func (c *Conn) readLoop() { c.sizeCh <- n case msg := <-c.receiveCh: c.msgs = append(c.msgs, msg) - case <-c.ticker.C: + case <-ticker.C: c.muActivity.RLock() deadline := c.lastActivity.Add(connTimeout) c.muActivity.RUnlock() @@ -281,6 +286,5 @@ func (c *Conn) Close() error { c.listener.mu.Lock() defer c.listener.mu.Unlock() delete(c.listener.conns, c.rAddr.String()) - c.ticker.Stop() return nil } diff --git a/pkg/udp/conn_test.go b/pkg/udp/conn_test.go index 91d3fe571..62c35d04a 100644 --- a/pkg/udp/conn_test.go +++ b/pkg/udp/conn_test.go @@ -10,6 +10,67 @@ import ( "github.com/stretchr/testify/require" ) +func TestConsecutiveWrites(t *testing.T) { + addr, err := net.ResolveUDPAddr("udp", ":0") + require.NoError(t, err) + + ln, err := Listen("udp", addr) + require.NoError(t, err) + defer func() { + err := ln.Close() + require.NoError(t, err) + }() + + go func() { + for { + conn, err := ln.Accept() + if err == errClosedListener { + return + } + require.NoError(t, err) + + go func() { + b := make([]byte, 2048) + b2 := make([]byte, 2048) + var n int + var n2 int + + n, err = conn.Read(b) + require.NoError(t, err) + // Wait to make sure that the second packet is received + time.Sleep(10 * time.Millisecond) + n2, err = conn.Read(b2) + require.NoError(t, err) + + _, err = conn.Write(b[:n]) + require.NoError(t, err) + _, err = conn.Write(b2[:n2]) + require.NoError(t, err) + }() + } + }() + + udpConn, err := net.Dial("udp", ln.Addr().String()) + require.NoError(t, err) + + // Send multiple packets of different content and length consecutively + // Read back packets afterwards and make sure that content matches + // This checks if any buffers are overwritten while the receiver is enqueuing multiple packets + b := make([]byte, 2048) + var n int + _, err = udpConn.Write([]byte("TESTLONG0")) + require.NoError(t, err) + _, err = udpConn.Write([]byte("1TEST")) + require.NoError(t, err) + + n, err = udpConn.Read(b) + require.NoError(t, err) + require.Equal(t, "TESTLONG0", string(b[:n])) + n, err = udpConn.Read(b) + require.NoError(t, err) + require.Equal(t, "1TEST", string(b[:n])) +} + func TestListenNotBlocking(t *testing.T) { addr, err := net.ResolveUDPAddr("udp", ":0")