diff --git a/include/proto/checks.h b/include/proto/checks.h index 1c76a8968..33ed492c4 100644 --- a/include/proto/checks.h +++ b/include/proto/checks.h @@ -71,6 +71,9 @@ int spoe_handle_healthcheck_response(char *frame, size_t size, char *err, int er int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx, const char *file, int line); +int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx, + const char *file, int line); + #endif /* _PROTO_CHECKS_H */ /* diff --git a/include/types/checks.h b/include/types/checks.h index b5f4649e2..4830f0d94 100644 --- a/include/types/checks.h +++ b/include/types/checks.h @@ -313,6 +313,7 @@ struct tcpcheck_rule { #define TCPCHK_RULES_DEF 0x00000002 /* Ruleset inherited from the default section */ #define TCPCHK_RULES_REDIS_CHK 0x00000020 +#define TCPCHK_RULES_SSL3_CHK 0x00000070 /* A list of tcp-check vars, to be registered before executing a ruleset */ struct tcpcheck_var { diff --git a/include/types/proxy.h b/include/types/proxy.h index 792c25225..9ace915d9 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -175,7 +175,7 @@ enum PR_SRV_STATE_FILE { #define PR_O2_HTTP_CHK 0x40000000 /* use HTTP 'OPTIONS' method to check server health */ #define PR_O2_MYSQL_CHK 0x50000000 /* use MYSQL check for server health */ #define PR_O2_LDAP_CHK 0x60000000 /* use LDAP check for server health */ -#define PR_O2_SSL3_CHK 0x70000000 /* use SSLv3 CLIENT_HELLO packets for server health */ +/* unused: 0x70000000 */ #define PR_O2_LB_AGENT_CHK 0x80000000 /* use a TCP connection to obtain a metric of server health */ #define PR_O2_TCPCHK_CHK 0x90000000 /* use TCPCHK check for server health */ #define PR_O2_EXT_CHK 0xA0000000 /* use external command for server health */ diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index fdcf68e83..291dca805 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -2397,16 +2397,8 @@ stats_error_parsing: goto out; } else if (!strcmp(args[1], "ssl-hello-chk")) { - /* use SSLv3 CLIENT HELLO to check servers' health */ - if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[1], NULL)) - err_code |= ERR_WARN; - - free(curproxy->check_req); - curproxy->check_req = NULL; - curproxy->options2 &= ~PR_O2_CHK_ANY; - curproxy->options2 |= PR_O2_SSL3_CHK; - - if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code)) + err_code |= proxy_parse_ssl_hello_chk_opt(args, 0, curproxy, &defproxy, file, linenum); + if (err_code & ERR_FATAL) goto out; } else if (!strcmp(args[1], "smtpchk")) { diff --git a/src/cfgparse.c b/src/cfgparse.c index 6bd816017..707046e47 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -85,36 +85,6 @@ #include -/* This is the SSLv3 CLIENT HELLO packet used in conjunction with the - * ssl-hello-chk option to ensure that the remote server speaks SSL. - * - * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details. - */ -const char sslv3_client_hello_pkt[] = { - "\x16" /* ContentType : 0x16 = Handshake */ - "\x03\x00" /* ProtocolVersion : 0x0300 = SSLv3 */ - "\x00\x79" /* ContentLength : 0x79 bytes after this one */ - "\x01" /* HanshakeType : 0x01 = CLIENT HELLO */ - "\x00\x00\x75" /* HandshakeLength : 0x75 bytes after this one */ - "\x03\x00" /* Hello Version : 0x0300 = v3 */ - "\x00\x00\x00\x00" /* Unix GMT Time (s) : filled with (@0x0B) */ - "HAPROXYSSLCHK\nHAPROXYSSLCHK\n" /* Random : must be exactly 28 bytes */ - "\x00" /* Session ID length : empty (no session ID) */ - "\x00\x4E" /* Cipher Suite Length : 78 bytes after this one */ - "\x00\x01" "\x00\x02" "\x00\x03" "\x00\x04" /* 39 most common ciphers : */ - "\x00\x05" "\x00\x06" "\x00\x07" "\x00\x08" /* 0x01...0x1B, 0x2F...0x3A */ - "\x00\x09" "\x00\x0A" "\x00\x0B" "\x00\x0C" /* This covers RSA/DH, */ - "\x00\x0D" "\x00\x0E" "\x00\x0F" "\x00\x10" /* various bit lengths, */ - "\x00\x11" "\x00\x12" "\x00\x13" "\x00\x14" /* SHA1/MD5, DES/3DES/AES... */ - "\x00\x15" "\x00\x16" "\x00\x17" "\x00\x18" - "\x00\x19" "\x00\x1A" "\x00\x1B" "\x00\x2F" - "\x00\x30" "\x00\x31" "\x00\x32" "\x00\x33" - "\x00\x34" "\x00\x35" "\x00\x36" "\x00\x37" - "\x00\x38" "\x00\x39" "\x00\x3A" - "\x01" /* Compression Length : 0x01 = 1 byte for types */ - "\x00" /* Compression Type : 0x00 = NULL compression */ -}; - /* Used to chain configuration sections definitions. This list * stores struct cfg_section */ @@ -3127,12 +3097,6 @@ out_uri_auth_compat: } } - if ((curproxy->options2 & PR_O2_CHK_ANY) == PR_O2_SSL3_CHK) { - curproxy->check_len = sizeof(sslv3_client_hello_pkt) - 1; - curproxy->check_req = malloc(curproxy->check_len); - memcpy(curproxy->check_req, sslv3_client_hello_pkt, curproxy->check_len); - } - if (curproxy->tcpcheck_rules.list != NULL && (curproxy->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK) { ha_warning("config : %s '%s' uses tcp-check rules without 'option tcp-check', so the rules are ignored.\n", diff --git a/src/checks.c b/src/checks.c index c7f156bec..0c62a03b0 100644 --- a/src/checks.c +++ b/src/checks.c @@ -741,13 +741,9 @@ static void chk_report_conn_err(struct check *check, int errno_bck, int expired) enum healthcheck_status tout = HCHK_STATUS_L7TOUT; /* connection established but expired check */ - if (check->type == PR_O2_SSL3_CHK) - set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg); - else { /* HTTP, SMTP, ... */ - if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) - tout = check->current_step->expect.tout_status; - set_server_check_status(check, tout, err_msg); - } + if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) + tout = check->current_step->expect.tout_status; + set_server_check_status(check, tout, err_msg); } return; @@ -953,17 +949,6 @@ static void __event_srv_chk_r(struct conn_stream *cs) } break; - case PR_O2_SSL3_CHK: - if (!done && b_data(&check->bi) < 5) - goto wait_more_data; - - /* Check for SSLv3 alert or handshake */ - if ((b_data(&check->bi) >= 5) && (*b_head(&check->bi) == 0x15 || *b_head(&check->bi) == 0x16)) - set_server_check_status(check, HCHK_STATUS_L6OK, NULL); - else - set_server_check_status(check, HCHK_STATUS_L6RSP, NULL); - break; - case PR_O2_SMTP_CHK: if (!done && b_data(&check->bi) < strlen("000\r")) goto wait_more_data; @@ -1623,15 +1608,10 @@ static int connect_conn_chk(struct task *t) if (check->type && check->type != PR_O2_TCPCHK_CHK && !(check->state & CHK_ST_AGENT)) { b_putblk(&check->bo, s->proxy->check_req, s->proxy->check_len); - /* we want to check if this host replies to HTTP or SSLv3 requests + /* we want to check if this host replies to HTTP requests * so we'll send the request, and won't wake the checker up now. */ - if ((check->type) == PR_O2_SSL3_CHK) { - /* SSL requires that we put Unix time in the request */ - int gmt_time = htonl(date.tv_sec); - memcpy(b_head(&check->bo) + 11, &gmt_time, 4); - } - else if ((check->type) == PR_O2_HTTP_CHK) { + if ((check->type) == PR_O2_HTTP_CHK) { /* prevent HTTP keep-alive when "http-check expect" is used */ if (s->proxy->options2 & PR_O2_EXP_TYPE) b_putist(&check->bo, ist("Connection: close\r\n")); @@ -5194,6 +5174,115 @@ int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, s goto out; } + +/* Parses the "option ssl-hello-chk" proxy keyword */ +int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx, + const char *file, int line) +{ + /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the + * ssl-hello-chk option to ensure that the remote server speaks SSL. + * + * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details. + */ + static char sslv3_client_hello[] = { + "16" /* ContentType : 0x16 = Hanshake */ + "0300" /* ProtocolVersion : 0x0300 = SSLv3 */ + "0079" /* ContentLength : 0x79 bytes after this one */ + "01" /* HanshakeType : 0x01 = CLIENT HELLO */ + "000075" /* HandshakeLength : 0x75 bytes after this one */ + "0300" /* Hello Version : 0x0300 = v3 */ + "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with (@0x0B) */ + "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */ + "00" /* Session ID length : empty (no session ID) */ + "004E" /* Cipher Suite Length : 78 bytes after this one */ + "0001" "0002" "0003" "0004" /* 39 most common ciphers : */ + "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */ + "0009" "000A" "000B" "000C" /* This covers RSA/DH, */ + "000D" "000E" "000F" "0010" /* various bit lengths, */ + "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */ + "0015" "0016" "0017" "0018" + "0019" "001A" "001B" "002F" + "0030" "0031" "0032" "0033" + "0034" "0035" "0036" "0037" + "0038" "0039" "003A" + "01" /* Compression Length : 0x01 = 1 byte for types */ + "00" /* Compression Type : 0x00 = NULL compression */ + }; + + struct tcpcheck_ruleset *rs = NULL; + struct tcpcheck_rules *rules = &curpx->tcpcheck_rules; + struct tcpcheck_rule *chk; + char *errmsg = NULL; + int err_code = 0; + + if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL)) + err_code |= ERR_WARN; + + if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code)) + goto out; + + if (rules->list && !(rules->flags & TCPCHK_RULES_SHARED)) { + ha_alert("parsing [%s:%d] : A custom tcp-check ruleset is already configured.\n", + file, line); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + curpx->options2 &= ~PR_O2_CHK_ANY; + curpx->options2 |= PR_O2_TCPCHK_CHK; + + free_tcpcheck_vars(&rules->preset_vars); + rules->list = NULL; + rules->flags = 0; + + rs = tcpcheck_ruleset_lookup("*ssl-hello-check"); + if (rs) + goto ruleset_found; + + rs = tcpcheck_ruleset_create("*ssl-hello-check"); + if (rs == NULL) { + ha_alert("parsing [%s:%d] : out of memory.\n", file, line); + goto error; + } + + chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", sslv3_client_hello, "log-format", ""}, + 1, curpx, &rs->rules, file, line, &errmsg); + if (!chk) { + ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg); + goto error; + } + chk->index = 0; + LIST_ADDQ(&rs->rules, &chk->list); + + chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]", + "min-recv", "5", + "error-status", "L6RSP", "tout-status", "L6TOUT", + ""}, + 1, curpx, &rs->rules, file, line, &errmsg); + if (!chk) { + ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg); + goto error; + } + chk->index = 1; + LIST_ADDQ(&rs->rules, &chk->list); + + LIST_ADDQ(&tcpchecks_list, &rs->list); + + ruleset_found: + rules->list = &rs->rules; + rules->flags |= (TCPCHK_RULES_SHARED|TCPCHK_RULES_SSL3_CHK); + + out: + free(errmsg); + return err_code; + + error: + tcpcheck_ruleset_release(rs); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; +} + + static struct cfg_kw_list cfg_kws = {ILH, { { CFG_LISTEN, "tcp-check", proxy_parse_tcpcheck }, { 0, NULL, NULL },