6.2.14 代理用户

MySQL 服务器使用身份验证插件对 Client 端连接进行身份验证。验证给定连接的插件可以请求出于特权检查目的将连接(外部)用户视为其他用户。这使外部用户可以成为第二个用户的代理。也就是说,要承担第二个用户的特权:

  • 外部用户是“代理用户”(可以模拟或成为另一个用户的用户)。

  • 第二用户是“代理用户”(其身份和特权可以由代理用户承担的用户)。

本节介绍代理用户功能的工作方式。有关身份验证插件的一般信息,请参见第 6.2.13 节“可插入身份验证”。有关特定插件的信息,请参见第 6.4.1 节“身份验证插件”。有关编写支持代理用户的身份验证插件的信息,请参见第 28.2.4.9.4 节“在身份验证插件中实现代理用户支持”

代理用户支持的要求

为了使给定的身份验证插件发生代理,必须满足以下条件:

  • 必须由插件本身或代 table 插件的 MySQL 服务器支持代理。在后一种情况下,可能需要显式启用服务器支持。参见服务器对代理用户 Map 的支持

  • 外部代理用户的帐户必须设置为由插件进行身份验证。使用CREATE USER语句将帐户与身份验证插件关联,或使用ALTER USER更改其插件。

  • 代理用户的帐户必须存在并被授予代理用户要承担的特权。为此,请使用CREATE USERGRANT语句。

  • 通常,已配置代理用户,使其只能在代理场景中使用,而不能用于直接登录。

  • 代理用户帐户必须对代理帐户具有PROXY特权。为此,请使用GRANT语句。

  • 为了使连接到代理帐户的 Client 端被视为代理用户,身份验证插件必须返回与 Client 端用户名不同的用户名,以指示定义了代理要享有的特权的代理帐户的用户名。用户。

或者,对于由服务器提供代理 Map 的插件,根据代理用户拥有的PROXY特权确定代理用户。

代理机制仅允许将外部 Client 端用户名 Map 到代理用户名。没有 Map 主机名的规定:

  • 当 Client 端连接到服务器时,服务器将根据 Client 端程序传递的用户名以及 Client 端连接的主机来确定适当的帐户。

  • 如果该帐户是代理帐户,则服务器尝试使用身份验证插件返回的用户名和代理帐户的主机名,找到与代理帐户匹配的项,从而确定适当的代理帐户。代理帐户中的主机名将被忽略。

简单代理用户示例

请考虑以下帐户定义:

-- create proxy account
CREATE USER 'employee_ext'@'localhost'
  IDENTIFIED WITH my_auth_plugin
  AS 'my_auth_string';

-- create proxied account and grant its privileges;
-- use mysql_no_login plugin to prevent direct login
CREATE USER 'employee'@'localhost'
  IDENTIFIED WITH mysql_no_login;
GRANT ALL
  ON employees.*
  TO 'employee'@'localhost';

-- grant to proxy account the
-- PROXY privilege for proxied account
GRANT PROXY
  ON 'employee'@'localhost'
  TO 'employee_ext'@'localhost';

当 Client 端以employee_ext的身份从 localhost 连接时,MySQL 使用名为my_auth_plugin的插件执行身份验证。假设my_auth_plugin根据'my_auth_string'的内容并可能通过咨询某些外部身份验证系统将employee的用户名返回到服务器。名称employeeemployee_ext不同,因此返回employee充当对服务器的请求,以出于特权检查的目的将employee_ext外部用户视为employee本地用户。

在这种情况下,employee_ext是代理用户,而employee是代理用户。

服务器通过检查employee_ext(代理用户)是否对employee(代理用户)具有PROXY特权,来验证employee_ext用户可以对employee进行代理身份验证。如果未授予此特权,则会发生错误。否则,employee_ext拥有employee的特权。服务器会根据employee授予的特权检查employee_ext在 Client 端会话期间执行的语句。在这种情况下,employee_ext可以访问employees数据库中的 table。

代理帐户employee使用mysql_no_login身份验证插件来防止 Client 端使用该帐户直接登录。 (这假定已安装插件.有关说明,请参阅第 6.4.1.10 节“无登录可插入身份验证”。)有关防止代理帐户直接使用的替代方法,请参阅防止直接登录到代理帐户

发生代理时,可以使用USER()CURRENT_USER()函数来查看连接用户(代理用户)和在当前会话期间具有特权的帐户(代理用户)之间的区别。对于上述示例,这些函数返回以下值:

mysql> SELECT USER(), CURRENT_USER();
+------------------------+--------------------+
| USER()                 | CURRENT_USER()     |
+------------------------+--------------------+
| employee_ext@localhost | employee@localhost |
+------------------------+--------------------+

