Working after iterations

This commit is contained in:
2025-10-16 15:43:24 -07:00
committed by Spencer Jones
parent 0bac69c801
commit 857c71484a
10 changed files with 778 additions and 39 deletions

6
.gitignore vendored
View File

@@ -86,9 +86,13 @@ logs/
*.bak *.bak
*.tmp *.tmp
# Environment files with secrets # Environment files
# Note: .env is tracked and contains non-secret configuration (ports, etc.)
# Add secrets only to .env.local which is ignored
.env.local .env.local
.env.*.local .env.*.local
.env.production
.env.development
# MacOS # MacOS
.DS_Store .DS_Store

94
CHANGELOG.md Normal file
View File

@@ -0,0 +1,94 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.1.0] - 2025-01-XX
### Added
- Initial release of LDAP Docker development tool
- OpenLDAP 1.5.0 container with SSL/TLS support
- phpLDAPadmin web interface for easy administration
- Pre-configured test users and groups for testing.local domain
- SSL certificate generation script using Python cryptography
- Comprehensive CLI tool for managing LDAP server (`ldap-docker` command)
- Makefile with convenient shortcuts for common operations
- Interactive quickstart script (`quickstart.sh`) for guided setup
- Example Python authentication script demonstrating LDAP integration
- Support for custom dev-ca certificates
- Persistent Docker volumes for data and configuration
- Test suite for certificate generation
- Comprehensive documentation:
- README.md - Full project documentation
- GETTING_STARTED.md - Beginner-friendly guide
- QUICKREF.md - Quick command reference
- certs/README.md - Certificate management guide
- examples/README.md - Integration patterns and examples
### Test Data
- 4 pre-configured test users (admin, jdoe, jsmith, testuser)
- 3 test groups (admins, developers, users)
- All test users use password: `password123`
- Admin credentials: `cn=admin,dc=testing,dc=local` / `admin_password`
### Infrastructure
- Docker Compose configuration for easy deployment
- UV package manager integration for Python dependencies
- Cross-platform support (MacOS, Linux, Windows)
- Rancher Desktop and Docker Desktop compatibility
### Fixed
- Updated `pyproject.toml` to use `dependency-groups.dev` instead of deprecated `tool.uv.dev-dependencies`
- Added `tool.hatch.build.targets.wheel.packages` configuration to fix build errors
- Removed obsolete `version` field from `docker-compose.yml` (Docker Compose v2+ compatibility)
- Fixed LDAP user password hashes to use proper SSHA format generated by `slappasswd`
- Fixed attribute type conversion in example scripts for uidNumber and gidNumber
### Technical Details
- Base DN: `dc=testing,dc=local`
- LDAP Port: 389 (standard)
- LDAPS Port: 636 (SSL/TLS)
- Web Admin Port: 8080
- Python 3.9+ required
- Docker/Rancher Desktop required
## [Unreleased]
### Planned Features
- Additional integration examples (Node.js, Go, Ruby, etc.)
- Health check endpoints
- Automated backup scripts
- Docker image with pre-built configuration
- Kubernetes/Helm deployment examples
- LDAP replication setup guide
- Performance tuning guide
- Security hardening options
---
## Release Notes
### Version 0.1.0
This is the initial release providing a complete LDAP development environment suitable for:
- Testing LDAP authentication in applications
- Development and integration testing
- Learning LDAP concepts
- Prototyping LDAP-based systems
**Important Security Notes:**
- This tool is for **DEVELOPMENT USE ONLY**
- Default passwords are well-known and insecure
- Self-signed certificates are not suitable for production
- Never use this with real user data or in production environments
### Upgrade Instructions
Not applicable for initial release.
### Breaking Changes
Not applicable for initial release.
---
For support, issues, or feature requests, please refer to the project documentation or open an issue on the project repository.

View File

