[nginx] SSL: caching certificates and certificate keys with variables.

noreply at nginx.com noreply at nginx.com
Fri Jan 17 00:38:02 UTC 2025


details:   https://github.com/nginx/nginx/commit/0e756d67aa1e42e3b1b360936eb4d6c06bced2c1
branches:  master
commit:    0e756d67aa1e42e3b1b360936eb4d6c06bced2c1
user:      Sergey Kandaurov <pluknet at nginx.com>
date:      Tue, 29 Oct 2024 16:25:11 +0400
description:
SSL: caching certificates and certificate keys with variables.

A new directive "ssl_certificate_cache max=N [valid=time] [inactive=time]"
enables caching of SSL certificate chain and secret key objects specified
by "ssl_certificate" and "ssl_certificate_key" directives with variables.

Co-authored-by: Aleksei Bavshin <a.bavshin at nginx.com>

---
 src/event/ngx_event_openssl.c          |  11 +-
 src/event/ngx_event_openssl.h          |  12 +-
 src/event/ngx_event_openssl_cache.c    | 240 +++++++++++++++++++++++++++++----
 src/http/modules/ngx_http_ssl_module.c | 106 +++++++++++++++
 src/http/modules/ngx_http_ssl_module.h |   2 +
 src/http/ngx_http_request.c            |   1 +
 src/http/ngx_http_upstream.c           |   2 +-
 src/stream/ngx_stream_proxy_module.c   |   2 +-
 src/stream/ngx_stream_ssl_module.c     | 107 +++++++++++++++
 src/stream/ngx_stream_ssl_module.h     |  66 ++++-----
 10 files changed, 481 insertions(+), 68 deletions(-)

diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index 35e9f3c88..8963c8124 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -562,15 +562,16 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
 
 ngx_int_t
 ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
