add debug output to lldap with mitmdump module

This commit is contained in:
ibizaman 2025-07-30 23:21:19 +02:00
parent d824da617c
commit b4d6965231
3 changed files with 102 additions and 48 deletions

View file

@ -342,7 +342,7 @@ in
allow ${cfg.restrictAccessIPRange}; allow ${cfg.restrictAccessIPRange};
deny all; deny all;
''); '');
proxyPass = "http://${toString config.services.lldap.settings.http_host}:${toString config.services.lldap.settings.http_port}/"; proxyPass = "http://${toString config.services.lldap.settings.http_host}:${toString config.shb.lldap.webUIListenPort}/";
}; };
}; };
}; };
@ -366,10 +366,10 @@ in
settings = { settings = {
http_url = "https://${fqdn}"; http_url = "https://${fqdn}";
http_host = "127.0.0.1"; http_host = "127.0.0.1";
http_port = cfg.webUIListenPort; http_port = if !cfg.debug then cfg.webUIListenPort else cfg.webUIListenPort + 1;
ldap_host = "127.0.0.1"; ldap_host = "127.0.0.1";
ldap_port = cfg.ldapPort; ldap_port = cfg.ldapPort; # Would be great to be able to inspect this but it requires tcpdump instead of mitmproxy.
ldap_base_dn = cfg.dcdomain; ldap_base_dn = cfg.dcdomain;
ldap_user_pass_file = toString cfg.ldapUserPassword.result.path; ldap_user_pass_file = toString cfg.ldapUserPassword.result.path;
@ -384,5 +384,15 @@ in
"password_file" = toString v.password.result.path; "password_file" = toString v.password.result.path;
}) cfg.ensureUsers; }) cfg.ensureUsers;
}; };
shb.mitmdump.instances."lldap-web" = lib.mkIf cfg.debug {
listenPort = config.shb.lldap.webUIListenPort;
upstreamPort = config.shb.lldap.webUIListenPort + 1;
after = [ "lldap.service" ];
enabledAddons = [ config.shb.mitmdump.addons.logger ];
extraArgs = [
"--set" "verbose_pattern=/api"
];
};
}; };
} }

View file