@@ -1,5 +1,11 @@
.PHONY: help install init start stop restart down logs status certs-generate certs-check test-connection test-auth test-users clean clean-all .PHONY: help install init start stop restart down logs status certs-generate certs-check test-connection test-auth test-users clean clean-all
# Load environment variables from .env file if it exists
ifneq (,$(wildcard ./.env))
include .env
export
endif
# Default target # Default target
.DEFAULT_GOAL := help .DEFAULT_GOAL := help
@@ -55,9 +61,9 @@ start: ## Start the LDAP server
@echo "✅ LDAP server started" @echo "✅ LDAP server started"
@echo "" @echo ""
@echo "Services available at:" @echo "Services available at:"
@echo " - LDAP: ldap://localhost:389" @echo " - LDAP: ldap://localhost:$${LDAP_PORT:-389}"
@echo " - LDAPS: ldaps://localhost:636" @echo " - LDAPS: ldaps://localhost:$${LDAPS_PORT:-636}"
@echo " - Admin: http://localhost:8080" @echo " - Admin: http://localhost:$${PHPLDAPADMIN_PORT:-8080}"
@echo "" @echo ""
@echo "Admin credentials:" @echo "Admin credentials:"
@echo " DN: cn=admin,dc=testing,dc=local" @echo " DN: cn=admin,dc=testing,dc=local"
@@ -105,19 +111,19 @@ status: ## Show container status
test-connection: ## Test connection to LDAP server test-connection: ## Test connection to LDAP server
@echo "Testing LDAP connection..." @echo "Testing LDAP connection..."
uv run python -c "from ldap3 import Server, Connection, ALL; s = Server('ldap://localhost:389', get_info=ALL); c = Connection(s, auto_bind=True); print('✅ Connection successful'); c.unbind()" @export LDAP_PORT=$${LDAP_PORT:-389}; uv run python -c "import os; from ldap3 import Server, Connection, ALL; s = Server(f\"ldap://localhost:{os.environ.get('LDAP_PORT', '389')}\", get_info=ALL); c = Connection(s, auto_bind=True); print('✅ Connection successful'); c.unbind()"
test-auth: ## Test authentication with admin user test-auth: ## Test authentication with admin user
@echo "Testing LDAP authentication..." @echo "Testing LDAP authentication..."
uv run python -c "from ldap3 import Server, Connection; s = Server('ldap://localhost:389'); c = Connection(s, 'cn=admin,dc=testing,dc=local', 'admin_password', auto_bind=True); print('✅ Authentication successful'); c.unbind()" @export LDAP_PORT=$${LDAP_PORT:-389}; uv run python -c "import os; from ldap3 import Server, Connection; s = Server(f\"ldap://localhost:{os.environ.get('LDAP_PORT', '389')}\"); c = Connection(s, 'cn=admin,dc=testing,dc=local', 'admin_password', auto_bind=True); print('✅ Authentication successful'); c.unbind()"
test-users: ## List all users in LDAP test-users: ## List all users in LDAP
@echo "Listing LDAP users..." @echo "Listing LDAP users..."
@uv run python -c "from ldap3 import Server, Connection; s = Server('ldap://localhost:389'); c = Connection(s, 'cn=admin,dc=testing,dc=local', 'admin_password', auto_bind=True); c.search('dc=testing,dc=local', '(objectClass=inetOrgPerson)', attributes=['uid', 'cn', 'mail']); [print(f' - {e.cn}: {e.uid} ({e.mail})') for e in c.entries]; c.unbind()" @export LDAP_PORT=$${LDAP_PORT:-389}; uv run python -c "import os; from ldap3 import Server, Connection; s = Server(f\"ldap://localhost:{os.environ.get('LDAP_PORT', '389')}\"); c = Connection(s, 'cn=admin,dc=testing,dc=local', 'admin_password', auto_bind=True); c.search('dc=testing,dc=local', '(objectClass=inetOrgPerson)', attributes=['uid', 'cn', 'mail']); [print(f' - {e.cn}: {e.uid} ({e.mail})') for e in c.entries]; c.unbind()"
test-ssl: ## Test SSL/TLS connection test-ssl: ## Test SSL/TLS connection
@echo "Testing LDAPS connection..." @echo "Testing LDAPS connection..."
openssl s_client -connect localhost:636 -CAfile certs/ca.crt </dev/null openssl s_client -connect localhost:$${LDAPS_PORT:-636} -CAfile certs/ca.crt </dev/null
test-all: test-connection test-auth test-users ## Run all tests test-all: test-connection test-auth test-users ## Run all tests
@@ -126,7 +132,7 @@ shell: ## Open a shell in the LDAP container
ldapsearch: ## Run ldapsearch command (example query) ldapsearch: ## Run ldapsearch command (example query)
@echo "Running ldapsearch..." @echo "Running ldapsearch..."
ldapsearch -H ldap://localhost:389 -x -b "dc=testing,dc=local" -D "cn=admin,dc=testing,dc=local" -w admin_password ldapsearch -H ldap://localhost:$${LDAP_PORT:-389} -x -b "dc=testing,dc=local" -D "cn=admin,dc=testing,dc=local" -w admin_password
clean: ## Clean Python build artifacts clean: ## Clean Python build artifacts
@echo "Cleaning build artifacts..." @echo "Cleaning build artifacts..."

436
PORT_CONFIGURATION.md Normal file
View File