-    ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords)
+    ngx_str_t *cert, ngx_str_t *key, ngx_ssl_cache_t *cache,
+    ngx_array_t *passwords)
 {
     char            *err;
     X509            *x509;
     EVP_PKEY        *pkey;
     STACK_OF(X509)  *chain;
 
-    chain = ngx_ssl_cache_connection_fetch(pool, NGX_SSL_CACHE_CERT, &err,
-                                           cert, NULL);
+    chain = ngx_ssl_cache_connection_fetch(cache, pool, NGX_SSL_CACHE_CERT,
+                                           &err, cert, NULL);
     if (chain == NULL) {
         if (err != NULL) {
             ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
@@ -610,8 +611,8 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
 
 #endif
 
-    pkey = ngx_ssl_cache_connection_fetch(pool, NGX_SSL_CACHE_PKEY, &err,
-                                          key, passwords);
+    pkey = ngx_ssl_cache_connection_fetch(cache, pool, NGX_SSL_CACHE_PKEY,
+                                          &err, key, passwords);
     if (pkey == NULL) {
         if (err != NULL) {
             ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
index 2147205d6..0713c5671 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -83,7 +83,8 @@
 #endif
 
 
-typedef struct ngx_ssl_ocsp_s  ngx_ssl_ocsp_t;
+typedef struct ngx_ssl_cache_s  ngx_ssl_cache_t;
+typedef struct ngx_ssl_ocsp_s   ngx_ssl_ocsp_t;
 
 
 struct ngx_ssl_s {
@@ -214,7 +215,8 @@ ngx_int_t ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl,
 ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
 ngx_int_t ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
-    ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
+    ngx_str_t *cert, ngx_str_t *key, ngx_ssl_cache_t *cache,
+    ngx_array_t *passwords);
 
 ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers,
     ngx_uint_t prefer_server_ciphers);
@@ -237,10 +239,12 @@ ngx_int_t ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s);
 void ngx_ssl_ocsp_cleanup(ngx_connection_t *c);
 ngx_int_t ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data);
 
+ngx_ssl_cache_t *ngx_ssl_cache_init(ngx_pool_t *pool, ngx_uint_t max,
+    time_t valid, time_t inactive);
 void *ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
     ngx_str_t *path, void *data);
-void *ngx_ssl_cache_connection_fetch(ngx_pool_t *pool, ngx_uint_t index,
-    char **err, ngx_str_t *path, void *data);
+void *ngx_ssl_cache_connection_fetch(ngx_ssl_cache_t *cache, ngx_pool_t *pool,
+    ngx_uint_t index, char **err, ngx_str_t *path, void *data);
 
 ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file);
 ngx_array_t *ngx_ssl_preserve_passwords(ngx_conf_t *cf,
diff --git a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c
index c79f77456..7589e6c90 100644
--- a/src/event/ngx_event_openssl_cache.c
+++ b/src/event/ngx_event_openssl_cache.c
@@ -46,21 +46,31 @@ typedef struct {
 
 typedef struct {
     ngx_rbtree_node_t           node;
+    ngx_queue_t                 queue;
     ngx_ssl_cache_key_t         id;
     ngx_ssl_cache_type_t       *type;
     void                       *value;
 
+    time_t                      created;
+    time_t                      accessed;
+
     time_t                      mtime;
     ngx_file_uniq_t             uniq;
 } ngx_ssl_cache_node_t;
 
 
-typedef struct {
+struct ngx_ssl_cache_s {
     ngx_rbtree_t                rbtree;
     ngx_rbtree_node_t           sentinel;
+    ngx_queue_t                 expire_queue;
 
     ngx_flag_t                  inheritable;
-} ngx_ssl_cache_t;
+
+    ngx_uint_t                  current;
+    ngx_uint_t                  max;
+    time_t                      valid;
+    time_t                      inactive;
+};
 
 
 typedef struct {
@@ -73,6 +83,8 @@ static ngx_int_t ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index,
     ngx_str_t *path, ngx_ssl_cache_key_t *id);
 static ngx_ssl_cache_node_t *ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache,
     ngx_ssl_cache_type_t *type, ngx_ssl_cache_key_t *id, uint32_t hash);
+static void ngx_ssl_cache_expire(ngx_ssl_cache_t *cache, ngx_uint_t n,
+    ngx_log_t *log);
 
 static void *ngx_ssl_cache_cert_create(ngx_ssl_cache_key_t *id, char **err,
     void *data);
@@ -101,6 +113,8 @@ static char *ngx_openssl_cache_init_conf(ngx_cycle_t *cycle, void *conf);
 static void ngx_ssl_cache_cleanup(void *data);
 static void ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp,
     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+static void ngx_ssl_cache_node_free(ngx_rbtree_t *rbtree,
+    ngx_ssl_cache_node_t *cn);
 
 
 static ngx_command_t  ngx_openssl_cache_commands[] = {
@@ -260,6 +274,8 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
 
     ngx_cpystrn(cn->id.data, id.data, id.len + 1);
 
+    ngx_queue_init(&cn->queue);
+
     ngx_rbtree_insert(&cache->rbtree, &cn->node);
 
     return type->ref(err, cn->value);
@@ -267,10 +283,15 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
 
 
 void *
-ngx_ssl_cache_connection_fetch(ngx_pool_t *pool, ngx_uint_t index, char **err,
-    ngx_str_t *path, void *data)
+ngx_ssl_cache_connection_fetch(ngx_ssl_cache_t *cache, ngx_pool_t *pool,
+    ngx_uint_t index, char **err, ngx_str_t *path, void *data)
 {
-    ngx_ssl_cache_key_t  id;
+    void                  *value;
+    time_t                 now;
+    uint32_t               hash;
+    ngx_ssl_cache_key_t    id;
+    ngx_ssl_cache_type_t  *type;
+    ngx_ssl_cache_node_t  *cn;
 
     *err = NULL;
 
@@ -278,7 +299,89 @@ ngx_ssl_cache_connection_fetch(ngx_pool_t *pool, ngx_uint_t index, char **err,
         return NULL;
     }
 
-    return ngx_ssl_cache_types[index].create(&id, err, &data);
+    type = &ngx_ssl_cache_types[index];
+
+    if (cache == NULL) {
+        return type->create(&id, err, &data);
+    }
+
+    now = ngx_time();
+
+    hash = ngx_murmur_hash2(id.data, id.len);
+
+    cn = ngx_ssl_cache_lookup(cache, type, &id, hash);
+
+    if (cn != NULL) {
+        ngx_queue_remove(&cn->queue);
+
+        if (id.type == NGX_SSL_CACHE_DATA) {
+            goto found;
+        }
+
+        if (now - cn->created > cache->valid) {
+            ngx_log_debug1(NGX_LOG_DEBUG_CORE, pool->log, 0,
+                           "update cached ssl object: %s", cn->id.data);
+
+            type->free(cn->value);
+
+            value = type->create(&id, err, &data);
+
+            if (value == NULL || data == NGX_SSL_CACHE_DISABLED) {
+                ngx_rbtree_delete(&cache->rbtree, &cn->node);
+
+                cache->current--;
+
+                ngx_free(cn);
+
+                return value;
+            }
+
+            cn->value = value;
+            cn->created = now;
+        }
+
+        goto found;
+    }
+
+    value = type->create(&id, err, &data);
+
+    if (value == NULL || data == NGX_SSL_CACHE_DISABLED) {
+        return value;
+    }
+
+    cn = ngx_alloc(sizeof(ngx_ssl_cache_node_t) + id.len + 1, pool->log);
+    if (cn == NULL) {
+        type->free(value);
+        return NULL;
+    }
+
+    cn->node.key = hash;
+    cn->id.data = (u_char *)(cn + 1);
+    cn->id.len = id.len;
+    cn->id.type = id.type;
+    cn->type = type;
+    cn->value = value;
+    cn->created = now;
+
+    ngx_cpystrn(cn->id.data, id.data, id.len + 1);
+
+    ngx_ssl_cache_expire(cache, 1, pool->log);
+
+    if (cache->current >= cache->max) {
+        ngx_ssl_cache_expire(cache, 0, pool->log);
+    }
+
+    ngx_rbtree_insert(&cache->rbtree, &cn->node);
+
+    cache->current++;
+
+found:
+
+    cn->accessed = now;
+
+    ngx_queue_insert_head(&cache->expire_queue, &cn->queue);
+
+    return type->ref(err, cn->value);
 }
 
 
@@ -365,6 +468,37 @@ ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache, ngx_ssl_cache_type_t *type,
 }
 
 
+static void
+ngx_ssl_cache_expire(ngx_ssl_cache_t *cache, ngx_uint_t n,
+    ngx_log_t *log)
+{
+    time_t                 now;
+    ngx_queue_t           *q;
+    ngx_ssl_cache_node_t  *cn;
+
+    now = ngx_time();
+
+    while (n < 3) {
+
+        if (ngx_queue_empty(&cache->expire_queue)) {
+            return;
+        }
+
+        q = ngx_queue_last(&cache->expire_queue);
+
+        cn = ngx_queue_data(q, ngx_ssl_cache_node_t, queue);
+
+        if (n++ != 0 && now - cn->accessed <= cache->inactive) {
+            return;
+        }
+
+        ngx_ssl_cache_node_free(&cache->rbtree, cn);
+
+        cache->current--;
+    }
+}
+
+
 static void *
 ngx_ssl_cache_cert_create(ngx_ssl_cache_key_t *id, char **err, void *data)
 {
@@ -822,27 +956,15 @@ ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err)
 static void *
 ngx_openssl_cache_create_conf(ngx_cycle_t *cycle)
 {
-    ngx_ssl_cache_t     *cache;
-    ngx_pool_cleanup_t  *cln;
+    ngx_ssl_cache_t  *cache;
 
-    cache = ngx_pcalloc(cycle->pool, sizeof(ngx_ssl_cache_t));
+    cache = ngx_ssl_cache_init(cycle->pool, 0, 0, 0);
     if (cache == NULL) {
         return NULL;
     }
 
     cache->inheritable = NGX_CONF_UNSET;
 
-    cln = ngx_pool_cleanup_add(cycle->pool, 0);
-    if (cln == NULL) {
-        return NULL;
-    }
-
-    cln->handler = ngx_ssl_cache_cleanup;
-    cln->data = cache;
-
-    ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
-                    ngx_ssl_cache_node_insert);
-
     return cache;
 }
 
