在部署 OpenStack Keystone 服務時,雖對 'keystone'@'%' 授權,卻持續報 Access denied for user 'keystone'@'controller'。根因在於 **MySQL 授權主機匹配機制與系統 hostname 解析的交互差異**。
% 不包含 localhost: MySQL 官方文檔明確說明,通配符 % 匹配「任意非本地主機」。本地連接(Unix Socket 或 127.0.0.1)屬於 localhost 或 127.0.0.1,需單獨授權。'user'@'host' 唯一標識。'keystone'@'%' 和 'keystone'@'localhost' 是兩個完全不同的賬號,權限獨立。skip_name_resolve=ON 關閉)。若 /etc/hosts 將 controller 解析到 127.0.0.1,則連接被識別為 localhost。-- 已執行授權
GRANT ALL ON keystone.* TO 'keystone'@'localhost' IDENTIFIED BY 'PASS';
GRANT ALL ON keystone.* TO 'keystone'@'%' IDENTIFIED BY 'PASS';
-- /etc/hosts 內容
127.0.0.1 localhost
192.168.38.100 controller
-- keystone.conf 連接串
connection = mysql://keystone:PASS@controller/keystone
-- 報錯
Access denied for user 'keystone'@'controller' (using password: YES)
controller → 192.168.38.100(本機 IP)。192.168.38.100。192.168.38.100 → controller(若啟用 DNS 或 /etc/hosts 映射)。'keystone'@'controller',**無此用戶**,授權失敗。關鍵點: 即使 /etc/hosts 有 127.0.0.1 localhost controller,MySQL 看到的是 TCP 連接的來源 IP(192.168.38.100),而非 127.0.0.1,因此不走 localhost 授權。
| 方案 | 操作 | 優點 | 缺點 |
|---|---|---|---|
| 方案 A:授權實際主機名(推薦) | GRANT ALL ON keystone.* TO 'keystone'@'controller' IDENTIFIED BY 'PASS'; | 精確匹配,符合最小權限 | 需預知所有客戶端主機名/IP |
| 方案 B:授權具體 IP | GRANT ALL ON keystone.* TO 'keystone'@'192.168.38.100' IDENTIFIED BY 'PASS'; | 不依賴 DNS/hosts 解析 | IP 變更需重新授權 |
| 方案 C:修改連接串用 127.0.0.1 | connection = mysql://keystone:PASS@127.0.0.1/keystone | 走 localhost 授權,繞過解析 | 僅適用同機部署,不利於高可用漂移 |
| 方案 D:關閉主機名解析 | my.cnf: skip_name_resolve=ON | 直接用 IP 匹配,性能略增 | 授權必須用 IP,不可用主機名 |
skip_name_resolve=ON,避免 DNS 故障導致連接超時,授權統一使用 IP 或 %。/etc/hosts 別名。localhost、127.0.0.1、VIP、%(內網),覆蓋所有連接場景。SELECT user, host FROM mysql.user WHERE user='keystone'; 確認授權覆蓋面。MySQL 授權看似簡單,實則涉及網絡層(IP/主機名)、傳輸層、應用層連接串的多層交互。**「授權 % 以為萬事大吉」是危險的誤區**。理解 localhost、127.0.0.1、具體IP/主機名、% 的匹配優先級與差異,結合 skip_name_resolve 策略,才能構建穩健的數據庫訪問控制體系。