Repository: traefik/traefik (language: go)

Issue:
To support HTTP Basic authentication for docker/swarm provider's endpoint
### Welcome!

- [x] Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any.
- [X] Yes, I've searched similar issues on the [Traefik community forum](https://community.traefik.io) and didn't find any.

### What did you expect to see?

Traafic config example:
```
providers:
  swarm:
    endpoint: http://username:password@docker-socket-proxy:2375
```
This is a legit URL to the service, but Traefik does not parse this correctly, not taking username/password from the URL and trying to resolve everything as a single hostname.

```
May 24 09:08:36 utils01 dc7052128204[861]: 2024-05-24T09:08:36Z ERR Failed to retrieve information of the docker client and server host error="error during connect: Get \"http://username:password%40docker-socket-proxy:2375/v1.24/version\": dial tcp: lookup username:password@docker-socket-proxy: no such host" providerName=swarm
May 24 09:08:36 utils01 dc7052128204[861]: 2024-05-24T09:08:36Z ERR Provider error, retrying in 319.082513ms error="error during connect: Get \"http://username:password%40docker-socket-proxy:2375/v1.24/version\": dial tcp: lookup username:password@docker-socket-proxy: no such host" providerName=swarm
```

--

I am trying to replace `http` to `tcp`, the host's address is parsed correctly, but authentication is lost:
```
providers:
  swarm:
    endpoint: tcp://username:password@docker-socket-proxy:2375
```
Errors:
```
May 24 09:39:34 utils01 d353b15e7944[861]: 2024-05-24T09:39:34Z ERR Failed to retrieve information of the docker client and server host error="Error response from daemon: <html>\r\n<head><title>401 Authorization
 Required</title></head>\r\n<body>\r\n<center><h1>401 Authorization Required</h1></center>\r\n<hr><center>nginx/1.25.5</center>\r\n</body>\r\n</html>" providerName=swarm
May 24 09:39:34 utils01 d353b15e7944[861]: 2024-05-24T09:39:34Z ERR Provider error, retrying in 703.423221ms error="Error response from daemon: <html>\r\n<head><title>401 Authorization Required</title></head>\r\
n<body>\r\n<center><h1>401 Authorization Required</h1></center>\r\n<hr><center>nginx/1.25.5</center>\r\n</body>\r\n</html>" providerName=swarm
```


Discussion / hints:
Hey @ba1dr,

Thanks for reaching out.

If any community member can help us find verified steps to reproduce and fix the issue if possible, we would love the help.

Fix patch:
diff --git a/docs/content/providers/docker.md b/docs/content/providers/docker.md
index e9cf20a092..f5d9a737b6 100644
--- a/docs/content/providers/docker.md
+++ b/docs/content/providers/docker.md
@@ -134,6 +134,7 @@ You can specify which Docker API Endpoint to use with the directive [`endpoint`]
         - Accounting at container level, by exposing the socket on a another container than Traefik's.
         - Accounting at kernel level, by enforcing kernel calls with mechanisms like [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux), to only allows an identified set of actions for Traefik's process (or the "socket exposer" process).
         - SSH public key authentication (SSH is supported with Docker > 18.09)
+        - Authentication using HTTP Basic authentication through an HTTP proxy that exposes the Docker daemon socket.
 
     ??? info "More Resources and Examples"
 
