commit 4be76416751aa22992a44f2f5cfdba506809fd89
Author: Dirkjan Bussink <d.bussink@gmail.com>
Date:   Fri Sep 14 11:14:21 2018 +0200

    MEDIUM: ssl: add support for ciphersuites option for TLSv1.3
    
    OpenSSL released support for TLSv1.3. It also added a separate function
    SSL_CTX_set_ciphersuites that is used to set the ciphers used in the
    TLS 1.3 handshake. This change adds support for that new configuration
    option by adding a ciphersuites configuration variable that works
    essentially the same as the existing ciphers setting.
    
    Note that it should likely be backported to 1.8 in order to ease usage
    of the now released openssl-1.1.1.
    
    (cherry picked from commit 415150f7640b06740fa832363d186c5c6565338e)
    Signed-off-by: Willy Tarreau <w@1wt.eu>

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 580194ec..7a268386 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -580,8 +580,10 @@ The following keywords are supported in the "global" section :
    - setenv
    - stats
    - ssl-default-bind-ciphers
+   - ssl-default-bind-ciphersuites
    - ssl-default-bind-options
    - ssl-default-server-ciphers
+   - ssl-default-server-ciphersuites
    - ssl-default-server-options
    - ssl-dh-param-file
    - ssl-server-verify
@@ -984,11 +986,25 @@ setenv <name> <value>
 ssl-default-bind-ciphers <ciphers>
   This setting is only available when support for OpenSSL was built in. It sets
   the default string describing the list of cipher algorithms ("cipher suite")
-  that are negotiated during the SSL/TLS handshake for all "bind" lines which
-  do not explicitly define theirs. The format of the string is defined in
-  "man 1 ciphers" from OpenSSL man pages, and can be for instance a string such
-  as "AES:ALL:!aNULL:!eNULL:+RC4:@STRENGTH" (without quotes). Please check the
-  "bind" keyword for more information.
+  that are negotiated during the SSL/TLS handshake except for TLSv1.3 for all
+  "bind" lines which do not explicitly define theirs. The format of the string
+  is defined in "man 1 ciphers" from OpenSSL man pages, and can be for instance
+  a string such as "AES:ALL:!aNULL:!eNULL:+RC4:@STRENGTH" (without quotes). For
+  TLSv1.3 cipher configuration, please check the "ssl-default-bind-ciphersuites"
+  keyword. Please check the "bind" keyword for more information.
+
+ssl-default-bind-ciphersuites <ciphersuites>
+  This setting is only available when support for OpenSSL was built in and
+  OpenSSL 1.1.1 or later was used to build HAProxy. It sets the default string
+  describing the list of cipher algorithms ("cipher suite") that are negotiated
+  during the TLSv1.3 handshake for all "bind" lines which do not explicitly define
+  theirs. The format of the string is defined in
+  "man 1 ciphers" from OpenSSL man pages under the section "ciphersuites", and can
+  be for instance a string such as
+  "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
+  (without quotes). For cipher configuration for TLSv1.2 and earlier, please check
+  the "ssl-default-bind-ciphers" keyword. Please check the "bind" keyword for more
+  information.
 
 ssl-default-bind-options [<option>]...
   This setting is only available when support for OpenSSL was built in. It sets
@@ -1002,10 +1018,21 @@ ssl-default-bind-options [<option>]...
 ssl-default-server-ciphers <ciphers>
   This setting is only available when support for OpenSSL was built in. It
   sets the default string describing the list of cipher algorithms that are
