MySQL user and host precedence - mysql

I have two users, let's call them foo on two different databases:
Database_A:
mysql> select user,host from user where user = 'foo';
+-----------+-----------+
| user | host |
+-----------+-----------+
| foo | % |
| foo | 10.% |
| foo | localhost |
+-----------+-----------+
3 rows in set (0.01 sec)
Database_B:
mysql> select user,host from user where user = 'foo';
+-----------+-----------+
| user | host |
+-----------+-----------+
| foo | % |
| foo | 10.% |
| foo | localhost |
+-----------+-----------+
3 rows in set (0.00 sec)
Now the issue I am running into is trying to run a SQL script w/ the DROP command. When I connect to the database on Database_A, I get an error trying to connect:
mysql -A -hdatabase_1.foo.bar.domain.com -ufoo -pbar Database_A <dbtables.sql
Warning: Using a password on the command line interface can be insecure.
ERROR 1142 (42000) at line 22: DROP command denied to user 'foo'#'ip-10-128-0-143.ec2.internal' for table 'bar_table'
I can run this on the Database_B w/ no issues. So far, I have checked with the grants on both users and have logged into the MySQL shell w/ the same user and grants (foo#'10.%') but I can't run the SQL script on database_1. Here are what the grants look like for both Database_A and Database_B:
mysql> show grants for foo;
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Grants for foo#% |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| GRANT SELECT, RELOAD ON *.* TO 'foo'#'%' IDENTIFIED BY PASSWORD '<redacted>' REQUIRE SSL |
| GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE ROUTINE, ALTER ROUTINE ON `reference`.* TO 'foo'#'%' |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)
mysql> show grants for foo#'10.%';
+------------------------------------------------------------------------------------------------------------+
| Grants for foo#10.% |
+------------------------------------------------------------------------------------------------------------+
| GRANT FILE ON *.* TO 'foo'#'10.%' IDENTIFIED BY PASSWORD '<redacted>' |
+------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
Now I can't seem to figure out why one database server would work vs the other one (both are set up exactly the same w/ the user and in the my.cnf). Is there a way where I can log in specifically as foo#'%'? I am trying to run this from a remote EC2 instance (same VPC):
mysql -A -hdatabase_1.foo.bar.domain.com -ufoo -pbar Database_A <dbtables.sql

No, you can't force connection as a particular user profile. MySQL uses the first matching profile, according to its sort order.
Read https://dev.mysql.com/doc/refman/8.0/en/connection-access.html:
When multiple matches are possible, the server must determine which of
them to use. It resolves this issue as follows:
Whenever the server reads the user table into memory, it sorts the rows.
When a client attempts to connect, the server looks through the rows in sorted order.
The server uses the first row that matches the client host name and user name.
The server uses sorting rules that order rows with the most-specific
Host values first. Literal host names and IP addresses are the most
specific. (The specificity of a literal IP address is not affected by
whether it has a netmask, so 198.51.100.13 and
198.51.100.0/255.255.255.0 are considered equally specific.) The pattern '%' means “any host” and is least specific. The empty string
'' also means “any host” but sorts after '%'. Rows with the same Host
value are ordered with the most-specific User values first (a blank
User value means “any user” and is least specific). For rows with
equally-specific Host and User values, the order is nondeterministic.
I would avoid giving different privileges to the same username, differing only by the hostname.
Even though MySQL allows you to define different privileges depending on the client host, it's confusing to manage your authorizations this way. I've never seen a good reason to do that.
If you need a distinct set of privileges, define a distinct username.

Related

Why didn't MySQL GRANT create associated user accounts?

I installed Percona Toolkit to use pt-show-grants but it's not showing up all the grants. When I run it I see the following output:
-- Grants dumped by pt-show-grants
-- Dumped from server Localhost via UNIX socket, MySQL 5.5.43-log at 2015-06-11 09:19:19
-- Grants for 'bob'#'12.34.56.78'
GRANT SUPER ON *.* TO 'bob'#'12.34.56.78' IDENTIFIED BY PASSWORD '*4F72B97CAAAAAAAAAAA9C38064C4CCB18CA0DD8';
GRANT SELECT ON `mydb`.* TO 'bob'#'12.34.56.78';
...
In this case, bob is just a user. However all the web sites use specific credentials, for instance developer Bob might have an account for his example.com web site, called bob_examplecom_1. When I show the grants for this account:
mysql> SHOW GRANTS FOR 'bob_examplecom_1'#'localhost';
+-------------------------------------------------------------------------------------------------------------------------+
| Grants for bob_examplecom_1#localhost |
+-------------------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'bob_examplecom_1'#'localhost' IDENTIFIED BY PASSWORD '*74AE8018AAAAAAAAAAAAAAAABB87B5C83E650CB' |
| GRANT ALL PRIVILEGES ON `bob_core`.* TO 'bob_examplecom_1'#'localhost' WITH GRANT OPTION |
| GRANT ALL PRIVILEGES ON `bob_examplecom_main`.* TO 'bob_examplecom_1'#'localhost' WITH GRANT OPTION |
| GRANT ALL PRIVILEGES ON `bob_blog`.* TO 'bob_examplecom_1'#'localhost' |
+-------------------------------------------------------------------------------------------------------------------------+
4 rows in set (0.00 sec)
However, when I try find an associated user:
mysql> SELECT User, Host FROM mysql.user WHERE User LIKE 'bob\_%';
Empty set (0.00 sec)
Presumably the original GRANTs didn't create an associated user account? Also note that the above is an example of one web site, where there are many sites. I'm probably missing something here but I expected to see NO_AUTO_CREATE_USER in a mode:
mysql> SELECT ##GLOBAL.sql_mode;
+-------------------+
| ##GLOBAL.sql_mode |
+-------------------+
| |
+-------------------+
1 row in set (0.00 sec)
So, my problem is I want to use pt-show-grants to create an SQL file of the many GRANTs that need running for a new user on this development server, but I can't figure out how. Do I need to retrospectively create user accounts that match up to the GRANTs? Should I change some settings and/or setup accounts differently in the future?
Update: I just ran FLUSH PRIVILEGES and all the GRANTs that were working that had no associated accounts in mysql.user vanished. Does this mean they're gone for good, and all need to be recreated manually? Why would such a thing happen? I've looked through the MySQL command history and see no commands that would have dropped these accounts in the past. The uptime on this server is over 400 days and the sites have all worked in that time with little messing about.
Update 2: I had to recreate all the accounts. This time, with the GRANT USAGE and then granting privileges did indeed create the user accounts. My question is now a simple one:
Why didn't MySQL GRANT create associated user accounts when performing GRANTs?
Based on your description of the observed behavior, it sounds as if rows were removed from the mysql.user table, using a DELETE statement, rather than a DROP USER statement.
Changes made to the privilege tables (mysql.user, mysql.db, et al.) via DML statements (DELETE, INSERT, UPDATE), do not take effect immediately. MySQL has already read those tables, and the information is held in memory. Checks of privileges go against the in memory store; MySQL doesn't check the contents of the tables.
So it's possible to make changes to the mysql.user table, and not have those changes reflected in the effective privileges.
The FLUSH PRIVILEGES statement is what causes MySQL to re-read all the privilege tables, and rebuild the "in memory" store of privilege information.
To answer your question(s)...
Q: Presumably the original GRANTs didn't create an associated user account?
Q: Why didn't MySQL GRANT create associated user accounts when performing GRANTs?
A: The GRANT for a "new" user did create the user account, if it completed successfully. The appropriate row was added to the mysql.user table, and the privileges became effective (the change was also applied to the "in memory" privilege structure.
Q: Does this mean they're gone for good, and all need to be recreated manually?
A: Yes. If the rows are not in the mysql.user table, then those will need to be recreated. The rows in the mysql.user, mysql.db tables could be restored from a backup.
Q: Why would such a thing happen?
A: As mentioned earlier, someone may have inadvertently run a DELETE statement against mysql.user table. (It's also possible a TRUNCATE, or a DROP and CREATE. (Executing the SQL from mysqldump script that includes DROP TABLE statement, to reload the table from an old backup?)
If operations like that weren't performed on the table, then another possibility is that MyISAM table became corrupted, and the repair of the corruption caused the loss of rows. (A known issue with MyISAM tables; and one of the reasons we take backups of the databases, and test restores.)
Here's a demonstration of the behavior... removing a row from mysql.user is not immediately reflected in the effective privileges:
Verify user does not exist:
mysql> SELECT USER, HOST FROM mysql.user WHERE USER LIKE 'bob' ;
Empty set (0.00 sec)
mysql> SHOW GRANTS FOR 'bob'#'192.168.11.121' ;
ERROR 1141 (42000): There is no such grant defined for user 'bob' on host '192.168.11.121'
Create user with GRANT statement:
mysql> GRANT SELECT ON ergo.* TO 'bob'#'192.168.11.121' IDENTIFIED BY 'mysecret';
Query OK, 0 rows affected (0.00 sec)
Check contents of mysql.user table and effective privileges:
mysql> SELECT USER, HOST FROM mysql.user WHERE USER LIKE 'bob' ;
+------+----------------+
| USER | HOST |
+------+----------------+
| bob | 192.168.11.121 |
+------+----------------+
1 row in set (0.00 sec)
mysql> SHOW GRANTS FOR 'bob'#'192.168.11.121' ;
+-----------------------------------------------------------------------------------------------------------------+
| Grants for bob#192.168.11.121 |
+-----------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'bob'#'192.168.11.121' IDENTIFIED BY PASSWORD '*440A4F469FD488A1C73204842936CC18A62A7D7F' |
| GRANT SELECT ON `ergo`.* TO 'bob'#'192.168.11.121' |
+-----------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)
Remove row from mysql.user table (using DML operation and not a DROP USER statement)
mysql> DELETE FROM mysql.user WHERE USER = 'bob' AND HOST = '192.168.11.121';
Query OK, 1 row affected (0.00 sec)
Row is gone from mysql.user table, but privileges are still effective:
mysql> SELECT USER, HOST FROM mysql.user WHERE USER LIKE 'bob' ;
Empty set (0.00 sec)
mysql> SHOW GRANTS FOR 'bob'#'192.168.11.121' ;
+-----------------------------------------------------------------------------------------------------------------+
| Grants for bob#192.168.11.121 |
+-----------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'bob'#'192.168.11.121' IDENTIFIED BY PASSWORD '*440A4F469FD488A1C73204842936CC18A62A7D7F' |
| GRANT SELECT ON `ergo`.* TO 'bob'#'192.168.11.121' |
+-----------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)
Force MySQL to rebuild privileges, reading from mysql.user table...
mysql> FLUSH PRIVILEGES ;
Query OK, 0 rows affected (0.00 sec)
Privileges are no longer effective:
mysql> SHOW GRANTS FOR 'bob'#'192.168.11.121' ;
ERROR 1141 (42000): There is no such grant defined for user 'bob' on host '192.168.11.121'

Accessing mysql as root keeps logging me as anonymous user

When I try
sudo mysql -uroot -p
I get no errors, but it appears I'm not logged in as root, since:
SELECT USER(),CURRENT_USER();
returns:
+----------------+----------------+
| USER() | CURRENT_USER() |
+----------------+----------------+
| root#localhost | #localhost |
+----------------+----------------+
and I'm unable to perform any administrative commands like GRANT or UPDATE, I can only view information_schema table.
http://dev.mysql.com/doc/refman/4.1/en/information-functions.html#function_current-user says:
The value of CURRENT_USER() can differ from the value of
USER(). ... One way this might occur is that there is no
account listed in the grant tables for davida.
If you can't login as root, you can start MySQL without permissions checks: How to start MySQL with --skip-grant-tables? or start mysql with a init file http://dev.mysql.com/doc/refman/5.0/en/resetting-permissions.html. For the second option, you should use INSERT instead of UPDATE since your table doesn't have the root user.

MySQL create table issue with --read-only error

I'd like to create a user who has all privileges with his own database in MySQL.
When I use this user to create a table, MySQL returns that the SQL server is running with read-only option.
However when I changed to an another existing user with all privileges on *.*, I can create table without error.
I'm wondering if the read-only option is global or what?
The following is my MySQL commands using MySQL root:
mysql> create user 'demo'#'localhost' identified by 'demo';
mysql> create database demo;
mysql> grant all privileges on demo.* to demo#localhost;
mysql> show grants for demo#localhost;
+-------------------------------------------------------------------------------------------------------------------+
| Grants for demo#localhost |
+-------------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'demo'#'localhost' IDENTIFIED BY PASSWORD '*demo-hashed*' |
| GRANT ALL PRIVILEGES ON `demo`.* TO 'demo'#'localhost' |
+-------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)
Then I switched to user "demo":
mysql> use demo;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> create table t(t1 int);
ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement
So I checked the read-only option, and it seems to be on.
However then I tried using another user with privileges on *.* and I can create tables successfully.
The another user grant setting:
mysql> show grants for demo2#'%';
+-----------------------------------------------------------------------------------------------------------------+
| Grants for demo2#% |
+-----------------------------------------------------------------------------------------------------------------+
| GRANT ALL PRIVILEGES ON *.* TO 'demo2'#'%' IDENTIFIED BY PASSWORD '*demo2-hased*' |
+-----------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
MySQL version:
mysql> select version();
+------------------------+
| version() |
+------------------------+
| 5.1.68.0 |
+------------------------+
1 row in set (0.00 sec)
BTW, after I set read_only = 0 I can use demo to create table. I just don't know why the demo2 can create table while read-only is on.
Thanks!
Please check the My.cnf for Linux or My.ini for windows under [mysqld] remove read only parameters then restart the service and try again that will solve the read only problem, but if you create table in read only that will be a temp table.