@ -168,6 +168,15 @@ so you will want to revert this at some point.
To see the logs, then run `journalctl -u lldap.service`. To see the logs, then run `journalctl -u lldap.service`.
Setting the `debug` option to `true` will also
add an [shb.mitmdump][] instance in front of the LLDAP [web UI port](#blocks-lldap-options-shb.lldap.webUIListenPort)
which prints all requests and responses headers and body
to the systemd service `mitmdump-lldap.service`. Note the you won't
see the query done using something like `ldapsearch` since those
go through the [`LDAP` port](#blocks-lldap-options-shb.lldap.ldapPort).
[shb.mitmdump]: ./blocks-mitmdump.html
## Tests {#blocks-lldap-tests} ## Tests {#blocks-lldap-tests}
Specific integration tests are defined in [`/test/blocks/lldap.nix`](@REPO@/test/blocks/lldap.nix). Specific integration tests are defined in [`/test/blocks/lldap.nix`](@REPO@/test/blocks/lldap.nix).

View file

@ -20,6 +20,7 @@ in
} }
../../modules/blocks/hardcodedsecret.nix ../../modules/blocks/hardcodedsecret.nix
../../modules/blocks/lldap.nix ../../modules/blocks/lldap.nix
../../modules/blocks/mitmdump.nix
]; ];
shb.lldap = { shb.lldap = {
@ -29,7 +30,6 @@ in
domain = "example.com"; domain = "example.com";
ldapUserPassword.result = config.shb.hardcodedsecret.ldapUserPassword.result; ldapUserPassword.result = config.shb.hardcodedsecret.ldapUserPassword.result;
jwtSecret.result = config.shb.hardcodedsecret.jwtSecret.result; jwtSecret.result = config.shb.hardcodedsecret.jwtSecret.result;
debug = true;
ensureUsers = { ensureUsers = {
"charlie" = { "charlie" = {
@ -56,64 +56,99 @@ in
}; };
networking.firewall.allowedTCPPorts = [ 80 ]; # nginx port networking.firewall.allowedTCPPorts = [ 80 ]; # nginx port
environment.systemPackages = [ pkgs.openldap ];
specialisation = {
withDebug.configuration = {
shb.lldap.debug = true;
};
};
}; };
nodes.client = {}; nodes.client = {};
# Inspired from https://github.com/lldap/lldap/blob/33f50d13a2e2d24a3e6bb05a148246bc98090df0/example_configs/lldap-ha-auth.sh # Inspired from https://github.com/lldap/lldap/blob/33f50d13a2e2d24a3e6bb05a148246bc98090df0/example_configs/lldap-ha-auth.sh
testScript = { nodes, ... }: '' testScript = { nodes, ... }:
let
specializations = "${nodes.server.system.build.toplevel}/specialisation";
in
''
import json import json
start_all() start_all()
server.wait_for_unit("lldap.service")
server.wait_for_open_port(${toString nodes.server.services.lldap.settings.http_port})
with subtest("fail without authenticating"): def tests():
client.fail( server.wait_for_unit("lldap.service")
"curl -f -s -X GET" server.wait_for_open_port(${toString nodes.server.shb.lldap.webUIListenPort})
+ """ -H "Content-type: application/json" """ server.wait_for_open_port(${toString nodes.server.shb.lldap.ldapPort})
+ """ -H "Host: ldap.example.com" """
+ " http://server/api/graphql"
)
with subtest("fail authenticating with wrong credentials"): with subtest("fail without authenticating"):
client.fail( client.fail(
"curl -f -s -X POST" "curl -f -s -X GET"
+ """ -H "Content-type: application/json" """ + """ -H "Content-type: application/json" """
+ """ -H "Host: ldap.example.com" """ + """ -H "Host: ldap.example.com" """
+ " http://server/auth/simple/login" + " http://server/api/graphql"
+ """ -d '{"username": "admin", "password": "wrong"}'""" )
)
with subtest("succeed with correct authentication"): with subtest("fail authenticating with wrong credentials"):
token = json.loads(client.succeed( resp = client.fail(
"curl -f -s -X POST " "curl -f -s -X POST"
+ """ -H "Content-type: application/json" """ + """ -H "Content-type: application/json" """
+ """ -H "Host: ldap.example.com" """ + """ -H "Host: ldap.example.com" """
+ " http://server/auth/simple/login " + " http://server/auth/simple/login"
+ """ -d '{"username": "admin", "password": "${password}"}' """ + """ -d '{"username": "admin", "password": "wrong"}'"""
))['token'] )
data = json.loads(client.succeed( print(resp)
"curl -f -s -X POST "
+ """ -H "Content-type: application/json" """
+ """ -H "Host: ldap.example.com" """
+ """ -H "Authorization: Bearer {token}" """.format(token=token)
+ " http://server/api/graphql "
+ """ -d '{"variables": {"id": "admin"}, "query":"query($id:String!){user(userId:$id){displayName groups{displayName}}}"}' """
))['data']
assert data['user']['displayName'] == "Administrator" with subtest("succeed with correct authentication"):
assert data['user']['groups'][0]['displayName'] == "lldap_admin" token = json.loads(client.succeed(
"curl -f -s -X POST "
+ """ -H "Content-type: application/json" """
+ """ -H "Host: ldap.example.com" """
+ " http://server/auth/simple/login "
+ """ -d '{"username": "admin", "password": "${password}"}' """
))['token']
with subtest("succeed charlie"): data = json.loads(client.succeed(
client.succeed( "curl -f -s -X POST "
"curl -f -s -X POST " + """ -H "Content-type: application/json" """
+ """ -H "Content-type: application/json" """ + """ -H "Host: ldap.example.com" """
+ """ -H "Host: ldap.example.com" """ + """ -H "Authorization: Bearer {token}" """.format(token=token)
+ " http://server/auth/simple/login " + " http://server/api/graphql "
+ """ -d '{"username": "charlie", "password": "${charliePassword}"}' """ + """ -d '{"variables": {"id": "admin"}, "query":"query($id:String!){user(userId:$id){displayName groups{displayName}}}"}' """
) ))['data']
assert data['user']['displayName'] == "Administrator"
assert data['user']['groups'][0]['displayName'] == "lldap_admin"
with subtest("succeed charlie"):
resp = client.succeed(
"curl -f -s -X POST "
+ """ -H "Content-type: application/json" """
+ """ -H "Host: ldap.example.com" """
+ " http://server/auth/simple/login "
+ """ -d '{"username": "charlie", "password": "${charliePassword}"}' """
)
print(resp)
with subtest("ldap user search"):
resp = server.succeed('ldapsearch -H ldap://127.0.0.1:${toString nodes.server.shb.lldap.ldapPort} -D uid=admin,ou=people,dc=example,dc=com -b "ou=people,dc=example,dc=com" -w ${password}')
print(resp)
if "uid=admin" not in resp:
raise Exception("Expected to find admin")
if "uid=charlie" not in resp:
raise Exception("Expected to find charlie")
with subtest("no debug"):
tests()
with subtest("with debug"):
server.succeed('${specializations}/withDebug/bin/switch-to-configuration test')
tests()
''; '';
}; };
} }