-  negotiated during the SSL/TLS handshake with the server, for all "server"
-  lines which do not explicitly define theirs. The format of the string is
-  defined in "man 1 ciphers". Please check the "server" keyword for more
-  information.
+  negotiated during the SSL/TLS handshake except for TLSv1.3 with the server,
+  for all "server" lines which do not explicitly define theirs. The format of
+  the string is defined in "man 1 ciphers". For TLSv1.3 cipher configuration,
+  please check the "ssl-default-server-ciphersuites" keyword. Please check the
+  "server" keyword for more information.
+
+ssl-default-server-ciphersuites <ciphersuites>
+  This setting is only available when support for OpenSSL was built in and
+  OpenSSL 1.1.1 or later was used to build HAProxy. It sets the default
+  string describing the list of cipher algorithms that are negotiated during
+  the TLSv1.3 handshake with the server, for all "server" lines which do not
+  explicitly define theirs. The format of the string is defined in
+  "man 1 ciphers" under the "ciphersuites" section. For cipher configuration for
+  TLSv1.2 and earlier, please check the "ssl-default-server-ciphers" keyword.
+  Please check the "server" keyword for more information.
 
 ssl-default-server-options [<option>]...
   This setting is only available when support for OpenSSL was built in. It sets
@@ -10510,13 +10537,26 @@ ca-sign-pass <passphrase>
 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
