This patch adds a new 'post-80' option that sets the
CLIENT_PLUGIN_AUTH (0x00080000) capability flag
and explicitly specifies mysql_native_password as
the authentication plugin in the handshake response.

MySQL 8.0 changed the default authentication plugin from
mysql_native_password to caching_sha2_password.
The current mysql-check implementation only supports pre-41
and post-41 client auth protocols, which lack the CLIENT_PLUGIN_AUTH
capability flag. When HAProxy sends a post-41 authentication
packet to a MySQL 8.x server, the server responds with error 1251:
"Client does not support authentication protocol requested by server".

The new client capabilities for post-80 are:
- CLIENT_PROTOCOL_41 (0x00000200)
- CLIENT_SECURE_CONNECTION (0x00008000)
- CLIENT_PLUGIN_AUTH (0x00080000)

Usage example:
backend mysql_servers
        option mysql-check user haproxy post-80
        server db1 192.168.1.10:3306 check

The health check user must be created with mysql_native_password:
CREATE USER 'haproxy'@'%' IDENTIFIED WITH mysql_native_password BY '';

This addresses https://github.com/haproxy/haproxy/issues/2934.
---
 src/tcpcheck.c | 37 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 36 insertions(+), 1 deletion(-)

diff --git a/src/tcpcheck.c b/src/tcpcheck.c
index a51d0017c..3d99dfd2a 100644
--- a/src/tcpcheck.c
+++ b/src/tcpcheck.c
@@ -4943,6 +4943,35 @@ int proxy_parse_mysql_check_opt(char **args, int 
cur_arg, struct proxy *curpx, c
                "01"                           /* COM_QUIT command */
        };
 
+       /* MySQL >=8.0 client Authentication packet with CLIENT_PLUGIN_AUTH 
capability.
+        * MySQL 8.0 changed the default authentication plugin from 
mysql_native_password
+        * to caching_sha2_password. By setting CLIENT_PLUGIN_AUTH and 
specifying
+        * mysql_native_password as the auth plugin, we can still perform 
health checks
+        * against MySQL 8.x servers when the health check user is configured 
with
+        * mysql_native_password authentication.
+        *
+        * Client capabilities: 0x00088200 (little-endian: 00820800)
+        *   - CLIENT_PROTOCOL_41       (0x00000200)
+        *   - CLIENT_SECURE_CONNECTION (0x00008000)
+        *   - CLIENT_PLUGIN_AUTH       (0x00080000)
+        */
+       static char mysql80_rsname[] = "*mysql80-check";
+       static char mysql80_req[] = {
+               "%[var(check.header),hex]"     /* 3 bytes for the packet length 
and 1 byte for the sequence ID */
+               "00820800"                     /* client capabilities with 
CLIENT_PLUGIN_AUTH */
+               "00800001"                     /* max packet */
+               "21"                           /* character set (UTF-8) */
+               "000000000000000000000000"     /* 23 bytes, all zeroes */
+               "0000000000000000000000"
+               "%[var(check.username),hex]00" /* the username */
+               "00"                           /* auth response length (0 = no 
password) */
+               "6d7973716c5f6e61746976655f"   /* auth plugin name: 
"mysql_native_password\0" */
+               "70617373776f726400"
+               "010000"                       /* packet length */
+               "00"                           /* sequence ID */
+               "01"                           /* COM_QUIT command */
+       };
+
        struct tcpcheck_ruleset *rs = NULL;
        struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
        struct tcpcheck_rule *chk;
@@ -4999,8 +5028,14 @@ int proxy_parse_mysql_check_opt(char **args, int 
cur_arg, struct proxy *curpx, c
                        mysql_req = mysql40_req;
                        mysql_rsname  = mysql40_rsname;
                }
+               else if (strcmp(args[cur_arg+2], "post-80") == 0) {
+                       /* post-80: CLIENT_PLUGIN_AUTH + mysql_native_password 
(22 bytes) */
+                       packetlen = userlen + 7 + 27 + 22;
+                       mysql_req = mysql80_req;
+                       mysql_rsname  = mysql80_rsname;
+               }
                else  {
-                       ha_alert("parsing [%s:%d] : keyword '%s' only supports 
'post-41' and 'pre-41' (got '%s').\n",
+                       ha_alert("parsing [%s:%d] : keyword '%s' only supports 
'post-41', 'pre-41' and 'post-80' (got '%s').\n",
                                 file, line, args[cur_arg], args[cur_arg+2]);
                        goto error;
                }
-- 
2.48.1



Reply via email to