]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: ssl: Add options to forge SSL certificates
authorChristopher Faulet <cfaulet@qualys.com>
Tue, 9 Jun 2015 15:29:50 +0000 (17:29 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 12 Jun 2015 16:06:59 +0000 (18:06 +0200)
With this patch, it is possible to configure HAProxy to forge the SSL
certificate sent to a client using the SNI servername. We do it in the SNI
callback.

To enable this feature, you must pass following BIND options:

 * ca-sign-file <FILE> : This is the PEM file containing the CA certitifacte and
   the CA private key to create and sign server's certificates.

 * (optionally) ca-sign-pass <PASS>: This is the CA private key passphrase, if
   any.

 * generate-certificates: Enable the dynamic generation of certificates for a
   listener.

Because generating certificates is expensive, there is a LRU cache to store
them. Its size can be customized by setting the global parameter
'tune.ssl.ssl-ctx-cache-size'.

doc/configuration.txt
include/common/defaults.h
include/proto/ssl_sock.h
include/types/global.h
include/types/listener.h
src/cfgparse.c
src/haproxy.c
src/ssl_sock.c

index 1e018451dda9920633d56a6c4c3c274e883ffef6..897e28500acd23cb4d6a788ee166fa9c40953212 100644 (file)
@@ -581,6 +581,7 @@ The following keywords are supported in the "global" section :
    - tune.ssl.force-private-cache
    - tune.ssl.maxrecord
    - tune.ssl.default-dh-param
+   - tune.ssl.ssl-ctx-cache-size
    - tune.zlib.memlevel
    - tune.zlib.windowsize
 
@@ -1278,6 +1279,12 @@ tune.ssl.default-dh-param <number>
   used if static Diffie-Hellman parameters are supplied either directly
   in the certificate file or by using the ssl-dh-param-file parameter.
 
+tune.ssl.ssl-ctx-cache-size <number>
+  Sets the size of the cache used to store generated certificates to <number>
+  entries. This is a LRU cache. Because generating a SSL certificate
+  dynamically is expensive, they are cached. The default cache size is set to
+  1000 entries.
+
 tune.zlib.memlevel <number>
   Sets the memLevel parameter in zlib initialization for each session. It
   defines how much memory should be allocated for the internal compression
@@ -9039,6 +9046,19 @@ ca-ignore-err [all|<errorID>,...]
   If set to 'all', all errors are ignored. SSL handshake is not aborted if an
   error is ignored.
 
+ca-sign-file <cafile>
+  This setting is only available when support for OpenSSL was built in. It
+  designates a PEM file containing both the CA certificate and the CA private
+  key used to create and sign server's certificates. This is a mandatory
+  setting when the dynamic generation of certificates is enabled. See
+  'generate-certificates' for details.
+
+ca-sign-passphrase <passphrase>
+  This setting is only available when support for OpenSSL was built in. It is
+  the CA private key passphrase. This setting is optional and used only when
+  the dynamic generation of certificates is enabled. See
+  'generate-certificates' for details.
+
 ciphers <ciphers>
   This setting is only available when support for OpenSSL was built in. It sets
   the string describing the list of cipher algorithms ("cipher suite") that are
@@ -9164,6 +9184,24 @@ force-tlsv12
   this listener. This option is also available on global statement
   "ssl-default-bind-options". See also "no-tlsv*", and "no-sslv3".
 
+generate-certificates
+  This setting is only available when support for OpenSSL was built in. It
+  enables the dynamic SSL certificates generation. A CA certificate and its
+  private key are necessary (see 'ca-sign-file'). When HAProxy is configured as
+  a transparent forward proxy, SSL requests generate errors because of a common
+  name mismatch on the certificate presented to the client. With this option
+  enabled, HAProxy will try to forge a certificate using the SNI hostname
+  indicated by the client. This is done only if no certificate matches the SNI
+  hostname (see 'crt-list'). If an error occurs, the default certificate is
+  used, else the 'strict-sni' option is set.
+  It can also be used when HAProxy is configured as a reverse proxy to ease the
+  deployment of an architecture with many backends.
+
+  Creating a SSL certificate is an expensive operation, so a LRU cache is used
+  to store forged certificates (see 'tune.ssl.ssl-ctx-cache-size'). It
+  increases the HAProxy's memroy footprint to reduce latency when the same
+  certificate is used many times.
+
 gid <gid>
   Sets the group of the UNIX sockets to the designated system gid. It can also
   be set by default in the global section's "unix-bind" statement. Note that
index 6193bdc73b2f4379315849720fea2037781a25ed..02962010e70b01b464aaca63fd850ece31201e9a 100644 (file)
 #define SSL_HANDSHAKE_MAX_COST (76*1024)  // measured
 #endif
 
+#ifndef DEFAULT_SSL_CTX_CACHE
+#define DEFAULT_SSL_CTX_CACHE 1000
+#endif
+
 /* approximate stream size (for maxconn estimate) */
 #ifndef STREAM_MAX_COST
 #define STREAM_MAX_COST (sizeof(struct stream) + \
index 4db516e5ff54d0ed7afa35000a2b848337020914..7a9e9882339133d1a96c0773cbfdbc490a6ecef9 100644 (file)
@@ -44,10 +44,11 @@ int ssl_sock_is_ssl(struct connection *conn)
 
 int ssl_sock_handshake(struct connection *conn, unsigned int flag);
 int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx, struct proxy *proxy);
