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=1、active=1、schemaname正则正确。
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_users与stats_mysql_query_rules统计租户高峰连接与规则命中,并输出结论。