137 lines
5.1 KiB
Text
137 lines
5.1 KiB
Text
---
|
|
pubDate: '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
|