diff --git a/modules/blocks/lldap.nix b/modules/blocks/lldap.nix index e40b801..b313d28 100644 --- a/modules/blocks/lldap.nix +++ b/modules/blocks/lldap.nix @@ -342,7 +342,7 @@ in allow ${cfg.restrictAccessIPRange}; 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 = { http_url = "https://${fqdn}"; 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_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_user_pass_file = toString cfg.ldapUserPassword.result.path; @@ -384,5 +384,15 @@ in "password_file" = toString v.password.result.path; }) 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" + ]; + }; }; } diff --git a/modules/blocks/lldap/docs/default.md b/modules/blocks/lldap/docs/default.md index da7ef3c..b22fb3c 100644 --- a/modules/blocks/lldap/docs/default.md +++ b/modules/blocks/lldap/docs/default.md @@ -168,6 +168,15 @@ so you will want to revert this at some point. 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} Specific integration tests are defined in [`/test/blocks/lldap.nix`](@REPO@/test/blocks/lldap.nix). diff --git a/test/blocks/lldap.nix b/test/blocks/lldap.nix index e988f82..af259d5 100644 --- a/test/blocks/lldap.nix +++ b/test/blocks/lldap.nix @@ -20,6 +20,7 @@ in } ../../modules/blocks/hardcodedsecret.nix ../../modules/blocks/lldap.nix + ../../modules/blocks/mitmdump.nix ]; shb.lldap = { @@ -29,7 +30,6 @@ in domain = "example.com"; ldapUserPassword.result = config.shb.hardcodedsecret.ldapUserPassword.result; jwtSecret.result = config.shb.hardcodedsecret.jwtSecret.result; - debug = true; ensureUsers = { "charlie" = { @@ -56,64 +56,99 @@ in }; networking.firewall.allowedTCPPorts = [ 80 ]; # nginx port + + environment.systemPackages = [ pkgs.openldap ]; + + specialisation = { + withDebug.configuration = { + shb.lldap.debug = true; + }; + }; }; nodes.client = {}; # 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 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"): - client.fail( - "curl -f -s -X GET" - + """ -H "Content-type: application/json" """ - + """ -H "Host: ldap.example.com" """ - + " http://server/api/graphql" - ) + def tests(): + server.wait_for_unit("lldap.service") + server.wait_for_open_port(${toString nodes.server.shb.lldap.webUIListenPort}) + server.wait_for_open_port(${toString nodes.server.shb.lldap.ldapPort}) - with subtest("fail authenticating with wrong credentials"): - client.fail( - "curl -f -s -X POST" - + """ -H "Content-type: application/json" """ - + """ -H "Host: ldap.example.com" """ - + " http://server/auth/simple/login" - + """ -d '{"username": "admin", "password": "wrong"}'""" - ) + with subtest("fail without authenticating"): + client.fail( + "curl -f -s -X GET" + + """ -H "Content-type: application/json" """ + + """ -H "Host: ldap.example.com" """ + + " http://server/api/graphql" + ) - with subtest("succeed with correct authentication"): - 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("fail authenticating with wrong credentials"): + resp = client.fail( + "curl -f -s -X POST" + + """ -H "Content-type: application/json" """ + + """ -H "Host: ldap.example.com" """ + + " http://server/auth/simple/login" + + """ -d '{"username": "admin", "password": "wrong"}'""" + ) - data = json.loads(client.succeed( - "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'] + print(resp) - assert data['user']['displayName'] == "Administrator" - assert data['user']['groups'][0]['displayName'] == "lldap_admin" + with subtest("succeed with correct authentication"): + 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"): - 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}"}' """ - ) + data = json.loads(client.succeed( + "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" + 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() ''; }; }