]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MAJOR: ssl: bind configuration per certificat
authorEmmanuel Hocdet <manu@gandi.net>
Thu, 29 Dec 2016 17:26:15 +0000 (18:26 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 13 Jan 2017 10:40:34 +0000 (11:40 +0100)
crt-list is extend to support ssl configuration. You can now have
such line in crt-list <file>:
mycert.pem [npn h2,http/1.1]

Support include "npn", "alpn", "verify", "ca_file", "crl_file",
"ecdhe", "ciphers" configuration and ssl options.

"crt-base" is also supported to fetch certificates.

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

index 739fad9d65fe2d2c386ae7d80cc58787b88b9555..abb36df8096c6f1f669234b551003985919ca88c 100644 (file)
@@ -10248,10 +10248,14 @@ crt-ignore-err <errors>
 
 crt-list <file>
   This setting is only available when support for OpenSSL was built in. It
-  designates a list of PEM file with an optional list of SNI filter per
-  certificate, with the following format for each line :
+  designates a list of PEM file with an optional ssl configuration and a SNI
+  filter per certificate, with the following format for each line :
 
-        <crtfile> [[!]<snifilter> ...]
+        <crtfile> [\[<sslbindconf> ...\]] [[!]<snifilter> ...]
+
+  sslbindconf support "npn", "alpn", "verify", "ca_file", "crl_file", "ecdhe",
+  "ciphers" configuration and ssl options (see "ssl-default-bind-options").
+  It override the configuration set in bind line for the certificate.
 
   Wildcards are supported in the SNI filter. Negative filter are also supported,
   only useful in combination with a wildcard filter to exclude a particular SNI.
@@ -10266,6 +10270,12 @@ crt-list <file>
   the base name is given in the crt-list. SNI filter will do the same work on
   all bundled certificates.
 
+  crt-list file example:
+        cert1.pem
+        cert2.pem [npn h2,http/1.1]
+        certW.pem                   *.domain.tld !secure.domain.tld
+        certS.pem [ecdhe secp521r1 ciphers ECDHE-ECDSA-AES256-GCM-SHA384] secure.domain.tld
+
 defer-accept
   Is an optional keyword which is supported only on certain Linux kernels. It
   states that a connection will only be accepted once some data arrive on it,
index 3e04f022c8ef40935ead80cc6f39483d6ed25dda..1618ab40f74457cd3c4c5dc6687ac0d4b7c0422c 100644 (file)
 // max # args on a configuration line
 #define MAX_LINE_ARGS   64
 
-// crt-list parsing factor for LINESIZE and MAX_LINE_ARGS
-#define CRTLIST_FACTOR  32
+// maximum line size when parsing crt-bind-list config
+#define CRT_LINESIZE    65536
+
+// max # args on crt-bind-list configuration line
+#define MAX_CRT_ARGS  2048
 
 // max # args on a stats socket
 // This should cover at least 5 + twice the # of data_types
index 9f43adc3aadcd347d3d36a85683a6d9d237b6a81..6f779faaed6df305d68e2b5ed42cecd65c684df3 100644 (file)
@@ -42,7 +42,7 @@ 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);
+int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_conf *, SSL_CTX *ctx);
 int ssl_sock_prepare_all_ctx(struct bind_conf *bind_conf);
 int ssl_sock_prepare_bind_conf(struct bind_conf *bind_conf);
 int ssl_sock_prepare_srv_ctx(struct server *srv);
