Skip to content

Add /check-header endpoint with caching, distributed locking, and LDAP authorization for Kerberos/SPNEGO#23

Open
semidark wants to merge 18 commits into
caltechads:masterfrom
semidark:kerberos-support
Open

Add /check-header endpoint with caching, distributed locking, and LDAP authorization for Kerberos/SPNEGO#23
semidark wants to merge 18 commits into
caltechads:masterfrom
semidark:kerberos-support

Conversation

@semidark
Copy link
Copy Markdown
Contributor

@semidark semidark commented Feb 23, 2026

Summary

This PR implements a stateless authorization endpoint for ActiveDirectory (Kerberos/SPNEGO) authentication, enabling SSO scenarios where NGINX handles authentication with the SPNEGO module and this service performs LDAP group authorization.

New Features

/check-header Endpoint nginx_ldap_auth/app/header_auth.py

  • Stateless authorization check: Accepts username from a trusted header (X-Ldap-User by default) set by NGINX after Kerberos/SPNEGO authentication
  • LDAP group authorization: Validates user membership against configurable LDAP filters
  • Per-request filter override: Supports X-Authorization-Filter header for dynamic authorization filters (configurable via allow_authorization_filter_header)
  • Clear response semantics:
    • 200 OK - User is authorized
    • 401 Unauthorized - Missing username header
    • 403 Forbidden - User failed LDAP authorization filter
    • 500 Internal Server Error - LDAP connection/query error

Authorization Cache nginx_ldap_auth/app/header_auth_cache.py

  • Dual backend support: In-memory cache or Redis-backed caching
  • Per-user/filter caching: Cache keys incorporate both username and authorization filter hash
  • Configurable TTL: header_auth_cache_ttl setting (default: 300 seconds, 0 to disable)

Distributed Locking for Thundering Herd Protection

  • Per-key locking: Uses asyncio.Lock for in-memory backend or Redis SETNX for distributed scenarios
  • LRU lock cleanup: In-memory backend prunes oldest locks when exceeding 10,000 entries
  • Retry with backoff: Redis lock acquisition uses exponential backoff with timeout protection

Configuration Settings nginx_ldap_auth/settings.py

  • header_auth_enabled - Enable/disable the /check-header endpoint (default: True)
  • ldap_trusted_user_header - Header containing the authenticated username (default: X-Ldap-User)
  • header_auth_cache_ttl - Cache TTL in seconds (default: 300, set to 0 to disable)
  • allow_authorization_filter_header - Allow per-request filter via header (default: True)

NGINX Integration Example

location / {
    auth_gss on;  # Kerberos/SPNEGO
    auth_request /check-header-auth;
    error_page 403 = @forbidden;
}

location /check-header-auth {
    internal;
    proxy_pass http://nginx-ldap-auth-service:8888/check-header;
    proxy_pass_request_headers off;
    proxy_set_header X-Ldap-User $remote_user;
    proxy_set_header X-Authorization-Filter "(&(sAMAccountName={username})(memberOf=cn=mygroup,ou=Groups,dc=example,dc=com))";
}

Benefits

  • Single Sign-On (SSO): Seamless authentication via existing Kerberos tickets
  • High Performance: Cached authorization results eliminate redundant LDAP queries
  • Scalability: Distributed locking prevents LDAP thundering herd and cache stampedes under high concurrent load
  • Flexibility: Supports both static (env) and dynamic (header) authorization filters

Testing

  • Comprehensive test coverage in test/test_header_auth.py including cache hit/miss scenarios, lock contention handling, Redis backend simulation, and edge cases

Documentation

  • Updated configuration and NGINX integration guides for the new SSO features

Migration Notes

  • This feature is disabled when header_auth_cache_ttl is set to 0
  • No breaking changes to existing cookie-based authentication flow

semidark-kiki and others added 15 commits February 12, 2026 15:00
…O support

Adds /check-header endpoint that trusts username from X-Ldap-User header
(set by NGINX after Kerberos authentication) and performs LDAP group
membership checks for authorization. Includes caching with TTL and
per-key locking to prevent thundering herd on LDAP.
- Fix test cache pollution with autouse fixture that resets cache
- Fix all ruff linting issues (line length, imports, style)
- Use module-level Settings() instance in cache.py for consistency
- Add field validator for header_auth_cache_ttl (must be >= 0)
- Add header_auth_enabled setting to conditionally enable feature
- Document private _connection attribute access from starsessions
- Use keyword-only arg for authorized parameter in set_cached_authorization
- Add LRU-based cleanup for per-key locks to prevent unbounded memory growth
- Track last access time per lock, prune oldest 10% when exceeding 10,000 locks
- Add comprehensive Kerberos/SPNEGO configuration documentation to nginx.rst
- Document new environment variables: HEADER_AUTH_ENABLED, LDAP_TRUSTED_USER_HEADER, HEADER_AUTH_CACHE_TTL
- Add tests for LRU lock cleanup behavior
The module contains authorization caching logic specific to the header-based
auth feature (/check-header endpoint). The new name better reflects its
purpose and leaves room for other caching modules in the future.
- Add Kerberos/SPNEGO to Features list
- Add HEADER_AUTH_ENABLED and HEADER_AUTH_CACHE_TTL to optional variables
- Add new Kerberos/SPNEGO Authentication section with Nginx example
…tting

Apply the same security setting to the /check-header endpoint used for
Kerberos/SPNEGO authentication. When allow_authorization_filter_header
is False, the X-Authorization-Filter header is ignored.

Also update test fixtures to patch settings consistently across all modules.
- Add header_auth.py and header_auth_cache.py to project structure
- Add note about installing test dependencies with uv sync --group test
- Condense from 247 to 188 lines while keeping essential information
- Update testing commands to reflect actual usage
@semidark
Copy link
Copy Markdown
Contributor Author

semidark commented Feb 23, 2026

I know this probably is a bit out of scope for your implementation, but i needed this and took your implementation as the base for my AD (Kerberos / SPNEGO / SSO) solution.

With this PR I wanted to ask if you would be interested in adding this Kerberos SSO functionality to the main Repository.

PS: The security findings i posted over the last days were the result of me getting to know the Codebase :-)
Greets semidark

@cmalek
Copy link
Copy Markdown
Collaborator

cmalek commented Feb 25, 2026

I have not forgotten about you @semidark. I both want to help you out but I also don't want to add code to the codebase with code that may not commonly be used. I know I am being hypocritical here since I included the Duo MFA workflow, which we use heavily at Caltech.

I have two ideas, one of which you could implement immediately, and the other is probably a longer term project:

  1. You fork this repo and change it to work for your use case; call it nginx-kerberos-auth-service or something
  2. We work out a plugin system whereby we build a library of plugins and swap MFA workflows and auth workflows that can be mixed and matched

What are your thoughts?

@cmalek
Copy link
Copy Markdown
Collaborator

cmalek commented Apr 20, 2026

@semidark Thank you for the pull request!

I think you should fork this repository, rename your fork project nginx-kerberos-ldap-auth-service and focus it on your use case. This is a great use case, but I don't want this project to become diluted away from pure LDAP interaction.

I'd like to deal with nginx external auth the way Apache used to with its mod_authz_* modules. Tightly focused modules that support one kind of backend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants