如何安全的使用 bash 操作 MySQL
最近在 percona blog 中看到这篇文章 use-mysql-shell-securely-from-bash , 讲述如何在 Bash 中安全的使用 MySQL 进行查询. 字面意思的安全要比文章所说的更宽泛, 不过这篇文章有几点可取之处值得学习, 因为 Bash 的方便性, 我们经常用到 Bash 来完成各种脚本任务, 如果操作数据库的话(比如备份, 监控等), 任何可以执行 ps aux
命令的用户都可以看到正在执行的命令, 一些敏感信息如配置文件, 用户密码等都可以看到, 另外在 MySQL 5.6 之后命令行下用密码连接数据库也会得到警告信息 Warning: Using a password on the command line interface can be insecure
.
如何隐藏进程中的密码信息是我们都需要关注的, 文章中的作者用了很巧妙的方式来达到隐藏的目的, 甚至 ip, port 等也可以隐藏, 并且 5.6 版本中的 warning 信息也不会再出现, 我在下面会基于此技巧进行阐述, 另外会稍作修改以满足其它场景的需要.
percona 文章中介绍的技巧主要通过管道和 MySQL 的 --defaults-file
参数来实现隐藏的目的, 如下 bash 函数 mysql_exec
所示:
mysql_exec() {
local query="$1"
local opts="$2"
mysql_exec_result=$(
printf "%s\n" \
"[client]" \
"user=${mysql_user}" \
"password=${mysql_password}" \
"host=${mysql_host}" \
"port=${mysql_port}" \
"database=${mysql_database}" \
| mysql --defaults-file=/dev/stdin "${opts}" -e "${query}"
)
printf 输出 mysql client 相关的配置, 类似默认配置 my.cnf 里的 [client] 部分, 然后通过管道将相关的配置信息给到 --defaults-file
指定的 /dev/stdin
标准输入中. 很巧妙的使用了 stdin 来实现隐藏的目的, 再用 ps 查看进程的话就是类似下面的输出:
root 5051 4899 0 15:31 pts/0 00:00:00 mysql --defaults-file=/dev/stdin -e select sleep(10)
下面部分是 blog 中介绍的第二点 , 通过将配置保存到临时文件中, 这样就可以使用 mysql <file.sql
的方式导入 sql 文件, 这种方式和第一点是类似的:
mysql_exec_from_file() {
local query_file="$1"
local opts="$2"
local tmpcnf="$(mktemp)"
chmod 600 "${tmpcnf}"
printf "%s\n"
"[client]"
"user=${mysql_user}"
"password=${mysql_password}"
"host=${mysql_host}"
"port=${mysql_port}"
"database=${mysql_database}"
> "${tmpcnf}"
mysql_exec_from_file_result=$(
mysql --defaults-file="${tmpcnf}" "$opts" < "${query_file}"
)
rm "${tmpcnf}" # unlink "${tmpcnf}"
}
到这里, 很多人会有一个疑问, 这个问题在 blog 评论部分也有人提到, 也就是为什么下面的方式是错误的:
printf ..... | mysql --defaults-file=/dev/stdin ${opts} < dump.sql
我们手工执行后报错为:
error: Found option without preceding group in config file: /dev/stdin at line: 1
Fatal error in defaults handling. Program aborted
报错信息是和 config file 相关的, 由此可知 < dump.sql
的内容重定向到了 /dev/stdin
中, 成为了配置信息的一部分. 我们可以做下相应的更改就可以实现同样的功能:
printf ..... | mysql --defaults-file=/dev/stdin ${opts} -e "$(cat dump.sql)"
其它的编程语言提供了更好的方式来访问 MySQL 数据库, 如果脚本执行的时候还是显示的存在密码等信息, 大家就可以借鉴上述的方法来隐藏敏感的信息.
另外, mysql 5.6.6 版本开始提供了 mysql_config_editor
工具对 [client]
相关的配置进行加密, 如何查看解密的信息参见 get-passwords-plain-text-mylogin-cnf.