-void ssl_sock_free_certs(struct bind_conf *bind_conf);
 int ssl_sock_prepare_all_ctx(struct bind_conf *bind_conf, struct proxy *px);
 int ssl_sock_prepare_srv_ctx(struct server *srv, struct proxy *px);
 void ssl_sock_free_all_ctx(struct bind_conf *bind_conf);
+int ssl_sock_load_ca(struct bind_conf *bind_conf, struct proxy *px);
+void ssl_sock_free_ca(struct bind_conf *bind_conf);
 const char *ssl_sock_get_cipher_name(struct connection *conn);
 const char *ssl_sock_get_proto_version(struct connection *conn);
 char *ssl_sock_get_version(struct connection *conn);
index b3b96720c55fd73081cacea0c07ec0e627c198d9..2996dda5c1228ac8244dbeef08cc53c1890e7073 100644 (file)
@@ -154,6 +154,7 @@ struct global {
                unsigned int ssllifetime;   /* SSL session lifetime in seconds */
                unsigned int ssl_max_record; /* SSL max record size */
                unsigned int ssl_default_dh_param; /* SSL maximum DH parameter size */
+               int ssl_ctx_cache; /* max number of entries in the ssl_ctx cache. */
 #endif
 #ifdef USE_ZLIB
                int zlibmemlevel;    /* zlib memlevel */
index 895cd00e0991adc66b7c437e646f185527bb0a7e..4da6cacb471c08970b4884e41b5eaebaf4ab0e35 100644 (file)
@@ -133,8 +133,15 @@ struct bind_conf {
        struct eb_root sni_ctx;    /* sni_ctx tree of all known certs full-names sorted by name */
        struct eb_root sni_w_ctx;  /* sni_ctx tree of all known certs wildcards sorted by name */
        struct tls_keys_ref *keys_ref; /* TLS ticket keys reference */
+
+       char *ca_sign_file;        /* CAFile used to generate and sign server certificates */
+       char *ca_sign_pass;        /* CAKey passphrase */
+
+       X509     *ca_sign_cert;    /* CA certificate referenced by ca_file */
+       EVP_PKEY *ca_sign_pkey;    /* CA private key referenced by ca_key */
 #endif
        int is_ssl;                /* SSL is required for these listeners */
+       int generate_certs;        /* 1 if generate-certificates option is set, else 0 */
        unsigned long bind_proc;   /* bitmask of processes allowed to use these listeners */
        struct {                   /* UNIX socket permissions */
                uid_t uid;         /* -1 to leave unchanged */
index 30d51c7823eb25800fa49fbb25d5515f920c76f4..3bfacade834a082c96269584338b0f6642442098 100644 (file)
@@ -770,6 +770,22 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
                }
        }
 #endif
+       else if (!strcmp(args[0], "tune.ssl.ssl-ctx-cache-size")) {
+               if (alertif_too_many_args(1, file, linenum, args, &err_code))
+                       goto out;
+               if (*(args[1]) == 0) {
+                       Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
+                       err_code |= ERR_ALERT | ERR_FATAL;
+                       goto out;
+               }
+               global.tune.ssl_ctx_cache = atoi(args[1]);
+               if (global.tune.ssl_ctx_cache < 0) {
+                       Alert("parsing [%s:%d] : '%s' expects a positive numeric value\n",
+                             file, linenum, args[0]);
+                       err_code |= ERR_ALERT | ERR_FATAL;
+                       goto out;
+               }
+       }
 #endif
        else if (!strcmp(args[0], "tune.buffers.limit")) {
                if (alertif_too_many_args(1, file, linenum, args, &err_code))
@@ -8219,6 +8235,9 @@ out_uri_auth_compat:
 
                        /* initialize all certificate contexts */
                        cfgerr += ssl_sock_prepare_all_ctx(bind_conf, curproxy);
+
+                       /* initialize CA variables if the certificates generation is enabled */
+                       cfgerr += ssl_sock_load_ca(bind_conf, curproxy);
                }
 #endif /* USE_OPENSSL */
 
@@ -8287,8 +8306,11 @@ out_uri_auth_compat:
                        if (bind_conf->is_ssl)
                                continue;
 #ifdef USE_OPENSSL
+                       ssl_sock_free_ca(bind_conf);
                        ssl_sock_free_all_ctx(bind_conf);
                        free(bind_conf->ca_file);
+                       free(bind_conf->ca_sign_file);
+                       free(bind_conf->ca_sign_pass);
                        free(bind_conf->ciphers);
                        free(bind_conf->ecdhe);
                        free(bind_conf->crl_file);
index c73d2e0d66bcc0ae42089c2cc36d44b14c9f9269..a17a65dbcabf4141ca366e5cb94277674b9f5290 100644 (file)
@@ -161,6 +161,7 @@ struct global global = {
 #ifdef DEFAULT_SSL_MAX_RECORD
                .ssl_max_record = DEFAULT_SSL_MAX_RECORD,
 #endif
+               .ssl_ctx_cache = DEFAULT_SSL_CTX_CACHE,
 #endif
 #ifdef USE_ZLIB
                .zlibmemlevel = 8,
@@ -1456,8 +1457,11 @@ void deinit(void)
                /* Release unused SSL configs. */
                list_for_each_entry_safe(bind_conf, bind_back, &p->conf.bind, by_fe) {
 #ifdef USE_OPENSSL
+                       ssl_sock_free_ca(bind_conf);
                        ssl_sock_free_all_ctx(bind_conf);
                        free(bind_conf->ca_file);
+                       free(bind_conf->ca_sign_file);
+                       free(bind_conf->ca_sign_pass);
                        free(bind_conf->ciphers);
                        free(bind_conf->ecdhe);
                        free(bind_conf->crl_file);
index 3bd6fa25491c62d50af7189fee848d754ba3bb84..75876a26d9875c7a8076c01de1882cb6bfd0c27d 100644 (file)
@@ -35,7 +35,7 @@
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-
+#include <netdb.h>
 #include <netinet/tcp.h>
 
 #include <openssl/ssl.h>
@@ -51,6 +51,9 @@
 #include <openssl/dh.h>
 #endif
 
+#include <import/lru.h>
+#include <import/xxhash.h>
+
 #include <common/buffer.h>
 #include <common/compat.h>
 #include <common/config.h>
@@ -75,6 +78,7 @@
 #include <proto/frontend.h>
 #include <proto/listener.h>
 #include <proto/pattern.h>
+#include <proto/proto_tcp.h>
 #include <proto/server.h>
 #include <proto/log.h>
 #include <proto/proxy.h>
@@ -138,6 +142,27 @@ struct certificate_ocsp {
        long expire;
 };
 
+/* X509V3 Extensions that will be added on generated certificates */
+#define X509V3_EXT_SIZE 5
+static char *x509v3_ext_names[X509V3_EXT_SIZE] = {
+       "basicConstraints",
+       "nsComment",
+       "subjectKeyIdentifier",
+       "authorityKeyIdentifier",
+       "keyUsage",
+};
+static char *x509v3_ext_values[X509V3_EXT_SIZE] = {
+       "CA:FALSE",
+       "\"OpenSSL Generated Certificate\"",
+       "hash",
+       "keyid,issuer:always",
+       "nonRepudiation,digitalSignature,keyEncipherment"
+};
+
+/* LRU cache to store generated certificate */
+static struct lru64_head *ssl_ctx_lru_tree = NULL;
+static unsigned int       ssl_ctx_lru_seed = 0;
+
 /*
  *  This function returns the number of seconds  elapsed
  *  since the Epoch, 1970-01-01 00:00:00 +0000 (UTC) and the
@@ -978,6 +1003,134 @@ static int ssl_sock_advertise_alpn_protos(SSL *s, const unsigned char **out,
 }
 #endif
 
+static SSL_CTX *
+ssl_sock_create_cert(const char *servername, unsigned int serial, X509 *cacert, EVP_PKEY *capkey)
+{
+       SSL_CTX      *ssl_ctx = NULL;
+       X509         *newcrt  = NULL;
+       EVP_PKEY     *pkey    = NULL;
+       RSA          *rsa;
+       X509_NAME    *name;
+       const EVP_MD *digest;
+       X509V3_CTX    ctx;
+       unsigned int  i;
+
+       /* Generate the public key */
+       if (!(rsa = RSA_generate_key(2048, 3, NULL, NULL)))
+               goto mkcert_error;
+       if (!(pkey = EVP_PKEY_new()))
+               goto mkcert_error;
+       if (EVP_PKEY_assign_RSA(pkey, rsa) != 1)
+               goto mkcert_error;
+
+       /* Create the certificate */
+       if (!(newcrt = X509_new()))
+               goto mkcert_error;
+
+       /* Set version number for the certificate (X509v3) and the serial
+        * number */
+       if (X509_set_version(newcrt, 2L) != 1)
+               goto mkcert_error;
+       ASN1_INTEGER_set(X509_get_serialNumber(newcrt), serial);
+
+       /* Set duration for the certificate */
+       if (!X509_gmtime_adj(X509_get_notBefore(newcrt), (long)-60*60*24) ||
+           !X509_gmtime_adj(X509_get_notAfter(newcrt),(long)60*60*24*365))
+               goto mkcert_error;
+
+       /* set public key in the certificate */
+       if (X509_set_pubkey(newcrt, pkey) != 1)
+               goto mkcert_error;
+
+       /* Set issuer name from the CA */
+       if (!(name = X509_get_subject_name(cacert)))
+               goto mkcert_error;
+       if (X509_set_issuer_name(newcrt, name) != 1)
+               goto mkcert_error;
+
+       /* Set the subject name using the same, but the CN */
+       name = X509_NAME_dup(name);
+       if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+                                      (const unsigned char *)servername,
+                                      -1, -1, 0) != 1) {
+               X509_NAME_free(name);
+               goto mkcert_error;
+       }
+       if (X509_set_subject_name(newcrt, name) != 1) {
+               X509_NAME_free(name);
+               goto mkcert_error;
+       }
+       X509_NAME_free(name);
+
+       /* Add x509v3 extensions as specified */
+       X509V3_set_ctx(&ctx, cacert, newcrt, NULL, NULL, 0);
+       for (i = 0; i < X509V3_EXT_SIZE; i++) {
+               X509_EXTENSION *ext;
+
+               if (!(ext = X509V3_EXT_conf(NULL, &ctx, x509v3_ext_names[i], x509v3_ext_values[i])))
+                       goto mkcert_error;
+               if (!X509_add_ext(newcrt, ext, -1)) {
+                       X509_EXTENSION_free(ext);
+                       goto mkcert_error;
+               }
+               X509_EXTENSION_free(ext);
+       }
+
+       /* Sign the certificate with the CA private key */
+       if (EVP_PKEY_type(capkey->type) == EVP_PKEY_DSA)
+               digest = EVP_dss1();
+       else if (EVP_PKEY_type (capkey->type) == EVP_PKEY_RSA)
+               digest = EVP_sha256();
+       else
+               goto mkcert_error;
+       if (!(X509_sign(newcrt, capkey, digest)))
+               goto mkcert_error;
+
+       /* Create and set the new SSL_CTX */
+       if (!(ssl_ctx = SSL_CTX_new(SSLv23_server_method())))
+               goto mkcert_error;
+       if (!SSL_CTX_use_PrivateKey(ssl_ctx, pkey))
+               goto mkcert_error;
+       if (!SSL_CTX_use_certificate(ssl_ctx, newcrt))
+               goto mkcert_error;
+       if (!SSL_CTX_check_private_key(ssl_ctx))
+               goto mkcert_error;
+
+       if (newcrt) X509_free(newcrt);
+       if (pkey)   EVP_PKEY_free(pkey);
+       return ssl_ctx;
+
+ mkcert_error:
+       if (ssl_ctx) SSL_CTX_free(ssl_ctx);
+       if (newcrt)  X509_free(newcrt);
+       if (pkey)    EVP_PKEY_free(pkey);
+       return NULL;
+}
+
+static SSL_CTX *
+ssl_sock_generate_certificate(const char *servername, struct bind_conf *bind_conf)
+{
+       X509         *cacert  = bind_conf->ca_sign_cert;
+       EVP_PKEY     *capkey  = bind_conf->ca_sign_pkey;
+       SSL_CTX      *ssl_ctx = NULL;
+       struct lru64 *lru     = NULL;
+       unsigned int  serial;
+
+       serial = XXH32(servername, strlen(servername), ssl_ctx_lru_seed);
+       if (ssl_ctx_lru_tree) {
+               lru = lru64_get(serial, ssl_ctx_lru_tree, cacert, 0);
+               if (lru && lru->domain)
+                       ssl_ctx = (SSL_CTX *)lru->data;
+       }
+
+       if (!ssl_ctx) {
+               ssl_ctx = ssl_sock_create_cert(servername, serial, cacert, capkey);
+               if (lru)
+                       lru64_commit(lru, ssl_ctx, cacert, 0, (void (*)(void *))SSL_CTX_free);
+       }
+       return ssl_ctx;
+}
+
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
 /* Sets the SSL ctx of <ssl> to match the advertised server name. Returns a
  * warning when no match is found, which implies the default (first) cert
@@ -1022,6 +1175,14 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al, struct bind_conf *s)
                node = ebst_lookup(&s->sni_w_ctx, wildp);
        }
        if (!node || container_of(node, struct sni_ctx, name)->neg) {
+               SSL_CTX *ctx;
+
+               if (s->generate_certs &&
+                   (ctx = ssl_sock_generate_certificate(servername, s))) {
+                       /* switch ctx */
+                       SSL_set_SSL_CTX(ssl, ctx);
+                       return SSL_TLSEXT_ERR_OK;
+               }
                return (s->strict_sni ?
                        SSL_TLSEXT_ERR_ALERT_FATAL :
                        SSL_TLSEXT_ERR_ALERT_WARNING);
