A hardened, self-contained LDAP lookup tool. Produces a statically linked binary with no runtime dependencies. Credentials are either sealed on the target machine (recommended) or embedded at build time - operators choose the mode per build.
- Zero dependencies: Single static binary with no runtime libraries required (no libldap, no ldapsearch)
- Portable: Runs on any Linux system without installation
- Secure credential handling: Credentials are obfuscated and embedded at build time, never exposed in process tables or environment variables
- Deployment locks: Restrict execution to specific hostnames and paths, rendering the binary useless if copied elsewhere (see details)
- Tamper protection: Built-in resistance to debugging and analysis (see details)
- Dual mode: Use
ldaplookupfor users,ldaplookupgfor groups - Smart lookups: Numeric input automatically searches by uidNumber/gidNumber
This tool embeds LDAP credentials directly in the binary. Before using it, consider these alternatives:
Preferred alternatives:
- Kerberos/GSSAPI: Uses ticket-based authentication with no stored secrets. If your environment supports Kerberos, this is the recommended approach.
- Secrets management platforms: Enterprise solutions offer better audit trails, credential rotation, and access controls.
When this tool may be appropriate:
- Parallel automation across many systems where secrets management platforms may hit rate limits or cause latency
- LDAP services that do not support GSSAPI authentication
- Environments without centralized secrets infrastructure
- Situations where a self-contained, dependency-free binary is required
This tool is not:
- A replacement for proper secrets management
- Immune to reverse engineering (obfuscation ≠ encryption)
- Suitable for public or untrusted systems
Recommendation: Always enable hostname and path locks to maximize protection.
Go produces native machine-code binaries. This can raise the effort to reverse engineer compared to shipping source or readily decompilable artifacts, but it does not prevent reverse engineering. This tool adds another layer using garble, a build wrapper that obfuscates Go binaries by randomizing symbol names, obscuring string literals, and stripping/altering metadata.
The build uses flags from garble, Go's build system, the Go linker, and environment variables:
| Flag | Source | Purpose |
|---|---|---|
-literals |
garble | Obfuscate string literals, preventing simple string extraction |
-tiny |
garble | Strip debug info and runtime panic/trace output, hindering analysis |
-seed |
garble | Use deterministic randomization for reproducible builds |
-trimpath |
go build | Remove local filesystem paths from binary |
-buildid= |
go linker | Remove Go build ID to hinder version fingerprinting |
GOGARBLE='*' |
env var | Obfuscate all packages including dependencies |
Sealed credentials protect against binary analysis by keeping the password out of the binary entirely.
How it works:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ BUILD │ │ DEPLOY │ │ SEAL │
│ No password │ ───► │ Copy binary │ ───► │ --seal │
│ embedded │ │ to target │ │ encrypts pw │
└─────────────┘ └─────────────┘ └──────┬──────┘
│
▼
┌─────────────┐
│ .seal file │
│ AES-256-GCM │
│machine-bound│
└─────────────┘
The password is encrypted using a key derived from:
- The target machine's unique ID (
/etc/machine-id) - A random salt generated at build time
- The deployment directory path
- The running user's UID
- A SHA-256 hash of the binary itself
This means:
- The binary contains no extractable password
- The
.sealfile is useless on any other machine, under any other user, or at any other path - Moving, copying, or upgrading the binary breaks decryption - re-run
--sealafter replacing the binary - Each user gets their own seal file (
<binary>.seal.<uid>)
Credential rotation:
./ldaplookup --seal # Prompts to overwrite if .seal existsChoosing a mode (selected when you run build.sh):
| Mode 1 - Sealed (recommended) | Mode 2 - Embedded (legacy) | |
|---|---|---|
| Password location | Encrypted .seal on the target; never in the binary |
XOR-obfuscated inside the binary |
| Static-analysis resistance | High - key is bound to machine, user, path, and binary | Low - recoverable from the binary |
| Deployment | Requires running --seal once on the target |
Single artifact, no post-deploy step |
Both modes are built from the same source; you choose one per build.
Hostname lock: Restricts execution to specific servers using DNS verification.
- At build time, specify a trusted DNS server IP and allowed hostnames
- At runtime, the binary queries the trusted DNS server to resolve its own hostname and verifies it matches the allowed list
Using a DNS server you control is critical. It prevents attackers from spoofing hostname resolution on a compromised system. If verification fails, tamper protection is triggered.
Important: Hostname matching is exact. Use the FQDN (e.g., myserver.umich.edu) not just the short name.
To check what your system will return, run: hostname -f
You can provide multiple FQDNs (comma-separated) for systems with multiple hostnames: server1.umich.edu,server2.umich.edu
Known limitation: The DNS step confirms that the allowed FQDN resolves via your trusted DNS server; it does not cryptographically prove that the local host's identity is that FQDN. Use it together with the path lock and OS-level access controls for defense in depth.
Path lock: Restricts execution to a specific deployment directory.
- At build time, specify the allowed deployment path (e.g.,
/opt/ldaplookup) - At runtime, the binary verifies its executable path matches the allowed location
This prevents the binary from being copied elsewhere and executed. If verification fails, tamper protection is triggered.
Built-in tamper resistance protects against analysis and unauthorized use.
Self-Destruct Behavior
| Trigger | Action | Reason |
|---|---|---|
| Hostname mismatch | Binary deletes itself | Unauthorized host |
| Path mismatch | Binary deletes itself | Moved/copied illegally |
| Debugger detected | Binary deletes itself | Analysis attempt |
Note: Self-destruct is best-effort. Deletion can fail on read-only filesystems, immutable (chattr +i) files, hardlinked binaries, or where the running user lacks write access to the binary's directory. Treat the locks as a strong deterrent, not a hard guarantee.
- Use
chmod 110and open up as needed - Use a dedicated LDAP service account with read-only permissions
- Deploy only to trusted, access-controlled systems
- Rotate credentials if a binary is compromised
Note: If you already have Go installed and configured (go version works), skip this section. The build scripts will install garble automatically.
If Go is not available system-wide and you prefer not to install it globally, use the included helper script:
./install-go-local.sh # Downloads and installs Go + garble
source ~/myGo/env.sh # Activate in current terminal
./test_build.sh # Verify build works
./build.sh # Build with real credentialsThe script installs to ~/myGo/ and creates an environment file. To make permanent, add source ~/myGo/env.sh to your ~/.bashrc.
./build.shYou'll be prompted for:
- Security disclaimer acknowledgment (type 'yes' to continue)
- LDAP server URL (e.g.,
ldaps://ldap.umich.edu) - User search base (e.g.,
ou=People,dc=umich,dc=edu) - Group search base (e.g.,
ou=User Groups,ou=Groups,dc=umich,dc=edu) - Bind DN (full Distinguished Name, e.g.,
cn=App01,ou=Applications,o=services- not justcn=App01) - Bind password
- Hostname lock (enabled by default)
- Path lock (enabled by default)
- Whether to generate a new obfuscation seed
To verify the build process works without real credentials:
./test_build.shThis script builds with dummy test data, verifies the binary is created and executes, then cleans up.
The build produces ldaplookup and a symlink ldaplookupg. To deploy:
# Copy the binary to target
cp ldaplookup /path/to/destination/
# Create the symlink for group lookups
ln -s ldaplookup /path/to/destination/ldaplookupg
# If using sealed credentials (default), seal on the target machine
/path/to/destination/ldaplookup --seal
# Enter the LDAP password when promptedThe binary detects its invocation name. When called as ldaplookupg, it queries groups instead of users.
Note: If you used legacy embedded credentials during build, the --seal step is not required.
The build script prompts whether to generate a new obfuscation seed. The seed controls how garble randomizes the obfuscation. The same seed produces the same obfuscated output.
When to keep the existing seed (answer N):
- Rebuilding with the same credentials
- You want the binary hash to remain consistent
- Avoiding detection as a "new" binary by endpoint security tools
When to generate a new seed (answer Y):
- Credentials have changed
- You want protection against differential analysis (comparing two binaries to identify patterns)
- Deploying to a new environment where a fresh fingerprint is acceptable
Trade-off: A new seed produces a completely different binary, which provides better protection against reverse engineering through comparison. However, endpoint detection and response (EDR) tools may flag it as an unknown binary until it's re-baselined in your environment.
The seed is stored in .garble_seed (gitignored) and reused on subsequent builds unless you choose to regenerate it.
# User lookup by uid
./ldaplookup <uid>
# User lookup by uidNumber (auto-detected)
./ldaplookup 12345
# Group lookup by name
./ldaplookupg <groupname>
# Group lookup by gidNumber (auto-detected)
./ldaplookupg 1001
# Specific attributes
./ldaplookup <uid> uid displayName uidNumber
./ldaplookupg <groupname> cn gidNumber memberUid# Get all attributes for user
./ldaplookup jsmith
# Get specific user attributes
./ldaplookup jsmith uid uidNumber mail
# Get group by name
./ldaplookupg staff
# Get group by gidNumber
./ldaplookupg 1001- Go 1.25.10+ (patch level matters: earlier 1.25.x have known crypto/tls and crypto/x509 advisories)
- garble, pinned:
GOTOOLCHAIN=local go install mvdan.cc/garble@v0.15.0(must match the Go version above) - openssl (for seed generation)