Closed d3xt3r01 closed 1 year ago
You have neglected to provide the version of ldapjs
you are using. However, what the stacktrace is showing is that an LDAP message is malformed (or not being processed correctly). I assume the message id is trying to be read, but has instead found a different field.
Without a reproduction, the minimum amount of information we would need would be any errors reported by some of the error events listed at https://github.com/ldapjs/node-ldapjs/blob/9613308c331ec944029cd01b9039c593f1cdd214/docs/client.md#client-events
Ideally, you would provide a Wireshark capture of the issue. It would be highly recommended that you create a test account with trash credentials to use during this capture.
port <ldap_port>
(e.g. port 636
)tls-capture.js
script (with env vars set as required)tls.log
file$ openssl rsautl -encrypt -inkey public.key -pubin -in files.zip -out files.rsa.zip
Thanks for the fast reply!
bind error: TimeoutError: request timeout (client interrupt)
at Timeout.onRequestTimeout (/home/dexter/public_html/work/personal/npm/node_modules/ldapjs/lib/client/client.js:1277:10)
at listOnTimeout (node:internal/timers:569:17)
at process.processTimers (node:internal/timers:512:7) {
lde_message: 'request timeout (client interrupt)',
lde_dn: null
}
I doubt it gets to talk to the ldap. I think it fails somewhere in the TLS part (traefik handles that part just fine for all other services, plus it works ok with a shell client). Might it be because it needs SNI?
Trying to use encrypt the output fails...
$ openssl rsautl -encrypt -inkey pk.key -pubin -in ldapjs853.zip -out ldapjs853.zip.enc
RSA operation error
140319407814464:error:0406D06E:rsa routines:RSA_padding_add_PKCS1_type_2:data too large for key size:crypto/rsa/rsa_pk1.c:124:
Attached the requested zip anyway. I don't have anything important there anyway, it's a local docker test environment in which I can reset everything fast.
If you load the capture in Wireshark, attach the tls.log
(https://wiki.wireshark.org/TLS#using-the-pre-master-secret), and look at the decrypted body of frame 13 you'll see:
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Connection: close
400 Bad Request
Frame 13 is a response to frame 10, which is an appropriate BIND
request:
0000 30 34 02 01 01 60 2f 02 01 03 04 18 63 6e 3d 61 04...`/.....cn=a
0010 64 6d 69 6e 2c 64 63 3d 6c 6f 63 61 6c 2c 64 63 dmin,dc=local,dc
0020 3d 6c 61 6e 80 10 <rest redacted>
So what it looks like to me is that you are attempting to talk LDAP over HTTP. That's not going to work.
So it is because of the client not sending SNI then? The shell client handles it just fine!
$ ldapwhoami -x -D "cn=admin,dc=local,dc=lan" -H ldaps://openldap.local.lan:1636 -W
Enter LDAP Password:
dn:cn=admin,dc=local,dc=lan
This is a capture from the ldapwhoami
above.
In this one on frame 4 ( Client Hello ) Extension: server_name
can be seen.
More context: I'm using Traefik as a tcp proxy. Which probably requires the tcp SNI to identify the route to send it to the ldap container... Otherwise probably it yells 400 not knowing what to do. I agree it's weird that given only one service is behind, it should transmit everything there... but also, the client should know SNI which is a very well known TLS extension and used almost everywhere where I've seen TLS involved.
I tried setting sniStrict: false
for this route with no luck. In this case it might be a bug in traefik's part but that's another story. The CLI client still works.
Sorry, but this is clearly not an issue with ldapjs
. You'll need to determine how to make your proxy function correctly as a plain TCP proxy instead of an HTTP proxy.
See https://community.traefik.io/t/how-to-proxy-tcp-with-tls-for-clients-lacking-hostsni-for-use-cases-such-as-ldap-redis/15570 (🤷♂️ just the first result from a search).
The issue is at another layer!
Http and ldap are layer7, TLS is below that.
It's not an http proxy, it's a TCP proxy.
SNI isn't http related, it's a TLS extension especially used to identify resources like in this case.
This can be seen in the last pcap file and I am sure the cli ldap clients don't do any http at all, they implemented sni on TLS as one should.
Another example that works just fine is authelia and any other service so far (nextcloud, postfix among others.)
This is clearly just a couple of lines somewhere to add the hostname in the server connection context when doing the TLS this can be extracted from the URI and passed on. Shouldn't affect anything otherwise.
The result is http because that's the way traefik answers to unknown TLS without sni requests.
You might not plan to implement this but this still is an issue in this library in this particular case.
On Mon, 13 Mar 2023, 00:21 James Sumners, @.***> wrote:
Closed #853 https://github.com/ldapjs/node-ldapjs/issues/853 as not planned.
— Reply to this email directly, view it on GitHub https://github.com/ldapjs/node-ldapjs/issues/853#event-8726408942, or unsubscribe https://github.com/notifications/unsubscribe-auth/AARHYG3LO54PU7ETOWBG7Z3W3ZLBFANCNFSM6AAAAAAVYEBDQI . You are receiving this because you authored the thread.Message ID: @.***>
This is not an issue of the ldapjs
library. This library uses the standard tls.connect
method from the Node.js core framework. We do not do anything special in regard to SNI or not.
See:
Unlike the https API, tls.connect() does not enable the SNI (Server Name Indication) extension by default, which may cause some servers to return an incorrect certificate or reject the connection altogether. To enable SNI, set the servername option in addition to host.
I guess it should turn it on, it doesn't do any harm.
Perfect, so all that's needed seems to be a servername: "openldap.local.lan"
.
I'll open a ticket with rocketchat to add this option.
Why not to automatically handover the hostname from the ldaps:// URI to the tls.connect()
option servername
?
If you use ldaps:// instead of ldap:// then you probably want to have SNI enabled.
Because we do not know that all clients will always want to pass in that option. As noted, the tlsOptions
parameter is directly passed to tls.connect
.
So it all started when trying to make rocketchat use ldap. It kept giving timeouts for some reason that I couldn't understand. Further digging pointed me to the ldapjs module.
My openldap server is behind a tcp router in traefik with TLS ( NOT STARTTLS!!! ). I can connect to it just fine from a shell
But using the small snippet here throws me the error I see in rocketchat...
and the error...
If this is an issue, I assume it's the same as in rocketchat... any pointers given would be useful to open up an issue with rocketchat.