-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b974efb
Showing
16 changed files
with
949 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
target/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
[package] | ||
name = "ldap_authz_proxy" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
description = "LDAP authorization proxy for authenticated HTTP users" | ||
homepage = "https://github.com/elonen/ldap_authz_proxy" | ||
license = "MIT" | ||
readme = "README.md" | ||
|
||
|
||
[package.metadata.deb] | ||
|
||
maintainer = "Jarno Elonen <[email protected]>" | ||
copyright = "2023, Jarno Elonen <[email protected]>" | ||
section = "unknown" | ||
changelog = "debian/changelog" | ||
|
||
depends = "$auto" | ||
|
||
extended-description = """\ | ||
LDAP authorization server/proxy for use with (e.g.) Nginx. | ||
Extracts usernames from HTTP headers and performs LDAP queries | ||
to authorize them agains Active Directory or other user databases. | ||
""" | ||
|
||
maintainer-scripts = "debian" | ||
assets = [ | ||
["target/release/ldap_authz_proxy", "usr/bin/", "755"], | ||
["README.md", "usr/share/doc/ldap_authz_proxy/README", "644"], | ||
["test/nginx-site.conf", "usr/share/doc/ldap_authz_proxy/examples/", "644"], | ||
["example.ini", "etc/ldap_authz_proxy.conf", "640"], | ||
] | ||
conf-files = ["/etc/ldap_authz_proxy.conf"] | ||
systemd-units = { enable = false } | ||
|
||
|
||
[[bin]] | ||
name = "ldap_authz_proxy" | ||
path = "src/main.rs" | ||
|
||
[dependencies] | ||
anyhow = "1.0.68" | ||
configparser = "3.0.2" | ||
docopt = "1.1.1" | ||
hyper = { version = "0.14.23", features = ["full"] } | ||
ldap3 = "0.11.1" | ||
lru_time_cache = "0.11.11" | ||
regex = "1.7.1" | ||
sha2 = "0.10.6" | ||
tokio = { version = "1.24.2", features = ["full"] } | ||
tracing = "0.1.37" | ||
tracing-appender = "0.2.2" | ||
tracing-subscriber = {version = "0.3.16", features = ["env-filter", "json"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# License | ||
|
||
The project is dual licensed under the terms of the Apache License, Version 2.0, | ||
and the MIT License. You may obtain copies of the two licenses at | ||
|
||
* https://www.apache.org/licenses/LICENSE-2.0 and | ||
* https://opensource.org/licenses/MIT, respectively. | ||
|
||
The following two notices apply to every file of the project. | ||
|
||
## The Apache License | ||
|
||
``` | ||
Copyright 2023 Jarno Elonen <[email protected]> | ||
Licensed under the Apache License, Version 2.0 (the “License”); you may not use | ||
this file except in compliance with the License. You may obtain a copy of the | ||
License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software distributed | ||
under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR | ||
CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
specific language governing permissions and limitations under the License. | ||
``` | ||
|
||
## The MIT License | ||
|
||
``` | ||
Copyright 2023 Jarno Elonen <[email protected]> | ||
Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
this software and associated documentation files (the “Software”), to deal in | ||
the Software without restriction, including without limitation the rights to | ||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
the Software, and to permit persons to whom the Software is furnished to do so, | ||
subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
# ldap_authz_proxy - LDAP authorization proxy for authenticated HTTP users | ||
|
||
Helper that allows Nginx to lookup from Active Directory / LDAP | ||
if a user is authorized to access some resource -- _after_ the user | ||
has been authenticated by some other means (Kerberos, Basic auth, Token | ||
etc). | ||
|
||
Technically it is a small HTTP server that reads usernames from request headers and performs | ||
configured LDAP queries with them, returning status 200 if query succeeded or | ||
403 if it failed. Results are cached for a configurable amount of time. | ||
|
||
## Configuration | ||
|
||
The server is configured with an INI file, such as: | ||
|
||
```ini | ||
[default] | ||
ldap_server_url = ldap://dc1.example.test | ||
ldap_conn_timeout = 10.0 | ||
ldap_bind_dn = CN=service,CN=Users,DC=example,DC=test | ||
ldap_bind_password = password123 | ||
ldap_search_base = DC=example,DC=test | ||
|
||
ldap_cache_size = 1024 | ||
ldap_cache_time = 30 | ||
|
||
username_http_header = X-Ldap-Authz-Username | ||
|
||
[users] | ||
http_path = /users$ | ||
ldap_query = (&(objectCategory=Person)(sAMAccountName=%USERNAME%)(memberOf:1.2.840.113556.1.4.1941:=CN=ACL_Users,CN=Users,DC=example,DC=test)) | ||
|
||
[admins] | ||
http_path = /admins$ | ||
ldap_query = (&(objectCategory=Person)(sAMAccountName=%USERNAME%)(memberOf:1.2.840.113556.1.4.1941:=CN=ACL_Admins,CN=Users,DC=example,DC=test)) | ||
``` | ||
|
||
The `[default]` section contains defaults that can be overridden in other sections. | ||
Other sections can have arbitrary names, and they each specify a URL path matching | ||
rule and settings to apply if it matches. The `http_path` value is a regular expression | ||
that is tested against HTTP requests. If it matches, `ldap_query` from that section | ||
is executed after replacing `%USERNAME%` with the username from `username_http_header` HTTP header. | ||
If the LDAP query succeeds, server returns status 200, otherwise 403. | ||
|
||
## Building | ||
|
||
The server is written in Rust and can be built with `cargo build --release`. | ||
Resulting binary is `target/release/ldap_authz_proxy`. | ||
|
||
## Running | ||
|
||
The server can be run with `ldap_authz_proxy <configfile>`. Additional | ||
options are available, see `--help` for details. | ||
|
||
The executable will stay in foreground, so it is recommended to run it | ||
with a process manager such as `systemd` or `supervisord`. Example | ||
`systemd` service file is included in `debian/service`. | ||
|
||
## Security | ||
|
||
The server doesn't require any special privileges, and can be run as a | ||
normal user; the example `systemd` service file runs it as `www-data`. | ||
|
||
Configuration file contains LDAP bind password(s), so it shouldn't be | ||
world-readable. The server itself doesn't need to be able to write | ||
to the configuration file. | ||
|
||
Usernames are quoted before being used in LDAP queries, so they (hopefully) | ||
can't be used to inject arbitrary LDAP queries. In any case, it is recommended | ||
to use a read-only LDAP bind user just in case. | ||
|
||
LDAPS is supported (even though the test scripts use plain ldap://), and is | ||
recommended in production. | ||
|
||
The server doesn't handle user passwords at all - it only reads usernames from | ||
HTTP headers and performs LDAP queries with them. | ||
|
||
## Packaging | ||
|
||
The server can be packaged for Debian variants with `cargo install cargo-deb && cargo deb`. | ||
This is the recommended way to install it when applicable. | ||
|
||
## Testing | ||
|
||
Use `./run-tests.sh` to execute test suite. It requires `docker compose` | ||
and `curl`. The script performs an end-to-end integratiot test with a | ||
real LDAP server (Active Directory in this case, using Samba) and an | ||
Nginx reverse proxy. It spins up necessary containers, and then performs | ||
Curl HTTP requests against Nginx, comparing their HTTP response status codes to | ||
expected values. | ||
|
||
## Ngix configuration | ||
|
||
See `test/nginx-site.conf` for a simple example where users are authenticated | ||
with the Basic method and then authorized with this server using _auth_request_ directive. | ||
|
||
### Kerberos | ||
|
||
This software was originally developed for Active Directory auth using | ||
Nginx, so here's a complementary real-world example on how to authenticate users against AD with | ||
Kerberos (spnego-http-auth-nginx-module) and to then authorize them using | ||
_ldap_authz_proxy_: | ||
|
||
```nginx | ||
server { | ||
listen 443 ssl; | ||
ssl_certificate /etc/ssl/private/example.com.fullchain.pem; | ||
ssl_certificate_key /etc/ssl/private/example.com.privkey.pem; | ||
server_name www.example.com; | ||
satisfy all; # Require 2 auths: auth_gss (Kerberos) for authn and auth_request (LDAP proxy) for authz | ||
auth_gss on; | ||
auth_gss_keytab /etc/krb5.keytab; | ||
auth_gss_realm EXAMPLE.COM; | ||
auth_gss_force_realm on; | ||
auth_gss_service_name HTTP/www.example.com; | ||
auth_request /authz_all; | ||
proxy_set_header X-Remote-User-Id $remote_user; | ||
proxy_set_header X-Remote-User-Name $remote_user; | ||
location = /authz_all { | ||
internal; | ||
proxy_pass http://127.0.0.1:10567/users; | ||
proxy_pass_request_body off; | ||
proxy_set_header Content-Length ""; | ||
proxy_set_header X-Ldap-Authz-Username $remote_user; | ||
} | ||
location / { | ||
root /var/www/; | ||
index index.html; | ||
try_files $uri $uri/ =404; | ||
} | ||
} | ||
``` | ||
|
||
The VM running Nginx (and ldap_authz_proxy) was joined to AD domain like this: | ||
|
||
``` | ||
kinit <account name> | ||
msktutil -u -s host -s HTTP --dont-expire-password -b OU=Servers --computer-name WWW -h www.example.com | ||
setfacl -m u:www-data:r-- /etc/krb5.keytab | ||
``` | ||
|
||
Some instructions for compiling _spnego-http-auth-nginx-module_ on Debian: https://docs.j7k6.org/sso-nginx-kerberos-spnego-debian/ | ||
|
||
## Development | ||
|
||
Probably the easiest way to develop this is to: | ||
|
||
```bash | ||
# Start test LDAP server | ||
cd test | ||
docker compose up -d | ||
cd .. | ||
|
||
# Config, build and run | ||
sed -i 's@ldap_server_url *=.*@ldap_server_url = ldap://127.0.0.1:3890@' example.ini | ||
cargo run -- example.ini --debug & | ||
|
||
# Test request | ||
curl http://127.0.0.1:10567/admins -H "X-Ldap-Authz-Username:alice" | ||
|
||
# Cleanup | ||
kill %1 # Or do: fg + ctrl-c | ||
cd test | ||
docker compose down | ||
cd .. | ||
git checkout -- example.ini # Reverse the config | ||
``` | ||
|
||
## Contributing | ||
|
||
This server was created to scratch a persistent sysop itch. | ||
Contributions are welcome. | ||
|
||
## License | ||
|
||
Copyright 2023 by Jarno Elonen. | ||
|
||
The project is dual licensed under the terms of the Apache License, Version 2.0, and the MIT License. | ||
See LICENSE.md for details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
This package uses cargo-deb. Build a package by issuing: | ||
|
||
$ cargo install cargo-deb | ||
$ cargo deb |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
ldap_authz_proxy (0.1-1) unstable; urgency=low | ||
|
||
* First release | ||
|
||
-- Jarno Elonen <[email protected]> Sat, 28 Jan 2023 23:08:00 +0000 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
#!/bin/sh | ||
|
||
# Cargo.toml apparently doesn't allow setting owner/group for files, so we do it here | ||
|
||
chown root:www-data /etc/ldap_authz_proxy.conf | ||
chmod 640 /etc/ldap_authz_proxy.conf | ||
|
||
if [ ! -f /var/log/ldap_authz_proxy.log ]; then | ||
touch /var/log/ldap_authz_proxy.log | ||
fi | ||
chown root:www-data /var/log/ldap_authz_proxy.log | ||
chmod 660 /var/log/ldap_authz_proxy.log | ||
|
||
#DEBHELPER# | ||
|
||
exit 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[Unit] | ||
Description=LDAP authz proxy | ||
After=network.target | ||
StartLimitIntervalSec=0 | ||
|
||
[Service] | ||
Type=simple | ||
Restart=always | ||
RestartSec=2 | ||
User=www-data | ||
ExecStart=/usr/bin/ldap_authz_proxy /etc/ldap_authz_proxy.conf --log /var/log/ldap_authz_proxy.log | ||
|
||
[Install] | ||
WantedBy=multi-user.target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[default] | ||
ldap_server_url = ldap://dc1.example.test | ||
ldap_conn_timeout = 10.0 | ||
ldap_bind_dn = CN=service,CN=Users,DC=example,DC=test | ||
ldap_bind_password = password123 | ||
ldap_search_base = DC=example,DC=test | ||
|
||
ldap_cache_size = 1024 | ||
ldap_cache_time = 30 | ||
|
||
username_http_header = X-Ldap-Authz-Username | ||
|
||
[users] | ||
http_path = /users$ | ||
ldap_query = (&(objectCategory=Person)(sAMAccountName=%USERNAME%)(memberOf:1.2.840.113556.1.4.1941:=CN=ACL_Users,CN=Users,DC=example,DC=test)) | ||
|
||
[admins] | ||
http_path = /admins$ | ||
ldap_query = (&(objectCategory=Person)(sAMAccountName=%USERNAME%)(memberOf:1.2.840.113556.1.4.1941:=CN=ACL_Admins,CN=Users,DC=example,DC=test)) |
Oops, something went wrong.