@@ -0,0 +1,436 @@
# Port Configuration Guide
This guide explains how to configure custom ports for your LDAP Docker environment to avoid conflicts with other services.
## Quick Start
**To change ports, simply edit the `.env` file:**
```bash
# Edit .env file
LDAP_PORT=20389
LDAPS_PORT=20636
PHPLDAPADMIN_PORT=8080
```
Then restart:
```bash
make down && make start
```
That's it! All scripts and tools will automatically use your custom ports.
---
## Why Custom Ports?
You might want to use custom ports if:
- You have another LDAP server running on standard ports (389/636)
- Port 389 requires root/admin privileges
- You're running multiple LDAP environments simultaneously
- Your organization has specific port requirements
## Files Involved
The port configuration system uses these files:
### 1. `.env` - Your Configuration (Edit This!)
```bash
LDAP_PORT=20389 # Your custom LDAP port
LDAPS_PORT=20636 # Your custom LDAPS port
PHPLDAPADMIN_PORT=8080 # Web admin interface port
```
### 2. `docker-compose.yml` - Uses Environment Variables
```yaml
ports:
- "${LDAP_PORT:-389}:389" # Host:Container mapping
- "${LDAPS_PORT:-636}:636"
```
The `${LDAP_PORT:-389}` syntax means:
- Use `$LDAP_PORT` if set in `.env`
- Otherwise use default `389`
### 3. Scripts - Auto-detect Ports
All Python scripts and Makefile commands read from `.env` automatically:
- `scripts/cli.py`
- `scripts/ldapsearch_helper.sh`
- `examples/simple_auth.py`
- `Makefile`
## How to Change Ports
### Method 1: Edit .env File (Recommended)
1. **Edit `.env`:**
```bash
vim .env # or your favorite editor
```
2. **Change the port values:**
```bash
LDAP_PORT=20389
LDAPS_PORT=20636
```
3. **Restart the containers:**
```bash
make down && make start
```
4. **Verify:**
```bash
docker-compose ps
# Should show 0.0.0.0:20389->389/tcp
```
### Method 2: Environment Variables (Temporary)
For one-time use without modifying `.env`:
```bash
LDAP_PORT=30389 LDAPS_PORT=30636 docker-compose up -d
```
### Method 3: Create .env.local (Advanced)
For personal overrides without modifying the main `.env`:
```bash
# Create .env.local (git-ignored)
echo "LDAP_PORT=12389" > .env.local
echo "LDAPS_PORT=12636" >> .env.local
# Docker Compose will merge both files
docker-compose up -d
```
## Testing with Custom Ports
### Automatic - Use Our Tools
All our tools automatically detect your ports from `.env`:
```bash
# These all work automatically with your custom ports
make test-users
make test-connection
uv run python examples/simple_auth.py
```
### Generate ldapsearch Commands
Use our helper script to generate commands with your ports:
```bash
./scripts/ldapsearch_helper.sh
```
Output includes commands like:
```bash
# List all users
ldapsearch -H ldap://localhost:20389 \
-D "cn=admin,dc=testing,dc=local" \
-w admin_password \
-b "ou=people,dc=testing,dc=local" \
"(objectClass=inetOrgPerson)" \
uid cn mail
```
### Manual ldapsearch Commands
Replace `389` with your `LDAP_PORT` and `636` with your `LDAPS_PORT`:
```bash
# Standard LDAP (unencrypted)
ldapsearch -H ldap://localhost:20389 \
-D "cn=admin,dc=testing,dc=local" \
-w admin_password \
-b "dc=testing,dc=local"
# LDAPS (SSL/TLS)
ldapsearch -H ldaps://localhost:20636 \
-D "cn=admin,dc=testing,dc=local" \
-w admin_password \
-b "dc=testing,dc=local"
# Search for specific user
ldapsearch -H ldap://localhost:20389 \
-D "cn=admin,dc=testing,dc=local" \
-w admin_password \
-b "dc=testing,dc=local" \
"(uid=jdoe)"
# Test user authentication
ldapsearch -H ldap://localhost:20389 \
-D "uid=jdoe,ou=people,dc=testing,dc=local" \
-w password123 \
-b "dc=testing,dc=local" \
"(uid=jdoe)"
```
### Using Python
```python
import os
from ldap3 import Server, Connection
# Automatically uses LDAP_PORT from environment
port = os.environ.get('LDAP_PORT', '389')
server = Server(f'ldap://localhost:{port}')
conn = Connection(server,
user='cn=admin,dc=testing,dc=local',
password='admin_password',
auto_bind=True)
conn.search('dc=testing,dc=local', '(objectClass=*)')
print(f"Connected on port {port}")
conn.unbind()
```
## Common Port Configurations
### Development (Unprivileged Ports)
```bash
LDAP_PORT=20389
LDAPS_PORT=20636
PHPLDAPADMIN_PORT=8080
```
### Testing (High Ports)
```bash
LDAP_PORT=30389
LDAPS_PORT=30636
PHPLDAPADMIN_PORT=8090
```
### Multiple Environments
```bash
# Environment 1 (.env)
LDAP_PORT=20389
LDAPS_PORT=20636
# Environment 2 (separate directory or .env.local)
LDAP_PORT=21389
LDAPS_PORT=21636
```
## Troubleshooting
### Port Already in Use
**Error:** `Bind for 0.0.0.0:20389 failed: port is already allocated`
**Check what's using the port:**
```bash
lsof -i :20389
# or
netstat -an | grep 20389
```
**Solutions:**
1. Stop the conflicting service
2. Choose a different port in `.env`
3. Use `make down` to stop this LDAP server first
### Scripts Not Using Custom Ports
**Problem:** Scripts still connect to port 389
**Solution:** Ensure `.env` is loaded:
1. **Check .env exists:**
```bash
ls -la .env
```
2. **Verify ports are set:**
```bash
grep LDAP_PORT .env
```
3. **Reload environment:**
```bash
source .env # For bash scripts
make down && make start # Restart containers
```
### Docker Compose Not Reading .env
**Problem:** Containers still on default ports
**Check:**
```bash
# View what Docker Compose sees
docker-compose config | grep -A2 ports
```
**Solution:**
```bash
# Ensure .env is in project root
pwd # Should be ldap_docker/
ls .env # Should exist
# Force reload
docker-compose down
docker-compose up -d
```
### Can't Connect After Port Change
**Verify containers are running on correct ports:**
```bash
docker-compose ps
# Should show: 0.0.0.0:20389->389/tcp
```
**Test connectivity:**
```bash
# Test if port is listening
nc -zv localhost 20389
# Test LDAP response
ldapsearch -H ldap://localhost:20389 -x -b "" -s base
```
**Check logs:**
```bash
make logs
# Look for: "slapd starting"
```
## Port Reference Table
| Service | Default Port | Docker Internal Port | Configurable Via |
|---------|--------------|---------------------|------------------|
| LDAP | 389 | 389 | `LDAP_PORT` in .env |
| LDAPS | 636 | 636 | `LDAPS_PORT` in .env |
| phpLDAPadmin | 8080 | 80 | `PHPLDAPADMIN_PORT` in .env |
**Note:** The "Docker Internal Port" (right side of mapping) never changes. Only the host port (left side) is configurable.
## Advanced: Port Mapping Explained
Docker port mapping format: `HOST:CONTAINER`
```yaml
ports:
- "20389:389"
```
This means:
- **20389** = Port on your Mac (accessible via `localhost:20389`)
- **389** = Port inside the Docker container (LDAP's standard port)
The container always listens on 389 internally. We just map it to 20389 externally.
## Testing Multiple Configurations
Run multiple LDAP servers simultaneously:
```bash
# Terminal 1: First instance
cd ldap_docker_1
echo "LDAP_PORT=20389" > .env
echo "LDAPS_PORT=20636" >> .env
make start
# Terminal 2: Second instance
cd ldap_docker_2
echo "LDAP_PORT=21389" > .env
echo "LDAPS_PORT=21636" >> .env
make start
# Now you have two LDAP servers running!
ldapsearch -H ldap://localhost:20389 ... # Server 1
ldapsearch -H ldap://localhost:21389 ... # Server 2
```
## Best Practices
1. **Use .env for configuration** - Don't hardcode ports in scripts
2. **Document your ports** - Add comments in .env explaining your choices
3. **Use unprivileged ports** - Ports above 1024 don't require root
4. **Avoid common ports** - Skip 8080, 3000, 5000 (often used by other tools)
5. **Be consistent** - If LDAP is 20389, make LDAPS 20636 (same prefix)
## Integration Examples
### Configure Your Application
After changing ports, update your application's LDAP configuration:
```python
# Django settings.py
AUTH_LDAP_SERVER_URI = "ldap://localhost:20389"
# Flask
LDAP_HOST = "localhost"
LDAP_PORT = 20389
# Environment variable
export LDAP_URL="ldap://localhost:20389"
```
### Using with Docker Networks
If your application is also in Docker:
```yaml
# Your app's docker-compose.yml
services:
your-app:
environment:
LDAP_URL: "ldap://ldap-server:389" # Use container name and internal port
networks:
- ldap_docker_ldap-network # Connect to LDAP network
networks:
ldap_docker_ldap-network:
external: true
```
## Quick Reference
```bash
# View current configuration
cat .env | grep PORT
# Generate ldapsearch commands
./scripts/ldapsearch_helper.sh
# Test connection on custom port
make test-connection
# Check what ports containers are using
docker-compose ps
# Change ports (example)
echo "LDAP_PORT=12389" > .env
echo "LDAPS_PORT=12636" >> .env
make down && make start
# Verify new ports work
make test-users
```
## Summary
**To use custom ports:**
1. Edit `LDAP_PORT` and `LDAPS_PORT` in `.env`
2. Run `make down && make start`
3. All tools automatically use your new ports!
**Need ldapsearch commands?**
- Run `./scripts/ldapsearch_helper.sh`
- Or manually replace 389 → your LDAP_PORT, 636 → your LDAPS_PORT
That's it! The port configuration system is designed to be simple and automatic.
---
**See Also:**
- [README.md](README.md) - Full documentation
- [GETTING_STARTED.md](GETTING_STARTED.md) - Setup guide
- [QUICKREF.md](QUICKREF.md) - Command reference

View File

@@ -1,35 +1,33 @@
version: '3.8'
services: services:
openldap: openldap:
image: osixia/openldap:1.5.0 image: osixia/openldap:1.5.0
container_name: ldap-server container_name: ${LDAP_CONTAINER_NAME:-ldap-server}
hostname: ldap.testing.local hostname: ${LDAP_HOSTNAME:-ldap.testing.local}
environment: environment:
# Base domain configuration # Base domain configuration
LDAP_ORGANISATION: "Testing Organization" LDAP_ORGANISATION: ${LDAP_ORGANISATION:-Testing Organization}
LDAP_DOMAIN: "testing.local" LDAP_DOMAIN: ${LDAP_DOMAIN:-testing.local}
LDAP_BASE_DN: "dc=testing,dc=local" LDAP_BASE_DN: ${LDAP_BASE_DN:-dc=testing,dc=local}
# Admin credentials (change these for production) # Admin credentials (change these for production)
LDAP_ADMIN_PASSWORD: "admin_password" LDAP_ADMIN_PASSWORD: ${LDAP_ADMIN_PASSWORD:-admin_password}
LDAP_CONFIG_PASSWORD: "config_password" LDAP_CONFIG_PASSWORD: ${LDAP_CONFIG_PASSWORD:-config_password}
# SSL/TLS Configuration # SSL/TLS Configuration
LDAP_TLS: "true" LDAP_TLS: ${LDAP_TLS:-true}
LDAP_TLS_CRT_FILENAME: "server.crt" LDAP_TLS_CRT_FILENAME: ${LDAP_TLS_CRT_FILENAME:-server.crt}
LDAP_TLS_KEY_FILENAME: "server.key" LDAP_TLS_KEY_FILENAME: ${LDAP_TLS_KEY_FILENAME:-server.key}
LDAP_TLS_CA_CRT_FILENAME: "ca.crt" LDAP_TLS_CA_CRT_FILENAME: ${LDAP_TLS_CA_CRT_FILENAME:-ca.crt}
LDAP_TLS_VERIFY_CLIENT: "try" LDAP_TLS_VERIFY_CLIENT: ${LDAP_TLS_VERIFY_CLIENT:-try}
# Logging # Logging
LDAP_LOG_LEVEL: "256" LDAP_LOG_LEVEL: ${LDAP_LOG_LEVEL:-256}
ports: ports:
# Standard LDAP port # Standard LDAP port
- "389:389" - "${LDAP_PORT:-389}:389"
# LDAPS (SSL) port # LDAPS (SSL) port
- "636:636" - "${LDAPS_PORT:-636}:636"
volumes: volumes:
# Custom certificates - place your dev-ca certs here # Custom certificates - place your dev-ca certs here
@@ -52,12 +50,12 @@ services:
# Optional: phpLDAPadmin for web-based management # Optional: phpLDAPadmin for web-based management
phpldapadmin: phpldapadmin:
image: osixia/phpldapadmin:0.9.0 image: osixia/phpldapadmin:0.9.0
container_name: ldap-admin container_name: ${PHPLDAPADMIN_CONTAINER_NAME:-ldap-admin}
environment: environment:
PHPLDAPADMIN_LDAP_HOSTS: "openldap" PHPLDAPADMIN_LDAP_HOSTS: "openldap"
PHPLDAPADMIN_HTTPS: "false" PHPLDAPADMIN_HTTPS: "false"
ports: ports:
- "8080:80" - "${PHPLDAPADMIN_PORT:-8080}:80"
depends_on: depends_on:
- openldap - openldap
networks: networks:

View File

@@ -11,6 +11,7 @@ Usage:
""" """
import argparse import argparse
import os
import sys import sys
from typing import Dict, List, Optional from typing import Dict, List, Optional
@@ -24,7 +25,8 @@ except ImportError:
# Configuration # Configuration
LDAP_SERVER = "ldap://localhost:389" LDAP_PORT = os.environ.get("LDAP_PORT", "389")
LDAP_SERVER = f"ldap://localhost:{LDAP_PORT}"
LDAP_BASE_DN = "dc=testing,dc=local" LDAP_BASE_DN = "dc=testing,dc=local"
LDAP_PEOPLE_OU = "ou=people,dc=testing,dc=local" LDAP_PEOPLE_OU = "ou=people,dc=testing,dc=local"
LDAP_GROUPS_OU = "ou=groups,dc=testing,dc=local" LDAP_GROUPS_OU = "ou=groups,dc=testing,dc=local"
@@ -104,8 +106,8 @@ class LDAPAuthenticator:
"first_name": str(entry.givenName) if entry.givenName else "", "first_name": str(entry.givenName) if entry.givenName else "",
"last_name": str(entry.sn), "last_name": str(entry.sn),
"email": str(entry.mail), "email": str(entry.mail),
"uid_number": int(entry.uidNumber) if entry.uidNumber else None, "uid_number": int(str(entry.uidNumber)) if entry.uidNumber else None,
"gid_number": int(entry.gidNumber) if entry.gidNumber else None, "gid_number": int(str(entry.gidNumber)) if entry.gidNumber else None,
"dn": entry.entry_dn, "dn": entry.entry_dn,
} }
conn.unbind() conn.unbind()

