mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-20 13:21:29 +02:00
MEDIUM: httpcheck/ssl: Base the SNI value on the HTTP host header by default
Similarly to the automic SNI selection for regulat SSL traffic, the SNI of health-checks HTTPS connection is now automatically set by default by using the host header value. "check-sni-auto" and "no-check-sni-auto" server settings were added to change this behavior. Only implicit HTTPS health-checks can take advantage of this feature. In this case, the host header value from the "option httpchk" directive is used to extract the SNI. It is disabled if http-check rules are used. So, the SNI must still be explicitly specified via a "http-check connect" rule. This patch with should paritally fix the issue #3081.
This commit is contained in:
parent
668916c1a2
commit
ffc1f096e0
@ -17635,6 +17635,28 @@ check-proto <name>
|
||||
protocol for health-check connections established to this server.
|
||||
If not defined, the server one will be used, if set.
|
||||
|
||||
check-sni-auto
|
||||
May be used in the following contexts: tcp, http, log
|
||||
|
||||
This option enables the automatic SNI selection when doing health checks over
|
||||
SSL, if no value was already set. It is enabled by default but this parameter
|
||||
may be used as "server" setting to reset any "no-check-sni-auto" setting
|
||||
which would have been inherited from "default-server" directive as default
|
||||
value. It may also be used as "default-server" setting to reset any previous
|
||||
"default-server" "no-check-sni-auto" setting.
|
||||
|
||||
For HTTPS connections, the SNI is automatically selected but only if there is
|
||||
no "http-check connect" rule. In that case, the selected SNI is based on the
|
||||
host header value, specified via the "option httpchk" directive or a
|
||||
"http-check send" rule. There is no automatic selection for "http-check
|
||||
connect" rules. For other protocols, the option is ignored.
|
||||
|
||||
If the automatic selection of the SNI is used for health-checks, the value is
|
||||
assigned to the connection name if "check-reuse-pool" setting is set, unless
|
||||
overridden by the "check-pool-conn-name" server keyword.
|
||||
|
||||
See "sni-auto" option to enable automatic SNI selection for proxied traffic.
|
||||
|
||||
check-sni <sni>
|
||||
May be used in the following contexts: tcp, http, log
|
||||
|
||||
@ -18129,6 +18151,15 @@ no-check-reuse-pool
|
||||
This option reverts any previous "check-reuse-pool" possibly inherited from a
|
||||
"default-server". Any checks will be conducted on its dedicated connection.
|
||||
|
||||
no-check-sni-auto
|
||||
May be used in the following contexts: tcp, http, log
|
||||
|
||||
This option may be used as "server" setting to disable the automatic SNI
|
||||
selection for SSL health checks which is enabled by default.
|
||||
|
||||
See "no-sni-auto" option to disable automatic SNI selection for proxied
|
||||
traffic.
|
||||
|
||||
no-check-ssl
|
||||
May be used in the following contexts: tcp, http, log
|
||||
|
||||
@ -18195,6 +18226,9 @@ no-sni-auto
|
||||
This option may be used as "server" setting to disable the automatic SNI
|
||||
selection which is enabled by default.
|
||||
|
||||
See "no-check-sni-auto" option to disable automatic SNI selection for SSL
|
||||
health checks.
|
||||
|
||||
no-ssl
|
||||
May be used in the following contexts: tcp, http, log, peers, ring
|
||||
|
||||
@ -18780,6 +18814,9 @@ sni-auto
|
||||
connection name for "http-reuse", unless overridden by the "pool-conn-name"
|
||||
server keyword.
|
||||
|
||||
See "check-sni-auto" option to enable automatic SNI selection for SSL health
|
||||
checks.
|
||||
|
||||
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>] ...
|
||||
|
@ -171,7 +171,7 @@ enum srv_init_state {
|
||||
#define SRV_F_DEFSRV_USE_SSL 0x4000 /* default-server uses SSL */
|
||||
#define SRV_F_DELETED 0x8000 /* srv is deleted but not yet purged */
|
||||
#define SRV_F_STRICT_MAXCONN 0x10000 /* maxconn is to be strictly enforced, as a limit of outbound connections */
|
||||
/* unused: 0x20000 */
|
||||
#define SRV_F_CHK_NO_AUTO_SNI 0x20000 /* disable automatic SNI selection for healthcheck */
|
||||
|
||||
/* configured server options for send-proxy (server->pp_opts) */
|
||||
#define SRV_PP_V1 0x0001 /* proxy protocol version 1 */
|
||||
|
@ -125,6 +125,7 @@ enum tcpcheck_rule_type {
|
||||
struct check;
|
||||
struct tcpcheck_connect {
|
||||
char *sni; /* server name to use for SSL connections */
|
||||
struct lf_expr *sni_fmt; /* log-format string used for SNI. if defined, point on the following HTTP host header value */
|
||||
char *alpn; /* ALPN to use for the SSL connection */
|
||||
int alpn_len; /* ALPN string length */
|
||||
const struct mux_proto_list *mux_proto; /* the mux to use for all outgoing connections (specified by the "proto" keyword) */
|
||||
|
@ -1878,6 +1878,20 @@ static int srv_parse_crt(char **args, int *cur_arg, struct proxy *px, struct ser
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* parse the "check-sni-auto" server keyword */
|
||||
static int srv_parse_check_sni_auto(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
|
||||
{
|
||||
newsrv->flags &= ~SRV_F_CHK_NO_AUTO_SNI;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* parse the "no-check-sni-auto" server keyword */
|
||||
static int srv_parse_no_check_sni_auto(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
|
||||
{
|
||||
newsrv->flags |= SRV_F_CHK_NO_AUTO_SNI;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* parse the "no-check-ssl" server keyword */
|
||||
static int srv_parse_no_check_ssl(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
|
||||
{
|
||||
@ -2594,6 +2608,7 @@ static struct srv_kw_list srv_kws = { "SSL", { }, {
|
||||
{ "ca-file", srv_parse_ca_file, 1, 1, 1 }, /* set CAfile to process verify server cert */
|
||||
{ "check-alpn", srv_parse_check_alpn, 1, 1, 1 }, /* Set ALPN used for checks */
|
||||
{ "check-sni", srv_parse_check_sni, 1, 1, 1 }, /* set SNI */
|
||||
{ "check-sni-auto", srv_parse_check_sni_auto, 0, 1, 0 }, /* enable automatic SNI selection for health checks */
|
||||
{ "check-ssl", srv_parse_check_ssl, 0, 1, 1 }, /* enable SSL for health checks */
|
||||
{ "ciphers", srv_parse_ciphers, 1, 1, 1 }, /* select the cipher suite */
|
||||
{ "ciphersuites", srv_parse_ciphersuites, 1, 1, 1 }, /* select the cipher suite */
|
||||
@ -2607,6 +2622,7 @@ static struct srv_kw_list srv_kws = { "SSL", { }, {
|
||||
{ "force-tlsv12", srv_parse_tls_method_options, 0, 1, 1 }, /* force TLSv12 */
|
||||
{ "force-tlsv13", srv_parse_tls_method_options, 0, 1, 1 }, /* force TLSv13 */
|
||||
{ "ktls", srv_parse_ktls, 1, 1, 1 }, /* enable or disable kTLS */
|
||||
{ "no-check-sni-auto", srv_parse_no_check_sni_auto, 0, 1, 0 }, /* disable automatic SNI selection for health checks */
|
||||
{ "no-check-ssl", srv_parse_no_check_ssl, 0, 1, 0 }, /* disable SSL for health checks */
|
||||
{ "no-renegotiate", srv_parse_renegotiate, 0, 1, 1 }, /* Disable renegotiation */
|
||||
{ "no-send-proxy-v2-ssl", srv_parse_no_send_proxy_ssl, 0, 1, 0 }, /* do not send PROXY protocol header v2 with SSL info */
|
||||
|
@ -1232,6 +1232,27 @@ static inline int tcpcheck_connect_use_ssl(const struct check *check,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void tcpcheck_connect_auto_sni(const struct check *check,
|
||||
const struct tcpcheck_connect *connect,
|
||||
struct buffer *chk)
|
||||
{
|
||||
chunk_reset(chk);
|
||||
chk->data = sess_build_logline(check->sess, NULL, b_orig(chk), b_size(chk), connect->sni_fmt);
|
||||
if (b_data(chk)) {
|
||||
char *beg = b_orig(chk);
|
||||
char *end = b_tail(chk) - 1;
|
||||
char *p;
|
||||
|
||||
for (p = end; p >= beg; p--) {
|
||||
if (*p == ':' || *p == ']')
|
||||
break;
|
||||
}
|
||||
if (p >= beg && *p == ':')
|
||||
b_set_data(chk, p - beg);
|
||||
*b_tail(chk) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Evaluates a TCPCHK_ACT_CONNECT rule. Returns TCPCHK_EVAL_WAIT to wait the
|
||||
* connection establishment, TCPCHK_EVAL_CONTINUE to evaluate the next rule or
|
||||
* TCPCHK_EVAL_STOP if an error occurred.
|
||||
@ -1247,6 +1268,7 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
|
||||
struct protocol *proto;
|
||||
struct xprt_ops *xprt;
|
||||
struct tcpcheck_rule *next;
|
||||
struct buffer *auto_sni = NULL;
|
||||
int status, port;
|
||||
|
||||
TRACE_ENTER(CHK_EV_TCPCHK_CONN, check);
|
||||
@ -1275,6 +1297,19 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
|
||||
check_release_buf(check, &check->bi);
|
||||
check_release_buf(check, &check->bo);
|
||||
|
||||
/* Deal with automatic SNI selection now because it can be used as connection name */
|
||||
if (tcpcheck_connect_use_ssl(check, connect) && s && !(s->flags & SRV_F_CHK_NO_AUTO_SNI) && connect->sni_fmt) {
|
||||
auto_sni = alloc_trash_chunk();
|
||||
if (auto_sni) {
|
||||
tcpcheck_connect_auto_sni(check, connect, auto_sni);
|
||||
if (!b_data(auto_sni)) {
|
||||
free_trash_chunk(auto_sni);
|
||||
auto_sni = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!(check->state & CHK_ST_AGENT) && check->reuse_pool &&
|
||||
!tcpcheck_use_nondefault_connect(check, connect) &&
|
||||
!srv_is_transparent(s)) {
|
||||
@ -1292,6 +1327,8 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
|
||||
pool_conn_name = ist(connect->sni);
|
||||
else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->sni)
|
||||
pool_conn_name = ist(check->sni);
|
||||
else if (auto_sni)
|
||||
pool_conn_name = ist2(b_orig(auto_sni), b_data(auto_sni));
|
||||
}
|
||||
|
||||
if (!(s->flags & SRV_F_RHTTP)) {
|
||||
@ -1444,6 +1481,8 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
|
||||
ssl_sock_set_servername(conn, connect->sni);
|
||||
else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.sni)
|
||||
ssl_sock_set_servername(conn, s->check.sni);
|
||||
else if (auto_sni)
|
||||
ssl_sock_set_servername(conn, b_orig(auto_sni));
|
||||
|
||||
if (connect->alpn)
|
||||
ssl_sock_set_alpn(conn, (unsigned char *)connect->alpn, connect->alpn_len);
|
||||
@ -1551,6 +1590,9 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
|
||||
if (ret == TCPCHK_EVAL_CONTINUE && check->proxy->timeout.check)
|
||||
check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check);
|
||||
|
||||
if (auto_sni)
|
||||
free_trash_chunk(auto_sni);
|
||||
|
||||
TRACE_LEAVE(CHK_EV_TCPCHK_CONN, check, 0, 0, (size_t[]){ret});
|
||||
return ret;
|
||||
}
|
||||
@ -3959,6 +4001,32 @@ static int check_proxy_tcpcheck(struct proxy *px)
|
||||
LIST_INSERT(px->tcpcheck_rules.list, &chk->list);
|
||||
}
|
||||
|
||||
/* Now, back again on HTTP ruleset. Try to resolve the sni log-format
|
||||
* string if necessary, but onlu for implicit connect rules, by getting
|
||||
* it from the following send rule.
|
||||
*/
|
||||
if ((px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
|
||||
struct tcpcheck_connect *connect = NULL;
|
||||
|
||||
list_for_each_entry(chk, px->tcpcheck_rules.list, list) {
|
||||
if (chk->action == TCPCHK_ACT_CONNECT && !chk->connect.sni &&
|
||||
(chk->connect.options & TCPCHK_OPT_IMPLICIT)) {
|
||||
/* Only eval connect rule with no explici SNI */
|
||||
connect = &chk->connect;
|
||||
}
|
||||
else if (connect && chk->action == TCPCHK_ACT_SEND) {
|
||||
struct tcpcheck_http_hdr *hdr;
|
||||
|
||||
list_for_each_entry(hdr, &chk->send.http.hdrs, list) {
|
||||
if (isteqi(hdr->name, ist("host")))
|
||||
connect->sni_fmt = &hdr->value;
|
||||
}
|
||||
connect = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Remove all comment rules. To do so, when a such rule is found, the
|
||||
* comment is assigned to the following rule(s).
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user