MySQL - zagubione ;) hasło root'a

Gdzie to hasło...Spodziewam się, że w swoim systemie (Linux) zainstalowałeś bazę MySQL5.7.x i spotkała Cię niespodzianka.

Nie tylko Ciebie i mnie - wystarczy wygooglować hasło: "mysql not asking for root password" ("mysql nie pyta o hasło superusera"). Nie chodzi tu również o "swobodny" (nieograniczony hasłem) dostęp. Podczas instalacji bazy - inaczej niż dotychczas - nie zostaliśmy poproszeni o podanie hasła dla użytkownika root (superusera).

Możliwe jest zalogowanie się do bazy wyłącznie z poziomu superusera, root'a (np. po wydaniu polecenia: sudo su). Wiele aplikacji wykorzystujących serwer MySQL oczekuje podania danych do autoryzacji: nazwy użytkownika oraz przypisanego mu hasła. A tu wykorzystywana dawniej procedura logowania kończy się błędem:

~ $ mysql -u root -p
Enter password: **********
ERROR 1698 (28000): Access denied for user 'root'@'localhost'

Ale sprawdzenie wersji zainstalowanej bazy (polecenie: mysql --version) bezbłędnie wyświetla oczekiwany komunikat. Opisana procedura została wykonana w środowisku Linux Mint 19 Cinnamon 3.8.8, mysql: v. 14.14, dist.: 5.7.23, (x86_64).

Dla innych dystrybucji i/lub wersji serwera bazy danych niektóre frazy poleceń mogą się różnić.

Brak pytania o hasło jest konsekwencją tego, że podczas instalacji bazy danych system domyślnie użył odmiennej metody identyfikacji użytkownika - poprzez (plugin) "auth_socket" (w niektórych dystrybucjach: unix_socket).

Metoda ta ma wiele zalet. Identyfikacja użytkownika (w dużym uproszczeniu) następuje tu na podstawie tego jak jesteś zalogowany w systemie. Sprawdza: czy użytkownik łączy się z bazą za pomocą gniazda UNIX. Następnie porównuje nazwę użytkownika. Jeśli jest poprawna (występuje na liście) uzyskujesz dostęp, jeśli nie próba połączenia kończy się błędem.

W sieci dostępne są opisy jak wymusić ten typ autoryzacji w konkretnych przypadkach i zastosowaniach. Tworcy MySQL wskazują, że autoryzacja hasłem "jest przestarzała i zostanie usunięta w przyszłych wersjach". Na dziś - chcielibyśmy z niej skorzystać. :) Metoda ta jakkolwiek może nie jest zgodna z nowymi trendami to daje oczekiwane efekty.

W sieci można też znaleźć opisy bardziej zaawansowanych metod wprowadzania zmian hasła (np. tutaj), ale poniższy opis dotyczy procedury wykonanej przeze mnie na podstawie różnych "podpowiedzi" odnalezionych w Internecie.

Stawiając lokalny serwer W3: apache2, php, mysql, itd. prościej jest skorzystać (?, przyzwyczajenia też odgrywają rolę) z haseł. Mowa tu o serwerach "nieprodukcyjnych".

Jeśli chcemy uzyskać założony cel musimy zmienić system (plugin) autoryzacji i ustawić hasło w tym samym czasie, w tej samej sesji dostępu do bazy. Inaczej - porażka. :/ W sieci znalazłem szereg opisów, ale przy kolejnych próbach... co chwilę potykałem się o własne błędy.

Poniżej opisuję tę która przyniosła oczekiwany (i powtarzalny) efekt. Zacznijmy od zatrzymania serwera mysql poleceniem (wydanym w oknie terminala - wszystkie polecenie wydawane są w tym trybie):

~ $ sudo /etc/init.d/mysql stop [ ok ]
Stopping mysql (via systemctl): mysql.service.

