DEV Community

dpm_bush
dpm_bush

Posted on Originally published at sshflow.com

LDAP on Port 389: TCP, UDP, StartTLS, and Troubleshooting

When an application says it cannot reach LDAP, “port 389 is open” is only part of the answer. You also need to know whether the client is using TCP or UDP, whether the connection is protected with TLS, and whether the LDAP request itself is valid.

Port 389 is the default LDAP port. Typical directory sessions—such as binding to authenticate and searching for entries—use TCP 389. UDP 389 has a different, narrower role: connectionless LDAP (CLDAP), including some Active Directory discovery scenarios. Allowing one transport does not allow the other.

What happens on port 389?

LDAP lets clients query and, when permitted, update directory information such as user, group, device, and application records. Active Directory commonly uses TCP 389 for directory queries, but port numbers are conventions: a server can use a different port, or nothing may be listening on 389 at all.

A successful TCP connection confirms that a network handshake completed. It does not prove that:

  • The client’s username or credentials are valid.
  • The requested directory entry is accessible.
  • The LDAP query has the right base DN or syntax.
  • The session is encrypted.

That last point is easy to miss. TCP 389 can negotiate encryption with StartTLS. Port 389 itself does not mean the session is either encrypted or unencrypted; that depends on the client and server configuration and whether TLS negotiation succeeds.

Port 389, 636, and the Global Catalog

The port helps identify the intended connection, but the client’s protocol settings matter too.

Port Typical use
TCP 389 LDAP sessions; StartTLS can upgrade the connection
UDP 389 CLDAP and specific discovery or lookup scenarios
TCP 636 LDAPS, where TLS is established from the start
TCP 3268 Active Directory Global Catalog queries
TCP 3269 Active Directory Global Catalog over TLS

In many clients, ldap:// indicates LDAP on port 389, while ldaps:// conventionally means TLS from the beginning on port 636. StartTLS begins with LDAP and then negotiates TLS on that connection. Check the client configuration and negotiated connection rather than assuming the port alone guarantees encryption.

For ordinary LDAP queries, TCP is generally the transport to test first. Add UDP only if the application or directory environment needs CLDAP or discovery. A firewall rule for TCP does not cover UDP.

Check the listener and the client path separately

A service can be listening locally while a host firewall, network firewall, VPN, or cloud security rule blocks remote clients. Check the server first, then test from a client on the network that needs access. For other ways to inspect local listeners, see this guide to checking open ports on Linux.

On a Linux LDAP server, check for a TCP listener:

sudo ss -ltnp 'sport = :389'
Enter fullscreen mode Exit fullscreen mode

Check UDP separately if CLDAP is required:

sudo ss -lunp 'sport = :389'
Enter fullscreen mode Exit fullscreen mode

From a Linux or macOS client with nc installed, test TCP reachability:

nc -vz ldap.example.com 389
Enter fullscreen mode Exit fullscreen mode

On Windows, PowerShell can test the TCP port:

Test-NetConnection ldap.example.com -Port 389
Enter fullscreen mode Exit fullscreen mode

Look at TcpTestSucceeded. This test checks TCP connectivity only; it does not test UDP, perform an LDAP bind, or verify TLS.

To test a StartTLS negotiation with an OpenLDAP client, use ldapsearch with your directory’s hostname and base DN:

ldapsearch -x -H ldap://ldap.example.com:389 -ZZ -b "dc=example,dc=com" -s base
Enter fullscreen mode Exit fullscreen mode

The -ZZ option requires StartTLS to succeed before the search continues. This example makes an anonymous search, which some directories disallow. An access-denied response can still mean the network connection and TLS negotiation succeeded.

Troubleshoot the layer that failed

Connection refused: The host responded, but nothing accepted the TCP connection at that address and port, or something actively rejected it. Confirm the LDAP service is running and listening on the interface clients should reach.

Timeout: Traffic may be getting dropped along the route. Check the client network, VPN, host firewall, network firewall, and cloud rules. Confirm that the rule allows the required transport—not just the port number. If you need help tracing where a rule belongs, see this overview of opening ports across Linux, Windows, routers, and cloud firewalls.

TCP succeeds, but the LDAP operation fails: Move on from basic reachability. Check the hostname, base DN, bind identity, credentials, and directory permissions. A completed TCP handshake does not authenticate the user or authorize a search.

StartTLS or certificate error: Confirm that StartTLS is supported and requested, the certificate is trusted, and its name matches the hostname used by the client. Do not treat switching to port 636 as a substitute for verifying certificate validation and client settings.

Works on the server, not remotely: Check whether the service is bound only to loopback. A loopback-only listener accepts local connections, not connections from other machines. Then check host and network firewall rules.

Keep LDAP access limited

Do not expose directory services to the public internet unless there is a specific, reviewed need and appropriate protections. Allow TCP 389 only from the client networks that need LDAP; allow UDP 389 only for documented CLDAP or discovery requirements. Use StartTLS or LDAPS as required by your environment, validate server certificates, and avoid sending credentials over an unprotected LDAP session.

The useful diagnostic distinction is: a port test checks the network path; an LDAP client checks the directory protocol; TLS verification checks the protection on the connection. Treat those as separate steps, and you can narrow down failures without mistaking an open port for a working or secure LDAP setup.

I originally published a more detailed version of this guide on the SSHFlow blog.

I'm also building SSHFlow — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.

Top comments (0)