當密碼儲存在選項文件中時 MySQL 拒絕登錄嘗試
第一的:
- 我們正在執行 MySQL 5.7.13。
- 作業系統是 Red Hat Enterprise Linux 7.2。
- 該問題最初是使用 Python/Connector 2.1.3 發現的
- 系統未加入域(尚未。)
- 從 MySQL 自己的 yum 儲存庫安裝的所有 MySQL 軟體包!
場景:我正在嘗試建構一個簡單的監控解決方案,以密切關注我控制的目前一百多台機器的某些方面(並且在不久的將來可能會增加數量);我需要能夠生成安全報告,例如,給他們包版本以證明最近披露的漏洞已在 x 台機器上得到修補。
我可以使用 Ansible 等人實時提取這些資訊,但是 a) 不能保證所有機器在任何給定時間都會連接,並且 b) 管理工具可以給我目前狀態,但不一定是我可能需要的歷史數據報告。所以從我的角度來看,解決方案是一個可以容納每個系統記錄的數據庫;當系統更新時,我們將一條新記錄放入數據庫中以指示更改,然後我可以稍後將其提取出來。
為此,我們使用 MySQL 數據庫。目前,我們有一個簡單的準系統 MySQL 安裝,其中創建了一個小表集並定義了一個非 root 使用者。MySQL中定義的使用者列表是:
select user, host, authentication_string from mysql.user; +-------------+-----------+-------------------------------------------+ | user | host | authentication_string | +-------------+-----------+-------------------------------------------+ | root | localhost | *D971D136A477A4C205AEF706... | | mysql.sys | localhost | *THISISNOTAVALIDPASSWORDT... | | pkg_manager | localhost | *E91158E2E26F343D6639E4BD... | +-------------+-----------+-------------------------------------------+
所以不存在空字元串使用者名。
有問題的帳戶是 pkg_manager 帳戶。授予它的權限是:
mysql> show grants for 'pkg_manager'@'localhost'; +-------------------------------------------------------------------------------------+ | Grants for pkg_manager@localhost | +-------------------------------------------------------------------------------------+ | GRANT USAGE ON *.* TO 'pkg_manager'@'localhost' | | GRANT SELECT, INSERT ON `panopticon`.`package` TO 'pkg_manager'@'localhost' | | GRANT SELECT, INSERT ON `panopticon`.`package_history` TO 'pkg_manager'@'localhost' | +-------------------------------------------------------------------------------------+
pkg_manager 帳戶連接沒有問題,使用如下命令行:
[~]$ mysql -u pkg_manager -p --database=panopticon Enter password: Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 6 Server version: 5.7.13 MySQL Community Server (GPL) Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
但是,我需要允許自動化工具登錄數據庫(它將提取目前不是最新的系統列表,檢查其中哪些目前線上,然後將更新推送給它們並記錄每個成功更新。)該工具將使用 Python 編寫,並且我已拉下 Python 2.1.3 連接器。
我們為該工具建構了一個 options.cnf 文件,並將其儲存在一個受限制的目錄中(最初是 root:root 0700,目前是一個 :。通過連接器在 python 中打開連接的初始測試:
connection = mysql.connector.connect(option_files='/path/to/options.cnf')
導致錯誤 1045,SQLState 28000 消息。(實際上,首先還有一些其他的事情,主要涉及指定套接字路徑。然而,這些都被清除了,我們知道它們已經不礙事了,因為現在 MySQL 日誌正在報告失敗的連接嘗試。)
我們知道這部分有效,因為 1045 錯誤的完整詳細資訊是“使用者 ‘pkg_manager’@’localhost’ 的訪問被拒絕(使用密碼:YES)”。所以它正在讀取選項文件,它正在辨識 pkg_manager 帳戶,並且它正在查看文件中的密碼條目(如果不存在密碼條目,則消息的結尾變為“(使用密碼:否)”。
然後,我們嘗試通過使用相同的選項文件來簡化問題,並在其中創建一個客戶端部分,指定出現在 connector_python 部分中的所有相同欄位。這也失敗了:
mysql --defaults-file="/path/to/options.cnf"
但是,我們知道在命令行上手動指定登錄資訊時該帳戶有效,如上所示。
經過數小時搜尋登錄失敗的使用者體驗後,我們決定嘗試一種混合方法 - 使用 –defaults 文件進行命令行登錄,同時還指定 -p 以強制我們在命令行中輸入密碼:
[~]$ mysql --defaults-file="/etc/pkg_manager/db_info/options.cnf" -p Enter password: Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Welcome to the MySQL monitor. Commands end with ; or \g. ... mysql>
因此,我們知道 options conf 文件中提供的其餘資訊是正確的。簡單地執行 mysql –print-defaults (以確定路徑搜尋列表中是否有任何其他選項文件可能會干擾)會導致列出的啟動選項為零,因此沒有其他選項可以阻止。
在這一點上,我幾乎被困住了。MySQL 官方文件明確指出,除了加密的本地登錄文件之外,您還可以將未加密的密碼儲存在選項文件中:http: //dev.mysql.com/doc/refman/5.7/en/password-security-user.html
然而,很明顯,它不起作用。我查看了可以設置的配置文件變數列表,我沒有看到任何似乎會阻止來自選項文件的密碼驗證的內容。我已將錯誤日誌記錄設置為 3,但沒有提供有關連接問題的詳細資訊,除了一條表明來自 ‘pkg_manager’@’localhost’ 的連接嘗試被拒絕的單行註釋。
在我忘記之前,這是選項文件(無密碼):
1 # Options file for pkg_manager, easy way to "securely" store database login info. 2 [connector_python] 3 user="pkg_manager" 4 password="*********" 5 database="panopticon" 6 host="localhost" 7 unix_socket="/var/lib/mysql/mysql.sock" 8 9 [client] 10 user="pkg_manager" 11 password="*********" 12 database="panopticon" 13 host="localhost" 14 socket="/var/lib/mysql/mysql.sock"
這是 /etc/my.cnf 文件:
1 # For advice on how to change settings please see 2 # http://dev.mysql.com/doc/refman/5.7/en/server-configuration-defaults.html 3 4 [mysqld] 5 # 6 # Remove leading # and set to the amount of RAM for the most important data 7 # cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%. 8 # innodb_buffer_pool_size = 128M 9 # 10 # Remove leading # to turn on a very important data integrity option: logging 11 # changes to the binary log between backups. 12 # log_bin 13 # 14 # Remove leading # to set options mainly useful for reporting servers. 15 # The server defaults are faster for transactions and fast SELECTs. 16 # Adjust sizes as needed, experiment to find the optimal values. 17 # join_buffer_size = 128M 18 # sort_buffer_size = 2M 19 # read_rnd_buffer_size = 2M 20 datadir=/data/panopticon 21 socket=/var/lib/mysql/mysql.sock 22 23 skip-networking 24 25 # Disabling symbolic-links is recommended to prevent assorted security risks 26 symbolic-links=0 27 28 log-error=/var/log/mysqld.log 29 log_error_verbosity=3 30 general-log=1 31 general_log_file=/var/log/mysql_general.log 32 pid-file=/var/run/mysqld/mysqld.pid
我終於有機會再花幾個小時來研究這個問題,我找到了解決方案。正如我所想的那樣,它簡單得令人沮喪,但它也違背了多個使用者論壇甚至 MySQL 參考文件上發布的建議。
如果我的密碼儲存在引號中的選項文件中,它將無法連接。當我從密碼中刪除引號時,它會連接。在我意識到這一點之前,我最終瀏覽了 Python 連接器程式碼,主要是因為我之前嘗試過刪除密碼上的引號,但是對於一個稍微不同的問題,然後在我的腦海中將這兩個問題混為一談。
我注意到,在瀏覽 Python 連接器程式碼時,我可以直接實例化一個選項文件解析器,所以我試了一下(將我的選項文件的路徑作為參數傳遞。)當我這樣做時,我看到了所有選項,包括密碼,被導入到解析器狀態,並帶有文件中的任何引用。但是,在那之後的任何階段都沒有採取措施從密碼中刪除引號(至少我看到了),所以當連接器使用解析器打開選項文件時,引號被包含並傳遞給伺服器作為密碼的一部分進行身份驗證。
我很可能會將其作為向 MySQL 開發人員報告的錯誤報告,因為所有其他選項都可以正常使用引號。