]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: checks: Implement LDAP check using tcp-check rules
authorChristopher Faulet <cfaulet@haproxy.com>
Fri, 3 Apr 2020 21:13:50 +0000 (23:13 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Mon, 27 Apr 2020 07:39:38 +0000 (09:39 +0200)
A shared tcp-check ruleset is now created to support LDAP check. This way no
extra memory is used if several backends use a LDAP check.

The following sequance is used :

    tcp-check send-binary "300C020101600702010304008000"

    tcp-check expect rbinary "^30" min-recv 14 \
        on-error "Not LDAPv3 protocol"

    tcp-check expect custom

The last expect rule relies on a custom function to check the LDAP server reply.

include/common/defaults.h
include/proto/checks.h
include/types/checks.h
include/types/proxy.h
src/cfgparse-listen.c
src/checks.c

index b63870e658207995560023f8d985cb591f53a466..0653a5310d191bebba291102a63ccac2437dada4 100644 (file)
 #define DEF_AGENT_RISETIME    1
 #define DEF_CHECK_REQ   "OPTIONS / HTTP/1.0\r\n"
 #define DEF_CHECK_PATH  ""
-#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
index 322f9ddf36e441384df381822e02a4920ddd57cc..ff432384a4d5d09e1ab1b2c5aa5b2307e51495dc 100644 (file)
@@ -79,6 +79,8 @@ int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, s
                                const char *file, int line);
 int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
                            const char *file, int line);
+int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+                              const char *file, int line);
 
 #endif /* _PROTO_CHECKS_H */
 
