| Crates.io | yamldap |
| lib.rs | yamldap |
| version | 0.1.2 |
| created_at | 2025-06-13 16:50:32.911754+00 |
| updated_at | 2025-06-16 09:38:57.9444+00 |
| description | A lightweight LDAP server that serves directory data from YAML files |
| homepage | https://github.com/rvben/yamldap |
| repository | https://github.com/rvben/yamldap |
| max_upload_size | |
| id | 1711660 |
| size | 442,694 |
A lightweight LDAP server that serves directory data from YAML files, designed for local development and testing.
cargo install yamldap
Download pre-built binaries from the GitHub Releases page for:
git clone https://github.com/rvben/yamldap
cd yamldap
cargo install --path .
Pull from GitHub Container Registry:
# Pull the latest version (multi-platform: linux/amd64, linux/arm64)
docker pull ghcr.io/rvben/yamldap:latest
# Or pull a specific version
docker pull ghcr.io/rvben/yamldap:0.0.1
# Run with your YAML directory file
docker run -p 389:389 -v $(pwd)/directory.yaml:/data/directory.yaml ghcr.io/rvben/yamldap:latest -f /data/directory.yaml
Or build locally:
docker build -t yamldap .
docker run -p 389:389 -v $(pwd)/examples/sample_directory.yaml:/data/directory.yaml yamldap:latest -f /data/directory.yaml
Using pre-built images from registry:
docker compose -f compose.registry.yml up
Or build and run locally:
docker compose up
directory:
base_dn: "dc=example,dc=com"
entries:
- dn: "dc=example,dc=com"
objectClass: ["top", "domain"]
dc: "example"
- dn: "ou=users,dc=example,dc=com"
objectClass: ["top", "organizationalUnit"]
ou: "users"
- dn: "uid=john,ou=users,dc=example,dc=com"
objectClass: ["top", "person", "inetOrgPerson"]
uid: "john"
cn: "John Doe"
sn: "Doe"
mail: "john@example.com"
userPassword: "secret123"
# On a non-privileged port
yamldap -f directory.yaml --port 3389
# Or with Docker from registry
docker run -p 389:389 -v $(pwd)/directory.yaml:/data/directory.yaml ghcr.io/rvben/yamldap:latest -f /data/directory.yaml
# Or with anonymous bind enabled
docker run -p 389:389 -v $(pwd)/directory.yaml:/data/directory.yaml ghcr.io/rvben/yamldap:latest -f /data/directory.yaml --allow-anonymous
# Search all entries
ldapsearch -x -H ldap://localhost:3389 -b "dc=example,dc=com" "(objectClass=*)"
# Authenticate and search
ldapsearch -x -H ldap://localhost:3389 \
-D "uid=john,ou=users,dc=example,dc=com" \
-w secret123 \
-b "dc=example,dc=com" "(uid=john)"
yamldap [OPTIONS]
Options:
-f, --file <FILE> Path to YAML directory file
-p, --port <PORT> Port to listen on [default: 389]
--bind-address <ADDR> Address to bind to [default: 0.0.0.0]
--allow-anonymous Allow anonymous bind operations
-v, --verbose Enable verbose logging
--log-level <LEVEL> Set log level: debug, info, warn, error [default: info]
-h, --help Print help
directory:
base_dn: "dc=example,dc=com" # Required: Base DN for the directory
entries: # List of directory entries
- dn: "..." # Distinguished Name
objectClass: [...] # Object classes
attribute: value # Attributes and values
# Plain text (for testing only!)
userPassword: "plaintext"
# SHA hash
userPassword: "{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g="
# Salted SHA
userPassword: "{SSHA}DkMTwBl+a/3DfY+MTDTrcd5kMT8dpDkE"
# Bcrypt
userPassword: "$2b$10$..."
See examples/sample_directory.yaml for a full example with users, groups, and organizational units.
yamldap supports comprehensive LDAP filter syntax including:
(uid=john)(mail=*)(cn=*smith*), (cn=john*), (cn=*doe)(age>=18), (created<=20240101)(&(objectClass=person)(uid=admin))(|(uid=john)(uid=jane))(!(uid=guest))(cn~=john) - Fuzzy matching(cn:=John Doe)(cn:caseExactMatch:=John Doe)(:dn:=users) - Matches entries with "users" in their DN(cn:dn:caseIgnoreMatch:=admin)Special characters can be escaped in filter values:
\28 for (\29 for )\2a for *\5c for \\00 for NULL./test_ldap.py
./test_basic.sh
import ldap
conn = ldap.initialize("ldap://localhost:389")
conn.simple_bind_s("uid=john,ou=users,dc=example,dc=com", "password")
results = conn.search_s("dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=john)")
# settings.py
import ldap
from django_auth_ldap.config import LDAPSearch, GroupOfNamesType
AUTH_LDAP_SERVER_URI = "ldap://yamldap:389"
AUTH_LDAP_BIND_DN = "cn=admin,dc=example,dc=com"
AUTH_LDAP_BIND_PASSWORD = "admin"
AUTH_LDAP_USER_SEARCH = LDAPSearch(
"dc=example,dc=com",
ldap.SCOPE_SUBTREE,
"(uid=%(user)s)",
)
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
"ou=groups,dc=example,dc=com",
ldap.SCOPE_SUBTREE,
"(objectClass=groupOfNames)",
)
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType()
const ldap = require('ldapjs');
const client = ldap.createClient({ url: 'ldap://localhost:389' });
client.bind('uid=john,ou=users,dc=example,dc=com', 'password', (err) => {
// Authenticated
});
@Bean
public LdapContextSource contextSource() {
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl("ldap://localhost:389");
contextSource.setBase("dc=example,dc=com");
contextSource.setUserDn("uid=john,ou=users,dc=example,dc=com");
contextSource.setPassword("password");
return contextSource;
}
# Run all tests
cargo test
# Run with coverage report
make coverage
# Check test coverage percentage
make coverage-check
# Run benchmarks
make bench
# Build release version
cargo build --release
# Build Docker image (3.99MB scratch image)
make docker-build
# Format code
cargo fmt
# Run linter
cargo clippy
# Run all CI checks
make ci
The project includes comprehensive unit tests with near 100% code coverage:
Run make help to see all available Make targets.
yamldap includes fuzz testing to ensure robustness against malformed input:
# Install cargo-fuzz
cargo install cargo-fuzz
# Run fuzz tests (requires nightly Rust)
cd fuzz
cargo +nightly fuzz run fuzz_ldap_decoder # Fuzz the LDAP decoder
cargo +nightly fuzz run fuzz_ldap_filter_parser # Fuzz the filter parser
cargo +nightly fuzz run fuzz_ldap_structured # Fuzz with structured input
See fuzz/README.md for detailed fuzzing instructions.
yamldap intentionally does not include built-in TLS support to maintain its core value: simplicity. For local development and testing, TLS is rarely needed. When TLS is required, you can easily add it using a reverse proxy:
# stunnel.conf
[ldaps]
accept = 636
connect = 127.0.0.1:389
cert = /path/to/certificate.pem
stream {
server {
listen 636 ssl;
proxy_pass localhost:389;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
}
}
This approach keeps yamldap simple while allowing TLS when needed for production-like testing.
Contributions are welcome! Please feel free to submit a Pull Request.
This project is dual-licensed under MIT OR Apache-2.0