diff --git a/doc/haproxy-en.txt b/doc/haproxy-en.txt index 953073193..14554f2ba 100644 --- a/doc/haproxy-en.txt +++ b/doc/haproxy-en.txt @@ -956,6 +956,21 @@ request, 3 other forms help to forge a request : - option httpchk METH URI -> HTTP/1.0 - option httpchk METH URI VER -> +Some people are using HAProxy to relay various TCP-based protocols such as +HTTPS, SMTP or LDAP, with the most common one being HTTPS. One problem commonly +encountered in data centers is the need to forward the traffic to far remote +servers while providing server fail-over. Often, TCP-only checks are not enough +because intermediate firewalls, load balancers or proxies might acknowledge the +connection before it reaches the real server. The only solution to this problem +is to send application-level health checks. Since the demand for HTTPS checks +is high, it has been implemented in 1.2.15 based on SSLv3 Client Hello packets. +To enable it, use 'option ssl-hello-chk'. It will send SSL CLIENT HELLO packets +to the servers, announcing support for most common cipher suites. If the server +responds what looks like a SERVER HELLO or an ALERT (refuses the ciphers) then +the response is considered as valid. Note that Apache does not generate a log +when it receives only an HELLO message, which makes this type of message +perfectly suit this need. + See examples below. Since version 1.1.17, it is possible to specify backup servers. These servers @@ -1068,6 +1083,15 @@ Examples : server srv1 192.168.1.1 check port 25 inter 30000 rise 1 fall 2 server srv2 192.168.1.2 backup +# HTTPS relaying with health-checks and backup servers + + listen http_proxy :443 + mode tcp + option ssl-hello-chk + balance roundrobin + server srv1 192.168.1.1 check inter 30000 rise 1 fall 2 + server srv2 192.168.1.2 backup + # Load-balancing using a backup pool (requires haproxy 1.2.9) listen http_proxy 0.0.0.0:80 mode http diff --git a/doc/haproxy-fr.txt b/doc/haproxy-fr.txt index fd4dc1a17..dd02891de 100644 --- a/doc/haproxy-fr.txt +++ b/doc/haproxy-fr.txt @@ -958,6 +958,24 @@ accepte donc 4 formes : - option httpchk METH URI -> HTTP/1.0 - option httpchk METH URI VER -> +HAProxy est souvent utilisé pour relayer divers protocoles reposant sur TCP, +tels que HTTPS, SMTP ou LDAP, le plus commun étant HTTPS. Un problème assez +couramment rencontré dans les data centers est le besoin de relayer du trafic +vers des serveurs lointains tout en maintenant la possibilité de basculer sur +un serveur de secours. Les tests purement TCP ne suffisent pas toujours dans +ces situations car l'on trouve souvent, dans la chaîne, des proxies, firewalls +ou répartiteurs de charge qui peuvent acquitter la connexion avant qu'elle +n'atteigne le serveur. La seule solution à ce problème est d'envoyer des tests +applicatifs. Comme la demande pour les tests HTTPS est élevée, ce test a été +implémenté en version 1.2.15 sur la base de messages SSLv3 CLIENT HELLO. Pour +l'activer, utiliser "option ssl-hello-chk". Ceci enverra des messages SSLv3 +CLIENT HELLO aux serveurs, en annonçant un support pour la majorité des +algorithmes de chiffrement. Si en retour, le serveur envoie ce qui ressemble à +une réponse SSLv3 SERVER HELLO ou ALERT (refus des algorithmes), alors la +réponse sera considérée comme valide. Noter qu'Apache ne produit pas de log +lorsqu'il reçoit des messages HELLO, ce qui en fait un type de message +parfaitement adapté à ce besoin. + Voir les exemples ci-après. Depuis la version 1.1.17, il est possible de définir des serveurs de secours, @@ -1078,6 +1096,15 @@ Exemples : server srv1 192.168.1.1 check port 25 inter 30000 rise 1 fall 2 server srv2 192.168.1.2 backup +# relayage HTTPS avec test du serveur et serveur de backup + + listen http_proxy :443 + mode tcp + option ssl-hello-chk + balance roundrobin + server srv1 192.168.1.1 check inter 30000 rise 1 fall 2 + server srv2 192.168.1.2 backup + # Utilisation d'un groupe de serveurs pour le backup (nécessite haproxy 1.2.9) listen http_proxy 0.0.0.0:80 mode http diff --git a/examples/haproxy.cfg b/examples/haproxy.cfg index ae72150bb..1add7ff34 100644 --- a/examples/haproxy.cfg +++ b/examples/haproxy.cfg @@ -53,6 +53,13 @@ listen appli4-backup 0.0.0.0:10004 server inst1 192.168.114.56:80 check inter 2000 fall 3 server inst2 192.168.114.56:81 check inter 2000 fall 3 backup +listen ssl-relay 0.0.0.0:8443 + option ssl-hello-chk + balance source + server inst1 192.168.110.56:443 check inter 2000 fall 3 + server inst2 192.168.110.57:443 check inter 2000 fall 3 + server back1 192.168.120.58:443 backup + listen appli5-backup 0.0.0.0:10005 option httpchk * balance roundrobin diff --git a/include/types/backend.h b/include/types/backend.h index 9fa179f7f..a6a393a51 100644 --- a/include/types/backend.h +++ b/include/types/backend.h @@ -51,6 +51,7 @@ #define PR_O_BALANCE_SH 0x00400000 /* balance on source IP hash */ #define PR_O_BALANCE (PR_O_BALANCE_RR | PR_O_BALANCE_SH) #define PR_O_ABRT_CLOSE 0x00800000 /* immediately abort request when client closes */ +#define PR_O_SSL3_CHK 0x01000000 /* use SSLv3 CLIENT_HELLO packets for server health */ #endif /* _TYPES_BACKEND_H */ diff --git a/include/types/proxy.h b/include/types/proxy.h index 306c31dcb..50c16ecad 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -114,8 +114,8 @@ struct proxy { void *req_cap_pool, *rsp_cap_pool; /* pools of pre-allocated char ** used to build the sessions */ char *req_add[MAX_NEWHDR], *rsp_add[MAX_NEWHDR]; /* headers to be added */ int grace; /* grace time after stop request */ - char *check_req; /* HTTP request to use if PR_O_HTTP_CHK is set, else NULL */ - int check_len; /* Length of the HTTP request */ + char *check_req; /* HTTP or SSL request to use for PR_O_HTTP_CHK|PR_O_SSL3_CHK */ + int check_len; /* Length of the HTTP or SSL3 request */ struct { char *msg400; /* message for error 400 */ int len400; /* message length for error 400 */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 091f6d98f..5e4aa51ee 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -105,6 +105,36 @@ const char *HTTP_504 = "\r\n" "