index 5b3fda4adc0743321d4c8af81857b7d2f71956d2..a5676c18ae321376d8db26129fcad5927cbb4ab1 100644 (file)
@@ -316,6 +316,7 @@ struct tcpcheck_rule {
 #define TCPCHK_RULES_REDIS_CHK   0x00000020
 #define TCPCHK_RULES_SMTP_CHK    0x00000030
 #define TCPCHK_RULES_MYSQL_CHK   0x00000050
+#define TCPCHK_RULES_LDAP_CHK    0x00000060
 #define TCPCHK_RULES_SSL3_CHK    0x00000070
 
 /* A list of tcp-check vars, to be registered before executing a ruleset */
index 6e840373705f31194716d641c43ef112499180b4..bf2a798b33d62f7b6fd91ccb3694b6d1fb70ffdd 100644 (file)
@@ -171,9 +171,7 @@ enum PR_SRV_STATE_FILE {
 #define PR_O2_CHK_NONE  0x00000000      /* no L7 health checks configured (TCP by default) */
 /* unused: 0x10000000..0x30000000 */
 #define PR_O2_HTTP_CHK  0x40000000      /* use HTTP 'OPTIONS' method to check server health */
-/* unused 0x50000000 */
-#define PR_O2_LDAP_CHK  0x60000000      /* use LDAP check for server health */
-/* unused: 0x70000000 */
+/* unused 0x50000000..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 */
index b731a29119c24e8148a7edb8926876a3763b1430..42867425876703ac09612042cac03ef1f8a69019 100644 (file)
@@ -2422,16 +2422,8 @@ stats_error_parsing:
                                goto out;
                }
                else if (!strcmp(args[1], "ldap-check")) {
-                       /* use LDAP request to check servers' health */
-                       free(curproxy->check_req);
-                       curproxy->check_req = NULL;
-                       curproxy->options2 &= ~PR_O2_CHK_ANY;
-                       curproxy->options2 |= PR_O2_LDAP_CHK;
-
-                       curproxy->check_req = 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;
-                       if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
+                       err_code |= proxy_parse_ldap_check_opt(args, 0, curproxy, &defproxy, file, linenum);
+                       if (err_code & ERR_FATAL)
                                goto out;
                }
                else if (!strcmp(args[1], "spop-check")) {
index 0fcf91592b4aa02cd10077358eff65d85f8d13d2..fa595ea6593e63e669b582ec350f9947b6eb561e 100644 (file)
@@ -857,7 +857,6 @@ static void __event_srv_chk_r(struct conn_stream *cs)
        struct task *t = check->task;
        char *desc;
        int done;
-       unsigned short msglen;
 
        if (unlikely(check->result == CHK_RES_FAILED))
                goto out_wakeup;
@@ -1156,59 +1155,6 @@ static void __event_srv_chk_r(struct conn_stream *cs)
                break;
        }
 
-       case PR_O2_LDAP_CHK:
-               if (!done && b_data(&check->bi) < 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 ((b_data(&check->bi) < 14) || (*(b_head(&check->bi)) != '\x30')) {
-                       set_server_check_status(check, HCHK_STATUS_L7RSP, "Not LDAPv3 protocol");
-               }
-               else {
-                        /* size of LDAPMessage */
-                       msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 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(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) {
-                               set_server_check_status(check, HCHK_STATUS_L7RSP, "Not LDAPv3 protocol");
-                               goto out_wakeup;
-                       }
-
-                       /* size of bindResponse */
-                       msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0;
-
-                       /* http://tools.ietf.org/html/rfc4511#section-4.1.9
-                        *   ldapResult: 0x0a 0x01: ENUMERATION
-                        */
-                       if ((msglen > 4) ||
-                           (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) {
-                               set_server_check_status(check, HCHK_STATUS_L7RSP, "Not LDAPv3 protocol");
-                               goto out_wakeup;
-                       }
-
-                       /* http://tools.ietf.org/html/rfc4511#section-4.1.9
-                        *   resultCode
-                        */
-                       check->code = *(b_head(&check->bi) + msglen + 9);
-                       if (check->code) {
-                               set_server_check_status(check, HCHK_STATUS_L7STS, "See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9");
-                       } else {
-                               set_server_check_status(check, HCHK_STATUS_L7OKD, "Success");
-                       }
-               }
-               break;
-
        case PR_O2_SPOP_CHK: {
                unsigned int framesz;
                char         err[HCHK_DESC_LEN];
@@ -2770,6 +2716,71 @@ static enum tcpcheck_eval_ret tcpcheck_mysql_expect_ok(struct check *check, stru
        return tcpcheck_mysql_expect_packet(check, rule, hslen, last_read);
 }
 
+static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct tcpcheck_rule *rule, int last_read)
+{
+       enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
+       enum healthcheck_status status;
+       struct buffer *msg = NULL;
+       struct ist desc = ist(NULL);
+       unsigned short msglen = 0;
+
+       /* Check if the server speaks LDAP (ASN.1/BER)
+        * http://en.wikipedia.org/wiki/Basic_Encoding_Rules
+        * http://tools.ietf.org/html/rfc4511
+        */
+       /* size of LDAPMessage */
+       msglen = (*(b_head(&check->bi) + 1) & 0x80) ? (*(b_head(&check->bi) + 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(b_head(&check->bi) + 2 + msglen, "\x02\x01\x01\x61", 4) != 0)) {
+               status = HCHK_STATUS_L7RSP;
+               desc = ist("Not LDAPv3 protocol");
+               goto error;
+       }
+
+       /* size of bindResponse */
+       msglen += (*(b_head(&check->bi) + msglen + 6) & 0x80) ? (*(b_head(&check->bi) + msglen + 6) & 0x7f) : 0;
+
+       /* http://tools.ietf.org/html/rfc4511#section-4.1.9
+        *   ldapResult: 0x0a 0x01: ENUMERATION
+        */
+       if ((msglen > 4) || (memcmp(b_head(&check->bi) + 7 + msglen, "\x0a\x01", 2) != 0)) {
+               status = HCHK_STATUS_L7RSP;
+               desc = ist("Not LDAPv3 protocol");
+               goto error;
+       }
+
+       /* http://tools.ietf.org/html/rfc4511#section-4.1.9
+        *   resultCode
+        */
+       check->code = *(b_head(&check->bi) + msglen + 9);
+       if (check->code) {
+               status = HCHK_STATUS_L7STS;
+               desc = ist("See RFC: http://tools.ietf.org/html/rfc4511#section-4.1.9");
+               goto error;
+       }
+
+       set_server_check_status(check, HCHK_STATUS_L7OKD, "Success");
+
+  out:
+       free_trash_chunk(msg);
+       return ret;
+
+  error:
+       ret = TCPCHK_EVAL_STOP;
+       msg = alloc_trash_chunk();
+       if (msg)
+               tcpcheck_onerror_message(msg, check, rule, 0, desc);
+       set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
+       goto out;
+
+  wait_more_data:
+       ret = TCPCHK_EVAL_WAIT;
+       goto out;
+}
 
 /* Evaluate a TCPCHK_ACT_CONNECT rule. It returns 1 to evaluate the next rule, 0
  * to wait and -1 to stop the check. */
@@ -5763,6 +5774,93 @@ int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, s
        goto out;
 }
 
+int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+                              const char *file, int line)
+{
+       static char *ldap_req = "300C020101600702010304008000";
+
+       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("*ldap-check");
+       if (rs)
+               goto ruleset_found;
+
+       rs = tcpcheck_ruleset_create("*ldap-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", ldap_req, ""},
+                                 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", "^30",
+                                              "min-recv", "14",
+                                              "on-error", "Not LDAPv3 protocol",
+                                              ""},
+                                   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);
+
+       chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
+                                   1, curpx, &rs->rules, file, line, &errmsg);
+       if (!chk) {
+               ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+               goto error;
+       }
+       chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
+       chk->index = 2;
+       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_LDAP_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 },