MySQL user permissions issue

I'm facing a problem with user access control. To be more clear, the mysql user I create has access to other tables than what I've given access to.
Let's say I log in as root into mysql and there are many databases, and in database "test", there are many tables one of which is "news".
I want to create user specifically having access only to "select" values from "test.news". This is how it looked:
mysql> grant select on test.news to 'new_user2'#'localhost' identified by 'XXXXXXX';
Query OK, 0 rows affected (0.00 sec)
Flushed privileges: flush privileges;
Now, log out, and I login as new_user2:
/opt/lampp/bin/./mysql -u new_user2 -p
and want to see databases there:
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| test |
+--------------------+
2 rows in set (0.00 sec)
This was good, in the sense, there were other databases in the "root" account which were not visible to new_user2. Then, I wanted to see tables in test. So-
use test;
show tables;
This is the first shock for me..
I saw all the tables in the "test" database, which I was not expecting. All I wanted to see was "news" table. Or is it normal that all users who have been given access to one table in a database, can actually view all the tables?
second shock, I used select * for another table- "user", which is essentially another table in "test", and I could see all the contents in user table.
So, when I created "new_user2" and granted permission only to access test.news, how can it access test.user? select * from news is working fine..
Did I really restrict access to all other tables in "test" database? If not, how do I do it?
OK. As if this is not sufficient, I can actually delete tables created by 'root'..
mysql> drop table member;
Query OK, 0 rows affected (0.42 sec)
These are the grants for 'new_user2'#'localhost' when logged in root...
mysql> SHOW GRANTS FOR 'new_user2'#'localhost';
+------------------------------------------------------------------------------------------------------------------+
| Grants for new_user2#localhost |
+------------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'new_user2'#'localhost' IDENTIFIED BY PASSWORD '*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' |
| GRANT SELECT ON `test`.`news` TO 'new_user2'#'localhost' |
+------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)
I feel, I'm missing something basic here. Please help me this. Thank you so much.

