MySQL rejeita tentativas de login quando a senha é armazenada no arquivo de opções

4

Primeiro:

  • Estamos executando o MySQL 5.7.13.
  • OS é o Red Hat Enterprise Linux 7.2.
  • O problema foi descoberto usando o Python / Connector 2.1.3
  • O sistema não está associado ao domínio (ainda.)
  • Todos os pacotes MySQL instalados a partir dos próprios repositórios yum do MySQL!

O cenário: Eu estou tentando construir uma solução de monitoramento simples para manter abas em alguns aspectos de um conjunto de atualmente cerca de uma centena de máquinas sob meu controle (e provavelmente aumentando em número num futuro próximo); Eu sou obrigado a produzir relatórios para segurança, por exemplo, dando-lhes versões de pacotes para provar que as vulnerabilidades divulgadas recentemente foram corrigidas em x de máquinas.

Eu poderia extrair essa informação em tempo real com Ansible et al, mas a) não há garantia de que todas as máquinas serão conectadas a qualquer momento eb) ferramentas de gerenciamento podem me fornecer status atual, mas não necessariamente dados históricos que Eu posso precisar relatar. Então a solução da minha perspectiva é um banco de dados que pode abrigar registros para cada sistema; Quando um sistema é atualizado, lançamos um novo registro no banco de dados, indicando a mudança, e depois eu posso desenhar isso mais tarde.

Para isso, estamos usando um banco de dados MySQL. Atualmente, temos uma instalação simples e simples do MySQL com um pequeno conjunto de tabelas criado e um único usuário não root definido. A lista de usuários, conforme definida no MySQL, é:

select user, host, authentication_string from mysql.user;
+-------------+-----------+-------------------------------------------+
| user        | host      | authentication_string                     |
+-------------+-----------+-------------------------------------------+
| root        | localhost | *D971D136A477A4C205AEF706...              |
| mysql.sys   | localhost | *THISISNOTAVALIDPASSWORDT...              |
| pkg_manager | localhost | *E91158E2E26F343D6639E4BD...              |
+-------------+-----------+-------------------------------------------+

Portanto, não há nomes de usuário de string vazios presentes.

A conta do problema em questão é a conta pkg_manager. Conceder permissões sobre isso:

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' |
+-------------------------------------------------------------------------------------+

A conta pkg_manager conecta-se sem problemas, usando uma linha de comando como:

[~]$ 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> 

No entanto, preciso permitir que uma ferramenta automatizada faça login no banco de dados (ela estará puxando uma lista de sistemas que não estão atualizados no momento, verificando quais deles estão atualmente on-line e enviando atualizações para eles e gravar cada atualização bem sucedida.) A ferramenta será escrita em Python, e eu puxei para baixo o conector do Python 2.1.3.

Criamos um arquivo options.cnf para a ferramenta e o armazenamos em um diretório restrito (inicialmente root: root 0700, atualmente um:. Testes iniciais de abertura de uma conexão em python através do conector com:

connection = mysql.connector.connect(option_files='/path/to/options.cnf')

resultou em um erro 1045, mensagem SQLState 28000. (Na verdade, houve algumas outras coisas primeiro, principalmente envolvendo a especificação do caminho do soquete. Essas, no entanto, são esclarecidas e sabemos que elas estão fora do caminho, porque agora o log do MySQL está relatando as tentativas de conexão com falha.)

Sabemos que isso está funcionando parcialmente, porque os detalhes completos do erro 1045 são "Acesso negado ao usuário 'pkg_manager' @ 'localhost' (usando a senha: YES)". Então está lendo o arquivo de opções, ele está identificando a conta pkg_manager, e está vendo a entrada da senha no arquivo (se não houver nenhuma entrada de senha presente, o final da mensagem se torna "(usando senha: NO)".

Em seguida, tentamos simplificar o problema usando o mesmo arquivo de opções e criando uma seção do cliente, especificando todos os mesmos campos que estão presentes na seção connector_python. Isso também falhou:

mysql --defaults-file="/path/to/options.cnf"

No entanto, sabíamos que a conta funcionava ao especificar manualmente as informações de login na linha de comando, conforme demonstrado acima.

Após muitas horas pesquisando experiências de usuários com falhas de login, decidimos tentar uma abordagem híbrida - login de linha de comando usando o arquivo --defaults, mas também especificando -p para nos forçar a inserir a senha na linha de comando:

[~]$ 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>

Portanto, sabemos que o restante das informações fornecidas no arquivo conf de opções está correto. Simplesmente executando mysql --print-defaults (para determinar se existem outros arquivos de opções na lista de busca de caminho que possam interferir) resulta em zero opções de inicialização listadas, então não há outras opções para atrapalhar.

E neste momento, estou praticamente preso. A documentação oficial do MySQL declara explicitamente que você pode armazenar senhas não criptografadas em um arquivo de opções, além do arquivo de login local criptografado: link

No entanto, claramente, não está funcionando. Examinei a lista de variáveis de arquivos de configuração que podem ser configuradas e não vejo nenhuma que pareça impedir a senha auth de um arquivo de opções. Transformei o registro de erros em até 3, mas nenhum detalhe foi apresentado sobre problemas de conexão além de uma única nota de linha indicando que uma tentativa de conexão de 'pkg_manager' @ 'localhost' foi rejeitada.

Antes que eu esqueça, aqui está o arquivo de opções (sans password):

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"

E aqui está o arquivo /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
    
por Robutt 08.07.2016 / 17:51

1 resposta

1

Eu finalmente tive a chance de colocar mais algumas horas para analisar o problema e encontrei a solução. Como eu tinha imaginado, é frustrantemente simples, mas também vai contra o conselho postado em vários fóruns de usuários e até mesmo na documentação de referência do MySQL.

Se minha senha estiver armazenada no arquivo de opções entre aspas, ela não conseguirá se conectar. Quando eu removo as aspas da senha, ele se conecta. Acabei passando pelo código do conector Python antes de me dar conta disso, principalmente porque tentei remover as aspas da senha anteriormente, mas para um problema um pouco diferente e, em seguida, colocar as duas questões em minha mente.

Eu observei, ao passar pelo código do conector Python, que eu poderia instanciar diretamente um analisador de arquivo de opções, então tentei (passando o caminho para o arquivo de opções como um parâmetro). Quando o fiz, vi que Todas as opções, incluindo a senha, foram importadas para o estado do analisador com as cotações que tinham no arquivo. No entanto, em nenhum estágio depois disso foram tomadas medidas para remover as aspas da senha (pelo menos que eu vi), por isso parece provável que quando o conector abre o arquivo de opções com o analisador, as aspas são incluídas e passadas para o servidor para autenticação como parte da senha.

Eu provavelmente tomarei isso como um relatório de bug para os desenvolvedores do MySQL, já que todas as outras opções funcionam bem com aspas.

    
por 30.07.2016 / 17:16