index 03f4a72b5ee31811922a24aefb5b1d5959d1cd03..b534c477ceb34d71cb8df0086ce14db50408acef 100644 (file)
@@ -115,22 +115,34 @@ enum li_state {
 #define BC_SSL_O_NO_TLS_TICKETS 0x0100 /* disable session resumption tickets */
 #endif
 
-/* "bind" line settings */
-struct bind_conf {
+/* ssl "bind" settings */
+struct ssl_bind_conf {
 #ifdef USE_OPENSSL
+#ifdef OPENSSL_NPN_NEGOTIATED
+       char *npn_str;             /* NPN protocol string */
+       int npn_len;               /* NPN protocol string length */
+#endif
+#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
+       char *alpn_str;            /* ALPN protocol string */
+       int alpn_len;              /* ALPN protocol string length */
+#endif
+       int verify;                /* verify method (set of SSL_VERIFY_* flags) */
        char *ca_file;             /* CAfile to use on verify */
-       unsigned long long ca_ignerr;  /* ignored verify errors in handshake if depth > 0 */
-       unsigned long long crt_ignerr; /* ignored verify errors in handshake if depth == 0 */
-       char *ciphers;             /* cipher suite to use if non-null */
        char *crl_file;            /* CRLfile to use on verify */
+       char *ciphers;             /* cipher suite to use if non-null */
        char *ecdhe;               /* named curve to use for ECDHE */
        int ssl_options;           /* ssl options */
-       int verify;                /* verify method (set of SSL_VERIFY_* flags) */
+#endif
+};
+
+/* "bind" line settings */
+struct bind_conf {
+#ifdef USE_OPENSSL
+       struct ssl_bind_conf ssl_conf; /* ssl conf for ctx setting */
+       unsigned long long ca_ignerr;  /* ignored verify errors in handshake if depth > 0 */
+       unsigned long long crt_ignerr; /* ignored verify errors in handshake if depth == 0 */
        SSL_CTX *default_ctx;      /* SSL context of first/default certificate */
-       char *npn_str;             /* NPN protocol string */
-       int npn_len;               /* NPN protocol string length */
-       char *alpn_str;            /* ALPN protocol string */
-       int alpn_len;              /* ALPN protocol string length */
+       struct ssl_bind_conf *default_ssl_conf; /* custom SSL conf of default_ctx */
        int strict_sni;            /* refuse negotiation if sni doesn't match a certificate */
        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 */
@@ -213,6 +225,11 @@ struct bind_kw {
        int (*parse)(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err);
        int skip; /* nb of args to skip */
 };
+struct ssl_bind_kw {
+       const char *kw;
+       int (*parse)(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err);
+       int skip; /* nb of args to skip */
+};
 
 /*
  * A keyword list. It is a NULL-terminated array of keywords. It embeds a
index e71ba793c4fe0e656471c8f01be3009a0cd72bbd..a384f055d48e38b95e692fac70025fe1b8511cee 100644 (file)
@@ -22,6 +22,7 @@
 #ifndef _TYPES_SSL_SOCK_H
 #define _TYPES_SSL_SOCK_H
 
+#include <types/listener.h>
 #include <openssl/ssl.h>
 #include <ebmbtree.h>
 
@@ -29,6 +30,7 @@ struct sni_ctx {
        SSL_CTX *ctx;             /* context associated to the certificate */
        int order;                /* load order for the certificate */
        int neg;                  /* reject if match */
+       struct ssl_bind_conf *conf; /* ssl "bind" conf for the certificate */
        struct ebmb_node name;    /* node holding the servername value */
 };
 
index 3705917a8fd6213f5ad7392813eabd1d22a6a035..b94166cdce4ac5422d6ee77d018d64f8e5989368 100644 (file)
@@ -194,6 +194,8 @@ static char *x509v3_ext_values[X509V3_EXT_SIZE] = {
        "nonRepudiation,digitalSignature,keyEncipherment"
 };
 
+static struct ssl_bind_kw ssl_bind_kws[];
+
 /* LRU cache to store generated certificate */
 static struct lru64_head *ssl_ctx_lru_tree = NULL;
 static unsigned int       ssl_ctx_lru_seed = 0;
@@ -1182,7 +1184,7 @@ void ssl_sock_msgcbk(int write_p, int version, int content_type, const void *buf
 static int ssl_sock_advertise_npn_protos(SSL *s, const unsigned char **data,
                                          unsigned int *len, void *arg)
 {
-       struct bind_conf *conf = arg;
+       struct ssl_bind_conf *conf = arg;
 
        *data = (const unsigned char *)conf->npn_str;
        *len = conf->npn_len;
@@ -1199,7 +1201,7 @@ static int ssl_sock_advertise_alpn_protos(SSL *s, const unsigned char **out,
                                           const unsigned char *server,
                                           unsigned int server_len, void *arg)
 {
-       struct bind_conf *conf = arg;
+       struct ssl_bind_conf *conf = arg;
 
        if (SSL_select_next_proto((unsigned char**) out, outlen, (const unsigned char *)conf->alpn_str,
                                  conf->alpn_len, server, server_len) != OPENSSL_NPN_NEGOTIATED) {
@@ -1330,7 +1332,7 @@ ssl_sock_do_create_cert(const char *servername, struct bind_conf *bind_conf, SSL
        SSL_CTX_set_tmp_dh_callback(ssl_ctx, ssl_get_tmp_dh);
 #if defined(SSL_CTX_set_tmp_ecdh) && !defined(OPENSSL_NO_ECDH)
        {
-               const char *ecdhe = (bind_conf->ecdhe ? bind_conf->ecdhe : ECDHE_DEFAULT_CURVE);
+               const char *ecdhe = (bind_conf->ssl_conf.ecdhe ? bind_conf->ssl_conf.ecdhe : ECDHE_DEFAULT_CURVE);
                EC_KEY     *ecc;
                int         nid;
 
@@ -1776,7 +1778,8 @@ end:
 }
 #endif
 
-static int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s, char *name, int order)
+static int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s,
+                                struct ssl_bind_conf *conf, char *name, int order)
 {
        struct sni_ctx *sc;
        int wild = 0, neg = 0;
@@ -1809,7 +1812,7 @@ static int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s, char *name,
                        node = ebst_lookup(&s->sni_ctx, trash.str);
                for (; node; node = ebmb_next_dup(node)) {
                        sc = ebmb_entry(node, struct sni_ctx, name);
-                       if (sc->ctx == ctx && sc->neg == neg)
+                       if (sc->ctx == ctx && sc->conf == conf && sc->neg == neg)
                                return order;
                }
 
@@ -1818,6 +1821,7 @@ static int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s, char *name,
                        return order;
                memcpy(sc->name.key, trash.str, len + 1);
                sc->ctx = ctx;
+               sc->conf = conf;
                sc->order = order++;
                sc->neg = neg;
                if (wild)
@@ -2072,7 +2076,8 @@ static void ssl_sock_populate_sni_keytypes_hplr(const char *str, struct eb_root
  *     0 on success
  *     1 on failure
  */
-static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_conf, char **sni_filter, int fcount, char **err)
+static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
+                                   char **sni_filter, int fcount, char **err)
 {
        char fp[MAXPATHLEN+1] = {0};
        int n = 0;
@@ -2246,7 +2251,7 @@ static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_con
                }
 
                /* Update SNI Tree */
-               key_combos[i-1].order = ssl_sock_add_cert_sni(cur_ctx, bind_conf, str, key_combos[i-1].order);
+               key_combos[i-1].order = ssl_sock_add_cert_sni(cur_ctx, bind_conf, ssl_conf, str, key_combos[i-1].order);
                node = ebmb_next(node);
        }
 
@@ -2256,6 +2261,7 @@ static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_con
                for (i = SSL_SOCK_POSSIBLE_KT_COMBOS - 1; i >= 0; i--) {
                        if (key_combos[i].ctx) {
                                bind_conf->default_ctx = key_combos[i].ctx;
+                               bind_conf->default_ssl_conf = ssl_conf;
                                break;
                        }
                }
@@ -2280,7 +2286,8 @@ end:
 }
 #else
 /* This is a dummy, that just logs an error and returns error */
-static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_conf, char **sni_filter, int fcount, char **err)
+static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
+                                   char **sni_filter, int fcount, char **err)
 {
        memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
                  err && *err ? *err : "", path, strerror(errno));
@@ -2292,7 +2299,8 @@ static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_con
 /* Loads a certificate key and CA chain from a file. Returns 0 on error, -1 if
  * an early error happens and the caller must call SSL_CTX_free() by itelf.
  */
-static int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct bind_conf *s, char **sni_filter, int fcount)
+static int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct bind_conf *s,
+                                        struct ssl_bind_conf *ssl_conf, char **sni_filter, int fcount)
 {
        BIO *in;
        X509 *x = NULL, *ca;
@@ -2325,7 +2333,7 @@ static int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct
 
        if (fcount) {
                while (fcount--)
-                       order = ssl_sock_add_cert_sni(ctx, s, sni_filter[fcount], order);
+                       order = ssl_sock_add_cert_sni(ctx, s, ssl_conf, sni_filter[fcount], order);
        }
        else {
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
@@ -2335,7 +2343,7 @@ static int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct
                                GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
                                if (name->type == GEN_DNS) {
                                        if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
-                                               order = ssl_sock_add_cert_sni(ctx, s, str, order);
+                                               order = ssl_sock_add_cert_sni(ctx, s, ssl_conf, str, order);
                                                OPENSSL_free(str);
                                        }
                                }
@@ -2351,7 +2359,7 @@ static int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct
 
                        value = X509_NAME_ENTRY_get_data(entry);
                        if (ASN1_STRING_to_UTF8((unsigned char **)&str, value) >= 0) {
-                               order = ssl_sock_add_cert_sni(ctx, s, str, order);
+                               order = ssl_sock_add_cert_sni(ctx, s, ssl_conf, str, order);
                                OPENSSL_free(str);
                        }
                }