MySQL: ERROR 1227 (42000): Access denied - Cannot CREATE USER

I'm using MySQL 5.5.16 noinstall Zip Archive on Win7.
After I started the server, the command show databases showed me a list of 2 databases: information_schema and test. The latter is empty.
Where is the table user?
I tried to create a new user through this command create user newUser; and got the following error message: ERROR 1227 (42000): Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation
What should I do to create, databases, tables, and do all the operations I want to do? I don't know if the fact that I'm using MySQL 5.5.16 noinstall Zip Archive has something to do with the error message?
First thing to do is run this:
SHOW GRANTS;
You will quickly see you were assigned the anonymous user to authenticate into mysql.
Instead of logging into mysql with
mysql
login like this:
mysql -uroot
By default, root#localhost has all rights and no password.
If you cannot login as root without a password, do the following:
Step 01) Add the two options in the mysqld section of my.ini:
[mysqld]
skip-grant-tables
skip-networking
Step 02) Restart mysql
net stop mysql
<wait 10 seconds>
net start mysql
Step 03) Connect to mysql
mysql
Step 04) Create a password from root#localhost
UPDATE mysql.user SET password=password('whateverpasswordyoulike')
WHERE user='root' AND host='localhost';
exit
Step 05) Restart mysql
net stop mysql
<wait 10 seconds>
net start mysql
Step 06) Login as root with password
mysql -u root -p
You should be good from there.
CAVEAT: Please remove anonymous users !!!
For me the issue was ( for a very strange reason ) the fact that root had Host of % instead of localhost
I received the above error when trying to DROP USER;
Privileges as suggested in the answer above - I already had, so the solution wasn't suitable for me.
The DB looked like this:
mysql> drop user 'testuser'#'%';
ERROR 1227 (42000): Access denied; you need (at least one of) the SYSTEM_USER privilege(s) for this operation
mysql> select Host,User,drop_priv from user;
+------+------------------+-----------+
| Host | User | drop_priv |
+------+------------------+-----------+
| % | mysql.infoschema | N |
| % | mysql.session | N |
| % | mysql.sys | N |
| % | root | Y |
+------+------------------+-----------+
And
mysql> SHOW GRANTS FOR 'root'#'%';
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Grants for root#% |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE, CREATE ROLE, DROP ROLE ON *.* TO `root`#`%` WITH GRANT OPTION |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
I tried many things, but eventually I changed the Host from % to localhost for security concerns, nothing else.
mysql> UPDATE user SET Host='localhost' WHERE user='root' LIMIT 1;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0
I don't know why, but it worked.
mysql> quit
$ mysql -u root -p
.. ENTER (NO PASSWORD) ..
mysql> drop user 'testuser'#'%';
Query OK, 0 rows affected (0.02 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.02 sec)
No idea why, but hope it will help others ...
I'm using CentOS which has SELinux and other stuff, which maybe other components are correlated with this. don't know.