504 Gateway Time-out

\nThe server didn't respond in time.\n\n"; +/* 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 = Hanshake */ + "\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 */ +}; + static struct proxy defproxy; /* fake proxy used to assign default values on all instances */ int cfg_maxpconn = DEFAULT_MAXCONN; /* # of simultaneous connections per proxy (-N) */ @@ -888,6 +918,7 @@ int cfg_parse_listen(char *file, int linenum, char **args) free(curproxy->check_req); } curproxy->options |= PR_O_HTTP_CHK; + curproxy->options &= ~PR_O_SSL3_CHK; if (!*args[2]) { /* no argument */ curproxy->check_req = strdup(DEF_CHECK_REQ); /* default request */ curproxy->check_len = strlen(DEF_CHECK_REQ); @@ -908,6 +939,14 @@ int cfg_parse_listen(char *file, int linenum, char **args) "%s %s %s\r\n\r\n", args[2], args[3], *args[4]?args[4]:"HTTP/1.0"); } } + else if (!strcmp(args[1], "ssl-hello-chk")) { + /* use SSLv3 CLIENT HELLO to check servers' health */ + if (curproxy->check_req != NULL) { + free(curproxy->check_req); + } + curproxy->options &= ~PR_O_HTTP_CHK; + curproxy->options |= PR_O_SSL3_CHK; + } else if (!strcmp(args[1], "persist")) { /* persist on using the server specified by the cookie, even when it's down */ curproxy->options |= PR_O_PERSIST; @@ -1881,7 +1920,13 @@ int readcfgfile(char *file) " | values are set to a non-zero value: clitimeout, contimeout, srvtimeout.\n", file, curproxy->id); } - + + if (curproxy->options & PR_O_SSL3_CHK) { + curproxy->check_len = sizeof(sslv3_client_hello_pkt); + curproxy->check_req = (char *)malloc(sizeof(sslv3_client_hello_pkt)); + memcpy(curproxy->check_req, sslv3_client_hello_pkt, sizeof(sslv3_client_hello_pkt)); + } + /* first, we will invert the servers list order */ newsrv = NULL; while (curproxy->srv) { diff --git a/src/checks.c b/src/checks.c index fd0f10637..817afc20d 100644 --- a/src/checks.c +++ b/src/checks.c @@ -119,11 +119,19 @@ int event_srv_chk_w(int fd) } else if (s->result != -1) { /* we don't want to mark 'UP' a server on which we detected an error earlier */ - if (s->proxy->options & PR_O_HTTP_CHK) { + if ((s->proxy->options & PR_O_HTTP_CHK) || + (s->proxy->options & PR_O_SSL3_CHK)) { int ret; - /* we want to check if this host replies to "OPTIONS / HTTP/1.0" + /* we want to check if this host replies to HTTP or SSLv3 requests * so we'll send the request, and won't wake the checker up now. */ + + if (s->proxy->options & PR_O_SSL3_CHK) { + /* SSL requires that we put Unix time in the request */ + int gmt_time = htonl(now.tv_sec); + memcpy(s->proxy->check_req + 11, &gmt_time, 4); + } + #ifndef MSG_NOSIGNAL ret = send(fd, s->proxy->check_req, s->proxy->check_len, MSG_DONTWAIT); #else @@ -151,9 +159,12 @@ int event_srv_chk_w(int fd) /* - * This function is used only for server health-checks. It handles - * the server's reply to an HTTP request. It returns 1 if the server replies - * 2xx or 3xx (valid responses), or -1 in other cases. + * This function is used only for server health-checks. It handles the server's + * reply to an HTTP request or SSL HELLO. It returns 1 in s->result if the + * server replies HTTP 2xx or 3xx (valid responses), or if it returns at least + * 5 bytes in response to SSL HELLO. The principle is that this is enough to + * distinguish between an SSL server and a pure TCP relay. All other cases will + * return -1. The function returns 0. */ int event_srv_chk_r(int fd) { @@ -177,10 +188,12 @@ int event_srv_chk_r(int fd) */ len = recv(fd, reply, sizeof(reply), MSG_NOSIGNAL); #endif - - if ((len >= sizeof("HTTP/1.0 000")) && + if (((s->proxy->options & PR_O_HTTP_CHK) && + (len >= sizeof("HTTP/1.0 000")) && !memcmp(reply, "HTTP/1.", 7) && (reply[9] == '2' || reply[9] == '3')) /* 2xx or 3xx */ + || ((s->proxy->options & PR_O_SSL3_CHK) && (len >= 5) && + (reply[0] == 0x15 || reply[0] == 0x16))) /* alert or handshake */ result = 1; }