@@ -2394,7 +2402,8 @@ end:
        return ret;
 }
 
-static int ssl_sock_load_cert_file(const char *path, struct bind_conf *bind_conf, char **sni_filter, int fcount, char **err)
+static int ssl_sock_load_cert_file(const char *path, struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
+                                  char **sni_filter, int fcount, char **err)
 {
        int ret;
        SSL_CTX *ctx;
@@ -2413,7 +2422,7 @@ static int ssl_sock_load_cert_file(const char *path, struct bind_conf *bind_conf
                return 1;
        }
 
-       ret = ssl_sock_load_cert_chain_file(ctx, path, bind_conf, sni_filter, fcount);
+       ret = ssl_sock_load_cert_chain_file(ctx, path, bind_conf, ssl_conf, sni_filter, fcount);
        if (ret <= 0) {
                memprintf(err, "%sunable to load SSL certificate from PEM file '%s'.\n",
                          err && *err ? *err : "", path);
@@ -2476,8 +2485,10 @@ static int ssl_sock_load_cert_file(const char *path, struct bind_conf *bind_conf
                return 1;
        }
 #endif
-       if (!bind_conf->default_ctx)
+       if (!bind_conf->default_ctx) {
                bind_conf->default_ctx = ctx;
+               bind_conf->default_ssl_conf = ssl_conf;
+       }
 
        return 0;
 }
@@ -2499,7 +2510,7 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err)
        if (stat(path, &buf) == 0) {
                dir = opendir(path);
                if (!dir)
-                       return ssl_sock_load_cert_file(path, bind_conf, NULL, 0, err);
+                       return ssl_sock_load_cert_file(path, bind_conf, NULL, NULL, 0, err);
 
                /* strip trailing slashes, including first one */
                for (end = path + strlen(path) - 1; end >= path && *end == '/'; end--)
@@ -2559,7 +2570,7 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err)
                                                }
 
                                                snprintf(fp, sizeof(fp), "%s/%s", path, dp);
-                                               ssl_sock_load_multi_cert(fp, bind_conf, NULL, 0, err);
+                                               ssl_sock_load_multi_cert(fp, bind_conf, NULL, NULL, 0, err);
 
                                                /* Successfully processed the bundle */
                                                goto ignore_entry;
@@ -2567,7 +2578,7 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err)
                                }
 
 #endif
-                               cfgerr += ssl_sock_load_cert_file(fp, bind_conf, NULL, 0, err);
+                               cfgerr += ssl_sock_load_cert_file(fp, bind_conf, NULL, NULL, 0, err);
 ignore_entry:
                                free(de);
                        }
@@ -2577,7 +2588,7 @@ ignore_entry:
                return cfgerr;
        }
 
-       cfgerr = ssl_sock_load_multi_cert(path, bind_conf, NULL, 0, err);
+       cfgerr = ssl_sock_load_multi_cert(path, bind_conf, NULL, NULL, 0, err);
 
        return cfgerr;
 }
@@ -2598,9 +2609,33 @@ static int ssl_initialize_random()
        return random_initialized;
 }
 