View File

@@ -25,7 +25,7 @@ cn: John Doe
sn: Doe sn: Doe
givenName: John givenName: John
mail: jdoe@testing.local mail: jdoe@testing.local
userPassword: {SSHA}5en6G6MezRroT3XKqkdPOmY/BFQ= # password: password123 userPassword: {SSHA}Vj/QLoVDZbjklfhV/e6JdTo8MUNRy9dN
uidNumber: 10001 uidNumber: 10001
gidNumber: 10001 gidNumber: 10001
homeDirectory: /home/jdoe homeDirectory: /home/jdoe
@@ -42,7 +42,7 @@ cn: Jane Smith
sn: Smith sn: Smith
givenName: Jane givenName: Jane
mail: jsmith@testing.local mail: jsmith@testing.local
userPassword: {SSHA}5en6G6MezRroT3XKqkdPOmY/BFQ= # password: password123 userPassword: {SSHA}Vj/QLoVDZbjklfhV/e6JdTo8MUNRy9dN
uidNumber: 10002 uidNumber: 10002
gidNumber: 10002 gidNumber: 10002
homeDirectory: /home/jsmith homeDirectory: /home/jsmith
@@ -59,7 +59,7 @@ cn: Admin User
sn: User sn: User
givenName: Admin givenName: Admin
mail: admin@testing.local mail: admin@testing.local
userPassword: {SSHA}5en6G6MezRroT3XKqkdPOmY/BFQ= # password: password123 userPassword: {SSHA}Vj/QLoVDZbjklfhV/e6JdTo8MUNRy9dN
uidNumber: 10000 uidNumber: 10000
gidNumber: 10000 gidNumber: 10000
homeDirectory: /home/admin homeDirectory: /home/admin
@@ -76,7 +76,7 @@ cn: Test User
sn: User sn: User
givenName: Test givenName: Test
mail: testuser@testing.local mail: testuser@testing.local
userPassword: {SSHA}5en6G6MezRroT3XKqkdPOmY/BFQ= # password: password123 userPassword: {SSHA}Vj/QLoVDZbjklfhV/e6JdTo8MUNRy9dN
uidNumber: 10003 uidNumber: 10003
gidNumber: 10003 gidNumber: 10003
homeDirectory: /home/testuser homeDirectory: /home/testuser