-  negotiated during the SSL/TLS handshake. The format of the string is defined
-  in "man 1 ciphers" from OpenSSL man pages, and can be for instance a string
-  such as "AES:ALL:!aNULL:!eNULL:+RC4:@STRENGTH" (without quotes).
-  Depending on the compatibility and security requirements, the list of suitable
-  ciphers depends on a variety of variables. For background information and
-  recommendations see e. g. (https://wiki.mozilla.org/Security/Server_Side_TLS)
-  and (https://mozilla.github.io/server-side-tls/ssl-config-generator/).
+  negotiated during the SSL/TLS handshake except for TLSv1.3. The format of the
+  string is defined in "man 1 ciphers" from OpenSSL man pages, and can be for
+  instance a string such as "AES:ALL:!aNULL:!eNULL:+RC4:@STRENGTH" (without
+  quotes). Depending on the compatibility and security requirements, the list
+  of suitable ciphers depends on a variety of variables. For background
+  information and recommendations see e.g.
+  (https://wiki.mozilla.org/Security/Server_Side_TLS) and
+  (https://mozilla.github.io/server-side-tls/ssl-config-generator/). For TLSv1.3
+  cipher configuration, please check the "ciphersuites" keyword.
+
+ciphersuites <ciphersuites>
+  This setting is only available when support for OpenSSL was built in and
+  OpenSSL 1.1.1 or later was used to build HAProxy. It sets the string describing
+  the list of cipher algorithms ("cipher suite") that are negotiated during the
+  TLSv1.3 handshake. The format of the string is defined in "man 1 ciphers" from
+  OpenSSL man pages under the "ciphersuites" section, and can be for instance a
+  string such as
+  "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
+  (without quotes). For cipher configuration for TLSv1.2 and earlier, please check
+  the "ciphers" keyword.
 
 crl-file <crlfile>
   This setting is only available when support for OpenSSL was built in. It
@@ -11226,8 +11266,9 @@ check-ssl
   this option.
 
 ciphers <ciphers>
-  This option sets the string describing the list of cipher algorithms that is
-  is negotiated during the SSL/TLS handshake with the server. The format of the
+  This setting is only available when support for OpenSSL was built in. This
+  option sets the string describing the list of cipher algorithms that is
+  negotiated during the SSL/TLS handshake with the server. The format of the
   string is defined in "man 1 ciphers". When SSL is used to communicate with
   servers on the local network, it is common to see a weaker set of algorithms
   than what is used over the internet. Doing so reduces CPU usage on both the
@@ -11235,6 +11276,13 @@ ciphers <ciphers>
   Some algorithms such as RC4-SHA1 are reasonably cheap. If no security at all
   is needed and just connectivity, using DES can be appropriate.
 
+ciphersuites <ciphersuites>
+  This setting is only available when support for OpenSSL was built in and
+  OpenSSL 1.1.1 or later was used to build HAProxy. This option sets the string
+  describing the list of cipher algorithms that is negotiated during the TLS
+  1.3 handshake with the server. The format of the string is defined in
+  "man 1 ciphers" under the "ciphersuites" section.
+
 cookie <value>
   The "cookie" parameter sets the cookie value assigned to the server to
   <value>. This value will be checked in incoming requests, and the first
diff --git a/include/common/defaults.h b/include/common/defaults.h
index f53c611e..a45ab0da 100644
--- a/include/common/defaults.h
+++ b/include/common/defaults.h
@@ -234,11 +234,21 @@
 #define CONNECT_DEFAULT_CIPHERS NULL
 #endif
 
+/* ciphers used as defaults on TLS 1.3 connect */
+#ifndef CONNECT_DEFAULT_CIPHERSUITES
+#define CONNECT_DEFAULT_CIPHERSUITES NULL
+#endif
+
 /* ciphers used as defaults on listeners */
 #ifndef LISTEN_DEFAULT_CIPHERS
 #define LISTEN_DEFAULT_CIPHERS NULL
 #endif
 
+/* cipher suites used as defaults on TLS 1.3 listeners */
+#ifndef LISTEN_DEFAULT_CIPHERSUITES
+#define LISTEN_DEFAULT_CIPHERSUITES NULL
+#endif
+
 /* named curve used as defaults for ECDHE ciphers */
 #ifndef ECDHE_DEFAULT_CURVE
 #define ECDHE_DEFAULT_CURVE "prime256v1"
diff --git a/include/types/listener.h b/include/types/listener.h
index c55569cd..ea2eadb5 100644
--- a/include/types/listener.h
+++ b/include/types/listener.h
@@ -128,6 +128,9 @@ struct ssl_bind_conf {
 	char *ca_file;             /* CAfile to use on verify */
 	char *crl_file;            /* CRLfile to use on verify */
 	char *ciphers;             /* cipher suite to use if non-null */
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+	char *ciphersuites;        /* TLS 1.3 cipher suite to use if non-null */
+#endif
 	char *curves;	           /* curves suite to use for ECDHE */
 	char *ecdhe;               /* named curve to use for ECDHE */
 	struct tls_version_filter ssl_methods; /* ssl methods */
diff --git a/include/types/server.h b/include/types/server.h
index fd3c8bad..79ae7b72 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -281,6 +281,9 @@ struct server {
 			int allocated_size;
 		} * reused_sess;
 		char *ciphers;			/* cipher suite to use if non-null */
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+		char *ciphersuites;			/* TLS 1.3 cipher suite to use if non-null */
+#endif
 		int options;			/* ssl options */
 		int verify;			/* verify method (set of SSL_VERIFY_* flags) */
 		struct tls_version_filter methods;	/* ssl methods */
diff --git a/src/server.c b/src/server.c
index 842e4149..4941bd03 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1380,6 +1380,10 @@ static void srv_ssl_settings_cpy(struct server *srv, struct server *src)
 		srv->ssl_ctx.verify_host = strdup(src->ssl_ctx.verify_host);
 	if (src->ssl_ctx.ciphers != NULL)
 		srv->ssl_ctx.ciphers = strdup(src->ssl_ctx.ciphers);
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+	if (src->ssl_ctx.ciphersuites != NULL)
+		srv->ssl_ctx.ciphersuites = strdup(src->ssl_ctx.ciphersuites);
+#endif
 	if (src->sni_expr != NULL)
 		srv->sni_expr = strdup(src->sni_expr);
 }
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 08fdffab..2da0df68 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -169,6 +169,10 @@ static struct {
 
 	char *listen_default_ciphers;
 	char *connect_default_ciphers;
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+	char *listen_default_ciphersuites;
+	char *connect_default_ciphersuites;
+#endif
 	int listen_default_ssloptions;
 	int connect_default_ssloptions;
 	struct tls_version_filter listen_default_sslmethods;
@@ -186,6 +190,14 @@ static struct {
 #endif
 #ifdef CONNECT_DEFAULT_CIPHERS
 	.connect_default_ciphers = CONNECT_DEFAULT_CIPHERS,
+#endif
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+#ifdef LISTEN_DEFAULT_CIPHERSUITES
+	.listen_default_ciphersuites = LISTEN_DEFAULT_CIPHERSUITES,
+#endif
+#ifdef CONNECT_DEFAULT_CIPHERSUITES
+	.connect_default_ciphersuites = CONNECT_DEFAULT_CIPHERSUITES,
+#endif
 #endif
 	.listen_default_ssloptions = BC_SSL_O_NONE,
 	.connect_default_ssloptions = SRV_SSL_O_NONE,
@@ -3528,6 +3540,10 @@ void ssl_sock_free_ssl_conf(struct ssl_bind_conf *conf)
 		conf->crl_file = NULL;
 		free(conf->ciphers);
 		conf->ciphers = NULL;
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+		free(conf->ciphersuites);
+		conf->ciphersuites = NULL;
+#endif
 		free(conf->curves);
 		conf->curves = NULL;
 		free(conf->ecdhe);
@@ -4061,6 +4077,9 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_
 	int verify = SSL_VERIFY_NONE;
 	struct ssl_bind_conf __maybe_unused *ssl_conf_cur;
 	const char *conf_ciphers;
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+	const char *conf_ciphersuites;
+#endif
 	const char *conf_curves = NULL;
 
 	if (ssl_conf) {
@@ -4160,6 +4179,16 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_
 		cfgerr++;
 	}
 
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+	conf_ciphersuites = (ssl_conf && ssl_conf->ciphersuites) ? ssl_conf->ciphersuites : bind_conf->ssl_conf.ciphersuites;
+	if (conf_ciphersuites &&
+	    !SSL_CTX_set_ciphersuites(ctx, conf_ciphersuites)) {
+		ha_alert("Proxy '%s': unable to set TLS 1.3 cipher suites to '%s' for bind '%s' at [%s:%d].\n",
+			 curproxy->id, conf_ciphersuites, bind_conf->arg, bind_conf->file, bind_conf->line);
+		cfgerr++;
+	}
+#endif
+
 #ifndef OPENSSL_NO_DH
 	/* If tune.ssl.default-dh-param has not been set,
 	   neither has ssl-default-dh-file and no static DH
@@ -4642,6 +4671,16 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
 		cfgerr++;
 	}
 
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+	if (srv->ssl_ctx.ciphersuites &&
+		!SSL_CTX_set_cipher_list(srv->ssl_ctx.ctx, srv->ssl_ctx.ciphersuites)) {
+		ha_alert("Proxy '%s', server '%s' [%s:%d] : unable to set TLS 1.3 cipher suites to '%s'.\n",
+			 curproxy->id, srv->id,
+			 srv->conf.file, srv->conf.line, srv->ssl_ctx.ciphersuites);
+		cfgerr++;
+	}
+#endif
+
 	return cfgerr;
 }
 
@@ -7101,6 +7140,26 @@ static int bind_parse_ciphers(char **args, int cur_arg, struct proxy *px, struct
 {
 	return ssl_bind_parse_ciphers(args, cur_arg, px, &conf->ssl_conf, err);
 }
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+/* parse the "ciphersuites" bind keyword */
+static int ssl_bind_parse_ciphersuites(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]);
+		return ERR_ALERT | ERR_FATAL;
+	}
+
+	free(conf->ciphersuites);
+	conf->ciphersuites = strdup(args[cur_arg + 1]);
+	return 0;
+}
+static int bind_parse_ciphersuites(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+	return ssl_bind_parse_ciphersuites(args, cur_arg, px, &conf->ssl_conf, err);
+}
+#endif
+
 /* parse the "crt" bind keyword */
 static int bind_parse_crt(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
@@ -7492,6 +7551,10 @@ static int bind_parse_ssl(char **args, int cur_arg, struct proxy *px, struct bin
 
 	if (global_ssl.listen_default_ciphers && !conf->ssl_conf.ciphers)
 		conf->ssl_conf.ciphers = strdup(global_ssl.listen_default_ciphers);
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+	if (global_ssl.listen_default_ciphersuites && !conf->ssl_conf.ciphersuites)
+		conf->ssl_conf.ciphersuites = strdup(global_ssl.listen_default_ciphersuites);
+#endif
 	conf->ssl_options |= global_ssl.listen_default_ssloptions;
 	conf->ssl_conf.ssl_methods.flags |= global_ssl.listen_default_sslmethods.flags;
 	if (!conf->ssl_conf.ssl_methods.min)
@@ -7689,6 +7752,10 @@ static int srv_parse_check_ssl(char **args, int *cur_arg, struct proxy *px, stru
 	newsrv->check.use_ssl = 1;
 	if (global_ssl.connect_default_ciphers && !newsrv->ssl_ctx.ciphers)
 		newsrv->ssl_ctx.ciphers = strdup(global_ssl.connect_default_ciphers);
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+	if (global_ssl.connect_default_ciphersuites && !newsrv->ssl_ctx.ciphersuites)
+		newsrv->ssl_ctx.ciphersuites = strdup(global_ssl.connect_default_ciphersuites);
+#endif
 	newsrv->ssl_ctx.options |= global_ssl.connect_default_ssloptions;
 	newsrv->ssl_ctx.methods.flags |= global_ssl.connect_default_sslmethods.flags;
 	if (!newsrv->ssl_ctx.methods.min)
@@ -7712,6 +7779,21 @@ static int srv_parse_ciphers(char **args, int *cur_arg, struct proxy *px, struct
 	return 0;
 }
 
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+/* parse the "ciphersuites" server keyword */
+static int srv_parse_ciphersuites(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
+{
+	if (!*args[*cur_arg + 1]) {
+		memprintf(err, "'%s' : missing cipher suite", args[*cur_arg]);
+		return ERR_ALERT | ERR_FATAL;
+	}
+
+	free(newsrv->ssl_ctx.ciphersuites);
+	newsrv->ssl_ctx.ciphersuites = strdup(args[*cur_arg + 1]);
+	return 0;
+}
+#endif
+
 /* parse the "crl-file" server keyword */
 static int srv_parse_crl_file(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
 {
@@ -7853,6 +7935,10 @@ static int srv_parse_ssl(char **args, int *cur_arg, struct proxy *px, struct ser
 	newsrv->use_ssl = 1;
 	if (global_ssl.connect_default_ciphers && !newsrv->ssl_ctx.ciphers)
 		newsrv->ssl_ctx.ciphers = strdup(global_ssl.connect_default_ciphers);
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+	if (global_ssl.connect_default_ciphersuites && !newsrv->ssl_ctx.ciphersuites)
+		newsrv->ssl_ctx.ciphersuites = strdup(global_ssl.connect_default_ciphersuites);
+#endif
 	return 0;
 }
 
@@ -8092,6 +8178,32 @@ static int ssl_parse_global_ciphers(char **args, int section_type, struct proxy
 	return 0;
 }
 
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+/* parse the "ssl-default-bind-ciphersuites" / "ssl-default-server-ciphersuites" keywords
+ * in global section. Returns <0 on alert, >0 on warning, 0 on success.
+ */
+static int ssl_parse_global_ciphersuites(char **args, int section_type, struct proxy *curpx,
+                                    struct proxy *defpx, const char *file, int line,
+                                    char **err)
+{
+	char **target;
+
+	target = (args[0][12] == 'b') ? &global_ssl.listen_default_ciphersuites : &global_ssl.connect_default_ciphersuites;
+
+	if (too_many_args(1, args, err, NULL))
+		return -1;
+
+	if (*(args[1]) == 0) {
+		memprintf(err, "global statement '%s' expects a cipher suite as an argument.", args[0]);
+		return -1;
+	}
+
+	free(*target);
+	*target = strdup(args[1]);
+	return 0;
+}
+#endif
+
 /* parse various global tune.ssl settings consisting in positive integers.
  * Returns <0 on alert, >0 on warning, 0 on success.
  */
@@ -8599,6 +8711,9 @@ 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 */
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+	{ "ciphersuites",          ssl_bind_parse_ciphersuites,     1 }, /* set TLS 1.3 cipher suite */
+#endif
 	{ "crl-file",              ssl_bind_parse_crl_file,         1 }, /* set certificat revocation list file use on client cert verify */
 	{ "curves",                ssl_bind_parse_curves,           1 }, /* set SSL curve suite */
 	{ "ecdhe",                 ssl_bind_parse_ecdhe,            1 }, /* defines named curve for elliptic curve Diffie-Hellman */
@@ -8618,6 +8733,9 @@ static struct bind_kw_list bind_kws = { "SSL", { }, {
 	{ "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 */
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+	{ "ciphersuites",          bind_parse_ciphersuites,       1 }, /* set TLS 1.3 cipher suite */
+#endif
 	{ "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 */
 	{ "crt-ignore-err",        bind_parse_ignore_err,         1 }, /* set error IDs to ingore on verify depth == 0 */
@@ -8661,6 +8779,9 @@ static struct srv_kw_list srv_kws = { "SSL", { }, {
 	{ "check-sni",               srv_parse_check_sni,          1, 1 }, /* set SNI */
 	{ "check-ssl",               srv_parse_check_ssl,          0, 1 }, /* enable SSL for health checks */
 	{ "ciphers",                 srv_parse_ciphers,            1, 1 }, /* select the cipher suite */
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+	{ "ciphersuites",            srv_parse_ciphersuites,       1, 1 }, /* select the cipher suite */
+#endif
 	{ "crl-file",                srv_parse_crl_file,           1, 1 }, /* set certificate revocation list file use on server cert verify */
 	{ "crt",                     srv_parse_crt,                1, 1 }, /* set client certificate */
 	{ "force-sslv3",             srv_parse_tls_method_options, 0, 1 }, /* force SSLv3 */
@@ -8716,6 +8837,10 @@ static struct cfg_kw_list cfg_kws = {ILH, {
 	{ CFG_GLOBAL, "tune.ssl.capture-cipherlist-size", ssl_parse_global_capture_cipherlist },
 	{ CFG_GLOBAL, "ssl-default-bind-ciphers", ssl_parse_global_ciphers },
 	{ CFG_GLOBAL, "ssl-default-server-ciphers", ssl_parse_global_ciphers },
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+	{ CFG_GLOBAL, "ssl-default-bind-ciphersuites", ssl_parse_global_ciphersuites },
+	{ CFG_GLOBAL, "ssl-default-server-ciphersuites", ssl_parse_global_ciphersuites },
+#endif
 	{ 0, NULL, NULL },
 }};
 
@@ -8793,6 +8918,12 @@ static void __ssl_sock_init(void)
 		global_ssl.listen_default_ciphers = strdup(global_ssl.listen_default_ciphers);
 	if (global_ssl.connect_default_ciphers)
 		global_ssl.connect_default_ciphers = strdup(global_ssl.connect_default_ciphers);
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
+	if (global_ssl.listen_default_ciphersuites)
+		global_ssl.listen_default_ciphersuites = strdup(global_ssl.listen_default_ciphersuites);
+	if (global_ssl.connect_default_ciphersuites)
+		global_ssl.connect_default_ciphersuites = strdup(global_ssl.connect_default_ciphersuites);
+#endif
 
 	xprt_register(XPRT_SSL, &ssl_sock);
 	SSL_library_init();
