]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: ssl: add sni support on the server lines
authorWilly Tarreau <w@1wt.eu>
Thu, 9 Jul 2015 09:40:25 +0000 (11:40 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 10 Jul 2015 09:43:15 +0000 (11:43 +0200)
The new "sni" server directive takes a sample fetch expression and
uses its return value as a hostname sent as the TLS SNI extension.
A typical use case consists in forwarding the front connection's SNI
value to the server in a bridged HTTPS forwarder :

   sni ssl_fc_sni

doc/configuration.txt
include/types/server.h
src/backend.c
src/ssl_sock.c

index 03388e368e3016d1beae4e42cb43f67f4cd9c86b..a8063127aa857fe33b750768de6be85f39e60495 100644 (file)
@@ -10183,6 +10183,15 @@ slowstart <start_time_in_ms>
 
   Supported in default-server: Yes
 
+sni <expression>
+  The "sni" parameter evaluates the sample fetch expression, converts it to a
+  string and uses the result as the host name sent in the SNI TLS extension to
+  the server. A typical use case is to send the SNI received from the client in
+  a bridged HTTPS scenario, using the "ssl_fc_sni" sample fetch for the
+  expression, though alternatives such as req.hdr(host) can also make sense.
+
+  Supported in default-server: no
+
 source <addr>[:<pl>[-<ph>]] [usesrc { <addr2>[:<port2>] | client | clientip } ]
 source <addr>[:<port>] [usesrc { <addr2>[:<port2>] | hdr_ip(<hdr>[,<occ>]) } ]
 source <addr>[:<pl>[-<ph>]] [interface <name>] ...
index 4b44f22eb47976149f8f137cb89f029ef4bf3cd9..6a7e5586cfed3b457d0db2ac5b849e0dd586180a 100644 (file)
@@ -222,6 +222,7 @@ struct server {
                char *ca_file;                  /* CAfile to use on verify */
                char *crl_file;                 /* CRLfile to use on verify */
                char *client_crt;               /* client certificate to send */
+               struct sample_expr *sni;        /* sample expression for SNI */
        } ssl_ctx;
 #endif
        struct {
index 26f558971f74b19a50b98839f67688815db9d8d2..48c5e79d36c33a84522df7778a1f9e96b40c5876 100644 (file)
 #include <proto/stream_interface.h>
 #include <proto/task.h>
 
+#ifdef USE_OPENSSL
+#include <proto/ssl_sock.h>
+#endif /* USE_OPENSSL */
+
 int be_lastsession(const struct proxy *be)
 {
        if (be->be_counters.last_sess)
@@ -1116,6 +1120,36 @@ int connect_server(struct stream *s)
                        srv->counters.cur_sess_max = srv->cur_sess;
                if (s->be->lbprm.server_take_conn)
                        s->be->lbprm.server_take_conn(srv);
+
+#ifdef USE_OPENSSL
+               if (srv->ssl_ctx.sni) {
+                       struct sample *smp;
+                       int rewind;
+
+                       /* Tricky case : we have already scheduled the pending
+                        * HTTP request or TCP data for leaving. So in HTTP we
+                        * rewind exactly the headers, otherwise we rewind the
+                        * output data.
+                        */
+                       rewind = s->txn ? http_hdr_rewind(&s->txn->req) : s->req.buf->o;
+                       b_rew(s->req.buf, rewind);
+
+                       smp = sample_fetch_as_type(s->be, s->sess, s, SMP_OPT_DIR_REQ | SMP_OPT_FINAL, srv->ssl_ctx.sni, SMP_T_STR);
+
+                       /* restore the pointers */
+                       b_adv(s->req.buf, rewind);
+
+                       if (smp) {
+                               /* get write access to terminate with a zero */
+                               smp_dup(smp);
+                               if (smp->data.str.len >= smp->data.str.size)
+                                       smp->data.str.len = smp->data.str.size - 1;
+                               smp->data.str.str[smp->data.str.len] = 0;
+                               ssl_sock_set_servername(srv_conn, smp->data.str.str);
+                       }
+               }
+#endif /* USE_OPENSSL */
+
        }
 
        return SF_ERR_NONE;  /* connection is OK */
index 7f5d2ae20f95088b62cb55577cd7ba17cf5bc1dd..4e755494240402f5760e222f73c01631d690902e 100644 (file)
@@ -4952,6 +4952,42 @@ static int srv_parse_send_proxy_cn(char **args, int *cur_arg, struct proxy *px,
        return 0;
 }
 
+/* parse the "sni" server keyword */
+static int srv_parse_sni(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
+{
+#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME
+       memprintf(err, "'%s' : the current SSL library doesn't support the SNI TLS extension", args[*cur_arg]);
+       return ERR_ALERT | ERR_FATAL;
+#else
+       struct sample_expr *expr;
+
+       if (!*args[*cur_arg + 1]) {
+               memprintf(err, "'%s' : missing sni expression", args[*cur_arg]);
+               return ERR_ALERT | ERR_FATAL;
+       }
+
+       (*cur_arg)++;
+       proxy->conf.args.ctx = ARGC_SRV;
+
+       expr = sample_parse_expr((char **)args, cur_arg, px->conf.file, px->conf.line, err, &proxy->conf.args);
+       if (!expr) {
+               memprintf(err, "error detected while parsing sni expression : %s", *err);
+               return ERR_ALERT | ERR_FATAL;
+       }
+
+       if (!(expr->fetch->val & SMP_VAL_BE_SRV_CON)) {
+               memprintf(err, "error detected while parsing sni expression : "
+                         " fetch method '%s' extracts information from '%s', none of which is available here.\n",
+                         args[*cur_arg-1], sample_src_names(expr->fetch->use));
+               return ERR_ALERT | ERR_FATAL;
+       }
+
+       px->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
+       newsrv->ssl_ctx.sni = expr;
+       return 0;
+#endif
+}
+
 /* parse the "ssl" server keyword */
 static int srv_parse_ssl(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
 {
@@ -5225,6 +5261,7 @@ static struct srv_kw_list srv_kws = { "SSL", { }, {
        { "no-tls-tickets",        srv_parse_no_tls_tickets, 0, 0 }, /* disable session resumption tickets */
        { "send-proxy-v2-ssl",     srv_parse_send_proxy_ssl, 0, 0 }, /* send PROXY protocol header v2 with SSL info */
        { "send-proxy-v2-ssl-cn",  srv_parse_send_proxy_cn,  0, 0 }, /* send PROXY protocol header v2 with CN */
+       { "sni",                   srv_parse_sni,            1, 0 }, /* send SNI extension */
        { "ssl",                   srv_parse_ssl,            0, 0 }, /* enable SSL processing */
        { "verify",                srv_parse_verify,         1, 0 }, /* set SSL verify method */
        { "verifyhost",            srv_parse_verifyhost,     1, 0 }, /* require that SSL cert verifies for hostname */