@@ -2245,6 +2406,75 @@ void ssl_sock_free_all_ctx(struct bind_conf *bind_conf)
        bind_conf->default_ctx = NULL;
 }
 
+/* Load CA cert file and private key used to generate certificates */
+int
+ssl_sock_load_ca(struct bind_conf *bind_conf, struct proxy *px)
+{
+       FILE     *fp;
+       X509     *cacert = NULL;
+       EVP_PKEY *capkey = NULL;
+       int       err    = 0;
+
+       if (!bind_conf || !bind_conf->generate_certs)
+               return err;
+
+       if (!bind_conf->ca_sign_file) {
+               Alert("Proxy '%s': cannot enable certificate generation, "
+                     "no CA certificate File configured at [%s:%d].\n",
+                     px->id, bind_conf->file, bind_conf->line);
+               err++;
+       }
+
+       if (err)
+               goto load_error;
+
+       /* read in the CA certificate */
+       if (!(fp = fopen(bind_conf->ca_sign_file, "r"))) {
+               Alert("Proxy '%s': Failed to read CA certificate file '%s' at [%s:%d].\n",
+                     px->id, bind_conf->ca_sign_file, bind_conf->file, bind_conf->line);
+               err++;
+               goto load_error;
+       }
+       if (!(cacert = PEM_read_X509(fp, NULL, NULL, NULL))) {
+               Alert("Proxy '%s': Failed to read CA certificate file '%s' at [%s:%d].\n",
+                     px->id, bind_conf->ca_sign_file, bind_conf->file, bind_conf->line);
+               fclose (fp);
+               err++;
+               goto load_error;
+       }
+       if (!(capkey = PEM_read_PrivateKey(fp, NULL, NULL, bind_conf->ca_sign_pass))) {
+               Alert("Proxy '%s': Failed to read CA private key file '%s' at [%s:%d].\n",
+                     px->id, bind_conf->ca_sign_file, bind_conf->file, bind_conf->line);
+               fclose (fp);
+               err++;
+               goto load_error;
+       }
+       fclose (fp);
+
+       bind_conf->ca_sign_cert = cacert;
+       bind_conf->ca_sign_pkey = capkey;
+       return err;
+
+ load_error:
+       bind_conf->generate_certs = 0;
+       if (capkey) EVP_PKEY_free(capkey);
+       if (cacert) X509_free(cacert);
+       return err;
+}
+
+/* Release CA cert and private key used to generate certificated */
+void
+ssl_sock_free_ca(struct bind_conf *bind_conf)
+{
+       if (!bind_conf)
+               return;
+
+       if (bind_conf->ca_sign_pkey)
+               EVP_PKEY_free(bind_conf->ca_sign_pkey);
+       if (bind_conf->ca_sign_cert)
+               X509_free(bind_conf->ca_sign_cert);
+}
+
 /*
  * This function is called if SSL * context is not yet allocated. The function
  * is designed to be called before any other data-layer operation and sets the
@@ -3994,6 +4224,36 @@ static int bind_parse_ca_file(char **args, int cur_arg, struct proxy *px, struct
        return 0;
 }
 
+/* parse the "ca-sign-file" bind keyword */
+static int bind_parse_ca_sign_file(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       if (!*args[cur_arg + 1]) {
+               if (err)
+                       memprintf(err, "'%s' : missing CAfile path", args[cur_arg]);
+               return ERR_ALERT | ERR_FATAL;
+       }
+
+       if ((*args[cur_arg + 1] != '/') && global.ca_base)
+               memprintf(&conf->ca_sign_file, "%s/%s", global.ca_base, args[cur_arg + 1]);
+       else
+               memprintf(&conf->ca_sign_file, "%s", args[cur_arg + 1]);
+
+       return 0;
+}
+
+/* parse the ca-sign-pass bind keyword */
+
+static int bind_parse_ca_sign_pass(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       if (!*args[cur_arg + 1]) {
+               if (err)
+                       memprintf(err, "'%s' : missing CAkey password", args[cur_arg]);
+               return ERR_ALERT | ERR_FATAL;
+       }
+       memprintf(&conf->ca_sign_pass, "%s", args[cur_arg + 1]);
+       return 0;
+}
+
 /* parse the "ciphers" bind keyword */
 static int bind_parse_ciphers(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
@@ -4326,6 +4586,18 @@ static int bind_parse_ssl(char **args, int cur_arg, struct proxy *px, struct bin
        return 0;
 }
 
+/* parse the "generate-certificates" bind keyword */
+static int bind_parse_generate_certs(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+       conf->generate_certs = 1;
+#else
+       memprintf(err, "%sthis version of openssl cannot generate SSL certificates.\n",
+                 err && *err ? *err : "");
+#endif
+       return 0;
+}
+
 /* parse the "strict-sni" bind keyword */
 static int bind_parse_strict_sni(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
@@ -4833,6 +5105,8 @@ static struct bind_kw_list bind_kws = { "SSL", { }, {
        { "alpn",                  bind_parse_alpn,            1 }, /* set ALPN supported protocols */
        { "ca-file",               bind_parse_ca_file,         1 }, /* set CAfile to process verify on client cert */
        { "ca-ignore-err",         bind_parse_ignore_err,      1 }, /* set error IDs to ignore on verify depth > 0 */
+       { "ca-sign-file",          bind_parse_ca_sign_file,    1 }, /* set CAFile used to generate and sign server certs */
+       { "ca-sign-pass",          bind_parse_ca_sign_pass,    1 }, /* set CAKey passphrase */
        { "ciphers",               bind_parse_ciphers,         1 }, /* set SSL cipher suite */
        { "crl-file",              bind_parse_crl_file,        1 }, /* set certificat revocation list file use on client cert verify */
        { "crt",                   bind_parse_crt,             1 }, /* load SSL certificates from this location */
@@ -4843,6 +5117,7 @@ static struct bind_kw_list bind_kws = { "SSL", { }, {
        { "force-tlsv10",          bind_parse_force_tlsv10,    0 }, /* force TLSv10 */
        { "force-tlsv11",          bind_parse_force_tlsv11,    0 }, /* force TLSv11 */
        { "force-tlsv12",          bind_parse_force_tlsv12,    0 }, /* force TLSv12 */
+       { "generate-certificates", bind_parse_generate_certs,  0 }, /* enable the server certificates generation */
        { "no-sslv3",              bind_parse_no_sslv3,        0 }, /* disable SSLv3 */
        { "no-tlsv10",             bind_parse_no_tlsv10,       0 }, /* disable TLSv10 */
        { "no-tlsv11",             bind_parse_no_tlsv11,       0 }, /* disable TLSv11 */
@@ -4953,11 +5228,18 @@ static void __ssl_sock_init(void)
 #ifndef OPENSSL_NO_DH
        ssl_dh_ptr_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
 #endif
+
+       /* Add a global parameter for the LRU cache size */
+       if (global.tune.ssl_ctx_cache)
+               ssl_ctx_lru_tree = lru64_new(global.tune.ssl_ctx_cache);
+       ssl_ctx_lru_seed = (unsigned int)time(NULL);
 }
 
 __attribute__((destructor))
 static void __ssl_sock_deinit(void)
 {
+       lru64_destroy(ssl_ctx_lru_tree);
+
 #ifndef OPENSSL_NO_DH
         if (local_dh_1024) {
                 DH_free(local_dh_1024);