-int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, char **err)
+/* release ssl bind conf */
+void ssl_sock_free_ssl_conf(struct ssl_bind_conf *conf)
 {
-       char thisline[LINESIZE*CRTLIST_FACTOR];
+       if (conf) {
+#ifdef OPENSSL_NPN_NEGOTIATED
+               free(conf->npn_str);
+               conf->npn_str = NULL;
+#endif
+#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
+               free(conf->alpn_str);
+               conf->alpn_str = NULL;
+#endif
+               free(conf->ca_file);
+               conf->ca_file = NULL;
+               free(conf->crl_file);
+               conf->crl_file = NULL;
+               free(conf->ciphers);
+               conf->ciphers = NULL;
+               free(conf->ecdhe);
+               conf->ecdhe = NULL;
+       }
+}
+
+int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, char **err)
+{
+       char thisline[CRT_LINESIZE];
+       char path[MAXPATHLEN+1];
        FILE *f;
        struct stat buf;
        int linenum = 0;
@@ -2612,11 +2647,12 @@ int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, char *
        }
 
        while (fgets(thisline, sizeof(thisline), f) != NULL) {
-               int arg;
-               int newarg;
+               int arg, newarg, cur_arg, i, ssl_b = 0, ssl_e = 0;
                char *end;
-               char *args[MAX_LINE_ARGS*CRTLIST_FACTOR + 1];
+               char *args[MAX_CRT_ARGS + 1];
                char *line = thisline;
+               char *crt_path;
+               struct ssl_bind_conf *ssl_conf = NULL;
 
                linenum++;
                end = line + strlen(line);
@@ -2637,15 +2673,40 @@ int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, char *
                                /* end of string, end of loop */
                                *line = 0;
                                break;
-                       }
-                       else if (isspace(*line)) {
+                       } else if (isspace(*line)) {
                                newarg = 1;
                                *line = 0;
-                       }
-                       else if (newarg) {
-                               if (arg == MAX_LINE_ARGS*CRTLIST_FACTOR) {
-                                       memprintf(err, "too many args on line %d in file '%s'.",
-                                                 linenum, file);
+                       } else if (*line == '[') {
+                               if (ssl_b) {
+                                       memprintf(err, "too many '[' on line %d in file '%s'.", linenum, file);
+                                       cfgerr = 1;
+                                       break;
+                               }
+                               if (!arg) {
+                                       memprintf(err, "file must start with a cert on line %d in file '%s'", linenum, file);
+                                       cfgerr = 1;
+                                       break;
+                               }
+                               ssl_b = arg;
+                               newarg = 1;
+                               *line = 0;
+                       } else if (*line == ']') {
+                               if (ssl_e) {
+                                       memprintf(err, "too many ']' on line %d in file '%s'.", linenum, file);
+                                       cfgerr = 1;
+                                       break;
+                               }
+                               if (!ssl_b) {
+                                       memprintf(err, "missing '[' in line %d in file '%s'.", linenum, file);
+                                       cfgerr = 1;
+                                       break;
+                               }
+                               ssl_e = arg;
+                               newarg = 1;
+                               *line = 0;
+                       } else if (newarg) {
+                               if (arg == MAX_CRT_ARGS) {
+                                       memprintf(err, "too many args on line %d in file '%s'.", linenum, file);
                                        cfgerr = 1;
                                        break;
                                }
@@ -2656,15 +2717,61 @@ int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, char *
                }
                if (cfgerr)
                        break;
+               args[arg++] = line;
 
                /* empty line */
-               if (!arg)
+               if (!*args[0])
                        continue;
 
-               if (stat(args[0], &buf) == 0) {
-                       cfgerr = ssl_sock_load_cert_file(args[0], bind_conf, &args[1], arg-1, err);
+               crt_path = args[0];
+               if (*crt_path != '/' && global_ssl.crt_base) {
+                       if ((strlen(global_ssl.crt_base) + 1 + strlen(crt_path)) > MAXPATHLEN) {
+                               memprintf(err, "'%s' : path too long on line %d in file '%s'",
+                                         crt_path, linenum, file);
+                               cfgerr = 1;
+                               break;
+                       }
+                       snprintf(path, sizeof(path), "%s/%s",  global_ssl.crt_base, crt_path);
+                       crt_path = path;
+               }
+
+               ssl_conf = calloc(1, sizeof *ssl_conf);
+               cur_arg = ssl_b ? ssl_b : 1;
+               while (cur_arg < ssl_e) {
+                       newarg = 0;
+                       for (i = 0; ssl_bind_kws[i].kw != NULL; i++) {
+                               if (strcmp(ssl_bind_kws[i].kw, args[cur_arg]) == 0) {
+                                       newarg = 1;
+                                       cfgerr = ssl_bind_kws[i].parse(args, cur_arg, curproxy, ssl_conf, err);
+                                       if (cur_arg + 1 + ssl_bind_kws[i].skip > ssl_e) {
+                                               memprintf(err, "ssl args out of '[]' for %s on line %d in file '%s'",
+                                                         args[cur_arg], linenum, file);
+                                               cfgerr = 1;
+                                       }
+                                       cur_arg += 1 + ssl_bind_kws[i].skip;
+                                       break;
+                               }
+                       }
+                       if (!cfgerr && !newarg) {
+                               memprintf(err, "unknown ssl keyword %s on line %d in file '%s'.",
+                                         args[cur_arg], linenum, file);
+                               cfgerr = 1;
+                               break;
+                       }
+               }
+               if (cfgerr) {
+                       ssl_sock_free_ssl_conf(ssl_conf);
+                       free(ssl_conf);
+                       ssl_conf = NULL;
+                       break;
+               }
+
+               if (stat(crt_path, &buf) == 0) {
+                       cfgerr = ssl_sock_load_cert_file(crt_path, bind_conf, ssl_conf,
+                                                        &args[cur_arg], arg - cur_arg - 1, err);
                } else {
-                       cfgerr = ssl_sock_load_multi_cert(args[0], bind_conf, &args[1], arg-1, err);
+                       cfgerr = ssl_sock_load_multi_cert(crt_path, bind_conf, ssl_conf,
+                                                         &args[cur_arg], arg - cur_arg - 1, err);
                }
 
                if (cfgerr) {
@@ -2712,7 +2819,7 @@ int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, char *
 #define SSL_MODE_SMALL_BUFFERS 0
 #endif
 
-int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx)
+int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf, SSL_CTX *ctx)
 {
        struct proxy *curproxy = bind_conf->frontend;
        int cfgerr = 0;
@@ -2741,6 +2848,9 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx)
        int idx = 0;
        int dhe_found = 0;
        SSL *ssl = NULL;
+       struct ssl_bind_conf *ssl_conf_cur;
+       int conf_ssl_options = bind_conf->ssl_conf.ssl_options | (ssl_conf ? ssl_conf->ssl_options : 0);
+       const char *conf_ciphers;
 
        /* Make sure openssl opens /dev/urandom before the chroot */
        if (!ssl_initialize_random()) {
@@ -2748,17 +2858,17 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx)
                cfgerr++;
        }
 
-       if (bind_conf->ssl_options & BC_SSL_O_NO_SSLV3)
+       if (conf_ssl_options & BC_SSL_O_NO_SSLV3)
                ssloptions |= SSL_OP_NO_SSLv3;
-       if (bind_conf->ssl_options & BC_SSL_O_NO_TLSV10)
+       if (conf_ssl_options & BC_SSL_O_NO_TLSV10)
                ssloptions |= SSL_OP_NO_TLSv1;
-       if (bind_conf->ssl_options & BC_SSL_O_NO_TLSV11)
+       if (conf_ssl_options & BC_SSL_O_NO_TLSV11)
                ssloptions |= SSL_OP_NO_TLSv1_1;
-       if (bind_conf->ssl_options & BC_SSL_O_NO_TLSV12)
+       if (conf_ssl_options & BC_SSL_O_NO_TLSV12)
                ssloptions |= SSL_OP_NO_TLSv1_2;
-       if (bind_conf->ssl_options & BC_SSL_O_NO_TLS_TICKETS)
+       if (conf_ssl_options & BC_SSL_O_NO_TLS_TICKETS)
                ssloptions |= SSL_OP_NO_TICKET;
-       if (bind_conf->ssl_options & BC_SSL_O_USE_SSLV3) {
+       if (conf_ssl_options & BC_SSL_O_USE_SSLV3) {
 #ifndef OPENSSL_NO_SSL3
                SSL_CTX_set_ssl_version(ctx, SSLv3_server_method());
 #else
@@ -2766,20 +2876,20 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx)
                cfgerr++;
 #endif
        }
-       if (bind_conf->ssl_options & BC_SSL_O_USE_TLSV10)
+       if (conf_ssl_options & BC_SSL_O_USE_TLSV10)
                SSL_CTX_set_ssl_version(ctx, TLSv1_server_method());
 #if SSL_OP_NO_TLSv1_1
-       if (bind_conf->ssl_options & BC_SSL_O_USE_TLSV11)
+       if (conf_ssl_options & BC_SSL_O_USE_TLSV11)
                SSL_CTX_set_ssl_version(ctx, TLSv1_1_server_method());
 #endif
 #if SSL_OP_NO_TLSv1_2
-       if (bind_conf->ssl_options & BC_SSL_O_USE_TLSV12)
+       if (conf_ssl_options & BC_SSL_O_USE_TLSV12)
                SSL_CTX_set_ssl_version(ctx, TLSv1_2_server_method());
 #endif
 
        SSL_CTX_set_options(ctx, ssloptions);
        SSL_CTX_set_mode(ctx, sslmode);
-       switch (bind_conf->verify) {
+       switch ((ssl_conf && ssl_conf->verify) ? ssl_conf->verify : bind_conf->ssl_conf.verify) {
                case SSL_SOCK_VERIFY_NONE:
                        verify = SSL_VERIFY_NONE;
                        break;
@@ -2792,15 +2902,17 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx)
        }
        SSL_CTX_set_verify(ctx, verify, ssl_sock_bind_verifycbk);
        if (verify & SSL_VERIFY_PEER) {
-               if (bind_conf->ca_file) {
+               char *ca_file = (ssl_conf && ssl_conf->ca_file) ? ssl_conf->ca_file : bind_conf->ssl_conf.ca_file;
+               char *crl_file = (ssl_conf && ssl_conf->crl_file) ? ssl_conf->crl_file : bind_conf->ssl_conf.crl_file;
+               if (ca_file) {
                        /* load CAfile to verify */
-                       if (!SSL_CTX_load_verify_locations(ctx, bind_conf->ca_file, NULL)) {
+                       if (!SSL_CTX_load_verify_locations(ctx, ca_file, NULL)) {
                                Alert("Proxy '%s': unable to load CA file '%s' for bind '%s' at [%s:%d].\n",
-                                     curproxy->id, bind_conf->ca_file, bind_conf->arg, bind_conf->file, bind_conf->line);
+                                     curproxy->id, ca_file, bind_conf->arg, bind_conf->file, bind_conf->line);
                                cfgerr++;
                        }
                        /* set CA names fo client cert request, function returns void */
-                       SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(bind_conf->ca_file));
+                       SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(ca_file));
                }
                else {
                        Alert("Proxy '%s': verify is enabled but no CA file specified for bind '%s' at [%s:%d].\n",
@@ -2808,12 +2920,12 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx)
                        cfgerr++;
                }
 #ifdef X509_V_FLAG_CRL_CHECK
-               if (bind_conf->crl_file) {
+               if (crl_file) {
                        X509_STORE *store = SSL_CTX_get_cert_store(ctx);
 
-                       if (!store || !X509_STORE_load_locations(store, bind_conf->crl_file, NULL)) {
+                       if (!store || !X509_STORE_load_locations(store, crl_file, NULL)) {
                                Alert("Proxy '%s': unable to configure CRL file '%s' for bind '%s' at [%s:%d].\n",
-                                     curproxy->id, bind_conf->crl_file, bind_conf->arg, bind_conf->file, bind_conf->line);
+                                     curproxy->id, crl_file, bind_conf->arg, bind_conf->file, bind_conf->line);
                                cfgerr++;
                        }
                        else {
@@ -2838,10 +2950,11 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx)
                SSL_CTX_set_timeout(ctx, global_ssl.life_time);
 
        shared_context_set_cache(ctx);
-       if (bind_conf->ciphers &&
-           !SSL_CTX_set_cipher_list(ctx, bind_conf->ciphers)) {
+       conf_ciphers = (ssl_conf && ssl_conf->ciphers) ? ssl_conf->ciphers : bind_conf->ssl_conf.ciphers;
+       if (conf_ciphers &&
+           !SSL_CTX_set_cipher_list(ctx, conf_ciphers)) {
                Alert("Proxy '%s': unable to set SSL cipher list to '%s' for bind '%s' at [%s:%d].\n",
-               curproxy->id, bind_conf->ciphers, bind_conf->arg, bind_conf->file, bind_conf->line);
+               curproxy->id, conf_ciphers, bind_conf->arg, bind_conf->file, bind_conf->line);
                cfgerr++;
        }
 
@@ -2905,12 +3018,22 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx)
 #endif
 
 #ifdef OPENSSL_NPN_NEGOTIATED
-       if (bind_conf->npn_str)
-               SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_sock_advertise_npn_protos, bind_conf);
+       ssl_conf_cur = NULL;
+       if (ssl_conf && ssl_conf->npn_str)
+               ssl_conf_cur = ssl_conf;
+       else if (bind_conf->ssl_conf.npn_str)
+               ssl_conf_cur = &bind_conf->ssl_conf;
+       if (ssl_conf_cur)
+               SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_sock_advertise_npn_protos, ssl_conf_cur);
 #endif
 #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
-       if (bind_conf->alpn_str)
-               SSL_CTX_set_alpn_select_cb(ctx, ssl_sock_advertise_alpn_protos, bind_conf);
+       ssl_conf_cur = NULL;
+       if (ssl_conf && ssl_conf->alpn_str)
+               ssl_conf_cur = ssl_conf;
+       else if (bind_conf->ssl_conf.alpn_str)
+               ssl_conf_cur = &bind_conf->ssl_conf;
+       if (ssl_conf_cur)
+               SSL_CTX_set_alpn_select_cb(ctx, ssl_sock_advertise_alpn_protos, ssl_conf_cur);
 #endif
 
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
@@ -2921,12 +3044,13 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx)
        {
                int i;
                EC_KEY  *ecdh;
+               const char *ecdhe = (ssl_conf && ssl_conf->ecdhe) ? ssl_conf->ecdhe :
+                       (bind_conf->ssl_conf.ecdhe ? bind_conf->ssl_conf.ecdhe : ECDHE_DEFAULT_CURVE);
 
-               i = OBJ_sn2nid(bind_conf->ecdhe ? bind_conf->ecdhe : ECDHE_DEFAULT_CURVE);
+               i = OBJ_sn2nid(ecdhe);
                if (!i || ((ecdh = EC_KEY_new_by_curve_name(i)) == NULL)) {
                        Alert("Proxy '%s': unable to set elliptic named curve to '%s' for bind '%s' at [%s:%d].\n",
-                             curproxy->id, bind_conf->ecdhe ? bind_conf->ecdhe : ECDHE_DEFAULT_CURVE,
-                             bind_conf->arg, bind_conf->file, bind_conf->line);
+                             curproxy->id, ecdhe, bind_conf->arg, bind_conf->file, bind_conf->line);
                        cfgerr++;
                }
                else {
@@ -3241,7 +3365,7 @@ int ssl_sock_prepare_all_ctx(struct bind_conf *bind_conf)
        global.ssl_used_frontend = 1;
 
        if (bind_conf->default_ctx)
-               err += ssl_sock_prepare_ctx(bind_conf, bind_conf->default_ctx);
+               err += ssl_sock_prepare_ctx(bind_conf, bind_conf->default_ssl_conf, bind_conf->default_ctx);
 
        node = ebmb_first(&bind_conf->sni_ctx);
        while (node) {
@@ -3249,7 +3373,7 @@ int ssl_sock_prepare_all_ctx(struct bind_conf *bind_conf)
                if (!sni->order && sni->ctx != bind_conf->default_ctx)
                        /* only initialize the CTX on its first occurrence and
                           if it is not the default_ctx */
-                       err += ssl_sock_prepare_ctx(bind_conf, sni->ctx);
+                       err += ssl_sock_prepare_ctx(bind_conf, sni->conf, sni->ctx);
                node = ebmb_next(node);
        }
 
@@ -3259,7 +3383,7 @@ int ssl_sock_prepare_all_ctx(struct bind_conf *bind_conf)
                if (!sni->order && sni->ctx != bind_conf->default_ctx)
                        /* only initialize the CTX on its first occurrence and
                           if it is not the default_ctx */
-                       err += ssl_sock_prepare_ctx(bind_conf, sni->ctx);
+                       err += ssl_sock_prepare_ctx(bind_conf, sni->conf, sni->ctx);
                node = ebmb_next(node);
        }
        return err;
@@ -3330,8 +3454,12 @@ void ssl_sock_free_all_ctx(struct bind_conf *bind_conf)
                sni = ebmb_entry(node, struct sni_ctx, name);
                back = ebmb_next(node);
                ebmb_delete(node);
-               if (!sni->order) /* only free the CTX on its first occurrence */
+               if (!sni->order) /* only free the CTX on its first occurrence */
                        SSL_CTX_free(sni->ctx);
+                       ssl_sock_free_ssl_conf(sni->conf);
+                       free(sni->conf);
+                       sni->conf = NULL;
+               }
                free(sni);
                node = back;
        }
@@ -3341,13 +3469,18 @@ void ssl_sock_free_all_ctx(struct bind_conf *bind_conf)
                sni = ebmb_entry(node, struct sni_ctx, name);
                back = ebmb_next(node);
                ebmb_delete(node);
-               if (!sni->order) /* only free the CTX on its first occurrence */
+               if (!sni->order) /* only free the CTX on its first occurrence */
                        SSL_CTX_free(sni->ctx);
+                       ssl_sock_free_ssl_conf(sni->conf);
+                       free(sni->conf);
+                       sni->conf = NULL;
+               }
                free(sni);
                node = back;
        }
 
        bind_conf->default_ctx = NULL;
+       bind_conf->default_ssl_conf = NULL;
 }
 
 /* Destroys all the contexts for a bind_conf. This is used during deinit(). */
@@ -3355,12 +3488,9 @@ void ssl_sock_destroy_bind_conf(struct bind_conf *bind_conf)
 {
        ssl_sock_free_ca(bind_conf);
        ssl_sock_free_all_ctx(bind_conf);
-       free(bind_conf->ca_file);
+       ssl_sock_free_ssl_conf(&bind_conf->ssl_conf);
        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);
        if (bind_conf->keys_ref) {
                free(bind_conf->keys_ref->filename);
                free(bind_conf->keys_ref->tlskeys);
@@ -3368,12 +3498,8 @@ void ssl_sock_destroy_bind_conf(struct bind_conf *bind_conf)
                free(bind_conf->keys_ref);
        }
        bind_conf->keys_ref = NULL;
-       bind_conf->crl_file = NULL;
-       bind_conf->ecdhe = NULL;
-       bind_conf->ciphers = NULL;
        bind_conf->ca_sign_pass = NULL;
        bind_conf->ca_sign_file = NULL;
-       bind_conf->ca_file = NULL;
 }
 
 /* Load CA cert file and private key used to generate certificates */
@@ -5249,7 +5375,7 @@ smp_fetch_ssl_c_verify(const struct arg *args, struct sample *smp, const char *k
 }
 
 /* parse the "ca-file" bind keyword */
-static int bind_parse_ca_file(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_ca_file(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
        if (!*args[cur_arg + 1]) {
                if (err)
@@ -5264,6 +5390,10 @@ static int bind_parse_ca_file(char **args, int cur_arg, struct proxy *px, struct
 
        return 0;
 }
+static int bind_parse_ca_file(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_ca_file(args, cur_arg, px, &conf->ssl_conf, err);
+}
 
 /* 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)
@@ -5295,7 +5425,7 @@ static int bind_parse_ca_sign_pass(char **args, int cur_arg, struct proxy *px, s
 }
 
 /* parse the "ciphers" bind keyword */
-static int bind_parse_ciphers(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_ciphers(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
        if (!*args[cur_arg + 1]) {
                memprintf(err, "'%s' : missing cipher suite", args[cur_arg]);
@@ -5306,7 +5436,10 @@ static int bind_parse_ciphers(char **args, int cur_arg, struct proxy *px, struct
        conf->ciphers = strdup(args[cur_arg + 1]);
        return 0;
 }
-
+static int bind_parse_ciphers(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_ciphers(args, cur_arg, px, &conf->ssl_conf, err);
+}
 /* parse the "crt" bind keyword */
 static int bind_parse_crt(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
@@ -5343,7 +5476,7 @@ static int bind_parse_crt_list(char **args, int cur_arg, struct proxy *px, struc
                return ERR_ALERT | ERR_FATAL;
        }
 
-       if (ssl_sock_load_cert_list_file(args[cur_arg + 1], conf, err) > 0) {
+       if (ssl_sock_load_cert_list_file(args[cur_arg + 1], conf, px, err) > 0) {
                memprintf(err, "'%s' : %s", args[cur_arg], *err);
                return ERR_ALERT | ERR_FATAL;
        }
@@ -5352,7 +5485,7 @@ static int bind_parse_crt_list(char **args, int cur_arg, struct proxy *px, struc
 }
 
 /* parse the "crl-file" bind keyword */
-static int bind_parse_crl_file(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_crl_file(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
 #ifndef X509_V_FLAG_CRL_CHECK
        if (err)
@@ -5373,9 +5506,13 @@ static int bind_parse_crl_file(char **args, int cur_arg, struct proxy *px, struc
        return 0;
 #endif
 }
+static int bind_parse_crl_file(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_crl_file(args, cur_arg, px, &conf->ssl_conf, err);
+}
 
 /* parse the "ecdhe" bind keyword keyword */
-static int bind_parse_ecdhe(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_ecdhe(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
 #if OPENSSL_VERSION_NUMBER < 0x0090800fL
        if (err)
@@ -5397,6 +5534,10 @@ static int bind_parse_ecdhe(char **args, int cur_arg, struct proxy *px, struct b
        return 0;
 #endif
 }
+static int bind_parse_ecdhe(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_ecdhe(args, cur_arg, px, &conf->ssl_conf, err);
+}
 
 /* parse the "crt-ignore-err" and "ca-ignore-err" bind keywords */
 static int bind_parse_ignore_err(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
@@ -5437,21 +5578,29 @@ static int bind_parse_ignore_err(char **args, int cur_arg, struct proxy *px, str
 }
 
 /* parse the "force-sslv3" bind keyword */
-static int bind_parse_force_sslv3(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_force_sslv3(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
        conf->ssl_options |= BC_SSL_O_USE_SSLV3;
        return 0;
 }
+static int bind_parse_force_sslv3(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_force_sslv3(args, cur_arg, px, &conf->ssl_conf, err);
+}
 
 /* parse the "force-tlsv10" bind keyword */
-static int bind_parse_force_tlsv10(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_force_tlsv10(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
        conf->ssl_options |= BC_SSL_O_USE_TLSV10;
        return 0;
 }
+static int bind_parse_force_tlsv10(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_force_tlsv10(args, cur_arg, px, &conf->ssl_conf, err);
+}
 
 /* parse the "force-tlsv11" bind keyword */
-static int bind_parse_force_tlsv11(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_force_tlsv11(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
 #if SSL_OP_NO_TLSv1_1
        conf->ssl_options |= BC_SSL_O_USE_TLSV11;
@@ -5462,9 +5611,13 @@ static int bind_parse_force_tlsv11(char **args, int cur_arg, struct proxy *px, s
        return ERR_ALERT | ERR_FATAL;
 #endif
 }
+static int bind_parse_force_tlsv11(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_force_tlsv11(args, cur_arg, px, &conf->ssl_conf, err);
+}
 
 /* parse the "force-tlsv12" bind keyword */
-static int bind_parse_force_tlsv12(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_force_tlsv12(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
 #if SSL_OP_NO_TLSv1_2
        conf->ssl_options |= BC_SSL_O_USE_TLSV12;
@@ -5475,46 +5628,68 @@ static int bind_parse_force_tlsv12(char **args, int cur_arg, struct proxy *px, s
        return ERR_ALERT | ERR_FATAL;
 #endif
 }
-
+static int bind_parse_force_tlsv12(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_force_tlsv12(args, cur_arg, px, &conf->ssl_conf, err);
+}
 
 /* parse the "no-tls-tickets" bind keyword */
-static int bind_parse_no_tls_tickets(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_no_tls_tickets(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
        conf->ssl_options |= BC_SSL_O_NO_TLS_TICKETS;
        return 0;
 }
-
+static int bind_parse_no_tls_tickets(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_no_tls_tickets(args, cur_arg, px, &conf->ssl_conf, err);
+}
 
 /* parse the "no-sslv3" bind keyword */
-static int bind_parse_no_sslv3(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_no_sslv3(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
        conf->ssl_options |= BC_SSL_O_NO_SSLV3;
        return 0;
 }
+static int bind_parse_no_sslv3(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_no_sslv3(args, cur_arg, px, &conf->ssl_conf, err);
+}
 
 /* parse the "no-tlsv10" bind keyword */
-static int bind_parse_no_tlsv10(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_no_tlsv10(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
        conf->ssl_options |= BC_SSL_O_NO_TLSV10;
        return 0;
 }
+static int bind_parse_no_tlsv10(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_no_tlsv10(args, cur_arg, px, &conf->ssl_conf, err);
+}
 
 /* parse the "no-tlsv11" bind keyword */
-static int bind_parse_no_tlsv11(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_no_tlsv11(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
        conf->ssl_options |= BC_SSL_O_NO_TLSV11;
        return 0;
 }
+static int bind_parse_no_tlsv11(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_no_tlsv11(args, cur_arg, px, &conf->ssl_conf, err);
+}
 
 /* parse the "no-tlsv12" bind keyword */
-static int bind_parse_no_tlsv12(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_no_tlsv12(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
        conf->ssl_options |= BC_SSL_O_NO_TLSV12;
        return 0;
 }
+static int bind_parse_no_tlsv12(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_no_tlsv12(args, cur_arg, px, &conf->ssl_conf, err);
+}
 
 /* parse the "npn" bind keyword */
-static int bind_parse_npn(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_npn(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
 #ifdef OPENSSL_NPN_NEGOTIATED
        char *p1, *p2;
@@ -5564,8 +5739,13 @@ static int bind_parse_npn(char **args, int cur_arg, struct proxy *px, struct bin
 #endif
 }
 
+static int bind_parse_npn(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_npn(args, cur_arg, px, &conf->ssl_conf, err);
+}
+
 /* parse the "alpn" bind keyword */
-static int bind_parse_alpn(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_alpn(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
 #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
        char *p1, *p2;
@@ -5615,15 +5795,20 @@ static int bind_parse_alpn(char **args, int cur_arg, struct proxy *px, struct bi
 #endif
 }
 
+static int bind_parse_alpn(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_alpn(args, cur_arg, px, &conf->ssl_conf, err);
+}
+
 /* parse the "ssl" bind keyword */
 static int bind_parse_ssl(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
        conf->xprt = &ssl_sock;
        conf->is_ssl = 1;
 
-       if (global_ssl.listen_default_ciphers && !conf->ciphers)
-               conf->ciphers = strdup(global_ssl.listen_default_ciphers);
-       conf->ssl_options |= global_ssl.listen_default_ssloptions;
+       if (global_ssl.listen_default_ciphers && !conf->ssl_conf.ciphers)
+               conf->ssl_conf.ciphers = strdup(global_ssl.listen_default_ciphers);
+       conf->ssl_conf.ssl_options |= global_ssl.listen_default_ssloptions;
 
        return 0;
 }
@@ -5723,7 +5908,7 @@ static int bind_parse_tls_ticket_keys(char **args, int cur_arg, struct proxy *px
 }
 
 /* parse the "verify" bind keyword */
-static int bind_parse_verify(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int ssl_bind_parse_verify(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
        if (!*args[cur_arg + 1]) {
                if (err)
@@ -5746,6 +5931,10 @@ static int bind_parse_verify(char **args, int cur_arg, struct proxy *px, struct
 
        return 0;
 }
+static int bind_parse_verify(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+       return ssl_bind_parse_verify(args, cur_arg, px, &conf->ssl_conf, err);
+}
 
 /************** "server" keywords ****************/
 
@@ -6620,6 +6809,26 @@ static struct acl_kw_list acl_kws = {ILH, {
  * the config parser can report an appropriate error when a known keyword was
  * not enabled.
  */
+static struct ssl_bind_kw ssl_bind_kws[] = {
+       { "alpn",                  ssl_bind_parse_alpn,             1 }, /* set ALPN supported protocols */
+       { "ca-file",               ssl_bind_parse_ca_file,          1 }, /* set CAfile to process verify on client cert */
+       { "ciphers",               ssl_bind_parse_ciphers,          1 }, /* set SSL cipher suite */
+       { "crl-file",              ssl_bind_parse_crl_file,         1 }, /* set certificat revocation list file use on client cert verify */
+       { "ecdhe",                 ssl_bind_parse_ecdhe,            1 }, /* defines named curve for elliptic curve Diffie-Hellman */
+       { "force-sslv3",           ssl_bind_parse_force_sslv3,      0 }, /* force SSLv3 */
+       { "force-tlsv10",          ssl_bind_parse_force_tlsv10,     0 }, /* force TLSv10 */
+       { "force-tlsv11",          ssl_bind_parse_force_tlsv11,     0 }, /* force TLSv11 */
+       { "force-tlsv12",          ssl_bind_parse_force_tlsv12,     0 }, /* force TLSv12 */
+       { "no-sslv3",              ssl_bind_parse_no_sslv3,         0 }, /* disable SSLv3 */
+       { "no-tlsv10",             ssl_bind_parse_no_tlsv10,        0 }, /* disable TLSv10 */
+       { "no-tlsv11",             ssl_bind_parse_no_tlsv11,        0 }, /* disable TLSv11 */
+       { "no-tlsv12",             ssl_bind_parse_no_tlsv12,        0 }, /* disable TLSv12 */
+       { "no-tls-tickets",        ssl_bind_parse_no_tls_tickets,   0 }, /* disable session resumption tickets */
+       { "npn",                   ssl_bind_parse_npn,              1 }, /* set NPN supported protocols */
+       { "verify",                ssl_bind_parse_verify,           1 }, /* set SSL verify method */
+       { NULL, NULL, 0 },
+};
+
 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 */