LDAP Groups Integration - Complete Guide¶
Overview¶
RustSocks now supports dynamic LDAP group resolution. This allows ACL rules to be applied based on user's LDAP groups without manual synchronization.
Key Feature: ACL only checks groups defined in ACL config - even if user has thousands of LDAP groups, only the relevant ones are evaluated (case-insensitive).
How It Works¶
1. User Authentication (PAM)¶
User "alice" → PAM authentication (pam_mysql.so) → Success
2. LDAP Group Resolution (NSS/SSSD)¶
System fetches ALL user's groups from LDAP via getgrouplist():
["alice", "developers", "engineering", "hr", "team_foo", "team_bar", ...]
3. ACL Group Filtering¶
ACL config defines ONLY:
[[groups]]
name = "developers"
ACL checks: Is "developers" in user's LDAP groups? → YES
ACL ignores: engineering, hr, team_foo, team_bar, ... (not in ACL config)
4. Rule Application¶
Apply rules from "developers" group → ALLOW/BLOCK connection
Configuration¶
rustsocks.toml¶
[server]
bind_address = "0.0.0.0"
bind_port = 1080
[auth]
client_method = "none" # No auth before SOCKS
socks_method = "pam.username" # PAM username/password auth
[auth.pam]
username_service = "sockd" # Your PAM service (e.g., pam_mysql.so)
address_service = "rustsocks-client"
default_user = "rhostusr"
verbose = true
verify_service = true
[acl]
enabled = true
config_file = "config/acl.toml"
watch = true
anonymous_user = "anonymous"
acl.toml (Simple - No User Mappings!)¶
[global]
default_policy = "block" # Block if user has no matching groups
# Define ONLY the groups you care about (not all LDAP groups!)
[[groups]]
name = "developers" # Case-insensitive: "Developers", "DEVELOPERS" also match
[[groups.rules]]
action = "allow"
description = "Developers access to internal dev servers"
destinations = ["*.dev.company.com", "10.0.0.0/8", "192.168.0.0/16"]
ports = ["*"]
protocols = ["tcp", "udp"]
priority = 100
[[groups.rules]]
action = "block"
description = "Block developers from production"
destinations = ["*.prod.company.com", "172.16.0.0/12"]
ports = ["*"]
protocols = ["tcp"]
priority = 200 # Higher priority = checked first
[[groups]]
name = "admins"
[[groups.rules]]
action = "allow"
description = "Admins full access"
destinations = ["*"]
ports = ["*"]
protocols = ["tcp", "udp"]
priority = 100
# Optional: Per-user overrides (if needed)
[[users]]
username = "alice"
# No need to specify groups - they come from LDAP automatically!
[[users.rules]]
action = "block"
description = "Alice blocked from specific admin panel"
destinations = ["admin.company.com"]
ports = ["*"]
protocols = ["tcp"]
priority = 1000 # Overrides group rules
Example Scenarios¶
Scenario 1: User with Multiple LDAP Groups¶
LDAP groups for user "alice":
["alice", "developers", "engineering", "hr", "team_marketing", ...]
ACL config defines:
[[groups]]
name = "developers" # ← Only this one
Result: - ACL checks if "developers" is in alice's groups → YES - Applies "developers" rules - Ignores: engineering, hr, team_marketing, ... (not in ACL)
Connection test:
curl -x socks5://alice:password@127.0.0.1:1080 http://api.dev.company.com
Logs:
INFO PAM authentication successful user=alice
INFO User authenticated with groups from LDAP user=alice group_count=50
DEBUG Matched LDAP group to ACL group (case-insensitive) ldap_group=developers acl_group=developers
INFO ACL allowed connection user=alice rule="Developers access to internal dev servers"
Scenario 2: User with No Matching Groups¶
LDAP groups for user "bob":
["bob", "hr", "finance", "team_sales"]
ACL config defines:
[[groups]]
name = "developers"
[[groups]]
name = "admins"
Result:
- ACL checks: "hr" in ACL? → NO
- ACL checks: "finance" in ACL? → NO
- ACL checks: "team_sales" in ACL? → NO
- No groups match → Apply default_policy = "block"
Connection test:
curl -x socks5://bob:password@127.0.0.1:1080 http://api.dev.company.com
Logs:
INFO PAM authentication successful user=bob
DEBUG User groups from LDAP: ["bob", "hr", "finance", "team_sales"]
WARN No ACL rules matched for user groups, applying default policy
INFO ACL blocked connection user=bob rule="Default policy (no matching groups)"
Scenario 3: Case-Insensitive Matching¶
LDAP groups: ["Developers"] (capital D)
ACL config: name = "developers" (lowercase)
Result: ✅ MATCH (case-insensitive)
Scenario 4: User in Multiple Defined Groups¶
LDAP groups: ["charlie", "developers", "admins"]
ACL config: Both "developers" and "admins" defined
Result: - Both groups' rules are collected - Rules sorted by priority (BLOCK first, then higher priority) - First matching rule wins
System Requirements¶
1. NSS/SSSD Configuration¶
Your system must have LDAP configured via NSS/SSSD:
# Test if LDAP users are visible
getent passwd alice
# Test if LDAP groups are visible
getent group developers
# Test group membership
id alice
# Output: uid=1001(alice) gid=1001(alice) groups=1001(alice),2001(developers),2002(engineering)
2. PAM Configuration¶
Requires /etc/pam.d/sockd (or your custom service):
#%PAM-1.0
auth required /usr/local/lib64/security/pam_mysql.so config_file=/opt/nsnras/config/pam-mysql.conf
account sufficient /lib64/security/pam_sss.so
account required /lib64/security/pam_unix_acct.so
3. /etc/nsswitch.conf¶
Ensure SSSD is enabled for group lookups:
passwd: files sss
group: files sss
shadow: files sss
Deployment Steps¶
1. Install Dependencies¶
# Development headers (if building from source)
sudo apt-get install libpam0g-dev # Debian/Ubuntu
sudo dnf install pam-devel gcc nodejs rust cargo # RHEL/CentOS
# SSSD (if not already installed)
sudo apt-get install sssd sssd-tools
2. Configure SSSD for LDAP¶
# Example /etc/sssd/sssd.conf
[sssd]
config_file_version = 2
services = nss, pam
domains = LDAP
[domain/LDAP]
id_provider = ldap
auth_provider = ldap
ldap_uri = ldap://ldap.company.com
ldap_search_base = dc=company,dc=com
# Restart SSSD
sudo systemctl restart sssd
3. Configure PAM Service¶
# Copy your PAM service file
sudo cp /path/to/sockd /etc/pam.d/sockd
sudo chmod 644 /etc/pam.d/sockd
4. Configure RustSocks¶
# Edit configs
vim config/rustsocks.toml # Set socks_method = "pam.username", username_service = "sockd"
vim config/acl.toml # Define your LDAP groups (ONLY the ones you need)
5. Build and Run¶
cargo build --release
# Run (may need sudo for PAM)
sudo ./target/release/rustsocks --config config/rustsocks.toml
Testing¶
1. Test LDAP Group Resolution¶
# Run unit tests (no LDAP required)
cargo test --all-features --lib groups
# Expected: 3 tests passed (including mock tests)
2. Test ACL with Mock LDAP Groups¶
# Integration tests use mock groups
cargo test --all-features --test ldap_groups
# Expected: 7 tests passed
# - test_ldap_groups_only_defined_groups_are_checked
# - test_ldap_groups_no_matching_groups_uses_default_policy
# - test_ldap_groups_case_insensitive_matching
# - test_ldap_groups_multiple_matching_groups
# - test_ldap_groups_with_per_user_override
# - test_ldap_groups_empty_groups_list
# - test_ldap_groups_mixed_case_variations
3. Test Real LDAP Integration¶
# Requires real LDAP user "alice" in group "developers"
cargo test --all-features --lib groups::tests::test_get_user_groups_current_user -- --ignored
4. Test End-to-End¶
# Start server
sudo ./target/release/rustsocks --config config/rustsocks.toml
# In another terminal, test with real LDAP user
curl -x socks5://alice:password@127.0.0.1:1080 -v http://api.dev.company.com
# Check logs for:
# - PAM authentication successful
# - User authenticated with groups from LDAP
# - Matched LDAP group to ACL group
# - ACL allowed/blocked connection
Troubleshooting¶
Problem: Groups not found via getgrouplist()¶
Symptoms:
WARN Failed to retrieve user groups from system, using empty list
Solution:
# Check SSSD status
sudo systemctl status sssd
# Check if LDAP groups are visible
getent group developers
# Check user's groups
id username
# Restart SSSD
sudo systemctl restart sssd
# Check SSSD logs
sudo tail -f /var/log/sssd/sssd_LDAP.log
Problem: PAM authentication fails¶
Symptoms:
WARN PAM authentication failed user=alice error=AuthFailed
Solution:
# Test PAM manually
pamtester sockd alice authenticate
# Check PAM service file
ls -la /etc/pam.d/sockd
# Check PAM logs
sudo tail -f /var/log/auth.log # Debian/Ubuntu
sudo tail -f /var/log/secure # RHEL/CentOS
Problem: ACL doesn't match groups¶
Symptoms:
DEBUG User groups from LDAP: ["developers", "engineering"]
WARN No ACL rules matched for user groups
Solution:
# Check ACL config
cat config/acl.toml | grep -A 5 "\[\[groups\]\]"
# Ensure group names match (case-insensitive)
# LDAP: "Developers" = ACL: "developers" ✅
# LDAP: "developers" ≠ ACL: "devs" ❌
# Reload ACL (if hot reload enabled)
curl -X POST http://127.0.0.1:9090/api/admin/reload-acl
Performance Considerations¶
getgrouplist() Overhead¶
- Called once per authentication (cached during session)
- Typical latency: 1-5ms for local NSS cache
- Up to 50-100ms if LDAP lookup required
- Recommendation: Ensure SSSD caching is enabled
ACL Evaluation Overhead¶
- Filtering 1000 LDAP groups: <1ms (only checks defined groups)
- Evaluating 10 ACL rules: <1ms
- Total overhead: ~5-10ms per connection (acceptable)
Security Best Practices¶
- Use TLS for SOCKS5 traffic - Password transmitted in clear-text
- Restrict PAM service - Only allow trusted modules
- Monitor failed authentications - Watch
/var/log/auth.log - Use BLOCK as default policy - Deny by default, allow explicitly
- Regular ACL audits - Review group access periodically
- SSSD caching - Reduce LDAP query load
Architecture Diagram¶
┌─────────────────────────────────────────────────────────────────┐
│ SOCKS5 Client │
│ curl -x socks5://alice:password@proxy:1080 http://dest.com │
└──────────────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ RustSocks Server (handler.rs) │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 1. SOCKS5 Handshake ││
│ │ 2. Authentication (PAM) ││
│ │ └─> AuthManager::authenticate() ││
│ └────────────────────┬────────────────────────────────────────┘│
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ groups.rs: get_user_groups("alice") ││
│ │ ├─> getgrouplist() syscall ││
│ │ └─> Returns: ["alice", "developers", "engineering", ...] ││
│ └────────────────────┬────────────────────────────────────────┘│
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ ACL Engine: evaluate_with_groups() ││
│ │ ├─> Filter LDAP groups (only "developers" defined) ││
│ │ ├─> Collect rules from "developers" group ││
│ │ ├─> Sort rules (BLOCK first, then by priority) ││
│ │ └─> Match destination/port/protocol → ALLOW/BLOCK ││
│ └────────────────────┬────────────────────────────────────────┘│
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 3. Connect to destination (if ALLOWED) ││
│ │ 4. Proxy data ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
▲
│
┌──────────────────────┴──────────────────────────────────────────┐
│ External Systems │
│ ┌───────────────┐ ┌─────────────┐ ┌──────────────────────┐ │
│ │ /etc/pam.d/ │ │ NSS/SSSD │ │ LDAP Server │ │
│ │ sockd │ │ (getgrouplist)│ │ ldap.company.com │ │
│ │ (pam_mysql.so)│ │ │ │ - Users │ │
│ │ │ │ │ │ - Groups │ │
│ └───────────────┘ └─────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
FAQ¶
Q: Do I need to define ALL LDAP groups in ACL config?¶
A: NO! Only define groups you want to grant/block access to. Other groups are ignored.
Q: What if user has 1000+ LDAP groups?¶
A: No problem. ACL only checks groups defined in config. Performance impact is minimal (<1ms filtering).
Q: Are group names case-sensitive?¶
A: NO. "developers" = "Developers" = "DEVELOPERS" (case-insensitive matching).
Q: Can I use per-user overrides?¶
A: YES. Define [[users]] with custom rules. They override group rules (higher priority).
Q: Does this work without LDAP?¶
A: YES. Works with any NSS-compatible system (LDAP, NIS, local files, etc.). Just needs getgrouplist().
Q: What happens if LDAP is down?¶
A: get_user_groups() fails → empty groups list → default_policy applies (usually BLOCK).
Summary¶
✅ Zero manual synchronization - Groups fetched automatically from LDAP ✅ Efficient - Only checks groups defined in ACL config ✅ Case-insensitive - Works regardless of case in LDAP ✅ Flexible - Supports per-user overrides ✅ Secure - Default BLOCK policy for unmatched groups ✅ Production-ready - Comprehensive error handling and logging
Ready to deploy! 🚀