@@ -216,6 +217,50 @@ See the [Docker API Access](#docker-api-access) section for more information.
     # ...
     ```
 
+??? example "Using HTTP"
+
+    Using Docker Engine API you can connect Traefik to remote daemon using HTTP.
+
+    ```yaml tab="File (YAML)"
+    providers:
+      docker:
+        endpoint: "http://127.0.0.1:2375"
+         # ...
+    ```
+
+    ```toml tab="File (TOML)"
+    [providers.docker]
+      endpoint = "http://127.0.0.1:2375"
+      # ...
+    ```
+
+    ```bash tab="CLI"
+    --providers.docker.endpoint=http://127.0.0.1:2375
+    # ...
+    ```
+
+??? example "Using TCP"
+
+    Using Docker Engine API you can connect Traefik to remote daemon using TCP.
+
+    ```yaml tab="File (YAML)"
+    providers:
+      docker:
+        endpoint: "tcp://127.0.0.1:2375"
+         # ...
+    ```
+
+    ```toml tab="File (TOML)"
+    [providers.docker]
+      endpoint = "tcp://127.0.0.1:2375"
+      # ...
+    ```
+
+    ```bash tab="CLI"
+    --providers.docker.endpoint=tcp://127.0.0.1:2375
+    # ...
+    ```
+
 ```yaml tab="File (YAML)"
 providers:
   docker:
@@ -231,6 +276,56 @@ providers:
 --providers.docker.endpoint=unix:///var/run/docker.sock
 ```
 
+### `username`
+
+_Optional, Default=""_
+
+Defines the username for Basic HTTP authentication.
+This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.
+
+```yaml tab="File (YAML)"
+providers:
+  docker:
+    username: foo
+    # ...
+```
+
+```toml tab="File (TOML)"
+[providers.docker]
+  username = "foo"
+  # ...
+```
+
+```bash tab="CLI"
+--providers.docker.username="foo"
+# ...
+```
+
+### `password`
+
+_Optional, Default=""_
+
+Defines the password for Basic HTTP authentication.
+This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.
+
+```yaml tab="File (YAML)"
+providers:
+  docker:
+    password: foo
+    # ...
+```
+
+```toml tab="File (TOML)"
+[providers.docker]
+  password = "foo"
+  # ...
+```
+
+```bash tab="CLI"
+--providers.docker.password="foo"
+# ...
+```
+
 ### `useBindPortIP`
 
 _Optional, Default=false_
diff --git a/docs/content/providers/swarm.md b/docs/content/providers/swarm.md
index 8e790c203b..427e54af97 100644
--- a/docs/content/providers/swarm.md
+++ b/docs/content/providers/swarm.md
@@ -151,6 +151,7 @@ You can specify which Docker API Endpoint to use with the directive [`endpoint`]
           It allows scheduling of Traefik on worker nodes, with only the "socket exposer" container on the manager nodes.
         - Accounting at kernel level, by enforcing kernel calls with mechanisms like [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux), to only allows an identified set of actions for Traefik's process (or the "socket exposer" process).
         - SSH public key authentication (SSH is supported with Docker > 18.09)
+        - Authentication using HTTP Basic authentication through an HTTP proxy that exposes the Docker daemon socket.
 
     ??? info "More Resources and Examples"
 
@@ -262,6 +263,50 @@ See the [Docker Swarm API Access](#docker-api-access) section for more informati
     # ...
     ```
 
+??? example "Using HTTP"
+
+    Using Docker Engine API you can connect Traefik to remote daemon using HTTP.
+
+    ```yaml tab="File (YAML)"
+    providers:
+      swarm:
+        endpoint: "http://127.0.0.1:2375"
+         # ...
+    ```
+
+    ```toml tab="File (TOML)"
+    [providers.swarm]
+      swarm = "http://127.0.0.1:2375"
+      # ...
+    ```
+
+    ```bash tab="CLI"
+    --providers.swarm.endpoint=http://127.0.0.1:2375
+    # ...
+    ```
+
+??? example "Using TCP"
+
+    Using Docker Engine API you can connect Traefik to remote daemon using TCP.
+
+    ```yaml tab="File (YAML)"
+    providers:
+      swarm:
+        endpoint: "tcp://127.0.0.1:2375"
+         # ...
+    ```
+
+    ```toml tab="File (TOML)"
+    [providers.swarm]
+      swarm = "tcp://127.0.0.1:2375"
+      # ...
+    ```
+
+    ```bash tab="CLI"
+    --providers.swarm.endpoint=tcp://127.0.0.1:2375
+    # ...
+    ```
+
 ```yaml tab="File (YAML)"
 providers:
   swarm:
@@ -277,6 +322,56 @@ providers:
 --providers.swarm.endpoint=unix:///var/run/docker.sock
 ```
 
+### `username`
+
+_Optional, Default=""_
+
+Defines the username for Basic HTTP authentication.
+This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.
+
+```yaml tab="File (YAML)"
+providers:
+  swarm:
+    username: foo
+    # ...
+```
+
+```toml tab="File (TOML)"
+[providers.swarm]
+  username = "foo"
+  # ...
+```
+
+```bash tab="CLI"
+--providers.swarm.username="foo"
+# ...
+```
+
+### `password`
+
+_Optional, Default=""_
+
+Defines the password for Basic HTTP authentication.
+This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.
+
+```yaml tab="File (YAML)"
+providers:
+  swarm:
+    password: foo
+    # ...
+```
+
+```toml tab="File (TOML)"
+[providers.swarm]
+  password = "foo"
+  # ...
+```
+
+```bash tab="CLI"
+--providers.swarm.password="foo"
+# ...
+```
+
 ### `useBindPortIP`
 
 _Optional, Default=false_
diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md
index a261ffa27c..ed7730922a 100644
--- a/docs/content/reference/static-configuration/cli-ref.md
+++ b/docs/content/reference/static-configuration/cli-ref.md
@@ -591,6 +591,9 @@ Client timeout for HTTP connections. (Default: ```0```)
 `--providers.docker.network`:  
 Default Docker network used.
 
+`--providers.docker.password`:  
+Password for Basic HTTP authentication.
+
 `--providers.docker.tls.ca`:  
 TLS CA
 
@@ -606,6 +609,9 @@ TLS key
 `--providers.docker.usebindportip`:  
 Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
 
+`--providers.docker.username`:  
+Username for Basic HTTP authentication.
+
 `--providers.docker.watch`:  
 Watch Docker events. (Default: ```true```)
 
@@ -969,6 +975,9 @@ Client timeout for HTTP connections. (Default: ```0```)
 `--providers.swarm.network`:  
 Default Docker network used.
 
+`--providers.swarm.password`:  
+Password for Basic HTTP authentication.
+
 `--providers.swarm.refreshseconds`:  
 Polling interval for swarm mode. (Default: ```15```)
 
@@ -987,6 +996,9 @@ TLS key
 `--providers.swarm.usebindportip`:  
 Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
 
+`--providers.swarm.username`:  
+Username for Basic HTTP authentication.
+
 `--providers.swarm.watch`:  
 Watch Docker events. (Default: ```true```)
 
diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md
index 43882e52f0..e8ca837f35 100644
--- a/docs/content/reference/static-configuration/env-ref.md
+++ b/docs/content/reference/static-configuration/env-ref.md
@@ -591,6 +591,9 @@ Client timeout for HTTP connections. (Default: ```0```)
 `TRAEFIK_PROVIDERS_DOCKER_NETWORK`:  
 Default Docker network used.
 
+`TRAEFIK_PROVIDERS_DOCKER_PASSWORD`:  
+Password for Basic HTTP authentication.
+
 `TRAEFIK_PROVIDERS_DOCKER_TLS_CA`:  
 TLS CA
 
@@ -606,6 +609,9 @@ TLS key
 `TRAEFIK_PROVIDERS_DOCKER_USEBINDPORTIP`:  
 Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
 
+`TRAEFIK_PROVIDERS_DOCKER_USERNAME`:  
+Username for Basic HTTP authentication.
+
 `TRAEFIK_PROVIDERS_DOCKER_WATCH`:  
 Watch Docker events. (Default: ```true```)
 
@@ -969,6 +975,9 @@ Client timeout for HTTP connections. (Default: ```0```)
 `TRAEFIK_PROVIDERS_SWARM_NETWORK`:  
 Default Docker network used.
 
+`TRAEFIK_PROVIDERS_SWARM_PASSWORD`:  
+Password for Basic HTTP authentication.
+
 `TRAEFIK_PROVIDERS_SWARM_REFRESHSECONDS`:  
 Polling interval for swarm mode. (Default: ```15```)
 
@@ -987,6 +996,9 @@ TLS key
 `TRAEFIK_PROVIDERS_SWARM_USEBINDPORTIP`:  
 Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
 
+`TRAEFIK_PROVIDERS_SWARM_USERNAME`:  
+Username for Basic HTTP authentication.
+
 `TRAEFIK_PROVIDERS_SWARM_WATCH`:  
 Watch Docker events. (Default: ```true```)
 
diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml
index f171b9e862..13b5db3b92 100644
--- a/docs/content/reference/static-configuration/file.toml
+++ b/docs/content/reference/static-configuration/file.toml
@@ -85,6 +85,8 @@
     useBindPortIP = true
     watch = true
     defaultRule = "foobar"
+    username = "foobar"
+    password = "foobar"
     endpoint = "foobar"
     httpClientTimeout = "42s"
     [providers.docker.tls]
@@ -100,6 +102,8 @@
     useBindPortIP = true
     watch = true
     defaultRule = "foobar"
+    username = "foobar"
+    password = "foobar"
     endpoint = "foobar"
     httpClientTimeout = "42s"
     refreshSeconds = "42s"
diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml
index 1c053dfafb..8fa68dada2 100644
--- a/docs/content/reference/static-configuration/file.yaml
+++ b/docs/content/reference/static-configuration/file.yaml
@@ -96,6 +96,8 @@ providers:
     useBindPortIP: true
     watch: true
     defaultRule: foobar
+    username: foobar
+    password: foobar
     endpoint: foobar
     tls:
       ca: foobar
@@ -111,6 +113,8 @@ providers:
     useBindPortIP: true
     watch: true
     defaultRule: foobar
+    username: foobar
+    password: foobar
     endpoint: foobar
     tls:
       ca: foobar
diff --git a/pkg/provider/docker/shared.go b/pkg/provider/docker/shared.go
index 74bcc62a18..98885b7c36 100644
--- a/pkg/provider/docker/shared.go
+++ b/pkg/provider/docker/shared.go
@@ -2,6 +2,7 @@ package docker
 
 import (
 	"context"
+	"encoding/base64"
 	"fmt"
 	"net/http"
 	"text/template"
@@ -101,6 +102,8 @@ func parseContainer(container dockertypes.ContainerJSON) dockerData {
 type ClientConfig struct {
 	apiVersion string
 
+	Username          string           `description:"Username for Basic HTTP authentication." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty"`
+	Password          string           `description:"Password for Basic HTTP authentication." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty"`
 	Endpoint          string           `description:"Docker server endpoint. Can be a TCP or a Unix socket endpoint." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
 	TLS               *types.ClientTLS `description:"Enable Docker TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
 	HTTPClientTimeout ptypes.Duration  `description:"Client timeout for HTTP connections." json:"httpClientTimeout,omitempty" toml:"httpClientTimeout,omitempty" yaml:"httpClientTimeout,omitempty" export:"true"`
@@ -115,6 +118,9 @@ func createClient(ctx context.Context, cfg ClientConfig) (*client.Client, error)
 	httpHeaders := map[string]string{
 		"User-Agent": "Traefik " + version.Version,
 	}
+	if cfg.Username != "" && cfg.Password != "" {
+		httpHeaders["Authorization"] = "Basic " + base64.StdEncoding.EncodeToString([]byte(cfg.Username+":"+cfg.Password))
+	}
 
 	opts = append(opts,
 		client.WithHTTPHeaders(httpHeaders),


Test patch: