mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-21 13:51:26 +02:00
MEDIUM: checks: Implement ssl-hello check using tcp-check rules
A shared tcp-check ruleset is now created to support ssl-hello check. This way no extra memory is used if several backends use a ssl-hello check. The following sequence is used : tcp-check send-binary SSLV3_CLIENT_HELLO log-format tcp-check expect rbinary "^1[56]" min-recv 5 \ error-status "L6RSP" tout-status "L6TOUT" SSLV3_CLIENT_HELLO is a log-format hexa string representing a SSLv3 CLIENT HELLO packet. It is the same than the one used by the old ssl-hello except the sample expression "%[date(),htonl,hex]" is used to set the date field.
This commit is contained in:
parent
33f05df650
commit
811f78ced1
@ -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 */
|
||||
|
||||
/*
|
||||
|
@ -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 {
|
||||
|
@ -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 */
|
||||
|
@ -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")) {
|
||||
|
@ -85,36 +85,6 @@
|
||||
#include <proto/connection.h>
|
||||
|
||||
|
||||
/* 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 <now> (@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",
|
||||
|
139
src/checks.c
139
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 <now> (@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 },
|
||||
|
Loading…
x
Reference in New Issue
Block a user