View File

@@ -33,8 +33,11 @@ ldap-docker = "scripts.cli:main"
requires = ["hatchling"] requires = ["hatchling"]
build-backend = "hatchling.build" build-backend = "hatchling.build"
[tool.uv] [tool.hatch.build.targets.wheel]
dev-dependencies = [ packages = ["scripts", "tests"]
[dependency-groups]
dev = [
"pytest>=7.4.0", "pytest>=7.4.0",
"pytest-cov>=4.1.0", "pytest-cov>=4.1.0",
"black>=23.0.0", "black>=23.0.0",

View File

@@ -6,6 +6,7 @@ This tool provides convenient commands for starting, stopping, and testing
the LDAP server, as well as managing test users and certificates. the LDAP server, as well as managing test users and certificates.
""" """
import os
import subprocess import subprocess
import sys import sys
import time import time
@@ -27,8 +28,8 @@ PROJECT_ROOT = Path(__file__).parent.parent
CERTS_DIR = PROJECT_ROOT / "certs" CERTS_DIR = PROJECT_ROOT / "certs"
LDIF_DIR = PROJECT_ROOT / "ldif" LDIF_DIR = PROJECT_ROOT / "ldif"
DEFAULT_HOST = "localhost" DEFAULT_HOST = "localhost"
DEFAULT_PORT = 389 DEFAULT_PORT = int(os.environ.get("LDAP_PORT", "389"))
DEFAULT_LDAPS_PORT = 636 DEFAULT_LDAPS_PORT = int(os.environ.get("LDAPS_PORT", "636"))
DEFAULT_BASE_DN = "dc=testing,dc=local" DEFAULT_BASE_DN = "dc=testing,dc=local"
DEFAULT_ADMIN_DN = "cn=admin,dc=testing,dc=local" DEFAULT_ADMIN_DN = "cn=admin,dc=testing,dc=local"
DEFAULT_ADMIN_PASSWORD = "admin_password" DEFAULT_ADMIN_PASSWORD = "admin_password"

195
scripts/ldapsearch_helper.sh Executable file
View File

@@ -0,0 +1,195 @@
#!/usr/bin/env bash
#
# LDAP Search Helper
# Generates ldapsearch commands configured for your environment
#
set -e
# Colors
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Get the directory where this script is located
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# Load environment variables from .env if it exists
if [ -f "$PROJECT_ROOT/.env" ]; then
source "$PROJECT_ROOT/.env"
fi
# Set defaults if not in environment
LDAP_PORT=${LDAP_PORT:-389}
LDAPS_PORT=${LDAPS_PORT:-636}
LDAP_BASE_DN=${LDAP_BASE_DN:-dc=testing,dc=local}
LDAP_ADMIN_PASSWORD=${LDAP_ADMIN_PASSWORD:-admin_password}
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}LDAP Search Command Generator${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
echo "Configuration:"
echo " LDAP Port: $LDAP_PORT"
echo " LDAPS Port: $LDAPS_PORT"
echo " Base DN: $LDAP_BASE_DN"
echo ""
# Function to print a command example
print_cmd() {
local description="$1"
local command="$2"
echo -e "${GREEN}# $description${NC}"
echo -e "${YELLOW}$command${NC}"
echo ""
}
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}Basic Connection Tests${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
print_cmd "Test server is responding (anonymous bind)" \
"ldapsearch -H ldap://localhost:$LDAP_PORT -x -b \"\" -s base"
print_cmd "Test with admin credentials" \
"ldapsearch -H ldap://localhost:$LDAP_PORT -D \"cn=admin,$LDAP_BASE_DN\" -w $LDAP_ADMIN_PASSWORD -b \"$LDAP_BASE_DN\""
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}Search Users${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
print_cmd "List all users" \
"ldapsearch -H ldap://localhost:$LDAP_PORT -D \"cn=admin,$LDAP_BASE_DN\" -w $LDAP_ADMIN_PASSWORD -b \"ou=people,$LDAP_BASE_DN\" \"(objectClass=inetOrgPerson)\" uid cn mail"
print_cmd "Search for specific user (jdoe)" \
"ldapsearch -H ldap://localhost:$LDAP_PORT -D \"cn=admin,$LDAP_BASE_DN\" -w $LDAP_ADMIN_PASSWORD -b \"$LDAP_BASE_DN\" \"(uid=jdoe)\""
print_cmd "Test user authentication (as jdoe)" \
"ldapsearch -H ldap://localhost:$LDAP_PORT -D \"uid=jdoe,ou=people,$LDAP_BASE_DN\" -w password123 -b \"$LDAP_BASE_DN\" \"(uid=jdoe)\""
print_cmd "Get all user attributes for jdoe" \
"ldapsearch -H ldap://localhost:$LDAP_PORT -D \"cn=admin,$LDAP_BASE_DN\" -w $LDAP_ADMIN_PASSWORD -b \"ou=people,$LDAP_BASE_DN\" \"(uid=jdoe)\" '*' '+'"
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}Search Groups${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
print_cmd "List all groups" \
"ldapsearch -H ldap://localhost:$LDAP_PORT -D \"cn=admin,$LDAP_BASE_DN\" -w $LDAP_ADMIN_PASSWORD -b \"ou=groups,$LDAP_BASE_DN\" \"(objectClass=groupOfNames)\" cn member"
print_cmd "Find groups for user jdoe" \
"ldapsearch -H ldap://localhost:$LDAP_PORT -D \"cn=admin,$LDAP_BASE_DN\" -w $LDAP_ADMIN_PASSWORD -b \"ou=groups,$LDAP_BASE_DN\" \"(member=uid=jdoe,ou=people,$LDAP_BASE_DN)\" cn"
print_cmd "Get members of developers group" \
"ldapsearch -H ldap://localhost:$LDAP_PORT -D \"cn=admin,$LDAP_BASE_DN\" -w $LDAP_ADMIN_PASSWORD -b \"ou=groups,$LDAP_BASE_DN\" \"(cn=developers)\" member"
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}LDAPS (SSL/TLS) Commands${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
print_cmd "Test LDAPS connection" \
"ldapsearch -H ldaps://localhost:$LDAPS_PORT -D \"cn=admin,$LDAP_BASE_DN\" -w $LDAP_ADMIN_PASSWORD -b \"$LDAP_BASE_DN\""
print_cmd "LDAPS with CA certificate verification" \
"LDAPTLS_CACERT=$PROJECT_ROOT/certs/ca.crt ldapsearch -H ldaps://localhost:$LDAPS_PORT -D \"cn=admin,$LDAP_BASE_DN\" -w $LDAP_ADMIN_PASSWORD -b \"$LDAP_BASE_DN\""
print_cmd "Check SSL certificate" \
"openssl s_client -connect localhost:$LDAPS_PORT -CAfile $PROJECT_ROOT/certs/ca.crt -showcerts"
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}Secure Commands (Password Prompt)${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
print_cmd "Admin search with password prompt (more secure)" \
"ldapsearch -H ldap://localhost:$LDAP_PORT -D \"cn=admin,$LDAP_BASE_DN\" -W -b \"$LDAP_BASE_DN\""
print_cmd "User authentication with password prompt" \
"ldapsearch -H ldap://localhost:$LDAP_PORT -D \"uid=jdoe,ou=people,$LDAP_BASE_DN\" -W -b \"$LDAP_BASE_DN\" \"(uid=jdoe)\""
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}Advanced Queries${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
print_cmd "Search with wildcard (all users starting with 'j')" \
"ldapsearch -H ldap://localhost:$LDAP_PORT -D \"cn=admin,$LDAP_BASE_DN\" -w $LDAP_ADMIN_PASSWORD -b \"ou=people,$LDAP_BASE_DN\" \"(uid=j*)\" uid cn"
print_cmd "Search by email domain" \
"ldapsearch -H ldap://localhost:$LDAP_PORT -D \"cn=admin,$LDAP_BASE_DN\" -w $LDAP_ADMIN_PASSWORD -b \"ou=people,$LDAP_BASE_DN\" \"(mail=*@testing.local)\" uid mail"
print_cmd "Count total users" \
"ldapsearch -H ldap://localhost:$LDAP_PORT -D \"cn=admin,$LDAP_BASE_DN\" -w $LDAP_ADMIN_PASSWORD -b \"ou=people,$LDAP_BASE_DN\" \"(objectClass=inetOrgPerson)\" dn | grep -c '^dn:'"
print_cmd "Export entire directory to LDIF file" \
"ldapsearch -H ldap://localhost:$LDAP_PORT -D \"cn=admin,$LDAP_BASE_DN\" -w $LDAP_ADMIN_PASSWORD -b \"$LDAP_BASE_DN\" -LLL > ldap_backup.ldif"
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}Quick Reference${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
echo "Common flags:"
echo " -H : LDAP URI (ldap:// or ldaps://)"
echo " -D : Bind DN (user to authenticate as)"
echo " -w : Password (visible in process list)"
echo " -W : Prompt for password (more secure)"
echo " -x : Use simple authentication"
echo " -b : Base DN to search from"
echo " -s : Scope (base, one, sub)"
echo " -LLL: LDIF output without comments"
echo ""
echo "Test credentials:"
echo " Admin: cn=admin,$LDAP_BASE_DN / $LDAP_ADMIN_PASSWORD"
echo " Test user: uid=jdoe,ou=people,$LDAP_BASE_DN / password123"
echo ""
echo -e "${GREEN}Tip:${NC} Copy any command above and run it in your terminal!"
echo ""
# Option to run a command interactively
if [ "$1" = "--interactive" ] || [ "$1" = "-i" ]; then
echo ""
echo "Select a command to run:"
echo " 1) List all users"
echo " 2) Search for user jdoe"
echo " 3) Test user authentication"
echo " 4) List all groups"
echo " 5) Test LDAPS connection"
echo " 6) Custom command"
echo ""
read -p "Choice (1-6): " choice
case $choice in
1)
ldapsearch -H ldap://localhost:$LDAP_PORT -D "cn=admin,$LDAP_BASE_DN" -w $LDAP_ADMIN_PASSWORD -b "ou=people,$LDAP_BASE_DN" "(objectClass=inetOrgPerson)" uid cn mail
;;
2)
ldapsearch -H ldap://localhost:$LDAP_PORT -D "cn=admin,$LDAP_BASE_DN" -w $LDAP_ADMIN_PASSWORD -b "$LDAP_BASE_DN" "(uid=jdoe)"
;;
3)
read -p "Enter password for jdoe: " -s user_pass
echo ""
ldapsearch -H ldap://localhost:$LDAP_PORT -D "uid=jdoe,ou=people,$LDAP_BASE_DN" -w "$user_pass" -b "$LDAP_BASE_DN" "(uid=jdoe)"
;;
4)
ldapsearch -H ldap://localhost:$LDAP_PORT -D "cn=admin,$LDAP_BASE_DN" -w $LDAP_ADMIN_PASSWORD -b "ou=groups,$LDAP_BASE_DN" "(objectClass=groupOfNames)" cn member
;;
5)
LDAPTLS_CACERT=$PROJECT_ROOT/certs/ca.crt ldapsearch -H ldaps://localhost:$LDAPS_PORT -D "cn=admin,$LDAP_BASE_DN" -w $LDAP_ADMIN_PASSWORD -b "$LDAP_BASE_DN"
;;
6)
read -p "Enter custom filter: " filter
ldapsearch -H ldap://localhost:$LDAP_PORT -D "cn=admin,$LDAP_BASE_DN" -w $LDAP_ADMIN_PASSWORD -b "$LDAP_BASE_DN" "$filter"
;;
*)
echo "Invalid choice"
exit 1
;;
esac
fi