MEDIUM: tcpcheck: add post-80 option for mysql-check to support MySQL 8.x
Some checks are pending
Contrib / build (push) Waiting to run
alpine/musl / gcc (push) Waiting to run
VTest / Generate Build Matrix (push) Waiting to run
VTest / (push) Blocked by required conditions
Windows / Windows, gcc, all features (push) Waiting to run

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.

This patch also addes documentation content for post-80 option
support in MySQL 8.x version. Which handles new default auth
plugin caching_sha2_password.

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.
This commit is contained in:
Hyeonggeun Oh 2026-02-02 22:31:33 +09:00 committed by Christopher Faulet
parent f26562bcb7
commit 2527d9dcd1
2 changed files with 43 additions and 2 deletions

View file

@ -10821,7 +10821,7 @@ no option logasap
logging.
option mysql-check [ user <username> [ { post-41 | pre-41 } ] ]
option mysql-check [ user <username> [ { post-41 | pre-41 | post-80 } ] ]
Use MySQL health checks for server testing
May be used in the following contexts: tcp
@ -10834,6 +10834,12 @@ option mysql-check [ user <username> [ { post-41 | pre-41 } ] ]
server.
post-41 Send post v4.1 client compatible checks (the default)
pre-41 Send pre v4.1 client compatible checks
post-80 Send post v8.0 client compatible checks with CLIENT_PLUGIN_AUTH
capability set and mysql_native_password as the authentication
plugin. Use this option when connecting to MySQL 8.0+ servers
where the health check user is created with mysql_native_password
authentication. Example:
CREATE USER 'haproxy'@'%' IDENTIFIED WITH mysql_native_password BY '';
If you specify a username, the check consists of sending two MySQL packet,
one Client Authentication packet, and one QUIT packet, to correctly close

View file

@ -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;
}