Zanim przejdę do dalszych działań chciałbym wskazać na istotny szczegół. W wielu dostępnych opisach powtarzane są komendy, sugerowane różne tryby wydawania poleceń. W jednym i tym samym oknie terminala lub w oddzielnych.

Gdy "opanowałem" emocje i zacząłem czytać ze zrozumieniem na ekranie treść komunikatów okazało się, że przyczyna niepowodzeń była oczywista. Gdy się wie... :P Nie stawiam sobie za cel gruntowne poznanie zasad działania MySQL'a. Chodziło tylko o rozwiązanie problemu, który się pojawił.

By zrozumieć gdzie w moich działaniach tkwił błąd wystarczyło jedno okno terminala. Zrób tak:  po zatrzymaniu serwera MySQL wykonaj polecenie:

~ $ ls /var/run/mysqld
ls: nie ma dostępu do '/var/run/mysqld': Nie ma takiego pliku ani katalogu

W środowisku Linux Mint 19 Cinnamon 3.8.8, mysql: v. 14.14, dist.: 5.7.23, (x86_64) po zatrzymaniu serwera MySQL z katalogu /var/run zniknął ;) podkatalog mysqld!  Tak przynajmniej było u mnie.

Jeśli teraz spróbujesz uruchomić serwer (w trybie safe) i wydasz polecenie:

~ $ sudo mysqld_safe --skip-grant-tables&
[1] 2232 ~ $ 2018-08-30T13:28:21.679650Z mysqld_safe Logging to syslog.
2018-08-30T13:28:21.689098Z mysqld_safe Logging to '/var/log/mysql/error.log'.
2018-08-30T13:28:21.694071Z mysqld_safe Directory '/var/run/mysqld' for UNIX socket file don't exists.
[1]+ Kod wyjścia 1 sudo mysqld_safe --skip-grant-tables

zakończy się ono błędem(!!!) braku dostępu właśnie do tego katalogu.

Dlatego po zatrzymaniu serwera musimy go stworzyć i nadać odpowiednie uprawnienia grupie mysql:

~ $ sudo mkdir /var/run/mysqld; sudo chown mysql /var/run/mysqld

Teraz wydanie polecenia:

~ $ sudo mysqld_safe --skip-grant-tables&
[1] 3658 ~ $ 2018-08-30T13:05:21.053348Z mysqld_safe Logging to syslog.
2018-08-30T13:05:21.057803Z mysqld_safe Logging to '/var/log/mysql/error.log'.
2018-08-30T13:05:21.087528Z mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
<może być tu potrzebne wciśnięcie klawisza Enter>

przebiega bez problemu i pozwala zalogować się do bazy po wydaniu polecenia (połącz użytkownika root z serwerem mysql, bazą mysql) :

~ $ sudo mysql --user=root mysql

W efekcie na ekranie pojawi się następujący komunikat oraz monit (znak zachęty) serwera (mysql>):

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 3
Server version: 5.7... /.../
mysql>

Od tej chwili wszystkie polecenia (komendy - poprzedzone znakiem zachęty: mysql> zostały wytłuszczone) wydawane są z poziomu serwera. Pamiętaj by wszystkie polecenia (z wyjątkiem tych poprzedzonych ukośnikiem \) kończyć znakiem średnika (;). Szerszy opis wskazanych poleceń, ich parametrów znajduje się w dokumentacji serwera MySQL.

W miejsce frazy Twoje_haslo wpisz treść hasła, przy pomocy którego będziesz się "logował" do serwera:

