From 857c71484a9099afa92358fc379d228c367c970a Mon Sep 17 00:00:00 2001 From: Spencer Jones Date: Thu, 16 Oct 2025 15:43:24 -0700 Subject: [PATCH] Working after iterations --- .gitignore | 6 +- CHANGELOG.md | 94 ++++++++ Makefile | 22 +- PORT_CONFIGURATION.md | 436 +++++++++++++++++++++++++++++++++++ docker-compose.yml | 36 ++- examples/simple_auth.py | 8 +- ldif/01-users.ldif | 8 +- pyproject.toml | 7 +- scripts/cli.py | 5 +- scripts/ldapsearch_helper.sh | 195 ++++++++++++++++ 10 files changed, 778 insertions(+), 39 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 PORT_CONFIGURATION.md create mode 100755 scripts/ldapsearch_helper.sh diff --git a/.gitignore b/.gitignore index 1eed54e..2ce549c 100644 --- a/.gitignore +++ b/.gitignore @@ -86,9 +86,13 @@ logs/ *.bak *.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.production +.env.development # MacOS .DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..28c97b7 --- /dev/null +++ b/CHANGELOG.md @@ -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. \ No newline at end of file diff --git a/Makefile b/Makefile index 5450c72..1820afd 100644 --- a/Makefile +++ b/Makefile @@ -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 +# Load environment variables from .env file if it exists +ifneq (,$(wildcard ./.env)) + include .env + export +endif + # Default target .DEFAULT_GOAL := help @@ -55,9 +61,9 @@ start: ## Start the LDAP server @echo "✅ LDAP server started" @echo "" @echo "Services available at:" - @echo " - LDAP: ldap://localhost:389" - @echo " - LDAPS: ldaps://localhost:636" - @echo " - Admin: http://localhost:8080" + @echo " - LDAP: ldap://localhost:$${LDAP_PORT:-389}" + @echo " - LDAPS: ldaps://localhost:$${LDAPS_PORT:-636}" + @echo " - Admin: http://localhost:$${PHPLDAPADMIN_PORT:-8080}" @echo "" @echo "Admin credentials:" @echo " DN: cn=admin,dc=testing,dc=local" @@ -105,19 +111,19 @@ status: ## Show container status test-connection: ## Test connection to LDAP server @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 @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 @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 @echo "Testing LDAPS connection..." - openssl s_client -connect localhost:636 -CAfile certs/ca.crt 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 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 73c23fd..66619b9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,35 +1,33 @@ -version: '3.8' - services: openldap: image: osixia/openldap:1.5.0 - container_name: ldap-server - hostname: ldap.testing.local + container_name: ${LDAP_CONTAINER_NAME:-ldap-server} + hostname: ${LDAP_HOSTNAME:-ldap.testing.local} environment: # Base domain configuration - LDAP_ORGANISATION: "Testing Organization" - LDAP_DOMAIN: "testing.local" - LDAP_BASE_DN: "dc=testing,dc=local" + LDAP_ORGANISATION: ${LDAP_ORGANISATION:-Testing Organization} + LDAP_DOMAIN: ${LDAP_DOMAIN:-testing.local} + LDAP_BASE_DN: ${LDAP_BASE_DN:-dc=testing,dc=local} # Admin credentials (change these for production) - LDAP_ADMIN_PASSWORD: "admin_password" - LDAP_CONFIG_PASSWORD: "config_password" + LDAP_ADMIN_PASSWORD: ${LDAP_ADMIN_PASSWORD:-admin_password} + LDAP_CONFIG_PASSWORD: ${LDAP_CONFIG_PASSWORD:-config_password} # SSL/TLS Configuration - LDAP_TLS: "true" - LDAP_TLS_CRT_FILENAME: "server.crt" - LDAP_TLS_KEY_FILENAME: "server.key" - LDAP_TLS_CA_CRT_FILENAME: "ca.crt" - LDAP_TLS_VERIFY_CLIENT: "try" + LDAP_TLS: ${LDAP_TLS:-true} + LDAP_TLS_CRT_FILENAME: ${LDAP_TLS_CRT_FILENAME:-server.crt} + LDAP_TLS_KEY_FILENAME: ${LDAP_TLS_KEY_FILENAME:-server.key} + LDAP_TLS_CA_CRT_FILENAME: ${LDAP_TLS_CA_CRT_FILENAME:-ca.crt} + LDAP_TLS_VERIFY_CLIENT: ${LDAP_TLS_VERIFY_CLIENT:-try} # Logging - LDAP_LOG_LEVEL: "256" + LDAP_LOG_LEVEL: ${LDAP_LOG_LEVEL:-256} ports: # Standard LDAP port - - "389:389" + - "${LDAP_PORT:-389}:389" # LDAPS (SSL) port - - "636:636" + - "${LDAPS_PORT:-636}:636" volumes: # Custom certificates - place your dev-ca certs here @@ -52,12 +50,12 @@ services: # Optional: phpLDAPadmin for web-based management phpldapadmin: image: osixia/phpldapadmin:0.9.0 - container_name: ldap-admin + container_name: ${PHPLDAPADMIN_CONTAINER_NAME:-ldap-admin} environment: PHPLDAPADMIN_LDAP_HOSTS: "openldap" PHPLDAPADMIN_HTTPS: "false" ports: - - "8080:80" + - "${PHPLDAPADMIN_PORT:-8080}:80" depends_on: - openldap networks: diff --git a/examples/simple_auth.py b/examples/simple_auth.py index 75adb9b..4fa15a1 100644 --- a/examples/simple_auth.py +++ b/examples/simple_auth.py @@ -11,6 +11,7 @@ Usage: """ import argparse +import os import sys from typing import Dict, List, Optional @@ -24,7 +25,8 @@ except ImportError: # 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_PEOPLE_OU = "ou=people,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 "", "last_name": str(entry.sn), "email": str(entry.mail), - "uid_number": int(entry.uidNumber) if entry.uidNumber else None, - "gid_number": int(entry.gidNumber) if entry.gidNumber else None, + "uid_number": int(str(entry.uidNumber)) if entry.uidNumber else None, + "gid_number": int(str(entry.gidNumber)) if entry.gidNumber else None, "dn": entry.entry_dn, } conn.unbind() diff --git a/ldif/01-users.ldif b/ldif/01-users.ldif index fe52c0c..78811dc 100644 --- a/ldif/01-users.ldif +++ b/ldif/01-users.ldif @@ -25,7 +25,7 @@ cn: John Doe sn: Doe givenName: John mail: jdoe@testing.local -userPassword: {SSHA}5en6G6MezRroT3XKqkdPOmY/BFQ= # password: password123 +userPassword: {SSHA}Vj/QLoVDZbjklfhV/e6JdTo8MUNRy9dN uidNumber: 10001 gidNumber: 10001 homeDirectory: /home/jdoe @@ -42,7 +42,7 @@ cn: Jane Smith sn: Smith givenName: Jane mail: jsmith@testing.local -userPassword: {SSHA}5en6G6MezRroT3XKqkdPOmY/BFQ= # password: password123 +userPassword: {SSHA}Vj/QLoVDZbjklfhV/e6JdTo8MUNRy9dN uidNumber: 10002 gidNumber: 10002 homeDirectory: /home/jsmith @@ -59,7 +59,7 @@ cn: Admin User sn: User givenName: Admin mail: admin@testing.local -userPassword: {SSHA}5en6G6MezRroT3XKqkdPOmY/BFQ= # password: password123 +userPassword: {SSHA}Vj/QLoVDZbjklfhV/e6JdTo8MUNRy9dN uidNumber: 10000 gidNumber: 10000 homeDirectory: /home/admin @@ -76,7 +76,7 @@ cn: Test User sn: User givenName: Test mail: testuser@testing.local -userPassword: {SSHA}5en6G6MezRroT3XKqkdPOmY/BFQ= # password: password123 +userPassword: {SSHA}Vj/QLoVDZbjklfhV/e6JdTo8MUNRy9dN uidNumber: 10003 gidNumber: 10003 homeDirectory: /home/testuser diff --git a/pyproject.toml b/pyproject.toml index 2521982..46c1853 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,8 +33,11 @@ ldap-docker = "scripts.cli:main" requires = ["hatchling"] build-backend = "hatchling.build" -[tool.uv] -dev-dependencies = [ +[tool.hatch.build.targets.wheel] +packages = ["scripts", "tests"] + +[dependency-groups] +dev = [ "pytest>=7.4.0", "pytest-cov>=4.1.0", "black>=23.0.0", diff --git a/scripts/cli.py b/scripts/cli.py index 61aab55..a1c64e5 100644 --- a/scripts/cli.py +++ b/scripts/cli.py @@ -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. """ +import os import subprocess import sys import time @@ -27,8 +28,8 @@ PROJECT_ROOT = Path(__file__).parent.parent CERTS_DIR = PROJECT_ROOT / "certs" LDIF_DIR = PROJECT_ROOT / "ldif" DEFAULT_HOST = "localhost" -DEFAULT_PORT = 389 -DEFAULT_LDAPS_PORT = 636 +DEFAULT_PORT = int(os.environ.get("LDAP_PORT", "389")) +DEFAULT_LDAPS_PORT = int(os.environ.get("LDAPS_PORT", "636")) DEFAULT_BASE_DN = "dc=testing,dc=local" DEFAULT_ADMIN_DN = "cn=admin,dc=testing,dc=local" DEFAULT_ADMIN_PASSWORD = "admin_password" diff --git a/scripts/ldapsearch_helper.sh b/scripts/ldapsearch_helper.sh new file mode 100755 index 0000000..6d1ad28 --- /dev/null +++ b/scripts/ldapsearch_helper.sh @@ -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