diff --git a/src/routes/blog/lldap-caddy/+page.md b/src/routes/blog/lldap-caddy/+page.md new file mode 100644 index 0000000..c87b431 --- /dev/null +++ b/src/routes/blog/lldap-caddy/+page.md @@ -0,0 +1,142 @@ +--- +created: '2022-09-24' +title: 'Securing a Caddy endpoint with LLDAP' +description: '' +keywords: + - LDAP + - Caddy +--- + + + +For my small home network, I was looking around for a solution to synchronize user +accounts across services. I host various services like a file server or smaller web +applications that are accessed by my significant other and a couple of friends. In the +past, I manually created accounts and passed on the password, which is slightly tedious. +Another pain point is, that my web applications are unsecured at the moment. I would like +to have an SSO login screen before being able to access a service. As a reverse proxy, I +already deployed [Caddy](https://caddyserver.com/). + +After researching I found out that for me and the services I am running, LDAP would be the +best choice as it has the best compatibility and appears to be the industry standard for +this kind of problem. Looking for a server proofed hard: most of them are rather heavy +with lots of functions that I don't need or are tedious to configure. Consequently, I +searched for a lightweight server and found one: +[LLDAP](https://github.com/nitnelave/lldap) is exactly what I was looking for. The project +includes a lightweight LDAP server, which only supports a bare minimum of features (users, +groups). Passwords can be reset by users in a small - admittedly ugly - web interface. +Perfect for me. For locking down web applications there is +[caddy-security](https://authp.github.io/) - an addon that allows interaction with LDAP +before granting access to a site. + +## Setup + +Since I installed a Caddy Docker image with caddy-security already integrated, I did not +have to do anything else. For example, [Alexander +Henderson's](https://hub.docker.com/r/alexandzors/caddy#!) image comes with several useful +modules preinstalled, that I require for other projects anyway. If that's not an option, +you can easily create your own Docker image including caddy-security. + +The installation is quickly done with Docker. I mounted a folder instead of the suggested +volume. The initial password can be reset in the administration panel. I utilize a custom +bridge for networking so that I can resolve my other services with DNS. After that, we can +navigate to http://IP:17170 and are presented with the administration panel, where we can +create users and groups. + + + +## Integration with Caddy + +Assume we have a domain `example.xyz`. You can already create A or CNAME records for the +domains `auth.example.xyz` and `example.xyz` and point them at your Caddy server. + +Now we need to prepare the authentication part in the global block of the Caddy file as +follows. In your Caddy docker-compose file make sure to add the two env vars for +LLDAP_ADMIN_PASSWORD and JWT_SHARED_KEY. The part with search_filter and group is crucial. +The search_filter is the query that is used to find your user object in the domain. Once +it is found, depending on the groups your user is in, groups get assigned within the Caddy +authentication procedure. In this example, a domain user that belongs to the group `user` +gets assigned the `authp/user` group. + +The file is an adoption of the caddy-security documentation[^1] to interact with LLDAP: + +``` +order authenticate before respond +order authorize before basicauth + +security { + ldap identity store example.xyz { + realm example.xyz + servers { + ldap://lldap:3890 + } + attributes { + name displayName + surename cn + username uid + member_of memberOf + email mail + } + username "CN=admin,OU=people,DC=example,DC=xyz" + password "{env.LLDAP_ADMIN_PASSWORD}" + search_base_dn "DC=example,DC=xyz" + search_filter "(&(uid=%s)(objectClass=person))" + groups { + "uid=user,ou=groups,dc=example,dc=xyz" authp/user + } + } + + authentication portal myportal { + crypto default token lifetime 3600 + crypto key sign-verify {env.JWT_SHARED_KEY} + enable identity store example.xyz + cookie domain example.xyz + ui { + logo url "https://caddyserver.com/resources/images/caddy-circle-lock.svg" + logo description "Caddy" + links { + "My Identity" "/whoami" icon "las la-user" + } + } + } + + authorization policy mypolicy { + # disable auth redirect + set auth url https://auth.example.xyz + + crypto key verify {env.JWT_SHARED_KEY} + allow roles authp/user + } +} +``` + +Finally, we need to create endpoints for auth and whatever service we need to protect. +Make sure that the names `myportal` and `mypolicy` match with the previously declared +ones. Note: the `import tls` originated from my globally defined TLS setup and is not +relevant to this guide. + +``` +auth.example.xyz { + route { + authenticate with myportal + } +} + +example.xyz { + root * /config/html + authorize with mypolicy + encode gzip + file_server browse + import tls +} +``` + +I strongly recommend consulting the documentation[^1][^2] for in-depth information. + +Happy authenticating! + +[^1]: https://authp.github.io/docs/authenticate/ldap/ldap +[^2]: https://github.com/nitnelave/lldap diff --git a/src/routes/blog/lldap-caddy/lldap_overview.png b/src/routes/blog/lldap-caddy/lldap_overview.png new file mode 100644 index 0000000..88dc2fd Binary files /dev/null and b/src/routes/blog/lldap-caddy/lldap_overview.png differ