From b4c81e4c819ccdc1b720ed15a6d58d04b67f4e3e Mon Sep 17 00:00:00 2001 From: Gabor Lekeny Date: Wed, 29 Sep 2010 18:17:05 +0200 Subject: [PATCH] [MINOR] checks: add support for LDAPv3 health checks This patch provides a new "option ldap-check" statement to enable server health checks based on LDAPv3 bind requests. (cherry picked from commit b76b44c6fed8a7ba6f0f565dd72a9cb77aaeca7c) --- doc/configuration.txt | 24 ++++++++++++++++ include/common/defaults.h | 1 + include/types/proxy.h | 1 + src/cfgparse.c | 18 ++++++++++++ src/checks.c | 58 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 101 insertions(+), 1 deletion(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 9eedf7987..12ce86ec2 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -898,6 +898,7 @@ option httpclose (*) X X X X option httplog X X X X option http_proxy (*) X X X X option independant-streams (*) X X X X +option ldap-check X - X X option log-health-checks (*) X - X X option log-separate-errors (*) X X X - option logasap (*) X X X - @@ -3120,6 +3121,29 @@ no option independant-streams See also : "timeout client" and "timeout server" +option ldap-check + Use LDAPv3 health checks for server testing + May be used in sections : defaults | frontend | listen | backend + yes | no | yes | yes + Arguments : none + + It is possible to test that the server correctly talks LDAPv3 instead of just + testing that it accepts the TCP connection. When this option is set, an + LDAPv3 anonymous simple bind message is sent to the server, and the response + is analyzed to find an LDAPv3 bind response message. + + The server is considered valid only when the LDAP response contains success + resultCode (http://tools.ietf.org/html/rfc4511#section-4.1.9). + + Logging of bind requests is server dependent see your documentation how to + configure it. + + Example : + option ldap-check + + See also : "option httpchk" + + option log-health-checks no option log-health-checks Enable or disable logging of health checks diff --git a/include/common/defaults.h b/include/common/defaults.h index 845319dff..84ab3d48a 100644 --- a/include/common/defaults.h +++ b/include/common/defaults.h @@ -118,6 +118,7 @@ #define DEF_RISETIME 2 #define DEF_CHECK_REQ "OPTIONS / HTTP/1.0\r\n" #define DEF_SMTP_CHECK_REQ "HELO localhost\r\n" +#define DEF_LDAP_CHECK_REQ "\x30\x0c\x02\x01\x01\x60\x07\x02\x01\x03\x04\x00\x80\x00" #define DEF_HANA_ONERR HANA_ONERR_FAILCHK #define DEF_HANA_ERRLIMIT 10 diff --git a/include/types/proxy.h b/include/types/proxy.h index 076a381d8..f28bc40cf 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -139,6 +139,7 @@ #define PR_O2_CHK_SNDST 0x00080000 /* send the state of each server along with HTTP health checks */ #define PR_O2_SSL3_CHK 0x00100000 /* use SSLv3 CLIENT_HELLO packets for server health */ #define PR_O2_FAKE_KA 0x00200000 /* pretend we do keep-alive with server eventhough we close */ +#define PR_O2_LDAP_CHK 0x00400000 /* use LDAP check for server health */ /* end of proxy->options2 */ /* bits for sticking rules */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 14171ce9f..0c1464881 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -2748,6 +2748,7 @@ stats_error_parsing: curproxy->options &= ~PR_O_SMTP_CHK; curproxy->options2 &= ~PR_O2_SSL3_CHK; curproxy->options2 &= ~PR_O2_MYSQL_CHK; + curproxy->options2 &= ~PR_O2_LDAP_CHK; curproxy->options |= PR_O_HTTP_CHK; if (!*args[2]) { /* no argument */ curproxy->check_req = strdup(DEF_CHECK_REQ); /* default request */ @@ -2779,6 +2780,7 @@ stats_error_parsing: curproxy->options &= ~PR_O_HTTP_CHK; curproxy->options &= ~PR_O_SMTP_CHK; curproxy->options2 &= ~PR_O2_MYSQL_CHK; + curproxy->options2 &= ~PR_O2_LDAP_CHK; curproxy->options2 |= PR_O2_SSL3_CHK; } else if (!strcmp(args[1], "smtpchk")) { @@ -2788,6 +2790,7 @@ stats_error_parsing: curproxy->options &= ~PR_O_HTTP_CHK; curproxy->options2 &= ~PR_O2_SSL3_CHK; curproxy->options2 &= ~PR_O2_MYSQL_CHK; + curproxy->options2 &= ~PR_O2_LDAP_CHK; curproxy->options |= PR_O_SMTP_CHK; if (!*args[2] || !*args[3]) { /* no argument or incomplete EHLO host */ @@ -2814,8 +2817,23 @@ stats_error_parsing: curproxy->options &= ~PR_O_HTTP_CHK; curproxy->options &= ~PR_O_SMTP_CHK; curproxy->options2 &= ~PR_O2_SSL3_CHK; + curproxy->options2 &= ~PR_O2_LDAP_CHK; curproxy->options2 |= PR_O2_MYSQL_CHK; } + else if (!strcmp(args[1], "ldap-check")) { + /* use LDAP request to check servers' health */ + free(curproxy->check_req); + curproxy->check_req = NULL; + curproxy->options &= ~PR_O_HTTP_CHK; + curproxy->options &= ~PR_O_SMTP_CHK; + curproxy->options2 &= ~PR_O2_SSL3_CHK; + curproxy->options2 &= ~PR_O2_MYSQL_CHK; + curproxy->options2 |= PR_O2_LDAP_CHK; + + curproxy->check_req = (char *) malloc(sizeof(DEF_LDAP_CHECK_REQ) - 1); + memcpy(curproxy->check_req, DEF_LDAP_CHECK_REQ, sizeof(DEF_LDAP_CHECK_REQ) - 1); + curproxy->check_len = sizeof(DEF_LDAP_CHECK_REQ) - 1; + } else if (!strcmp(args[1], "forwardfor")) { int cur_arg; diff --git a/src/checks.c b/src/checks.c index ef7839b14..3059d5714 100644 --- a/src/checks.c +++ b/src/checks.c @@ -743,7 +743,8 @@ static int event_srv_chk_w(int fd) if ((s->proxy->options & PR_O_HTTP_CHK) || (s->proxy->options & PR_O_SMTP_CHK) || (s->proxy->options2 & PR_O2_SSL3_CHK) || - (s->proxy->options2 & PR_O2_MYSQL_CHK)) { + (s->proxy->options2 & PR_O2_MYSQL_CHK) || + (s->proxy->options2 & PR_O2_LDAP_CHK)) { int ret; const char *check_req = s->proxy->check_req; int check_len = s->proxy->check_len; @@ -866,6 +867,7 @@ static int event_srv_chk_r(int fd) struct server *s = t->context; char *desc; int done; + unsigned short msglen; if (unlikely((s->result & SRV_CHK_ERROR) || (fdtab[fd].state == FD_STERROR))) { /* in case of TCP only, this tells us if the connection failed */ @@ -1024,6 +1026,60 @@ static int event_srv_chk_r(int fd) set_server_check_status(s, HCHK_STATUS_L7STS, desc); } } + else if (s->proxy->options2 & PR_O2_LDAP_CHK) { + if (!done && s->check_data_len < 14) + goto wait_more_data; + + /* Check if the server speaks LDAP (ASN.1/BER) + * http://en.wikipedia.org/wiki/Basic_Encoding_Rules + * http://tools.ietf.org/html/rfc4511 + */ + + /* http://tools.ietf.org/html/rfc4511#section-4.1.1 + * LDAPMessage: 0x30: SEQUENCE + */ + if ((s->check_data_len < 14) || (*(s->check_data) != '\x30')) { + set_server_check_status(s, HCHK_STATUS_L7RSP, "Not LDAPv3 protocol"); + } + else { + /* size of LDAPMessage */ + msglen = (*(s->check_data + 1) & 0x80) ? (*(s->check_data + 1) & 0x7f) : 0; + + /* http://tools.ietf.org/html/rfc4511#section-4.2.2 + * messageID: 0x02 0x01 0x01: INTEGER 1 + * protocolOp: 0x61: bindResponse + */ + if ((msglen > 2) || + (memcmp(s->check_data + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) { + set_server_check_status(s, HCHK_STATUS_L7RSP, "Not LDAPv3 protocol"); + + goto out_wakeup; + } + + /* size of bindResponse */ + msglen += (*(s->check_data + msglen + 6) & 0x80) ? (*(s->check_data + msglen + 6) & 0x7f) : 0; + + /* http://tools.ietf.org/html/rfc4511#section-4.1.9 + * ldapResult: 0x0a 0x01: ENUMERATION + */ + if ((msglen > 4) || + (memcmp(s->check_data + 7 + msglen, "\x0a\x01", 2) != 0)) { + set_server_check_status(s, HCHK_STATUS_L7RSP, "Not LDAPv3 protocol"); + + goto out_wakeup; + } + + /* http://tools.ietf.org/html/rfc4511#section-4.1.9 + * resultCode + */ + s->check_code = *(s->check_data + msglen + 9); + if (s->check_code) { + set_server_check_status(s, HCHK_STATUS_L7STS, "See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9"); + } else { + set_server_check_status(s, HCHK_STATUS_L7OKD, "Success"); + } + } + } else { /* other checks are valid if the connection succeeded anyway */ set_server_check_status(s, HCHK_STATUS_L4OK, NULL);