MySQL Proxy: Roles
June 25th, 2009
On the MySQL Proxy channel we get questions from time to time if the authentication can be intercepted and replaced data from a external source.
From now on, you can. For example if you want to get data from a external source (like LDAP) or want to implement roles.
Mapping Accounts to "Roles"
There isn't much needed to implement Roles for MySQL with the help of the MySQL Proxy.
mysql.userdoesn't contain users, but roles instead- the proxy maps user-accounts to role-accounts with a script like above
It works like this:
login to the proxy
$ mysql --user=jan --password=secret --port=4040proxy looks up username password, finds a role for him
- proxy replaces credentials ad hoc
- mysql-server sees the role-name and role-password and lets the user in
This works nicely as long as you only have 1:n mapping between roles and users. One user has one role, but one role has many users.
All you need is a storage that has user's password in one of three forms:
- cleartext as in the example below
- SHA1(cleartext) as you may have in LDAP
- SHA1(SHA1(cleartext)) as in
mysql.user:SELECT CONCAT("*", SHA1(UNHEX(SHA1("pass")))), PASSWORD("pass");
How the Auth works
The MySQL Proxy has access to all protocol data and exposes it as part of different stages of the authentication to the scripting layer:
---
-- show how to use the mysql.password functions
function read_auth()
local c = proxy.connection.client
local s = proxy.connection.server
print(("for challenge %q the client sent %q"):format(
s.scramble_buffer,
c.scrambled_password
))
end
The challenge and response secures the password and is works like this:
SERVER: scramble_buffer=create_random_string()
send(scramble_buffer)
CLIENT: recv(scramble_buffer)
hash_stage1=sha1("password")
hash_stage2=sha1(hash_stage1)
scrambled_password=xor(hash_stage1, sha1(scramble_buffer, hash_stage2)
send(scrambled_password)
SERVER: recv(scrambled_password)
hash_stage1=xor(scrambled_password, sha1(scramble_buffer,hash_stage2))
candidate_hash2=sha1(hash_stage1)
check(candidate_hash2==hash_stage2)
That's taken from sql/password.c of the MySQL Server Source. The hash_stage2 is the same as result of the PASSWORD() function without the * and the HEX() and is stored in mysql.user.
Replacing the auth response
Based on the read_auth() from above you can also replace the credentials on the fly:
---
-- map replace:me with root:secret
--
if c.username == "replace" and -- the username we want to map
password.check(
s.scramble_buffer,
c.scrambled_password,
password.hash(password.hash("me")) -- its valid password
) then
proxy.queries:append(1,
proto.to_response_packet({
username = "root",
response = password.scramble(s.scramble_buffer, password.hash("secret")),
charset = 8, -- default charset
database = c.default_db,
max_packet_size = 1 * 1024 * 1024
})
)
return proxy.PROXY_SEND_QUERY -- works if you use lp:mysql-proxy r694 or later
end
At
you can see the full version of it.
Let's see if it works:
$ mysql-proxy --proxy-lua-script=tutorial-scramble.lua
$ mysql --port=4040 --user=replace --password=me
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2702
Server version: 5.1.34-log MySQL Community Server (GPL)
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
root@127.0.0.1 [(none)]>
I logged in as the root user.
Fixing the hostcheck
This can also be used to rewrite the username to something that includes his original ip. Like
...,
username = c.username .. "-" .c.src.address,
...
to make the server see something else than the proxy's client-address if you use the host-field in the mysql.user tables.
3 Responses to “MySQL Proxy: Roles”
Sorry, comments are closed for this article.
June 25th, 2009 at 04:43 PM Hi Jan, Yet another interesting addition to MySQL Proxy. You can find an n:n user:role relationship model at http://www.securich.com which apart from the "roles" functionality contains password expiry, password size and age restrictions, enhanced user administration etc. I should also add it's GPL2 and available to download and install manually or through it's install script. Cheers Darren
June 26th, 2009 at 11:57 AM Jan! Although I appreciate the effort, and think this is a good first start, this is not really complete. The issues is that a ROLE based system typically aalows a user to have multiple roles, this is what makes roles truly usful. You define a few roles, and then assign one or more roles to the user, i.e. a user is a superuser for the ERP system and is an ordinary iser in the CRM system or something like that. As far as I can see in your case, you are mapping a user to a role, whereas what you would want to do is map a user to 1 or more roles. As I said, this is a good starting point and is appreciated, but we still have to fix the MySQL rights system properly. Cheers /Karlsson (And I sure will test this BTW).
June 27th, 2009 at 07:06 PM Hi Jan Elegant solution for one of the gaps on the "most wanted" list of many Enterprise MySQL users. You could relax the 1:n requirement by creating (by a separate tool, probably not inside proxy) "combination roles" that contains a sum of all permissions of 2 (or more) real roles. That is, suppose roles A and B grant some permissions which are a disjoint set, ie both roles have some rights the other doesn't (and could have some in common). Suppose further that a role C is a superset of A and B, ie C can do anything A or B could (and possibly more). Let's also define users: x € A y € A and C z € A and B Now, when a user logs in, proxy needs to assign the role as following: x -> A (as in your example) y -> - recognize that C is a superset of A, therefore -> C. z -> - the external tool has created a "combination role" AB which contains the rights from A + B. - recognize that AB is a superset of both A and B. -> AB. In really complex scenarios you could end up with many "combination roles" and it could be a hassle to maintain, but in theory this covers everything you need. (It doesn't change the fact that MySQL really only GRANTs permissions, there is no "deny" list, like in Apache or Windows NT/AD. But use of deny-permissions is debatable whether it is good or evil anyway, some amount of KISS should be preserved.)