Using mysql_embedded and mysqld –bootstrap to tinker with privilege tables

A couple weeks ago, I started wondering about some alternative approaches to resetting the MySQL or MariaDB password. The tried-and-true methods of --skip-grant-tables
and --init-file
are fine, but I thought there must be some more “simple” (in terms of fewer steps at least) and more programmatic way. It occurred to me that it should be possible to use the embedded server (libmysqld
) to accomplish this task. In this post, I’ll look at how to use the mysql_embedded
tool to inspect and modify grant tables, as well as how to use the --bootstrap
option to mysqld
to do the same. MariaDB and MySQL distributions include an executable called mysql_embedded
. The documentation for this thing is very poor, but basically it is just a MySQL client statically linked to libmysqld
. When you execute the program, an embedded MySQL or MariaDB server is instantiated and you can execute statements just as you would using a normal MySQL client. Because mysql_embedded
will in effect start a new “server” instance, do not do this while your MySQL or MariaDB server is running. If you have installed the client RPM, you can simply run mysql_embedded
and get a command-line that allows you to interact with tables, even when the server isn’t running. This, of course, requires that the user running mysql_embedded
has filesystem access to the data files.
[kolbe ~]$ sudo service mysql status ERROR! MySQL is not running [kolbe ~]$ sudo mysql_embedded -e 'select user, host, password from mysql.user where user="root"' +------+-----------+-------------------------------------------+ | user | host | password | +------+-----------+-------------------------------------------+ | root | localhost | *196BDEDE2AE4F84CA44C47D54D78478C7E2BD7B7 | | root | db1 | *196BDEDE2AE4F84CA44C47D54D78478C7E2BD7B7 | | root | 127.0.0.1 | *196BDEDE2AE4F84CA44C47D54D78478C7E2BD7B7 | | root | ::1 | *196BDEDE2AE4F84CA44C47D54D78478C7E2BD7B7 | +------+-----------+-------------------------------------------+
If I try doing this as a user without the right privileges, I get lots of errors and I can’t see anything:
[kolbe ~]$ mysql_embedded -e 'select user, host, password from mysql.user where user="root"' 2014-05-29 20:45:22 7f14105767e0 InnoDB: Operating system error number 13 in a file operation. InnoDB: The error means mysqld does not have the access rights to InnoDB: the directory. 2014-05-29 20:45:22 7f14105767e0 InnoDB: Operating system error number 13 in a file operation. InnoDB: The error means mysqld does not have the access rights to InnoDB: the directory. 2014-05-29 20:45:22 7f14105767e0 InnoDB: Operating system error number 13 in a file operation. InnoDB: The error means mysqld does not have the access rights to InnoDB: the directory. ERROR 1146 (42S02) at line 1: Table 'mysql.user' doesn't exist
As we can see, the server is starting InnoDB here, which is not necessary if we just want to read privilege tables (until MySQL 5.7!). So, we can actually tell the embedded server not to bother loading InnoDB at all, which makes the whole thing much faster and means we aren’t going to accidentally start crash recovery or have to worry about the variety of other strange problems we could probably cause ourselves by interacting with the InnoDB tablespace. We can pass options to the embedded server by using the --server-arg
option to mysql_embedded
:
[kolbe ~]$ time sudo mysql_embedded --server-arg='--skip-innodb' --server-arg='--default-storage-engine=myisam' --server-arg='--log-error=/tmp/mysql.err' -e 'select user, host, password from mysql.user where user="root"' +------+-----------+-------------------------------------------+ | user | host | password | +------+-----------+-------------------------------------------+ | root | localhost | *196BDEDE2AE4F84CA44C47D54D78478C7E2BD7B7 | | root | db1 | *196BDEDE2AE4F84CA44C47D54D78478C7E2BD7B7 | | root | 127.0.0.1 | *196BDEDE2AE4F84CA44C47D54D78478C7E2BD7B7 | | root | ::1 | *196BDEDE2AE4F84CA44C47D54D78478C7E2BD7B7 | +------+-----------+-------------------------------------------+ real 0m0.244s user 0m0.078s sys 0m0.135s [kolbe ~]$ time sudo mysql_embedded -e 'select user, host, password from mysql.user where user="root"' +------+-----------+-------------------------------------------+ | user | host | password | +------+-----------+-------------------------------------------+ | root | localhost | *196BDEDE2AE4F84CA44C47D54D78478C7E2BD7B7 | | root | db1 | *196BDEDE2AE4F84CA44C47D54D78478C7E2BD7B7 | | root | 127.0.0.1 | *196BDEDE2AE4F84CA44C47D54D78478C7E2BD7B7 | | root | ::1 | *196BDEDE2AE4F84CA44C47D54D78478C7E2BD7B7 | +------+-----------+-------------------------------------------+ real 0m2.525s user 0m0.157s sys 0m0.221s
So just disabling InnoDB saves a couple seconds here. Notice that I included both --skip-innodb
as well as --default-storage-engine=myisam
. If your default storage engine is InnoDB, the server will refuse to start unless you set it to something else, because the server won’t start if default_storage_engine
is not a recognized storage engine. In 5.6, it is also necessary to set --default-tmp-storage-engine
. Since we can execute any query we like, it’s easy enough to just reset the password for 'root'@'localhost'
to whatever we like:
[kolbe ~]$ sudo mysql_embedded --server-arg='--skip-innodb' --server-arg='--default-storage-engine=myisam' -e 'update mysql.user set password=password("newpass") where user="root" and host="localhost"' [kolbe ~]$ sudo mysql_embedded -e 'select user, host, password from mysql.user where user="root"' +------+-----------+-------------------------------------------+ | user | host | password | +------+-----------+-------------------------------------------+ | root | localhost | *D8DECEC305209EEFEC43008E1D420E1AA06B19E0 | | root | db1 | *196BDEDE2AE4F84CA44C47D54D78478C7E2BD7B7 | | root | 127.0.0.1 | *196BDEDE2AE4F84CA44C47D54D78478C7E2BD7B7 | | root | ::1 | *196BDEDE2AE4F84CA44C47D54D78478C7E2BD7B7 | +------+-----------+-------------------------------------------+
This is a pretty straightforward technique that can be used without modifying server configuration, worrying about the security implications of starting the server without any privilege checking in place, et cetera. You don’t have to worry about flushing privileges, because the server is not actually running at the time the password is changed. The server will “see” the new password when it is started again. If your platform doesn’t support the embedded server, or there is some other problem with using it, there is another technique: mysqld --bootstrap
. The --bootstrap
mode is normally used by mysql_install_db
to execute an SQL script before any privilege or other system tables exist. It can also be used for our purposes to start the server in a minimal mode in order to execute a small number of queries. Because mysqld --bootstrap
will in effect start a new “server” instance, do not do this while your MySQL or MariaDB server is running. You don’t get a resultset back when using --bootstrap
, but you can do SELECT ... INTO OUTFILE
, like this:
[kolbe ~]$ sudo -u mysql mysqld --bootstrap Let's disable InnoDB as we did before, and we can of course then also reset the password using this technique:
[kolbe ~]$ sudo rm /tmp/mysql.users [kolbe ~]$ sudo -u mysql mysqld --skip-innodb --default-storage-engine=myisam --log-warnings=0 --bootstrap Note that you need to have a literal newline in there. The server requires a single SQL statement per line in--bootstrap
mode. You can also add normal command-line arguments such as--skip-innodb
and--datadir
as necessary. You could of course also put the statements into a file and redirect those to stdin of themysqld
process. Both themysql_embedded
andmysqld --bootstrap
techniques can be used to inspect and modify privilege tables without having to start the server at all, or especially to edit the server's configuration before starting it. These might be attractive options in some situations where it makes more sense to verify what users exist on a system before trying to start the server, or to reset passwords in a more programmatic way without having to start and later restart the server. Even if you don't use these methods to do anything wth privilege tables, I hope they provide a little bit of insight into a couple relatively unknown features and behaviors available in virtually all installations of MySQL.
Post a Comment
Log into your MariaDB ID account to post a comment.