@@ -858,6 +980,39 @@ ngx_openssl_cache_init_conf(ngx_cycle_t *cycle, void *conf)
 }
 
 
+ngx_ssl_cache_t *
+ngx_ssl_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t valid,
+    time_t inactive)
+{
+    ngx_ssl_cache_t     *cache;
+    ngx_pool_cleanup_t  *cln;
+
+    cache = ngx_pcalloc(pool, sizeof(ngx_ssl_cache_t));
+    if (cache == NULL) {
+        return NULL;
+    }
+
+    ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
+                    ngx_ssl_cache_node_insert);
+
+    ngx_queue_init(&cache->expire_queue);
+
+    cache->max = max;
+    cache->valid = valid;
+    cache->inactive = inactive;
+
+    cln = ngx_pool_cleanup_add(pool, 0);
+    if (cln == NULL) {
+        return NULL;
+    }
+
+    cln->handler = ngx_ssl_cache_cleanup;
+    cln->data = cache;
+
+    return cache;
+}
+
+
 static void
 ngx_ssl_cache_cleanup(void *data)
 {
@@ -873,12 +1028,47 @@ ngx_ssl_cache_cleanup(void *data)
         return;
     }
 
-    for (node = ngx_rbtree_min(tree->root, tree->sentinel);
-         node;
-         node = ngx_rbtree_next(tree, node))
-    {
+    node = ngx_rbtree_min(tree->root, tree->sentinel);
+
+    while (node != NULL) {
         cn = ngx_rbtree_data(node, ngx_ssl_cache_node_t, node);
-        cn->type->free(cn->value);
+        node = ngx_rbtree_next(tree, node);
+
+        ngx_ssl_cache_node_free(tree, cn);
+
+        if (cache->max) {
+            cache->current--;
+        }
+    }
+
+    if (cache->current) {
+        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+                      "%ui items still left in ssl cache",
+                      cache->current);
+    }
+
+    if (!ngx_queue_empty(&cache->expire_queue)) {
+        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+                      "queue still is not empty in ssl cache");
+
+    }
+}
+
+
+static void
+ngx_ssl_cache_node_free(ngx_rbtree_t *rbtree, ngx_ssl_cache_node_t *cn)
+{
+    cn->type->free(cn->value);
+
+    ngx_rbtree_delete(rbtree, &cn->node);
+
+    if (!ngx_queue_empty(&cn->queue)) {
+        ngx_queue_remove(&cn->queue);
+
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
+                       "delete cached ssl object: %s", cn->id.data);
+
+        ngx_free(cn);
     }
 }
 
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index 0e892b04d..dbfe5c08b 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -43,6 +43,8 @@ static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf,
 static ngx_int_t ngx_http_ssl_compile_certificates(ngx_conf_t *cf,
     ngx_http_ssl_srv_conf_t *conf);
 
