14.4.6 多租户与账号隔离实践

多租户与账号隔离的目标是“入口鉴权、路由隔离、后端最小权限”。在ProxySQL层同时做到:前端账号独立、规则强制路由、后端账号最小化授权,从而避免跨租户访问与连接污染,并便于审计与限流。

原理草图(前端账号、规则路由、后端账号的三层隔离):

文章图片

实施步骤与完整示例#

以下示例演示两租户隔离:tenant_a、tenant_b。假设ProxySQL管理端口6032已可访问。

1) 后端数据库账号准备(MySQL)#

-- 在MySQL后端执行
CREATE DATABASE tenant_a;
CREATE DATABASE tenant_b;

CREATE USER 'u_tenant_a'@'%' IDENTIFIED BY 'Apass#2024';
CREATE USER 'u_tenant_b'@'%' IDENTIFIED BY 'Bpass#2024';

GRANT ALL PRIVILEGES ON tenant_a.* TO 'u_tenant_a'@'%';
GRANT ALL PRIVILEGES ON tenant_b.* TO 'u_tenant_b'@'%';
FLUSH PRIVILEGES;

2) ProxySQL 后端实例与hostgroup配置#

-- 连接管理端
mysql -u admin -padmin -h 127.0.0.1 -P6032

-- 后端实例
INSERT INTO mysql_servers(hostgroup_id, hostname, port, max_connections) VALUES
(10, '10.0.0.11', 3306, 200),
(20, '10.0.0.12', 3306, 200);

LOAD MYSQL SERVERS TO RUNTIME;
SAVE MYSQL SERVERS TO DISK;

3) 前端账号(租户账号)与后端账号映射#

-- 前端租户账号,仅能访问各自的默认hostgroup
INSERT INTO mysql_users(
  username, password, default_hostgroup, max_connections,
  transaction_persistent, active, default_schema,
  backend_username, backend_password
) VALUES
('tenant_a', 'Ta#2024', 10, 50, 1, 1, 'tenant_a', 'u_tenant_a', 'Apass#2024'),
('tenant_b', 'Tb#2024', 20, 50, 1, 1, 'tenant_b', 'u_tenant_b', 'Bpass#2024');

LOAD MYSQL USERS TO RUNTIME;
SAVE MYSQL USERS TO DISK;

4) 路由规则(禁止跨库访问)#

-- 规则:tenant_a只能访问tenant_a库,tenant_b只能访问tenant_b库
INSERT INTO mysql_query_rules(
  rule_id, username, schemaname, destination_hostgroup, apply, active, comment
) VALUES
(1001, 'tenant_a', '^tenant_a$', 10, 1, 1, 'tenant_a only'),
(1002, 'tenant_b', '^tenant_b$', 20, 1, 1, 'tenant_b only');

-- 防御性规则:tenant_a访问非tenant_a库直接拒绝
INSERT INTO mysql_query_rules(
  rule_id, username, schemaname, error_msg, apply, active, comment
) VALUES
(1003, 'tenant_a', '^(?!tenant_a$).*', 'Access denied for tenant_a', 1, 1, 'deny cross-db'),
(1004, 'tenant_b', '^(?!tenant_b$).*', 'Access denied for tenant_b', 1, 1, 'deny cross-db');

LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;

5) 连接验证#

# tenant_a连接并访问自身库
mysql -u tenant_a -pTa#2024 -h 127.0.0.1 -P6033 -D tenant_a -e "SELECT DATABASE();"
# 预期输出:tenant_a

# tenant_a尝试访问tenant_b库(应被拒绝)
mysql -u tenant_a -pTa#2024 -h 127.0.0.1 -P6033 -D tenant_b -e "SHOW TABLES;"
# 预期输出:ERROR 1045 (28000): Access denied for tenant_a

关键命令与参数解释#

  • default_hostgroup:用户默认路由组,确保未命中规则时仍走本租户组。
  • transaction_persistent=1:同一事务保持在同一后端连接,降低跨租户连接复用风险。
  • max_connections:单租户最大连接数,防止资源挤占。
  • backend_username/backend_password:前端账号映射后端最小权限账号。

监控与审计示例#

-- 查看租户连接情况
SELECT username, frontend_connections, backend_connections
FROM stats_mysql_users;

-- 查看路由规则命中
SELECT rule_id, hits, comment
FROM stats_mysql_query_rules
ORDER BY hits DESC;

排错清单与定位命令#

1) 租户无法连接

SELECT * FROM mysql_users WHERE username='tenant_a'\G
SELECT * FROM runtime_mysql_users WHERE username='tenant_a'\G
  • 若runtime中无该用户,执行LOAD MYSQL USERS TO RUNTIME

2) 连接上但路由不生效

SELECT * FROM mysql_query_rules WHERE username='tenant_a'\G
SELECT * FROM runtime_mysql_query_rules WHERE username='tenant_a'\G
  • 确保apply=1active=1schemaname正则正确。

3) 访问被拒绝但期望允许

SELECT rule_id, schemaname, error_msg FROM mysql_query_rules
WHERE username='tenant_a' ORDER BY rule_id;
  • 检查是否被“deny cross-db”规则误匹配;调整规则优先级(rule_id越小优先级越高)。

练习#

1) 新增租户tenant_c,要求只能访问数据库tenant_c,并设置max_connections=20
2) 为tenant_a设置更高优先级的读写路由规则,使读走hostgroup 11、写走hostgroup 10。
3) 使用stats_mysql_usersstats_mysql_query_rules统计租户高峰连接与规则命中,并输出结论。