在创建代理用户帐户的CREATE USER语句中,命名支持代理的身份验证插件的IDENTIFIED WITH子句后面可以有一个AS 'auth_string'子句,该子句指定用户连接时服务器传递给插件的字符串。字符串(如果存在)提供的信息可帮助插件确定如何将代理(外部)Client 端用户名 Map 到代理用户名。是否需要AS子句取决于每个插件。如果是这样,认证字符串的格式取决于插件打算如何使用它。有关给定插件接受的认证字符串值的信息,请查阅文档。

防止直接登录代理帐户

代理帐户通常仅旨在通过代理帐户使用。也就是说,Client 端使用代理帐户进行连接,然后被 Map 到适当的代理用户并承担相应的代理用户的特权。

有多种方法可以确保不能直接使用代理帐户:

  • 将该帐户与mysql_no_login身份验证插件关联。在这种情况下,该帐户在任何情况下都不能用于直接登录。假设已安装插件。有关说明,请参见第 6.4.1.10 节“无登录可插入身份验证”

  • 创建帐户时,请包含ACCOUNT LOCK选项。参见第 13.7.1.2 节“ CREATE USER 语句”。使用此方法时,还应包括一个密码,以便以后再解锁该帐户时,将无法使用无密码访问该帐户。 (如果启用了validate_password插件,则即使该帐户已锁定,也将不允许创建没有密码的帐户.请参见第 6.4.3 节“密码验证插件”。)

  • 用密码创建帐户,但不要告诉其他人该密码。如果您不让任何人知道该帐户的密码,则 Client 端将无法使用该帐户直接连接到 MySQL 服务器。

授予和撤销 PROXY 特权

需要PROXY特权才能使外部用户以其他用户的身份连接并具有其他用户的特权。要授予此特权,请使用GRANT语句。例如:

GRANT PROXY ON 'proxied_user' TO 'proxy_user';

该语句在mysql.proxies_priv授予 table 中创建一行。

在连接时,* proxy_user 必须代 table 一个有效的外部认证的 MySQL 用户,而 proxied_user *必须代 table 一个有效的本地认证的用户。否则,连接尝试将失败。

相应的REVOKE语法为:

REVOKE PROXY ON 'proxied_user' FROM 'proxy_user';

MySQL GRANTREVOKE语法扩展正常运行。例子:

-- grant PROXY to multiple accounts
GRANT PROXY ON 'a' TO 'b', 'c', 'd';

-- revoke PROXY from multiple accounts
REVOKE PROXY ON 'a' FROM 'b', 'c', 'd';

-- grant PROXY to an account and enable the account to grant
-- PROXY to the proxied account
GRANT PROXY ON 'a' TO 'd' WITH GRANT OPTION;

-- grant PROXY to default proxy account
GRANT PROXY ON 'a' TO ''@'';

在以下情况下,可以授予PROXY特权:

  • 由具有GRANT PROXY ... WITH GRANT OPTIONtable 示* proxied_user *的用户组成。

  • 通过* proxied_user 本身:对于帐户名的用户名和主机名部分,USER()的值必须与CURRENT_USER() proxied_user *完全匹配。

在 MySQL 安装期间创建的初始root帐户具有''@''代理...带有许可选项特权,即对所有用户和所有主机。这使root可以设置代理用户,以及将设置代理用户的权限委托给其他帐户。例如,root可以执行以下操作:

CREATE USER 'admin'@'localhost'
  IDENTIFIED BY 'admin_password';
GRANT PROXY
  ON ''@''
  TO 'admin'@'localhost'
  WITH GRANT OPTION;

这些语句创建了一个admin用户,该用户可以 Management 所有GRANT PROXYMap。例如,admin可以执行以下操作:

GRANT PROXY ON sally TO joe;

默认代理用户

要指定部分或所有用户应使用给定的身份验证插件进行连接,请创建一个具有空用户名和主机名(''@'')的“空白” MySQL 帐户,并将其与该插件关联,然后让该插件返回经过身份验证的真实用户名(如果与空白用户不同)。假设存在一个名为ldap_auth的插件,该插件实现 LDAP 身份验证并将连接的用户 Map 到开发人员或 Management 员帐户。要在这些帐户上设置用户代理,请使用以下语句:

-- create default proxy account
CREATE USER ''@''
  IDENTIFIED WITH ldap_auth
  AS 'O=Oracle, OU=MySQL';

-- create proxied accounts; use
-- mysql_no_login plugin to prevent direct login
CREATE USER 'developer'@'localhost'
  IDENTIFIED WITH mysql_no_login;
