Migrating from Nginx RP to Traefix - how to use "upgrade-insecure-requests"

I'm trying to use Traefik as I am using docker more frequently.

I have a bunch of non-docker servers that I need to reverse proxy for. When using nginx, I would route all of my inbound traffic to nginx and then do url based routing and Lets Encrypt certificates.

So nginx would do the https and then use http internally. There were problems with serving mixed content data that would trip up some of my sites so I found that I would need to use this in my nginx config.

add_header 'Content-Security-Policy' 'upgrade-insecure-requests';

Can anyone help a noob out with how to do this on my Docker/Traefik trial?

Here is my main docker compose file and the dynamic file-provider that I am using to define my internal target system.

I am a real noob so please be gentle...

My Docker Compose File

  traefik:
    image: traefik:v2.9.6
    container_name: traefik
    volumes:
      - ./traefik:/etc/traefik
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - TZ=$TZ
    networks:
      - net
    labels:
      - 'traefik.enable=true'
      - 'traefik.http.routers.api.rule=Host(`traefik.my_domain_0.com`)'
      - 'traefik.http.routers.api.entrypoints=https'
      - 'traefik.http.routers.api.service=api@internal'
      - 'traefik.http.routers.api.tls=true'
      - 'traefik.http.routers.api.tls.certresolver=letsencrypt'
      - 'traefik.http.routers.api.middlewares=authelia@docker'
    ports:
      - 80:80
      - 443:443
    command:
      - '--api.insecure=true'
      - '--providers.docker=true'
      - '--providers.docker.exposedByDefault=false'
      - '--providers.file.directory=/etc/traefik'
      - '--providers.file.watch=true'
      - '--entrypoints.http=true'
      - '--entrypoints.http.address=:80'
      - '--entrypoints.http.http.redirections.entrypoint.to=https'
      - '--entrypoints.http.http.redirections.entrypoint.scheme=https'
      - '--entrypoints.https=true'
      - '--entrypoints.https.address=:443'
      - '--certificatesResolvers.letsencrypt.acme.email=me@gmail.com'
      - '--certificatesResolvers.letsencrypt.acme.storage=/etc/traefik/acme.json'
      - '--certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=http'
      - '--log=true'
      - '--log.filePath=/var/log/traefik.log'
      - '--log.level=DEBUG'

And here is my dynamic file provider
I have two non-docker systems I am trying to reverse proxy for. 1) my Blue Iris video camera system and 2) a Drupal system.

http:
  # Add the routers
  routers:
    lxd-router:
      rule: "Host(`www.my_domain_1.org`) || Host(`my_domain_1.org`)"
      entryPoints:
      - https
      tls:
        certResolver: letsencrypt
      service: lxd-service
    bi5-router:
      rule: "Host(`video.myhome.org`)"
      entryPoints:
      - https
      tls:
        certResolver: letsencrypt
      service: bi5-service

  # Add the services
  services:
    lxd-service:
      loadBalancer:
        servers:
        - url: "http://192.168.50.181:81"
        passHostHeader: true
    bi5-service:
      loadBalancer:
        servers:
        - url: "http://192.168.50.61:81"
        passHostHeader: true

So my very rookie read of this shows that I am only using one sort-of-middleware which is the TLS Let's Encrypt service.

So maybe I need to either tweak that TLS module to add the required upgrade headers?

By the way, this is the error that I get with the mixed content when the internal http site is redirected through the reverse proxy.

Mixed Content: The page at 'https://www.mydomain.org/civicrm/a#/mailing/2' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://www.mydomain.org/civicrm/ajax/api4/EntitySet%3Aautocomplete?params=%7B%22input%22%3A%22%22%2C%22page%22%3A1%2C%22formName%22%3A%22crmMailing.2%22%2C%22fieldName%22%3A%22Mailing.recipients_exclude%22%7D&_=1700414486079'. This request has been blocked; the content must be served over HTTPS.

It is only on some pages were the main page seems fine being redirected to https but there is some content on that page that screws with things. In this specific case there was an autocomplete field that fails.

I'll keep researching while I hope for feedback...

So maybe I need to attach a middleware to my LXD-Router defined in my dynamic file provider and I see that one of the middlewares in the catalog is the Headers Middleware.

So in these documentation snippets,

Are the labels shown creating a middleware called "testHeader" that is then available for one to add to their routers as needed?

And I would use a line like shown to add my

add_header 'Content-Security-Policy' 'upgrade-insecure-requests';

And I see the RedirectScheme Middleware, but how does that jive with what the Lets Encrypt TLS module is already doing for me?

Maybe some of this stuff can be done on the entry point as opposed to being done on a Middleware?

So if I want to force my "upgrade-insecure-requests' header, can that be done on the entry point or a middleware? What are the pros and cons of each? Maybe if there was something more unique that isn't desired on all inbounds then we would use a middleware selectively applied as opposed to the entry point?

So going back to my original docker file

      - '--entrypoints.http=true'
      - '--entrypoints.http.address=:80'
      - '--entrypoints.http.http.redirections.entrypoint.to=https'
      - '--entrypoints.http.http.redirections.entrypoint.scheme=https'

      - '--entrypoints.https=true'
      - '--entrypoints.https.address=:443'

      - '--certificatesResolvers.letsencrypt.acme.email=me@gmail.com'
      - '--certificatesResolvers.letsencrypt.acme.storage=/etc/traefik/acme.json'
      - '--certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=http'

can I put some type of add-header onto my https entrypoint to tell it to upgrade all content?

I would think that the header Content-Security-Policy is added by the proxy in the response, so you would need to declare a headers middleware with customresponseheaders and assign it to entrypoint globally or to each router ([doc).

Edit - sorry, making attempts

Wow, I think this worked...

    labels:
      - .
      - .
      - 'traefik.http.middlewares.testheader.headers.contentSecurityPolicy=upgrade-insecure-requests'
      - .
      - .
      - .
      - .
      - .

the middleware showed up in Traefik and I could apply it to my dynamic file provider service.

Below is the documentation page I am trying to follow to get my contentSecurityPolicy=upgrade-insecure-requests to work. I tried adding it to both my http and https entry points.

=======================================

Headers

Managing Request/Response headers

Headers

The Headers middleware manages the headers of requests and responses.

A set of forwarded headers are automatically added by default. See the FAQ for more information.

Configuration Examples

Adding Headers to the Request and the Response

The following example adds the X-Script-Name header to the proxied request and the X-Custom-Response-Header header to the response

Docker

labels:
  - "traefik.http.middlewares.testHeader.headers.customrequestheaders.X-Script-Name=test"
  - "traefik.http.middlewares.testHeader.headers.customresponseheaders.X-Custom-Response-Header=value"

Configuration Options

General

Warning

Custom headers will overwrite existing headers if they have identical names.

The detailed documentation for security headers can be found in unrolled/secure.

contentSecurityPolicy

The contentSecurityPolicy option allows the Content-Security-Policy header value to be set with a custom value.