LDAP Authentication
This guide covers configuring LDAP/Active Directory authentication for Vulcan.
Prerequisites
- LDAP or Active Directory server
- LDAP bind credentials (service account)
- Network connectivity between Vulcan and LDAP server
- SSL certificates for LDAPS (recommended)
Basic Configuration
Environment Variables
bash
# Enable LDAP authentication
VULCAN_ENABLE_LDAP=true
# LDAP server settings
VULCAN_LDAP_HOST=ldap.example.com
VULCAN_LDAP_PORT=389
VULCAN_LDAP_TITLE="Corporate LDAP"
# Bind credentials
VULCAN_LDAP_BIND_DN="CN=service-account,CN=Users,DC=example,DC=com"
VULCAN_LDAP_ADMIN_PASS="service_account_password"
# Search base
VULCAN_LDAP_BASE="DC=example,DC=com"
# Username attribute
VULCAN_LDAP_ATTRIBUTE=uid # or sAMAccountName for AD
# Encryption
VULCAN_LDAP_ENCRYPTION=plain # or simple_tls, start_tls
Configuration File
Edit config/vulcan.yml
:
yaml
production:
ldap:
enabled: true
servers:
main:
host: ldap.example.com
port: 389
title: "Corporate LDAP"
uid: uid # or sAMAccountName for Active Directory
encryption: plain
bind_dn: "CN=service-account,CN=Users,DC=example,DC=com"
password: "service_account_password"
base: "DC=example,DC=com"
Active Directory Configuration
Standard Active Directory
yaml
ldap:
enabled: true
servers:
main:
host: dc01.corp.example.com
port: 389
title: "Active Directory"
uid: sAMAccountName
encryption: start_tls
bind_dn: "CN=Vulcan Service,CN=Service Accounts,DC=corp,DC=example,DC=com"
password: "secure_password"
base: "DC=corp,DC=example,DC=com"
# AD-specific options
filter: "(&(objectClass=user)(memberOf=CN=VulcanUsers,CN=Groups,DC=corp,DC=example,DC=com))"
attributes:
username: ['sAMAccountName', 'uid']
email: ['mail', 'userPrincipalName']
name: 'displayName'
first_name: 'givenName'
last_name: 'sn'
Multiple Domain Controllers
yaml
ldap:
enabled: true
servers:
primary:
host: dc01.corp.example.com
port: 636
title: "Primary DC"
uid: sAMAccountName
encryption: simple_tls
bind_dn: "CN=Service,CN=Users,DC=corp,DC=example,DC=com"
password: "secure_password"
base: "DC=corp,DC=example,DC=com"
secondary:
host: dc02.corp.example.com
port: 636
title: "Secondary DC"
uid: sAMAccountName
encryption: simple_tls
bind_dn: "CN=Service,CN=Users,DC=corp,DC=example,DC=com"
password: "secure_password"
base: "DC=corp,DC=example,DC=com"
Secure LDAP (LDAPS)
Simple TLS (Port 636)
yaml
ldap:
servers:
main:
host: ldaps.example.com
port: 636
encryption: simple_tls
tls_options:
ca_file: /path/to/ca_certificate.pem
ssl_version: TLSv1_2
verify_mode: OpenSSL::SSL::VERIFY_PEER
StartTLS (Port 389)
yaml
ldap:
servers:
main:
host: ldap.example.com
port: 389
encryption: start_tls
tls_options:
ca_file: /path/to/ca_certificate.pem
ssl_version: TLSv1_2
User Filtering
Group-based Access
yaml
ldap:
servers:
main:
# ... other settings ...
# Only allow users in specific groups
filter: "(&(objectClass=user)(memberOf=CN=VulcanUsers,CN=Groups,DC=example,DC=com))"
# Or multiple groups
filter: "(|(&(objectClass=user)(memberOf=CN=Developers,CN=Groups,DC=example,DC=com))(&(objectClass=user)(memberOf=CN=Security,CN=Groups,DC=example,DC=com)))"
Attribute-based Filtering
yaml
ldap:
servers:
main:
# Only active employees
filter: "(&(objectClass=user)(employeeStatus=active)(department=Engineering))"
Attribute Mapping
Custom Attribute Mapping
yaml
ldap:
servers:
main:
# ... other settings ...
attributes:
username: 'sAMAccountName'
email: 'mail'
name: 'displayName'
first_name: 'givenName'
last_name: 'sn'
department: 'department'
phone: 'telephoneNumber'
title: 'title'
manager: 'manager'
Multiple Attribute Sources
yaml
ldap:
servers:
main:
attributes:
# Try multiple attributes in order
username: ['sAMAccountName', 'uid', 'cn']
email: ['mail', 'userPrincipalName', 'emailAddress']
Authentication Flow
User Login Process
- User enters username and password
- Vulcan constructs DN from username and base
- Attempts bind with user credentials
- If successful, retrieves user attributes
- Creates or updates local user account
- Establishes session
Account Provisioning
yaml
ldap:
servers:
main:
# ... other settings ...
# Auto-provision user accounts
create_user: true
update_user: true
# Default role for new users
default_role: 'viewer'
# Admin users (by username)
admin_users:
- 'admin_user1'
- 'admin_user2'
Testing LDAP Connection
Command Line Test
bash
# Test LDAP connectivity
ldapsearch -x -H ldap://ldap.example.com:389 \
-D "CN=service-account,CN=Users,DC=example,DC=com" \
-W -b "DC=example,DC=com" \
"(sAMAccountName=testuser)"
# Test LDAPS with TLS
ldapsearch -x -H ldaps://ldap.example.com:636 \
-D "CN=service-account,CN=Users,DC=example,DC=com" \
-W -b "DC=example,DC=com" \
"(sAMAccountName=testuser)"
Rails Console Test
ruby
# Test LDAP configuration
Rails.console
# Create LDAP connection
require 'net/ldap'
ldap = Net::LDAP.new(
host: 'ldap.example.com',
port: 389,
auth: {
method: :simple,
username: "CN=service-account,CN=Users,DC=example,DC=com",
password: "password"
}
)
# Test bind
if ldap.bind
puts "LDAP bind successful"
else
puts "LDAP bind failed: #{ldap.get_operation_result}"
end
# Search for user
ldap.search(
base: "DC=example,DC=com",
filter: "(sAMAccountName=testuser)",
attributes: ['mail', 'displayName']
) do |entry|
puts "DN: #{entry.dn}"
entry.each do |attribute, values|
puts " #{attribute}: #{values.join(', ')}"
end
end
Troubleshooting
Common Issues
Connection Refused
Problem: Cannot connect to LDAP server
Solution:
bash
# Check network connectivity
telnet ldap.example.com 389
# Check firewall rules
sudo iptables -L -n | grep 389
# Verify LDAP service is running
nmap -p 389,636 ldap.example.com
Invalid Credentials
Problem: Bind fails with invalid credentials
Solution:
- Verify bind DN format is correct
- Check password for special characters that need escaping
- Ensure service account is not locked
- Test with ldapsearch command
User Not Found
Problem: Users can't authenticate despite correct credentials
Solution:
- Verify search base is correct
- Check uid/username attribute matches
- Review filter if configured
- Test user search with ldapsearch
SSL/TLS Errors
Problem: Certificate verification failures
Solution:
yaml
# Temporarily disable verification (not for production!)
tls_options:
verify_mode: OpenSSL::SSL::VERIFY_NONE
# Or provide CA certificate
tls_options:
ca_file: /etc/ssl/certs/ldap-ca.pem
verify_mode: OpenSSL::SSL::VERIFY_PEER
Debug Logging
Enable LDAP debug logging:
ruby
# config/initializers/ldap_debug.rb
if Rails.env.development?
Devise::LDAP::Connection.class_eval do
def initialize(params = {})
super
@ldap.verbose = true # Enable verbose logging
end
end
end
Performance Optimization
Connection Pooling
yaml
ldap:
servers:
main:
# ... other settings ...
connection_pool:
size: 5
timeout: 5
checkout_timeout: 2
Caching
ruby
# Cache LDAP lookups
Rails.cache.fetch("ldap_user_#{username}", expires_in: 1.hour) do
ldap_lookup(username)
end
Security Best Practices
- Use LDAPS or StartTLS: Always encrypt LDAP traffic
- Service Account: Use dedicated service account with minimal permissions
- Strong Bind Password: Use complex password for bind account
- Validate Certificates: Verify SSL certificates in production
- Filter Users: Restrict access to specific groups or OUs
- Audit Logging: Log all authentication attempts
- Rate Limiting: Implement rate limiting for failed attempts
High Availability
Failover Configuration
yaml
ldap:
servers:
main:
hosts:
- ldap1.example.com
- ldap2.example.com
- ldap3.example.com
port: 389
# Failover settings
timeout: 5
retry_count: 3
retry_delay: 2
Load Balancing
Use a load balancer or LDAP proxy:
yaml
ldap:
servers:
main:
host: ldap-lb.example.com # Load balancer address
port: 389
Migration from Local Auth
Linking Existing Accounts
ruby
# Script to link LDAP to existing users
User.find_each do |user|
# Match by email
ldap_entry = ldap_search(email: user.email)
if ldap_entry
user.update!(
ldap_uid: ldap_entry.uid,
provider: 'ldap'
)
end
end