mirror of
https://github.com/mainnika/nginx-auth-ldap.git
synced 2026-05-22 15:53:39 +00:00
Implement a per-process cache for increased performance
A configured number of cached (server,username,password) entries is held for a configured amount of time to avoid repeated authentications for each of several requests quickly following each other.
This commit is contained in:
+248
-32
@@ -27,6 +27,7 @@
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
#include <ngx_md5.h>
|
||||
#include <ldap.h>
|
||||
|
||||
typedef struct {
|
||||
@@ -49,6 +50,9 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t *servers; /* array of ngx_http_auth_ldap_server_t */
|
||||
ngx_flag_t cache_enabled;
|
||||
ngx_msec_t cache_expiration_time;
|
||||
size_t cache_size;
|
||||
} ngx_http_auth_ldap_main_conf_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -56,7 +60,29 @@ typedef struct {
|
||||
ngx_array_t *servers; /* array of ngx_http_auth_ldap_server_t* */
|
||||
} ngx_http_auth_ldap_loc_conf_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t small_hash; /* murmur2 hash of username ^ &server */
|
||||
uint32_t outcome; /* 0 = authentication failed, 1 = succeeded */
|
||||
ngx_msec_t time; /* ngx_current_msec when created */
|
||||
u_char big_hash[16]; /* md5 hash of (username, server, password) */
|
||||
} ngx_http_auth_ldap_cache_elt_t;
|
||||
|
||||
typedef struct {
|
||||
ngx_http_auth_ldap_cache_elt_t *buckets;
|
||||
ngx_uint_t num_buckets;
|
||||
ngx_uint_t elts_per_bucket;
|
||||
ngx_msec_t expiration_time;
|
||||
} ngx_http_auth_ldap_cache_t;
|
||||
|
||||
typedef struct {
|
||||
ngx_http_auth_ldap_cache_elt_t *cache_bucket;
|
||||
u_char cache_big_hash[16];
|
||||
uint32_t cache_small_hash;
|
||||
} ngx_http_auth_ldap_ctx_t;
|
||||
|
||||
static void * ngx_http_auth_ldap_create_main_conf(ngx_conf_t *cf);
|
||||
static char * ngx_http_auth_ldap_init_main_conf(ngx_conf_t *cf, void *parent);
|
||||
static ngx_int_t ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle);
|
||||
static char * ngx_http_auth_ldap_ldap_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||
static char * ngx_http_auth_ldap_parse_url(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server);
|
||||
static char * ngx_http_auth_ldap_parse_require(ngx_conf_t *cf, ngx_http_auth_ldap_server_t *server);
|
||||
@@ -69,10 +95,13 @@ static char * ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *, void *, void *);
|
||||
static ngx_int_t ngx_http_auth_ldap_authenticate_against_server(ngx_http_request_t *r, ngx_http_auth_ldap_server_t *server,
|
||||
ngx_http_auth_ldap_loc_conf_t *conf);
|
||||
static ngx_int_t ngx_http_auth_ldap_set_realm(ngx_http_request_t *r, ngx_str_t *realm);
|
||||
static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_loc_conf_t *conf);
|
||||
static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx,
|
||||
ngx_http_auth_ldap_loc_conf_t *conf);
|
||||
static char * ngx_http_auth_ldap(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||
static char * ngx_http_auth_ldap_servers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||
|
||||
ngx_http_auth_ldap_cache_t ngx_http_auth_ldap_cache;
|
||||
|
||||
static ngx_command_t ngx_http_auth_ldap_commands[] = {
|
||||
{
|
||||
ngx_string("ldap_server"),
|
||||
@@ -82,6 +111,30 @@ static ngx_command_t ngx_http_auth_ldap_commands[] = {
|
||||
0,
|
||||
NULL
|
||||
},
|
||||
{
|
||||
ngx_string("auth_ldap_cache_enabled"),
|
||||
NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||
offsetof(ngx_http_auth_ldap_main_conf_t, cache_enabled),
|
||||
NULL
|
||||
},
|
||||
{
|
||||
ngx_string("auth_ldap_cache_expiration_time"),
|
||||
NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||
offsetof(ngx_http_auth_ldap_main_conf_t, cache_expiration_time),
|
||||
NULL
|
||||
},
|
||||
{
|
||||
ngx_string("auth_ldap_cache_size"),
|
||||
NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1,
|
||||
ngx_conf_set_size_slot,
|
||||
NGX_HTTP_MAIN_CONF_OFFSET,
|
||||
offsetof(ngx_http_auth_ldap_main_conf_t, cache_size),
|
||||
NULL
|
||||
},
|
||||
{
|
||||
ngx_string("auth_ldap"),
|
||||
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_TAKE1,
|
||||
@@ -102,29 +155,29 @@ static ngx_command_t ngx_http_auth_ldap_commands[] = {
|
||||
};
|
||||
|
||||
static ngx_http_module_t ngx_http_auth_ldap_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_auth_ldap_init, /* postconfiguration */
|
||||
NULL, /* preconfiguration */
|
||||
ngx_http_auth_ldap_init, /* postconfiguration */
|
||||
ngx_http_auth_ldap_create_main_conf, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
NULL, //ngx_http_auth_ldap_create_server_conf, /* create server configuration */
|
||||
NULL, //ngx_http_auth_ldap_merge_server_conf, /* merge server configuration */
|
||||
ngx_http_auth_ldap_create_loc_conf, /* create location configuration */
|
||||
ngx_http_auth_ldap_merge_loc_conf /* merge location configuration */
|
||||
ngx_http_auth_ldap_init_main_conf, /* init main configuration */
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
ngx_http_auth_ldap_create_loc_conf, /* create location configuration */
|
||||
ngx_http_auth_ldap_merge_loc_conf /* merge location configuration */
|
||||
};
|
||||
|
||||
ngx_module_t ngx_http_auth_ldap_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_auth_ldap_module_ctx, /* module context */
|
||||
ngx_http_auth_ldap_commands, /* module directives */
|
||||
NGX_HTTP_MODULE, /* module type */
|
||||
NULL, /* init master */
|
||||
NULL, /* init module */
|
||||
NULL, /* init process */
|
||||
NULL, /* init thread */
|
||||
NULL, /* exit thread */
|
||||
NULL, /* exit process */
|
||||
NULL, /* exit master */
|
||||
NGX_MODULE_V1_PADDING /**/
|
||||
&ngx_http_auth_ldap_module_ctx, /* module context */
|
||||
ngx_http_auth_ldap_commands, /* module directives */
|
||||
NGX_HTTP_MODULE, /* module type */
|
||||
NULL, /* init master */
|
||||
NULL, /* init module */
|
||||
ngx_http_auth_ldap_init_worker, /* init process */
|
||||
NULL, /* init thread */
|
||||
NULL, /* exit thread */
|
||||
NULL, /* exit process */
|
||||
NULL, /* exit master */
|
||||
NGX_MODULE_V1_PADDING
|
||||
};
|
||||
|
||||
|
||||
@@ -456,9 +509,144 @@ ngx_http_auth_ldap_create_main_conf(ngx_conf_t *cf)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf->cache_enabled = NGX_CONF_UNSET;
|
||||
conf->cache_expiration_time = NGX_CONF_UNSET_MSEC;
|
||||
conf->cache_size = NGX_CONF_UNSET_SIZE;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
static char *
|
||||
ngx_http_auth_ldap_init_main_conf(ngx_conf_t *cf, void *parent)
|
||||
{
|
||||
ngx_http_auth_ldap_main_conf_t *conf = parent;
|
||||
|
||||
if (conf->cache_enabled == NGX_CONF_UNSET) {
|
||||
conf->cache_enabled = 0;
|
||||
}
|
||||
if (conf->cache_enabled == 0) {
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (conf->cache_size == NGX_CONF_UNSET_SIZE) {
|
||||
conf->cache_size = 100;
|
||||
}
|
||||
if (conf->cache_size < 100) {
|
||||
ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "auth_ldap_cache_size cannot be smaller than 100 entries.");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (conf->cache_expiration_time == NGX_CONF_UNSET_MSEC) {
|
||||
conf->cache_expiration_time = 10000;
|
||||
}
|
||||
if (conf->cache_expiration_time < 1000) {
|
||||
ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "auth_ldap_cache_expiration_time cannot be smaller than 1000 ms.");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_auth_ldap_init_worker(ngx_cycle_t *cycle)
|
||||
{
|
||||
ngx_http_auth_ldap_main_conf_t *conf;
|
||||
ngx_uint_t want, count, i;
|
||||
ngx_http_auth_ldap_cache_t *cache;
|
||||
static const uint16_t primes[] = {
|
||||
13, 53, 101, 151, 199, 263, 317, 383, 443, 503,
|
||||
577, 641, 701, 769, 839, 911, 983, 1049, 1109
|
||||
};
|
||||
|
||||
|
||||
if (ngx_process != NGX_PROCESS_SINGLE && ngx_process != NGX_PROCESS_WORKER) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
conf = (ngx_http_auth_ldap_main_conf_t *) ngx_http_cycle_get_module_main_conf(cycle, ngx_http_auth_ldap_module);
|
||||
if (conf == NULL || !conf->cache_enabled) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
want = (conf->cache_size + 7) / 8;
|
||||
for (i = 0; i < sizeof(primes)/sizeof(primes[0]); i++) {
|
||||
count = primes[i];
|
||||
if (count >= want) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cache = &ngx_http_auth_ldap_cache;
|
||||
cache->expiration_time = conf->cache_expiration_time;
|
||||
cache->num_buckets = count;
|
||||
cache->elts_per_bucket = 8;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "http_auth_ldap: Allocating %ud bytes of LDAP cache.",
|
||||
cache->num_buckets * cache->elts_per_bucket * sizeof(ngx_http_auth_ldap_cache_elt_t));
|
||||
|
||||
cache->buckets = (ngx_http_auth_ldap_cache_elt_t *) ngx_calloc(count * 8 * sizeof(ngx_http_auth_ldap_cache_elt_t), cycle->log);
|
||||
if (cache->buckets == NULL) {
|
||||
ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "http_auth_ldap: Unable to allocate memory for LDAP cache.");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_auth_ldap_check_cache(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx,
|
||||
ngx_http_auth_ldap_cache_t *cache, ngx_http_auth_ldap_server_t *server)
|
||||
{
|
||||
ngx_http_auth_ldap_cache_elt_t *elt;
|
||||
ngx_md5_t md5ctx;
|
||||
ngx_msec_t time_limit;
|
||||
ngx_uint_t i;
|
||||
|
||||
ctx->cache_small_hash = ngx_murmur_hash2(r->headers_in.user.data, r->headers_in.user.len) ^ (uint32_t)(ngx_uint_t)server;
|
||||
|
||||
ngx_md5_init(&md5ctx);
|
||||
ngx_md5_update(&md5ctx, r->headers_in.user.data, r->headers_in.user.len);
|
||||
ngx_md5_update(&md5ctx, server, sizeof(*server));
|
||||
ngx_md5_update(&md5ctx, r->headers_in.passwd.data, r->headers_in.passwd.len);
|
||||
ngx_md5_final(ctx->cache_big_hash, &md5ctx);
|
||||
|
||||
ctx->cache_bucket = &cache->buckets[ctx->cache_small_hash % cache->num_buckets];
|
||||
|
||||
elt = ctx->cache_bucket;
|
||||
time_limit = ngx_current_msec - cache->expiration_time;
|
||||
for (i = 0; i < cache->elts_per_bucket; i++, elt++) {
|
||||
if (elt->small_hash == ctx->cache_small_hash &&
|
||||
elt->time > time_limit &&
|
||||
memcmp(elt->big_hash, ctx->cache_big_hash, 16) == 0) {
|
||||
return elt->outcome;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
ngx_http_auth_ldap_update_cache(ngx_http_auth_ldap_ctx_t *ctx,
|
||||
ngx_http_auth_ldap_cache_t *cache, ngx_flag_t outcome)
|
||||
{
|
||||
ngx_http_auth_ldap_cache_elt_t *elt, *oldest_elt;
|
||||
ngx_uint_t i;
|
||||
|
||||
elt = ctx->cache_bucket;
|
||||
oldest_elt = elt;
|
||||
for (i = 1; i < cache->elts_per_bucket; i++, elt++) {
|
||||
if (elt->time < oldest_elt->time) {
|
||||
oldest_elt = elt;
|
||||
}
|
||||
}
|
||||
|
||||
oldest_elt->time = ngx_current_msec;
|
||||
oldest_elt->outcome = outcome;
|
||||
oldest_elt->small_hash = ctx->cache_small_hash;
|
||||
ngx_memcpy(oldest_elt->big_hash, ctx->cache_big_hash, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create location conf
|
||||
*/
|
||||
@@ -494,29 +682,40 @@ ngx_http_auth_ldap_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) {
|
||||
* LDAP Authentication handler
|
||||
*/
|
||||
static ngx_int_t ngx_http_auth_ldap_handler(ngx_http_request_t *r) {
|
||||
int rc;
|
||||
ngx_http_auth_ldap_loc_conf_t *alcf;
|
||||
ngx_http_auth_ldap_ctx_t *ctx;
|
||||
int rc;
|
||||
|
||||
alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_ldap_module);
|
||||
if (alcf->realm.len == 0) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
rc = ngx_http_auth_basic_user(r);
|
||||
if (rc == NGX_DECLINED) {
|
||||
return ngx_http_auth_ldap_set_realm(r, &alcf->realm);
|
||||
}
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_auth_ldap_module);
|
||||
if (ctx == NULL) {
|
||||
rc = ngx_http_auth_basic_user(r);
|
||||
if (rc == NGX_DECLINED) {
|
||||
return ngx_http_auth_ldap_set_realm(r, &alcf->realm);
|
||||
}
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_ldap_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_auth_ldap_module);
|
||||
}
|
||||
|
||||
return ngx_http_auth_ldap_authenticate(r, alcf);
|
||||
return ngx_http_auth_ldap_authenticate(r, ctx, alcf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read user credentials from request, set LDAP parameters and call authentication against required servers
|
||||
*/
|
||||
static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_loc_conf_t *conf) {
|
||||
static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http_auth_ldap_ctx_t *ctx,
|
||||
ngx_http_auth_ldap_loc_conf_t *conf) {
|
||||
|
||||
ngx_http_auth_ldap_server_t *server;
|
||||
int rc;
|
||||
@@ -525,7 +724,6 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http
|
||||
int version = LDAP_VERSION3;
|
||||
int reqcert = LDAP_OPT_X_TLS_ALLOW;
|
||||
struct timeval timeOut = { 10, 0 };
|
||||
ngx_flag_t pass = NGX_CONF_UNSET;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP username: %V", &r->headers_in.user);
|
||||
|
||||
@@ -546,10 +744,28 @@ static ngx_int_t ngx_http_auth_ldap_authenticate(ngx_http_request_t *r, ngx_http
|
||||
|
||||
for (i = 0; i < conf->servers->nelts; i++) {
|
||||
server = ((ngx_http_auth_ldap_server_t **) conf->servers->elts)[i];
|
||||
pass = ngx_http_auth_ldap_authenticate_against_server(r, server, conf);
|
||||
if (pass == 1) {
|
||||
|
||||
if (ngx_http_auth_ldap_cache.buckets != NULL) {
|
||||
rc = ngx_http_auth_ldap_check_cache(r, ctx, &ngx_http_auth_ldap_cache, server);
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: Cached outcome %d", rc);
|
||||
if (rc == 0) {
|
||||
continue;
|
||||
}
|
||||
if (rc == 1) {
|
||||
return NGX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ngx_http_auth_ldap_authenticate_against_server(r, server, conf);
|
||||
|
||||
if ((rc == 0 || rc == 1) && ngx_http_auth_ldap_cache.buckets != NULL) {
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "LDAP: Caching outcome %d", rc);
|
||||
ngx_http_auth_ldap_update_cache(ctx, &ngx_http_auth_ldap_cache, rc);
|
||||
}
|
||||
|
||||
if (rc == 1) {
|
||||
return NGX_OK;
|
||||
} else if (pass == NGX_HTTP_INTERNAL_SERVER_ERROR) {
|
||||
} else if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user