+static char *ngx_http_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 static char *ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
@@ -108,6 +110,13 @@ static ngx_command_t  ngx_http_ssl_commands[] = {
       offsetof(ngx_http_ssl_srv_conf_t, certificate_keys),
       NULL },
 
+    { ngx_string("ssl_certificate_cache"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE123,
+      ngx_http_ssl_certificate_cache,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("ssl_password_file"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
       ngx_http_ssl_password_file,
@@ -619,6 +628,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
     sscf->verify_depth = NGX_CONF_UNSET_UINT;
     sscf->certificates = NGX_CONF_UNSET_PTR;
     sscf->certificate_keys = NGX_CONF_UNSET_PTR;
+    sscf->certificate_cache = NGX_CONF_UNSET_PTR;
     sscf->passwords = NGX_CONF_UNSET_PTR;
     sscf->conf_commands = NGX_CONF_UNSET_PTR;
     sscf->builtin_session_cache = NGX_CONF_UNSET;
@@ -664,6 +674,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
     ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,
                          NULL);
 
+    ngx_conf_merge_ptr_value(conf->certificate_cache, prev->certificate_cache,
+                         NULL);
+
     ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
 
     ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
@@ -984,6 +997,99 @@ found:
 }
 
 
+static char *
+ngx_http_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_ssl_srv_conf_t *sscf = conf;
+
+    time_t       inactive, valid;
+    ngx_str_t   *value, s;
+    ngx_int_t    max;
+    ngx_uint_t   i;
+
+    if (sscf->certificate_cache != NGX_CONF_UNSET_PTR) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    max = 0;
+    inactive = 10;
+    valid = 60;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
+
+            max = ngx_atoi(value[i].data + 4, value[i].len - 4);
+            if (max <= 0) {
+                goto failed;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
+
+            s.len = value[i].len - 9;
+            s.data = value[i].data + 9;
+
+            inactive = ngx_parse_time(&s, 1);
+            if (inactive == (time_t) NGX_ERROR) {
+                goto failed;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
+
+            s.len = value[i].len - 6;
+            s.data = value[i].data + 6;
+
+            valid = ngx_parse_time(&s, 1);
+            if (valid == (time_t) NGX_ERROR) {
+                goto failed;
+            }
+
+            continue;
+        }
+
+        if (ngx_strcmp(value[i].data, "off") == 0) {
+
+            sscf->certificate_cache = NULL;
+
+            continue;
+        }
+
+    failed:
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid parameter \"%V\"", &value[i]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (sscf->certificate_cache == NULL) {
+        return NGX_CONF_OK;
+    }
+
+    if (max == 0) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"ssl_certificate_cache\" must have "
+                           "the \"max\" parameter");
+        return NGX_CONF_ERROR;
+    }
+
+    sscf->certificate_cache = ngx_ssl_cache_init(cf->pool, max, valid,
+                                                 inactive);
+    if (sscf->certificate_cache == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
 static char *
 ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h
index c69c8ffd2..8650fab93 100644
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -38,6 +38,8 @@ typedef struct {
     ngx_array_t                    *certificate_values;
     ngx_array_t                    *certificate_key_values;
 
+    ngx_ssl_cache_t                *certificate_cache;
+
     ngx_str_t                       dhparam;
     ngx_str_t                       ecdh_curve;
     ngx_str_t                       client_certificate;
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index 3cca57cf5..f44c9e79b 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1054,6 +1054,7 @@ ngx_http_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg)
                        "ssl key: \"%s\"", key.data);
 
         if (ngx_ssl_connection_certificate(c, r->pool, &cert, &key,
+                                           sscf->certificate_cache,
                                            sscf->passwords)
             != NGX_OK)
         {
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index d95662c56..e6382ef79 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -2018,7 +2018,7 @@ ngx_http_upstream_ssl_certificate(ngx_http_request_t *r,
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http upstream ssl key: \"%s\"", key.data);
 
-    if (ngx_ssl_connection_certificate(c, r->pool, &cert, &key,
+    if (ngx_ssl_connection_certificate(c, r->pool, &cert, &key, NULL,
                                        u->conf->ssl_passwords)
         != NGX_OK)
     {
diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c
index e978056ef..21b579af3 100644
--- a/src/stream/ngx_stream_proxy_module.c
+++ b/src/stream/ngx_stream_proxy_module.c
@@ -1324,7 +1324,7 @@ ngx_stream_proxy_ssl_certificate(ngx_stream_session_t *s)
     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
                    "stream upstream ssl key: \"%s\"", key.data);
 
-    if (ngx_ssl_connection_certificate(c, c->pool, &cert, &key,
+    if (ngx_ssl_connection_certificate(c, c->pool, &cert, &key, NULL,
                                        pscf->ssl_passwords)
         != NGX_OK)
     {
diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
index dfbaa0e2f..b84995d61 100644
--- a/src/stream/ngx_stream_ssl_module.c
+++ b/src/stream/ngx_stream_ssl_module.c
@@ -47,6 +47,8 @@ static char *ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent,
 static ngx_int_t ngx_stream_ssl_compile_certificates(ngx_conf_t *cf,
     ngx_stream_ssl_srv_conf_t *conf);
 
+static char *ngx_stream_ssl_certificate_cache(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf);
 static char *ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 static char *ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
@@ -117,6 +119,13 @@ static ngx_command_t  ngx_stream_ssl_commands[] = {
       offsetof(ngx_stream_ssl_srv_conf_t, certificate_keys),
       NULL },
 
+    { ngx_string("ssl_certificate_cache"),
+      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE123,
+      ngx_stream_ssl_certificate_cache,
+      NGX_STREAM_SRV_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("ssl_password_file"),
       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
       ngx_stream_ssl_password_file,
@@ -718,6 +727,7 @@ ngx_stream_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg)
                        "ssl key: \"%s\"", key.data);
 
         if (ngx_ssl_connection_certificate(c, c->pool, &cert, &key,
+                                           sscf->certificate_cache,
                                            sscf->passwords)
             != NGX_OK)
         {
@@ -844,6 +854,7 @@ ngx_stream_ssl_create_srv_conf(ngx_conf_t *cf)
     sscf->handshake_timeout = NGX_CONF_UNSET_MSEC;
     sscf->certificates = NGX_CONF_UNSET_PTR;
     sscf->certificate_keys = NGX_CONF_UNSET_PTR;
+    sscf->certificate_cache = NGX_CONF_UNSET_PTR;
     sscf->passwords = NGX_CONF_UNSET_PTR;
     sscf->conf_commands = NGX_CONF_UNSET_PTR;
     sscf->prefer_server_ciphers = NGX_CONF_UNSET;
@@ -892,6 +903,9 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
     ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,
                          NULL);
 
+    ngx_conf_merge_ptr_value(conf->certificate_cache, prev->certificate_cache,
+                         NULL);
+
     ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
 
     ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
@@ -1202,6 +1216,99 @@ found:
 }
 
 
+static char *
+ngx_stream_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_stream_ssl_srv_conf_t *sscf = conf;
+
+    time_t       inactive, valid;
+    ngx_str_t   *value, s;
+    ngx_int_t    max;
+    ngx_uint_t   i;
+
+    if (sscf->certificate_cache != NGX_CONF_UNSET_PTR) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    max = 0;
+    inactive = 10;
+    valid = 60;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
+
+            max = ngx_atoi(value[i].data + 4, value[i].len - 4);
+            if (max <= 0) {
+                goto failed;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
+
+            s.len = value[i].len - 9;
+            s.data = value[i].data + 9;
+
+            inactive = ngx_parse_time(&s, 1);
+            if (inactive == (time_t) NGX_ERROR) {
+                goto failed;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
+
+            s.len = value[i].len - 6;
+            s.data = value[i].data + 6;
+
+            valid = ngx_parse_time(&s, 1);
+            if (valid == (time_t) NGX_ERROR) {
+                goto failed;
+            }
+
+            continue;
+        }
+
+        if (ngx_strcmp(value[i].data, "off") == 0) {
+
+            sscf->certificate_cache = NULL;
+
+            continue;
+        }
+
+    failed:
+
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid parameter \"%V\"", &value[i]);
+        return NGX_CONF_ERROR;
+    }
+
+    if (sscf->certificate_cache == NULL) {
+        return NGX_CONF_OK;
+    }
+
+    if (max == 0) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"ssl_certificate_cache\" must have "
+                           "the \"max\" parameter");
+        return NGX_CONF_ERROR;
+    }
+
+    sscf->certificate_cache = ngx_ssl_cache_init(cf->pool, max, valid,
+                                                 inactive);
+    if (sscf->certificate_cache == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
 static char *
 ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
diff --git a/src/stream/ngx_stream_ssl_module.h b/src/stream/ngx_stream_ssl_module.h
index e6769426c..ffa03a6f3 100644
--- a/src/stream/ngx_stream_ssl_module.h
+++ b/src/stream/ngx_stream_ssl_module.h
@@ -15,53 +15,55 @@
 
 
 typedef struct {
-    ngx_msec_t       handshake_timeout;
+    ngx_msec_t        handshake_timeout;
 
-    ngx_flag_t       prefer_server_ciphers;
-    ngx_flag_t       reject_handshake;
+    ngx_flag_t        prefer_server_ciphers;
+    ngx_flag_t        reject_handshake;
 
-    ngx_ssl_t        ssl;
+    ngx_ssl_t         ssl;
 
-    ngx_uint_t       protocols;
+    ngx_uint_t        protocols;
 
-    ngx_uint_t       verify;
-    ngx_uint_t       verify_depth;
+    ngx_uint_t        verify;
+    ngx_uint_t        verify_depth;
 
-    ssize_t          builtin_session_cache;
+    ssize_t           builtin_session_cache;
 
-    time_t           session_timeout;
+    time_t            session_timeout;
 
-    ngx_array_t     *certificates;
-    ngx_array_t     *certificate_keys;
+    ngx_array_t      *certificates;
+    ngx_array_t      *certificate_keys;
 
-    ngx_array_t     *certificate_values;
-    ngx_array_t     *certificate_key_values;
+    ngx_array_t      *certificate_values;
+    ngx_array_t      *certificate_key_values;
 
-    ngx_str_t        dhparam;
-    ngx_str_t        ecdh_curve;
-    ngx_str_t        client_certificate;
-    ngx_str_t        trusted_certificate;
-    ngx_str_t        crl;
-    ngx_str_t        alpn;
+    ngx_ssl_cache_t  *certificate_cache;
 
-    ngx_str_t        ciphers;
+    ngx_str_t         dhparam;
+    ngx_str_t         ecdh_curve;
+    ngx_str_t         client_certificate;
+    ngx_str_t         trusted_certificate;
+    ngx_str_t         crl;
+    ngx_str_t         alpn;
 
-    ngx_array_t     *passwords;
-    ngx_array_t     *conf_commands;
+    ngx_str_t         ciphers;
 
-    ngx_shm_zone_t  *shm_zone;
+    ngx_array_t      *passwords;
+    ngx_array_t      *conf_commands;
 
-    ngx_flag_t       session_tickets;
-    ngx_array_t     *session_ticket_keys;
+    ngx_shm_zone_t   *shm_zone;
 
-    ngx_uint_t       ocsp;
-    ngx_str_t        ocsp_responder;
-    ngx_shm_zone_t  *ocsp_cache_zone;
+    ngx_flag_t        session_tickets;
+    ngx_array_t      *session_ticket_keys;
 
-    ngx_flag_t       stapling;
-    ngx_flag_t       stapling_verify;
-    ngx_str_t        stapling_file;
-    ngx_str_t        stapling_responder;
+    ngx_uint_t        ocsp;
+    ngx_str_t         ocsp_responder;
+    ngx_shm_zone_t   *ocsp_cache_zone;
+
+    ngx_flag_t        stapling;
+    ngx_flag_t        stapling_verify;
+    ngx_str_t         stapling_file;
+    ngx_str_t         stapling_responder;
 } ngx_stream_ssl_srv_conf_t;
 
 


More information about the nginx-devel mailing list