Repository: traefik/traefik (language: go)

Issue:
Feature: ServersTransports k8s CRD - Root CA through config maps
### 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?

# Context

As of today, when configuring SeversTransports using KubernetesCRD, it is only possible to provide root CA using Secret ressources (using `rootCAsSecrets`).

CA certificates are public ressources, hence does not need to be provided as secrets.

# Request

It would be interesting to provides them through other ressources, including config maps. For the CRD, it could use a structure similar to kubernetes volumes or to envFrom, e.g.:
```yaml
apiVersion: traefik.us/v1alpha1
kind: ServersTransport
metadata:
  name: my-server-transport
  namespace: default

spec:
  rootCAs:
    - secretRef: root-ca-as-secret
    - configMapRef: root-ca-as-cm
  serverName: server.default.svc.cluster.local
```

# Additional notes

Such solution would increase compatibility with Cert-Manager [trust-manager](https://github.com/cert-manager/trust-manager): by default it can only provides bundle through config maps. (Since recently it can create secrets too, but it comes with high security costs as it requires full read access to all secrets in a cluster)


Discussion / hints:
Hello @zeronounours,

Thanks for your suggestion, we think it makes a lot of sense.  Unfortunately, this would not make it to our roadmap for a while as we are focused elsewhere.  If you or another community member would like to build it, let us know, and we will work with you to make sure you have all the information needed so that it can be merged.  

We prefer to work with our community members at the beginning of the design process so that we can make sure that we are aligned and can move quickly with the review and merge process.  Let us know here or create a PR before you start, and we will work with you there.  

Don’t forget to check out the [contributor docs](https://github.com/traefik/contributors-guide/blob/master/pr_guidelines.md) and to link the PR to this issue.
@nmengin I think I can work on this. One question though, what if instead of this suggested by @zeronounours 
```yaml
spec:
  rootCAs:
    - secretRef: root-ca-as-secret
    - configMapRef: root-ca-as-cm
  serverName: server.default.svc.cluster.local
```
We do something like:
```yaml
spec:
  rootCAsSecrets:
    - root-ca-as-secret
  rootCAsMaps:
    - root-ca-as-cm
  serverName: server.default.svc.cluster.local
```
That way we can still be backwards compatible and we don't duplicate logic for secrets stored in rootCAsSecrets and those stored in rootCAs

@zeronounours will this implementation also be compatible with trust-manager?
Hey @Nelwhix,

Thank you for the proposition.
Indeed, we think that both solution make sense, even if we prefer the first solution.
If you go this way, you can create a new configuration node and deprecate the current one.

WDYT?
Oh if the plan is to deprecate `rootCAsSecrets` then I like the first solution. working on a PR

Fix patch:
diff --git a/docs/content/migration/v3.md b/docs/content/migration/v3.md
index f38081fa73..19a2370d50 100644
--- a/docs/content/migration/v3.md
+++ b/docs/content/migration/v3.md
@@ -192,6 +192,8 @@ To be consistent with the naming and other metrics providers, the metric now rep
 
 ### Kubernetes CRD Provider
 
+#### Load-Balancing
+
 In `v3.4`, the HTTP service definition has been updated.
 The strategy field now supports two new values: `wrr` and `p2c` (please refer to the [HTTP Services Load Balancing documentation](../../routing/services/#load-balancing-strategy) for more details).
 
@@ -202,3 +204,37 @@ kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/con
 ```
 
 Please note that the `RoundRobin` strategy value is now deprecated, but still supported and equivalent to `wrr`, and will be removed in the next major release.
+
+#### ServersTransport CA Certificate
+
+In `v3.4`, a new `rootCAs` option has been added to the `ServersTransport` and `ServersTransportTCP` CRDs.
+It allows the configuration of CA certificates from both `ConfigMaps` and `Secrets`,
+and replaces the `rootCAsSecrets` option, as shown below:
+
+```yaml
+---
+apiVersion: traefik.io/v1alpha1
+kind: ServersTransport
+metadata:
+  name: foo
+  namespace: bar
+spec:
+  rootCAs:
+    - configMap: ca-config-map
+    - secret: ca-secret
+      
+---      
+apiVersion: traefik.io/v1alpha1
+kind: ServersTransportTCP
+metadata:
+  name: foo
+  namespace: bar
+spec:
+  rootCAs:
+    - configMap: ca-config-map
+    - secret: ca-secret
+```
+
+The `rootCAsSecrets` option, which allows only `Secrets` references,
+is still supported, but is now deprecated,
+and will be removed in the next major release.
diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
index 76f596493a..1ae7938771 100644
--- a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
+++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
@@ -2252,9 +2252,33 @@ spec:
                 description: PeerCertURI defines the peer cert URI used to match against
                   SAN URI during the peer certificate verification.
                 type: string
+              rootCAs:
+                description: RootCAs defines a list of CA certificate Secrets or ConfigMaps
+                  used to validate server certificates.
+                items:
+                  description: |-
+                    RootCA defines a reference to a Secret or a ConfigMap that holds a CA certificate.
+                    If both a Secret and a ConfigMap reference are defined, the Secret reference takes precedence.
+                  properties:
+                    configMap:
+                      description: |-
+                        ConfigMap defines the name of a ConfigMap that holds a CA certificate.
+                        The referenced ConfigMap must contain a certificate under either a tls.ca or a ca.crt key.
+                      type: string
+                    secret:
+                      description: |-
+                        Secret defines the name of a Secret that holds a CA certificate.
+                        The referenced Secret must contain a certificate under either a tls.ca or a ca.crt key.
+                      type: string
+                  type: object
+                  x-kubernetes-validations:
+                  - message: RootCA cannot have both Secret and ConfigMap defined.
+                    rule: has(self.secret) && has(self.configMap)
+                type: array
               rootCAsSecrets:
-                description: RootCAsSecrets defines a list of CA secret used to validate
-                  self-signed certificate.
+                description: |-
+                  RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
+                  Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
                 items:
                   type: string
                 type: array
@@ -2373,9 +2397,33 @@ spec:
                       MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host.
                       PeerCertURI defines the peer cert URI used to match against SAN URI during the peer certificate verification.
                     type: string
+                  rootCAs:
+                    description: RootCAs defines a list of CA certificate Secrets
+                      or ConfigMaps used to validate server certificates.
+                    items:
+                      description: |-
+                        RootCA defines a reference to a Secret or a ConfigMap that holds a CA certificate.
+                        If both a Secret and a ConfigMap reference are defined, the Secret reference takes precedence.
+                      properties:
+                        configMap:
+                          description: |-
+                            ConfigMap defines the name of a ConfigMap that holds a CA certificate.
+                            The referenced ConfigMap must contain a certificate under either a tls.ca or a ca.crt key.
+                          type: string
+                        secret:
+                          description: |-
+                            Secret defines the name of a Secret that holds a CA certificate.
+                            The referenced Secret must contain a certificate under either a tls.ca or a ca.crt key.
+                          type: string
+                      type: object
+                      x-kubernetes-validations:
+                      - message: RootCA cannot have both Secret and ConfigMap defined.
+                        rule: has(self.secret) && has(self.configMap)
+                    type: array
                   rootCAsSecrets:
-                    description: RootCAsSecrets defines a list of CA secret used to
-                      validate self-signed certificates.
+                    description: |-
+                      RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
+                      Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
                     items:
                       type: string
                     type: array
diff --git a/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml b/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml
index 3226cc9c7c..50b15ea0cf 100644
--- a/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml
+++ b/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml
@@ -113,9 +113,33 @@ spec:
                 description: PeerCertURI defines the peer cert URI used to match against
                   SAN URI during the peer certificate verification.
                 type: string
+              rootCAs:
+                description: RootCAs defines a list of CA certificate Secrets or ConfigMaps
+                  used to validate server certificates.
+                items:
+                  description: |-
+                    RootCA defines a reference to a Secret or a ConfigMap that holds a CA certificate.
+                    If both a Secret and a ConfigMap reference are defined, the Secret reference takes precedence.
+                  properties:
+                    configMap:
+                      description: |-
+                        ConfigMap defines the name of a ConfigMap that holds a CA certificate.
+                        The referenced ConfigMap must contain a certificate under either a tls.ca or a ca.crt key.
+                      type: string
+                    secret:
+                      description: |-
+                        Secret defines the name of a Secret that holds a CA certificate.
+                        The referenced Secret must contain a certificate under either a tls.ca or a ca.crt key.
+                      type: string
+                  type: object
+                  x-kubernetes-validations:
+                  - message: RootCA cannot have both Secret and ConfigMap defined.
+                    rule: has(self.secret) && has(self.configMap)
+                type: array
               rootCAsSecrets:
-                description: RootCAsSecrets defines a list of CA secret used to validate
-                  self-signed certificate.
+                description: |-
+                  RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
+                  Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
                 items:
                   type: string
                 type: array
diff --git a/docs/content/reference/dynamic-configuration/traefik.io_serverstransporttcps.yaml b/docs/content/reference/dynamic-configuration/traefik.io_serverstransporttcps.yaml
index 21df387730..8cd6030501 100644
--- a/docs/content/reference/dynamic-configuration/traefik.io_serverstransporttcps.yaml
+++ b/docs/content/reference/dynamic-configuration/traefik.io_serverstransporttcps.yaml
@@ -89,9 +89,33 @@ spec:
                       MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host.
                       PeerCertURI defines the peer cert URI used to match against SAN URI during the peer certificate verification.
                     type: string
+                  rootCAs:
+                    description: RootCAs defines a list of CA certificate Secrets
+                      or ConfigMaps used to validate server certificates.
+                    items:
+                      description: |-
+                        RootCA defines a reference to a Secret or a ConfigMap that holds a CA certificate.
+                        If both a Secret and a ConfigMap reference are defined, the Secret reference takes precedence.
+                      properties:
+                        configMap:
+                          description: |-
+                            ConfigMap defines the name of a ConfigMap that holds a CA certificate.
+                            The referenced ConfigMap must contain a certificate under either a tls.ca or a ca.crt key.
+                          type: string
+                        secret:
+                          description: |-
+                            Secret defines the name of a Secret that holds a CA certificate.
+                            The referenced Secret must contain a certificate under either a tls.ca or a ca.crt key.
+                          type: string
+                      type: object
+                      x-kubernetes-validations:
+                      - message: RootCA cannot have both Secret and ConfigMap defined.
+                        rule: has(self.secret) && has(self.configMap)
+                    type: array
                   rootCAsSecrets:
-                    description: RootCAsSecrets defines a list of CA secret used to
-                      validate self-signed certificates.
+                    description: |-
+                      RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
+                      Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
                     items:
                       type: string
                     type: array
diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md
index be1e038d21..a026cd5527 100644
--- a/docs/content/routing/providers/kubernetes-crd.md
+++ b/docs/content/routing/providers/kubernetes-crd.md
@@ -1851,9 +1851,9 @@ Register the `TLSStore` kind in the Kubernetes cluster before creating `TLSStore
     spec:
       serverName: foobar                        # [1]
       insecureSkipVerify: true                  # [2]
-      rootCAsSecrets:                           # [3]
-        - foobar
-        - foobar
+      rootCAs:                                  # [3]
+        - configMap: foobar
+        - secret: foobar
       certificatesSecrets:                      # [4]
         - foobar
         - foobar
@@ -1871,22 +1871,22 @@ Register the `TLSStore` kind in the Kubernetes cluster before creating `TLSStore
         trustDomain: "spiffe://trust-domain"    # [14]
     ```
 
-| Ref  | Attribute               | Purpose                                                                                                                                                                 |
-|------|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| [1]  | `serverName`            | ServerName used to contact the server.                                                                                                                                  |
-| [2]  | `insecureSkipVerify`    | Controls whether the server's certificate chain and host name is verified.                                                                                              |
-| [3]  | `rootCAsSecrets`        | Defines the set of root certificate authorities to use when verifying server certificates. The secret must contain a certificate under either a tls.ca or a ca.crt key. |
-| [4]  | `certificatesSecrets`   | Certificates to present to the server for mTLS.                                                                                                                         |
-| [5]  | `maxIdleConnsPerHost`   | Controls the maximum idle (keep-alive) connections to keep per-host. If zero, `defaultMaxIdleConnsPerHost` is used.                                                     |
-| [6]  | `forwardingTimeouts`    | Timeouts for requests forwarded to the servers.                                                                                                                         |
-| [7]  | `dialTimeout`           | The amount of time to wait until a connection to a server can be established. If zero, no timeout exists.                                                               |
-| [8]  | `responseHeaderTimeout` | The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists.                    |
-| [9]  | `idleConnTimeout`       | The maximum amount of time an idle (keep-alive) connection will remain idle before closing itself. If zero, no timeout exists.                                          |
-| [10] | `peerCertURI`           | URI used to match against SAN URIs during the server's certificate verification.                                                                                        |
-| [11] | `disableHTTP2`          | Disables HTTP/2 for connections with servers.                                                                                                                           |
-| [12] | `spiffe`                | The spiffe configuration.                                                                                                                                               |
-| [13] | `ids`                   | Defines the allowed SPIFFE IDs (takes precedence over the SPIFFE TrustDomain).                                                                                          |
-| [14] | `trustDomain`           | Defines the allowed SPIFFE trust domain.                                                                                                                                |
+| Ref  | Attribute               | Purpose                                                                                                                                                                                         |
+|------|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [1]  | `serverName`            | ServerName used to contact the server.                                                                                                                                                          |
+| [2]  | `insecureSkipVerify`    | Controls whether the server's certificate chain and host name is verified.                                                                                                                      |
+| [3]  | `rootCAs`               | Defines the set of root certificate authorities to use when verifying server certificates. The referenced Secret or ConfigMap must contain a certificate under either a tls.ca or a ca.crt key. |
+| [4]  | `certificatesSecrets`   | Certificates to present to the server for mTLS.                                                                                                                                                 |
+| [5]  | `maxIdleConnsPerHost`   | Controls the maximum idle (keep-alive) connections to keep per-host. If zero, `defaultMaxIdleConnsPerHost` is used.                                                                             |
+| [6]  | `forwardingTimeouts`    | Timeouts for requests forwarded to the servers.                                                                                                                                                 |
+| [7]  | `dialTimeout`           | The amount of time to wait until a connection to a server can be established. If zero, no timeout exists.                                                                                       |
+| [8]  | `responseHeaderTimeout` | The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists.                                            |
+| [9]  | `idleConnTimeout`       | The maximum amount of time an idle (keep-alive) connection will remain idle before closing itself. If zero, no timeout exists.                                                                  |
+| [10] | `peerCertURI`           | URI used to match against SAN URIs during the server's certificate verification.                                                                                                                |
+| [11] | `disableHTTP2`          | Disables HTTP/2 for connections with servers.                                                                                                                                                   |
+| [12] | `spiffe`                | The spiffe configuration.                                                                                                                                                                       |
+| [13] | `ids`                   | Defines the allowed SPIFFE IDs (takes precedence over the SPIFFE TrustDomain).                                                                                                                  |
+| [14] | `trustDomain`           | Defines the allowed SPIFFE trust domain.                                                                                                                                                        |
 
 !!! info "CA Secret"
 
@@ -1960,9 +1960,9 @@ The `default@internal` serversTransportTCP is created from the [static configura
         serverName: foobar                      # [5]
         insecureSkipVerify: true                # [6]
         peerCertURI: foobar                     # [7]
-        rootCAsSecrets:                         # [8]
-          - foobar
-          - foobar
+        rootCAs:                                # [8]
+          - secret: foobar
+          - configMap: foobar
         certificatesSecrets:                    # [9]
           - foobar
           - foobar
@@ -1982,7 +1982,7 @@ The `default@internal` serversTransportTCP is created from the [static configura
 | [5]  | `serverName`          | ServerName used to contact the server.                                                                                                                                                                                                                                                                                                              |
 | [6]  | `insecureSkipVerify`  | Controls whether the server's certificate chain and host name is verified.                                                                                                                                                                                                                                                                          |
 | [7]  | `peerCertURI`         | URI used to match against SAN URIs during the server's certificate verification.                                                                                                                                                                                                                                                                    |
-| [8]  | `rootCAsSecrets`      | Defines the set of root certificate authorities to use when verifying server certificates. The secret must contain a certificate under either a tls.ca or a ca.crt key.                                                                                                                                                                             |
+| [8]  | `rootCAs`             | Defines the set of root certificate authorities to use when verifying server certificates. The referenced Secret or ConfigMap must contain a certificate under either a tls.ca or a ca.crt key.                                                                                                                                                     |
 | [9]  | `certificatesSecrets` | Certificates to present to the server for mTLS.                                                                                                                                                                                                                                                                                                     |
 | [10] | `spiffe`              | The SPIFFE configuration.                                                                                                                                                                                                                                                                                                                           |
 | [11] | `ids`                 | Defines the allowed SPIFFE IDs (takes precedence over the SPIFFE TrustDomain).                                                                                                                                                                                                                                                                      |
diff --git a/integration/fixtures/k8s/01-traefik-crd.yml b/integration/fixtures/k8s/01-traefik-crd.yml
index 76f596493a..1ae7938771 100644
--- a/integration/fixtures/k8s/01-traefik-crd.yml
+++ b/integration/fixtures/k8s/01-traefik-crd.yml
@@ -2252,9 +2252,33 @@ spec:
                 description: PeerCertURI defines the peer cert URI used to match against
                   SAN URI during the peer certificate verification.
                 type: string
+              rootCAs:
+                description: RootCAs defines a list of CA certificate Secrets or ConfigMaps
+                  used to validate server certificates.
+                items:
+                  description: |-
+                    RootCA defines a reference to a Secret or a ConfigMap that holds a CA certificate.
+                    If both a Secret and a ConfigMap reference are defined, the Secret reference takes precedence.
+                  properties:
+                    configMap:
+                      description: |-
+                        ConfigMap defines the name of a ConfigMap that holds a CA certificate.
+                        The referenced ConfigMap must contain a certificate under either a tls.ca or a ca.crt key.
+                      type: string
+                    secret:
+                      description: |-
+                        Secret defines the name of a Secret that holds a CA certificate.
+                        The referenced Secret must contain a certificate under either a tls.ca or a ca.crt key.
+                      type: string
+                  type: object
+                  x-kubernetes-validations:
+                  - message: RootCA cannot have both Secret and ConfigMap defined.
+                    rule: has(self.secret) && has(self.configMap)
+                type: array
               rootCAsSecrets:
-                description: RootCAsSecrets defines a list of CA secret used to validate
-                  self-signed certificate.
+                description: |-
+                  RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
+                  Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
                 items:
                   type: string
                 type: array
@@ -2373,9 +2397,33 @@ spec:
                       MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host.
                       PeerCertURI defines the peer cert URI used to match against SAN URI during the peer certificate verification.
                     type: string
+                  rootCAs:
+                    description: RootCAs defines a list of CA certificate Secrets
+                      or ConfigMaps used to validate server certificates.
+                    items:
+                      description: |-
+                        RootCA defines a reference to a Secret or a ConfigMap that holds a CA certificate.
+                        If both a Secret and a ConfigMap reference are defined, the Secret reference takes precedence.
+                      properties:
+                        configMap:
+                          description: |-
+                            ConfigMap defines the name of a ConfigMap that holds a CA certificate.
+                            The referenced ConfigMap must contain a certificate under either a tls.ca or a ca.crt key.
+                          type: string
+                        secret:
+                          description: |-
+                            Secret defines the name of a Secret that holds a CA certificate.
+                            The referenced Secret must contain a certificate under either a tls.ca or a ca.crt key.
+                          type: string
+                      type: object
+                      x-kubernetes-validations:
+                      - message: RootCA cannot have both Secret and ConfigMap defined.
+                        rule: has(self.secret) && has(self.configMap)
+                    type: array
                   rootCAsSecrets:
-                    description: RootCAsSecrets defines a list of CA secret used to
-                      validate self-signed certificates.
+                    description: |-
+                      RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
+                      Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
                     items:
                       type: string
                     type: array
diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go
index deb2abeda4..a7c9270582 100644
--- a/pkg/config/dynamic/http_config.go
+++ b/pkg/config/dynamic/http_config.go
@@ -331,8 +331,8 @@ type HealthCheck struct{}
 type ServersTransport struct {
 	ServerName          string                  `description:"Defines the serverName used to contact the server." json:"serverName,omitempty" toml:"serverName,omitempty" yaml:"serverName,omitempty"`
 	InsecureSkipVerify  bool                    `description:"Disables SSL certificate verification." json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"`
-	RootCAs             []types.FileOrContent   `description:"Defines a list of CA secret used to validate self-signed certificate" json:"rootCAs,omitempty" toml:"rootCAs,omitempty" yaml:"rootCAs,omitempty"`
-	Certificates        traefiktls.Certificates `description:"Defines a list of secret storing client certificates for mTLS." json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" export:"true"`
+	RootCAs             []types.FileOrContent   `description:"Defines a list of CA certificates used to validate server certificates." json:"rootCAs,omitempty" toml:"rootCAs,omitempty" yaml:"rootCAs,omitempty"`
+	Certificates        traefiktls.Certificates `description:"Defines a list of client certificates for mTLS." json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" export:"true"`
 	MaxIdleConnsPerHost int                     `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" json:"maxIdleConnsPerHost,omitempty" toml:"maxIdleConnsPerHost,omitempty" yaml:"maxIdleConnsPerHost,omitempty" export:"true"`
 	ForwardingTimeouts  *ForwardingTimeouts     `description:"Defines the timeouts for requests forwarded to the backend servers." json:"forwardingTimeouts,omitempty" toml:"forwardingTimeouts,omitempty" yaml:"forwardingTimeouts,omitempty" export:"true"`
 	DisableHTTP2        bool                    `description:"Disables HTTP/2 for connections with backend servers." json:"disableHTTP2,omitempty" toml:"disableHTTP2,omitempty" yaml:"disableHTTP2,omitempty" export:"true"`
diff --git a/pkg/config/dynamic/tcp_config.go b/pkg/config/dynamic/tcp_config.go
index e0f607d841..de7515f5b4 100644
--- a/pkg/config/dynamic/tcp_config.go
+++ b/pkg/config/dynamic/tcp_config.go
@@ -159,8 +159,8 @@ type TCPServersTransport struct {
 type TLSClientConfig struct {
 	ServerName         string                  `description:"Defines the serverName used to contact the server." json:"serverName,omitempty" toml:"serverName,omitempty" yaml:"serverName,omitempty"`
 	InsecureSkipVerify bool                    `description:"Disables SSL certificate verification." json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"`
-	RootCAs            []types.FileOrContent   `description:"Defines a list of CA secret used to validate self-signed certificate" json:"rootCAs,omitempty" toml:"rootCAs,omitempty" yaml:"rootCAs,omitempty"`
-	Certificates       traefiktls.Certificates `description:"Defines a list of secret storing client certificates for mTLS." json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" export:"true"`
+	RootCAs            []types.FileOrContent   `description:"Defines a list of CA certificates used to validate server certificates." json:"rootCAs,omitempty" toml:"rootCAs,omitempty" yaml:"rootCAs,omitempty"`
+	Certificates       traefiktls.Certificates `description:"Defines a list of client certificates for mTLS." json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" export:"true"`
 	PeerCertURI        string                  `description:"Defines the URI used to match against SAN URI during the peer certificate verification." json:"peerCertURI,omitempty" toml:"peerCertURI,omitempty" yaml:"peerCertURI,omitempty" export:"true"`
 	Spiffe             *Spiffe                 `description:"Defines the SPIFFE TLS configuration." json:"spiffe,omitempty" toml:"spiffe,omitempty" yaml:"spiffe,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
 }
diff --git a/pkg/provider/kubernetes/crd/client.go b/pkg/provider/kubernetes/crd/client.go
index f82852e9ff..dfaa7584a8 100644
--- a/pkg/provider/kubernetes/crd/client.go
+++ b/pkg/provider/kubernetes/crd/client.go
@@ -50,6 +50,7 @@ type Client interface {
 	GetSecret(namespace, name string) (*corev1.Secret, bool, error)
 	GetEndpointSlicesForService(namespace, serviceName string) ([]*discoveryv1.EndpointSlice, error)
 	GetNodes() ([]*corev1.Node, bool, error)
+	GetConfigMap(namespace, name string) (*corev1.ConfigMap, bool, error)
 }
 
 // TODO: add tests for the clientWrapper (and its methods) itself.
@@ -227,6 +228,10 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
 		if err != nil {
 			return nil, err
 		}
+		_, err = factoryKube.Core().V1().ConfigMaps().Informer().AddEventHandler(eventHandler)
+		if err != nil {
+			return nil, err
+		}
 
 		factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm))
 		_, err = factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
@@ -478,6 +483,17 @@ func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool,
 	return secret, exist, err
 }
 
+// GetConfigMap returns the named config map from the given namespace.
+func (c *clientWrapper) GetConfigMap(namespace, name string) (*corev1.ConfigMap, bool, error) {
+	if !c.isWatchedNamespace(namespace) {
+		return nil, false, fmt.Errorf("failed to get config map %s/%s: namespace is not within watched namespaces", namespace, name)
+	}
+
+	configMap, err := c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).Get(name)
+	exist, err := translateNotFoundError(err)
+	return configMap, exist, err
+}
+
 func (c *clientWrapper) GetNodes() ([]*corev1.Node, bool, error) {
 	nodes, err := c.clusterScopeFactory.Core().V1().Nodes().Lister().List(labels.Everything())
 	exist, err := translateNotFoundError(err)
diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/with_servers_transport.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/with_servers_transport.yml
index f0fb21f5d3..fa6e2a3bd0 100644
--- a/pkg/provider/kubernetes/crd/fixtures/tcp/with_servers_transport.yml
+++ b/pkg/provider/kubernetes/crd/fixtures/tcp/with_servers_transport.yml
@@ -48,6 +48,26 @@ data:
   ca.crt: VEVTVFJPT1RDQVM0
   tls.ca: VEVTVFJPT1RDQVM1 # <-- This should be the preferred one.
 
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  name: root-ca5
+  namespace: foo
+
+data:
+  ca.crt: VEVTVFJPT1RDQVM2
+
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  name: root-ca6
+  namespace: foo
+
+data:
+  ca.crt: VEVTVFJPT1RDQVM3
+
 ---
 apiVersion: v1
 kind: Secret
@@ -82,6 +102,26 @@ data:
   tls.crt: VEVTVENFUlQz
   tls.key: VEVTVEtFWTM=
 
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: root-ca-as-config-map
+  namespace: foo
+
+data:
+  ca.crt: "TESTROOTCASFROMCONFIGMAP"
+
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: root-ca-as-config-map-2
+  namespace: foo
+
+data:
+  ca.crt: "TESTROOTCASFROMCONFIGMAP2"
+
 ---
 apiVersion: traefik.io/v1alpha1
 kind: ServersTransportTCP
@@ -101,6 +141,12 @@ spec:
       - root-ca3
       - root-ca4
       - allcerts
+    rootCAs:
+      - configMap: root-ca-as-config-map
+      - secret: root-ca5
+        # referencing both a ConfigMap and a Secret should fail.
+      - configMap: root-ca-as-config-map-2
+        secret: root-ca6
     certificatesSecrets:
       - mtls1
       - mtls2
diff --git a/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml b/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml
index 8515039f4f..496c5af300 100644
--- a/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml
+++ b/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml
@@ -1,4 +1,10 @@
 apiVersion: v1
+kind: Namespace
+metadata:
+  name: foo
+
+---
+apiVersion: v1
 kind: Secret
 metadata:
   name: root-ca0
@@ -48,6 +54,26 @@ data:
   ca.crt: VEVTVFJPT1RDQVM0
   tls.ca: VEVTVFJPT1RDQVM1 # <-- This should be the preferred one.
 
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  name: root-ca5
+  namespace: foo
+
+data:
+  ca.crt: VEVTVFJPT1RDQVM2
+
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  name: root-ca6
+  namespace: foo
+
+data:
+  ca.crt: VEVTVFJPT1RDQVM3
+
 ---
 apiVersion: v1
 kind: Secret
@@ -82,6 +108,26 @@ data:
   tls.crt: VEVTVENFUlQz
   tls.key: VEVTVEtFWTM=
 
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: root-ca-as-config-map
+  namespace: foo
+
+data:
+  ca.crt: "TESTROOTCASFROMCONFIGMAP"
+
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: root-ca-as-config-map-2
+  namespace: foo
+
+data:
+  ca.crt: "TESTROOTCASFROMCONFIGMAP2"
+
 ---
 apiVersion: traefik.io/v1alpha1
 kind: ServersTransport
@@ -102,6 +148,12 @@ spec:
     - root-ca3
     - root-ca4
     - allcerts
+  rootCAs:
+    - configMap: root-ca-as-config-map
+    - secret: root-ca5
+    # referencing both a ConfigMap and a Secret should fail.
+    - configMap: root-ca-as-config-map-2
+      secret: root-ca6
   certificatesSecrets:
     - mtls1
     - mtls2
diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go
index f9a8809c13..566a2bc390 100644
--- a/pkg/provider/kubernetes/crd/kubernetes.go
+++ b/pkg/provider/kubernetes/crd/kubernetes.go
@@ -340,19 +340,61 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
 	}
 
 	for _, serversTransport := range client.GetServersTransports() {
-		logger := log.Ctx(ctx).With().Str(logs.ServersTransportName, serversTransport.Name).Logger()
+		logger := log.Ctx(ctx).With().
+			Str(logs.ServersTransportName, serversTransport.Name).
+			Str("namespace", serversTransport.Namespace).
+			Logger()
+
+		if len(serversTransport.Spec.RootCAsSecrets) > 0 {
+			logger.Warn().Msg("RootCAsSecrets option is deprecated, please use the RootCA option instead.")
+		}
 
 		var rootCAs []types.FileOrContent
 		for _, secret := range serversTransport.Spec.RootCAsSecrets {
 			caSecret, err := loadCASecret(serversTransport.Namespace, secret, client)
 			if err != nil {
-				logger.Error().Err(err).Msgf("Error while loading rootCAs %s", secret)
+				logger.Error().
+					Err(err).
+					Str("secret", secret).
+					Msg("Error while loading CA Secret")
 				continue
 			}
 
 			rootCAs = append(rootCAs, types.FileOrContent(caSecret))
 		}
 
+		for _, rootCA := range serversTransport.Spec.RootCAs {
+			if rootCA.Secret != nil && rootCA.ConfigMap != nil {
+				logger.Error().Msg("Error while loading CA: both Secret and ConfigMap are defined")
+				continue
+			}
+
+			if rootCA.Secret != nil {
+				ca, err := loadCASecret(serversTransport.Namespace, *rootCA.Secret, client)
+				if err != nil {
+					logger.Error().
+						Err(err).
+						Str("secret", *rootCA.Secret).
+						Msg("Error while loading CA Secret")
+					continue
+				}
+
+				rootCAs = append(rootCAs, types.FileOrContent(ca))
+				continue
+			}
+
+			ca, err := loadCAConfigMap(serversTransport.Namespace, *rootCA.ConfigMap, client)
+			if err != nil {
+				logger.Error().
+					Err(err).
+					Str("configMap", *rootCA.ConfigMap).
+					Msg("Error while loading CA ConfigMap")
+				continue
+			}
+
+			rootCAs = append(rootCAs, types.FileOrContent(ca))
+		}
+
 		var certs tls.Certificates
 		for _, secret := range serversTransport.Spec.CertificatesSecrets {
 			tlsSecret, tlsKey, err := loadAuthTLSSecret(serversTransport.Namespace, secret, client)
@@ -449,20 +491,56 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
 		}
 
 		if serversTransportTCP.Spec.TLS != nil {
+			if len(serversTransportTCP.Spec.TLS.RootCAsSecrets) > 0 {
+				logger.Warn().Msg("RootCAsSecrets option is deprecated, please use the RootCA option instead.")
+			}
+
 			var rootCAs []types.FileOrContent
 			for _, secret := range serversTransportTCP.Spec.TLS.RootCAsSecrets {
 				caSecret, err := loadCASecret(serversTransportTCP.Namespace, secret, client)
 				if err != nil {
 					logger.Error().
 						Err(err).
-						Str("rootCAs", secret).
-						Msg("Error while loading rootCAs")
+						Str("secret", secret).
+						Msg("Error while loading CA Secret")
 					continue
 				}
 
 				rootCAs = append(rootCAs, types.FileOrContent(caSecret))
 			}
 
+			for _, rootCA := range serversTransportTCP.Spec.TLS.RootCAs {
+				if rootCA.Secret != nil && rootCA.ConfigMap != nil {
+					logger.Error().Msg("Error while loading CA: both Secret and ConfigMap are defined")
+					continue
+				}
+
+				if rootCA.Secret != nil {
+					ca, err := loadCASecret(serversTransportTCP.Namespace, *rootCA.Secret, client)
+					if err != nil {
+						logger.Error().
+							Err(err).
+							Str("secret", *rootCA.Secret).
+							Msg("Error while loading CA Secret")
+						continue
+					}
+
+					rootCAs = append(rootCAs, types.FileOrContent(ca))
+					continue
+				}
+
+				ca, err := loadCAConfigMap(serversTransportTCP.Namespace, *rootCA.ConfigMap, client)
+				if err != nil {
+					logger.Error().
+						Err(err).
+						Str("configMap", *rootCA.ConfigMap).
+						Msg("Error while loading CA ConfigMap")
+					continue
+				}
+
+				rootCAs = append(rootCAs, types.FileOrContent(ca))
+			}
+
 			var certs tls.Certificates
 			for _, secret := range serversTransportTCP.Spec.TLS.CertificatesSecrets {
 				tlsCert, tlsKey, err := loadAuthTLSSecret(serversTransportTCP.Namespace, secret, client)
@@ -936,7 +1014,7 @@ func loadCASecret(namespace, secretName string, k8sClient Client) (string, error
 		return tlsCAData, nil
 	}
 
-	// TODO: remove this behavior in the next major version (v3)
+	// TODO: remove this behavior in the next major version (v4)
 	if len(secret.Data) == 1 {
 		// For backwards compatibility, use the only available secret data as CA if both 'ca.crt' and 'tls.ca' are missing.
 		for _, v := range secret.Data {
@@ -944,7 +1022,29 @@ func loadCASecret(namespace, secretName string, k8sClient Client) (string, error
 		}
 	}
 
-	return "", fmt.Errorf("could not find CA block: %w", err)
+	return "", fmt.Errorf("secret '%s/%s' has no CA block: %w", namespace, secretName, err)
+}
+
+func loadCAConfigMap(namespace, name string, k8sClient Client) (string, error) {
+	configMap, ok, err := k8sClient.GetConfigMap(namespace, name)
+	if err != nil {
+		return "", fmt.Errorf("failed to fetch configMap '%s/%s': %w", namespace, name, err)
+	}
+
+	if !ok {
+		return "", fmt.Errorf("configMap '%s/%s' not found", namespace, name)
+	}
+
+	if configMap == nil {
+		return "", fmt.Errorf("data for configMap '%s/%s' must not be nil", namespace, name)
+	}
+
+	tlsCAData, err := getCABlocksFromConfigMap(configMap, namespace, name)
+	if err == nil {
+		return tlsCAData, nil
+	}
+
+	return "", fmt.Errorf("configMap '%s/%s' has no CA block: %w", namespace, name, err)
 }
 
 func loadAuthTLSSecret(namespace, secretName string, k8sClient Client) (string, string, error) {
@@ -1384,6 +1484,20 @@ func getCABlocks(secret *corev1.Secret, namespace, secretName string) (string, e
 	return "", fmt.Errorf("secret %s/%s contains neither tls.ca nor ca.crt", namespace, secretName)
 }
 
+func getCABlocksFromConfigMap(configMap *corev1.ConfigMap, namespace, name string) (string, error) {
+	tlsCrtData, tlsCrtExists := configMap.Data["tls.ca"]
+	if tlsCrtExists {
+		return tlsCrtData, nil
+	}
+
+	tlsCrtData, tlsCrtExists = configMap.Data["ca.crt"]
+	if tlsCrtExists {
+		return tlsCrtData, nil
+	}
+
+	return "", fmt.Errorf("config map %s/%s contains neither tls.ca nor ca.crt", namespace, name)
+}
+
 func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *safe.Pool, eventsChan <-chan interface{}) chan interface{} {
 	if throttleDuration == 0 {
 		return nil
diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go
index 3b13986fdc..a205ae9301 100644
--- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go
+++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go
@@ -31,7 +31,10 @@ type ServersTransportSpec struct {
 	ServerName string `json:"serverName,omitempty"`
 	// InsecureSkipVerify disables SSL certificate verification.
 	InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"`
+	// RootCAs defines a list of CA certificate Secrets or ConfigMaps used to validate server certificates.
+	RootCAs []RootCA `json:"rootCAs,omitempty"`
 	// RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
+	// Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
 	RootCAsSecrets []string `json:"rootCAsSecrets,omitempty"`
 	// CertificatesSecrets defines a list of secret storing client certificates for mTLS.
 	CertificatesSecrets []string `json:"certificatesSecrets,omitempty"`
@@ -74,6 +77,20 @@ type ForwardingTimeouts struct {
 	PingTimeout *intstr.IntOrString `json:"pingTimeout,omitempty"`
 }
 
+// +k8s:deepcopy-gen=true
+
+// RootCA defines a reference to a Secret or a ConfigMap that holds a CA certificate.
+// If both a Secret and a ConfigMap reference are defined, the Secret reference takes precedence.
+// +kubebuilder:validation:XValidation:rule="has(self.secret) && has(self.configMap)",message="RootCA cannot have both Secret and ConfigMap defined."
+type RootCA struct {
+	// Secret defines the name of a Secret that holds a CA certificate.
+	// The referenced Secret must contain a certificate under either a tls.ca or a ca.crt key.
+	Secret *string `json:"secret,omitempty"`
+	// ConfigMap defines the name of a ConfigMap that holds a CA certificate.
+	// The referenced ConfigMap must contain a certificate under either a tls.ca or a ca.crt key.
+	ConfigMap *string `json:"configMap,omitempty"`
+}
+
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // ServersTransportList is a collection of ServersTransport resources.
diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransporttcp.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransporttcp.go
index 46ba8694e1..bd6fe637e5 100644
--- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransporttcp.go
+++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransporttcp.go
@@ -49,7 +49,10 @@ type TLSClientConfig struct {
 	ServerName string `json:"serverName,omitempty"`
 	// InsecureSkipVerify disables TLS certificate verification.
 	InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"`
-	// RootCAsSecrets defines a list of CA secret used to validate self-signed certificates.
+	// RootCAs defines a list of CA certificate Secrets or ConfigMaps used to validate server certificates.
+	RootCAs []RootCA `json:"rootCAs,omitempty"`
+	// RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
+	// Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
 	RootCAsSecrets []string `json:"rootCAsSecrets,omitempty"`
 	// CertificatesSecrets defines a list of secret storing client certificates for mTLS.
 	CertificatesSecrets []string `json:"certificatesSecrets,omitempty"`
diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go
index e9dd2386ba..f975a8bfbc 100644
--- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go
@@ -1162,6 +1162,32 @@ func (in *Retry) DeepCopy() *Retry {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *RootCA) DeepCopyInto(out *RootCA) {
+	*out = *in
+	if in.Secret != nil {
+		in, out := &in.Secret, &out.Secret
+		*out = new(string)
+		**out = **in
+	}
+	if in.ConfigMap != nil {
+		in, out := &in.ConfigMap, &out.ConfigMap
+		*out = new(string)
+		**out = **in
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RootCA.
+func (in *RootCA) DeepCopy() *RootCA {
+	if in == nil {
+		return nil
+	}
+	out := new(RootCA)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Route) DeepCopyInto(out *Route) {
 	*out = *in
@@ -1347,6 +1373,13 @@ func (in *ServersTransportList) DeepCopyObject() runtime.Object {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ServersTransportSpec) DeepCopyInto(out *ServersTransportSpec) {
 	*out = *in
+	if in.RootCAs != nil {
+		in, out := &in.RootCAs, &out.RootCAs
+		*out = make([]RootCA, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
 	if in.RootCAsSecrets != nil {
 		in, out := &in.RootCAsSecrets, &out.RootCAsSecrets
 		*out = make([]string, len(*in))
@@ -1593,6 +1626,13 @@ func (in *TLS) DeepCopy() *TLS {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *TLSClientConfig) DeepCopyInto(out *TLSClientConfig) {
 	*out = *in
+	if in.RootCAs != nil {
+		in, out := &in.RootCAs, &out.RootCAs
+		*out = make([]RootCA, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
 	if in.RootCAsSecrets != nil {
 		in, out := &in.RootCAsSecrets, &out.RootCAsSecrets
 		*out = make([]string, len(*in))


Test patch:
diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go
index 0f7e86d03f..e14eef1ef9 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_test.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_test.go
@@ -1491,7 +1491,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
 							TLS: &dynamic.TLSClientConfig{
 								ServerName:         "test",
 								InsecureSkipVerify: true,
-								RootCAs:            []types.FileOrContent{"TESTROOTCAS0", "TESTROOTCAS1", "TESTROOTCAS2", "TESTROOTCAS3", "TESTROOTCAS5", "TESTALLCERTS"},
+								RootCAs:            []types.FileOrContent{"TESTROOTCAS0", "TESTROOTCAS1", "TESTROOTCAS2", "TESTROOTCAS3", "TESTROOTCAS5", "TESTALLCERTS", "TESTROOTCASFROMCONFIGMAP", "TESTROOTCAS6"},
 								Certificates: tls.Certificates{
 									{CertFile: "TESTCERT1", KeyFile: "TESTKEY1"},
 									{CertFile: "TESTCERT2", KeyFile: "TESTKEY2"},
@@ -4734,7 +4734,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 						"foo-test": {
 							ServerName:         "test",
 							InsecureSkipVerify: true,
-							RootCAs:            []types.FileOrContent{"TESTROOTCAS0", "TESTROOTCAS1", "TESTROOTCAS2", "TESTROOTCAS3", "TESTROOTCAS5", "TESTALLCERTS"},
+							RootCAs:            []types.FileOrContent{"TESTROOTCAS0", "TESTROOTCAS1", "TESTROOTCAS2", "TESTROOTCAS3", "TESTROOTCAS5", "TESTALLCERTS", "TESTROOTCASFROMCONFIGMAP", "TESTROOTCAS6"},
 							Certificates: tls.Certificates{
 								{CertFile: "TESTCERT1", KeyFile: "TESTKEY1"},
 								{CertFile: "TESTCERT2", KeyFile: "TESTKEY2"},