CREATE USER 'manager'@'localhost'
  IDENTIFIED WITH mysql_no_login;

-- grant to default proxy account the
-- PROXY privilege for proxied accounts
GRANT PROXY
  ON 'manager'@'localhost'
  TO ''@'';
GRANT PROXY
  ON 'developer'@'localhost'
  TO ''@'';

现在假设 Client 端连接如下:

shell> mysql --user=myuser --password ...
Enter password: myuser_password

服务器将找不到myuser定义为 MySQL 用户。但是,因为存在与 Client 端用户名和主机名匹配的空白用户帐户(''@''),所以服务器使用该帐户对 Client 端进行身份验证:服务器调用ldap_auth身份验证插件,并将myuser和* myuser_password *作为用户名传递给它和密码。

如果ldap_auth插件在 LDAP 目录中发现* myuser_password *不是myuser的正确密码,则身份验证失败并且服务器拒绝连接。

如果密码正确,并且ldap_auth发现myuser是开发人员,则它将用户名developer返回到 MySQL 服务器,而不是myuser。返回与myuser的 Client 端用户名不同的用户名,将向服务器发出 signal,table 示应将myuser作为代理。服务器验证''@''可以认证为developer(因为''@''拥有PROXY特权),然后接受连接。会话以具有developer代理用户权限的myuser进行。 (这些特权应由 DBA 使用GRANT语句设置,未显示。)USER()CURRENT_USER()函数返回以下值:

mysql> SELECT USER(), CURRENT_USER();
+------------------+---------------------+
| USER()           | CURRENT_USER()      |
+------------------+---------------------+
| myuser@localhost | developer@localhost |
+------------------+---------------------+

相反,如果插件在 LDAP 目录中找到myuser是 Management 员,则它将返回manager作为用户名,并且会话以具有manager代理用户特权的myusercontinue。

mysql> SELECT USER(), CURRENT_USER();
+------------------+-------------------+
| USER()           | CURRENT_USER()    |
+------------------+-------------------+
| myuser@localhost | manager@localhost |
+------------------+-------------------+

为简单起见,外部身份验证不能是多级的:在上一示例中,没有考虑developermanager的凭据。但是,如果 Client 端尝试以developermanager帐户直接连接并进行身份验证,则仍会使用它们,这就是为什么应保护那些代理帐户以防止直接登录(请参阅防止直接登录到代理帐户)。

默认代理用户和匿名用户冲突

如果要创建默认代理用户,请检查其他优先于默认代理用户的现有“匹配任何用户”帐户,因为它们会阻止该用户按预期工作。

在前面的讨论中,默认代理用户帐户的主机部分带有'',与任何主机匹配。如果您设置默认的代理用户,请注意还要检查非代理帐户是否存在与同一用户部分和主机部分中的'%'相同的帐户,因为'%'也与任何主机匹配,但服务器使用的规则优先于''。在内部对帐户行进行排序(请参阅第 6.2.5 节“访问控制,第 1 阶段:连接验证”)。

假设 MySQL 安装包含以下两个帐户:

-- create default proxy account
CREATE USER ''@''
  IDENTIFIED WITH some_plugin
  AS 'some_auth_string';
-- create anonymous account
CREATE USER ''@'%'
  IDENTIFIED BY 'anon_user_password';

第一个帐户(''@'')是默认的代理用户,用于为与其他特定帐户不匹配的用户认证连接。第二个帐户(''@'%')是一个匿名用户帐户,例如,它可以被创建为允许没有自己帐户的用户匿名连接。

两个帐户具有相同的用户部分(''),该用户部分与任何用户都匹配。每个帐户都有一个与任何主机匹配的主机部分。但是,帐户匹配中的连接尝试优先级高,因为匹配规则将''之前的主机_排序。对于与任何其他特定帐户都不匹配的帐户,服务器将尝试针对''@'%'(匿名用户)而不是''@''(默认代理用户)对它们进行身份验证。结果,从不使用默认代理帐户。

为避免此问题,请使用以下策略之一:

  • 删除匿名帐户,以免与默认代理用户冲突。

  • 使用更特定的默认代理用户,该用户比匿名用户先匹配。例如,要仅允许localhost个代理连接,请使用''@'localhost'

CREATE USER ''@'localhost'
  IDENTIFIED WITH some_plugin
  AS 'some_auth_string';

此外,将任何GRANT PROXY语句修改为名称''@'localhost'而不是''@''作为代理用户。

请注意,此策略会阻止来自localhost的匿名用户连接。

  • 使用命名的默认帐户,而不是匿名的默认帐户。有关此技术的示例,请查阅有关使用authentication_windows插件的说明。参见第 6.4.1.8 节“ Windows 可插入身份验证”

  • 创建多个代理用户,一个用于本地连接,一个用于“其他所有”(远程连接)。这在本地用户应具有与远程用户不同的特权时特别有用。

