Configure Caddy in front of Authelia

This started as a single page but became too large to manage, I've now split it up into these sections.

  1. Authelia for MFA on docker
  2. Deploying test hosts for Authelia MFA authentication
  3. Configuring Nginx reverse proxy in front of Authelia
  4. This page: Configuring Caddy reverse proxy in front of Authelia
  5. Logging in to Authelia

Caddy Background

Caddy is a very capable web server that can do, amongst its many many features, a role as a reverse proxy providing SSL communications across public infrastructure to a backend service or services. I encourage you to visit the Caddy Wiki or reviews the Caddy Documentation.

When deploying services using docker, especially on remote servers I find Caddy and its simple config file structure very useful rather than the Nginx Proxy Manager with its portal that I wouldn't want to be publically available. Now I know there are many ways of achieving a secure Nginx deployment, I just consider Caddy an easier option sometimes. Personal choice is a fickle thing. 😀

Pre-Requisites

These are basically the same as those for the Nginx page, so can be skipped if you have already checked them in the previous blog.

All the following items should be in place, configured accordingly and functioning.

  • Public DNS resolution for your three hostnames on your guide. Mine are Mine and will be deleted once this has been confirmed as working.
  • If you are deploying on broadband, and wish to check from your own home network you will need to configure some internal DNS hosts pointing to your internal LAN IP Addresses.
  • TCP-80 and TCP-443 forwarding if you are doing this in your home lab with Internet connectivity from a broadband supplier. If you are using one of the many public VPS (Virtual Private Server) then this is most likely not required.
  • Authelia should be up and running listening to TCP-9091 on your host.
  • The Notifier section for Authelia should be able to send public emails to your chosen email address for the user account. In my example I used Gmail.com
  • In these examples you need two separate web based services to test the 1-factor, and 2-factor authentication. I have just spun up two separate apache web servers, but any other web app can be used as long as it is functioning. Simple is best when troubleshooting in my opinion.

Assuming all those are in place then we are good to go forward.

Caddy file Configuration

Caddy when deployed as a docker (never used it any other way, so don't know) uses a flat file known as "Caddyfile" to store its configuration for hosts etc.

There are a number of ways this Caddyfile can be structured as it appears to be not fussy with respect to order. I'm no expert though, but this is what worked in my environment.

The first element I have is a definition of my trusted subnets. I've just included the RFC-1918 IPv4 addresses, along with the IPv6 unique local address.

(trusted_proxy_list) {
       trusted_proxies 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 fc00::/7
}

Following the trusted_proxy_list definition I have a section called secure_site in which I define auth.milpixel.com:9091 as the forward_auth provider. There rest of the block is fairly straight forward, the uri (Uniform Resource Identifier) is specified.


(secure_site) {
       forward_auth {args[0]} auth.milpixel.com:9091 {
                uri /api/verify?rd=https://auth.milpixel.com
                copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
                import trusted_proxy_list
                header_up Host {upstream_hostport}
       }
}

One point to take care with is, in docker, if you deploy this on a docker network, the hosts can resolve each others container name. However, in this example I haven't done it with a docker network, so I need to provide the full host.domain.name address. If this is wrong, you will see a lot of errors reporting "server misbehaving" as it tries to resolve the container names using DNS rather than on a docker network.

Moving past that, I pull in the trusted_proxy_list here as well so it will apply to all site definitions later in the file.

The first site I have defined is the auth.milpixel.com, this is so that clients can communicate with authelia which is required for future authentication tasks for later sites. Again, the full host.domain.name has been specified rather than docker container names.

# Authelia Portal.
auth.milpixel.com {
        reverse_proxy auth.milpixel.com:9091 {
                import trusted_proxy_list
        }
}

Finally we can get to the definition of sites 1-factor-website.milpixel.com and 2-factor-website.milpixel.com. Both of these items pull in the secure_site definition we defined earlier.

# Protected Endpoint.
1-factor-website.milpixel.com {
        import secure_site *
        reverse_proxy 1-factor-website.milpixel.com:8001 {
                import trusted_proxy_list
        }
}


# Protected Endpoint.
2-factor-website.milpixel.com {
        import secure_site *
        reverse_proxy 2-factor-website.milpixel.com:8002 {
                import trusted_proxy_list
        }
}

When a client connects to one of these sites, it gets pushed to Authelia by the definitions in the secure_site definition, if authenticated, Authelia returns a 200 code back to Caddy which then will proxy to the specified upstream site.

You can see above that both the 1 and 2 factor sites are defined the same. It is Authelia which manages the type of authentication to apply, so Caddy isn't aware and doesn't care about it. It would be possible to use a wildcard to simplify the configuration still further, but I like having specific definitions per site for ease.

Caddyfile using Authelia

The entire Caddyfile is below. This has been tested whilst pulling this blog together, and functions as expected.

(trusted_proxy_list) {
       trusted_proxies 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 fc00::/7
}

(secure_site) {
       forward_auth {args[0]} auth.milpixel.com:9091 {
                uri /api/verify?rd=https://auth.milpixel.com
                copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
                import trusted_proxy_list
                header_up Host {upstream_hostport}
       }
}


# Authelia Portal.
auth.milpixel.com {
        reverse_proxy auth.milpixel.com:9091 {
                import trusted_proxy_list
        }
}


# Protected Endpoint.
1-factor-website.milpixel.com {
        import secure_site *
        reverse_proxy 1-factor-website.milpixel.com:8001 {
                import trusted_proxy_list
        }
}


# Protected Endpoint.
2-factor-website.milpixel.com {
        import secure_site *
        reverse_proxy 2-factor-website.milpixel.com:8002 {
                import trusted_proxy_list
        }
}

That concludes the Caddy configuration for Authelia. If you are interested there are additional pages on this series of blogs below, including using Nginx Proxy Manager as an alternative to Caddy.

  1. Authelia for MFA on docker
  2. Deploying test hosts for Authelia MFA authentication
  3. Configuring Nginx reverse proxy in front of Authelia
  4. This page: Configuring Caddy reverse proxy in front of Authelia
  5. Logging in to Authelia