mysql> select user, host, plugin from mysql.user;
+------------------+-----------+-----------------------+
|        user      |   host    |       plugin          |
+------------------+-----------+-----------------------+
| root             | localhost | auth_socket           |
| mysql.session    | localhost | mysql_native_password |
| mysql.sys        | localhost | mysql_native_password |
| debian-sys-maint | localhost | mysql_native_password |
+------------------+-----------+-----------------------+
4 rows in set (0.00 sec)
mysql> update mysql.user set authentication_string = password('Twoje_haslo') where user = 'root' and host = 'localhost';
Query OK, 1 row affected, 1 warning (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 1

Zwróć uwagę na fakt, że jedynie użytkownik root ma odmiennie ustawiony tryb autoryzacji. Fakt, nie ma innych (rzeczywistych) użytkowników. Ale istotne jest to, że system autoryzacji poprzez "mysql_native_password" (a tego potrzebujemy) nadal jest wykorzystywany.

Aha, mamy jedno ostrzeżenie (warnings). Warto zobaczyć czego dotyczy:

mysql> show warnings;

+---------+------+-------------------------------------------------------------------+
|  Level  | Code |                          Message                                  |
+---------+------+-------------------------------------------------------------------+
| Warning | 1681 | 'PASSWORD' is deprecated and will be removed in a future release. |
+---------+------+-------------------------------------------------------------------+

(PASSWORD jest przestarzałe i zostanie usunięte w przyszłych wersjach!)

Teraz musimy wskazać, że autoryzacja dostępu dla root'a będzie odbywała się poprzez hasło:

mysql> update mysql.user set plugin="mysql_native_password" where user = 'root';
Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0

Proces można nieco uprościć. Oba polecenia można złożyć "w jednej linii", rozdzielając je przecinkiem. Zostaną wykonane po kolei.

Na końcu linii - oczywiście średnik!

Efekt polecenia ten sam. Ostrzeżenie również :D :

mysql> update mysql.user set authentication_string = password(‘Twoje_haslo’), plugin = ‘mysql_native_password’ where user = ‘root’ and host = ‘localhost’;

Pozostało tylko "aktywowanie" zmian, wylogowanie się z MySQL i zatrzymanie serwera:

mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
mysql> \q
Bye
~ $ sudo /etc/init.d/mysql stop

Najważniejsze mamy już za sobą. Trzeba jeszcze sprawdzić czy to działa :D .

Spróbujmy połączyć się z serwerem jako root podając wskazane uprzednio hasło.

Pojawienie się powitalnego bannera MySQL, znaku zachęty (mysql> ) oraz prawidłowe wyniki wykonania dwóch poleceń wskazuje, że jest dobrze:

~ $ mysql -u root -p
Enter password: *********
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7... /.../

mysql> select user, host, hex(authentication_string) from mysql.user;
+------------------+-----------+-------------------------------+
|       User       |    Host   |   HEX(authentication_string)  |
+------------------+-----------+-------------------------------+
|       root       | localhost | 2A371824............325424333 |
+------------------+-----------+-------------------------------+

mysql> select user, host, plugin from mysql.user where user="root";
+------+-----------+-----------------------+
| user |    host   |       plugin          |
+------+-----------+-----------------------+
| root | localhost | mysql_native_password |
+------+-----------+-----------------------+

mysql> \q

Na zakończenie nie pozostaje nic innego jak sprawdzenie co znajduje się w katalogu /var/run/mysqld:

~ $ ll /var/run/mysqld
razem 8
drwxr-xr-x 2 mysql mysql 100 sie 30 15:19 ./
drwxr-xr-x 36 root root 1040 sie 30 15:20 ../
-rw-r----- 1 mysql mysql 5 sie 30 15:19 mysqld.pid
srwxrwxrwx 1 mysql mysql 0 sie 30 15:19 mysqld.sock=
-rw------- 1 mysql mysql 5 sie 30 15:19 mysqld.sock.lock
~ $

Dla porządku dodam tylko, że opisana procedura działa i po wprowadzonych zmianach instalacja lokalnego serwera webowego przebiegła już bez problemów.

Jeśli opis przyda się komuś - to OK. Proszę jednocześnie zaawansowanych użytkowników MySQL o wyrozumiałóść dla dostrzeżonych uchybień prezentowanego opisu.