创建代理用户:

-- create proxy user for local connections
CREATE USER ''@'localhost'
  IDENTIFIED WITH some_plugin
  AS 'some_auth_string';
-- create proxy user for remote connections
CREATE USER ''@'%'
  IDENTIFIED WITH some_plugin
  AS 'some_auth_string';

创建代理用户:

-- create proxied user for local connections
CREATE USER 'developer'@'localhost'
  IDENTIFIED WITH mysql_no_login;
-- create proxied user for remote connections
CREATE USER 'developer'@'%'
  IDENTIFIED WITH mysql_no_login;

为每个代理帐户授予相应代理帐户的PROXY特权:

GRANT PROXY
  ON 'developer'@'localhost'
  TO ''@'localhost';
GRANT PROXY
  ON 'developer'@'%'
  TO ''@'%';

最后,向本地和远程代理用户(未显示)授予适当的特权。

假设some_plugin/'some_auth_string'组合使some_plugin将 Client 端用户名 Map 到developer。本地连接与''@'localhost'代理用户匹配,后者 Map 到'developer'@'localhost'代理用户。远程连接与''@'%'代理用户匹配,后者 Map 到'developer'@'%'代理用户。

服务器支持代理用户 Map

一些身份验证插件自己实现代理用户 Map(例如,PAM 和 Windows 身份验证插件)。默认情况下,其他身份验证插件不支持代理用户。其中,有些可以请求 MySQL 服务器本身根据授予的代理特权mysql_native_passwordsha256_passwordMap 代理用户。如果启用了check_proxy_users系统变量,则服务器将对发出此请求的所有身份验证插件执行代理用户 Map:

  • 默认情况下,check_proxy_users是禁用的,因此即使请求服务器支持代理用户的身份验证插件,服务器也不执行代理用户 Map。

例如,要启用所有上述功能,请在my.cnf文件中的以下行启动服务器:

[mysqld]
check_proxy_users=ON
mysql_native_password_proxy_users=ON
sha256_password_proxy_users=ON

假设已启用相关的系统变量,请照常使用CREATE USER创建代理用户,然后将PROXY特权授予其他单个帐户以作为代理用户。当服务器收到对代理用户的成功连接请求时,它将发现该用户具有PROXY特权,并使用该特权来确定适当的代理用户。

-- create proxy account
CREATE USER 'proxy_user'@'localhost'
  IDENTIFIED WITH mysql_native_password
  BY 'password';

-- create proxied account and grant its privileges;
-- use mysql_no_login plugin to prevent direct login
CREATE USER 'proxied_user'@'localhost'
  IDENTIFIED WITH mysql_no_login;
-- grant privileges to proxied account
GRANT ...
  ON ...
  TO 'proxied_user'@'localhost';

-- grant to proxy account the
-- PROXY privilege for proxied account
GRANT PROXY
  ON 'proxied_user'@'localhost'
  TO 'proxy_user'@'localhost';

要使用代理帐户,请使用其名称和密码连接到服务器:

shell> mysql -u proxy_user -p
Enter password: (enter proxy_user password here)

身份验证成功,服务器发现proxy_user拥有proxied_userPROXY特权,并且会话以proxy_user拥有proxied_user的特权 continue 进行。

服务器执行的代理用户 Map 受以下限制:

  • 即使授予了关联的PROXY特权,服务器也不会代理匿名用户或从匿名用户代理。

  • 如果单个帐户已被授予多个代理帐户的代理特权,则服务器代理用户 Map 是不确定的。因此,不鼓励向单个帐户授予多个代理帐户的代理权限。

代理用户系统变量

有两个系统变量可帮助跟踪代理登录过程:

  • proxy_user:如果不使用代理,则此值为NULL。否则,它指示代理用户帐户。例如,如果 Client 端通过''@''代理帐户进行身份验证,则此变量的设置如下:
mysql> SELECT @@proxy_user;
+--------------+
| @@proxy_user |
+--------------+
| ''@''        |
+--------------+
  • external_user:有时,身份验证插件可以使用外部用户向 MySQL 服务器进行身份验证。例如,当使用 Windows 本机身份验证时,使用 Windows API 进行身份验证的插件不需要将登录 ID 传递给它。但是,它仍然使用 Windows 用户 ID 进行身份验证。插件可以使用external_user只读会话变量将此外部用户 ID(或其首 512 个 UTF-8 字节)返回给服务器。如果插件未设置此变量,则其值为NULL