Question

We need to have same set of users between Master and Slave MySQL databases but want to prevent the readonly users from connection to Master DB as well as to prevent Read-write users from connection to the slave DB. So I am writing a Login trigger to prevent some users from connecting to the MySql DB based on the following table:

create table deny_login (user varchar(20),SLAVE_RUNNING varchar (3), deny varchar (1));

I used "SLAVE_RUNNING" column to explain how am I going to distinguish between Master and Slave Databases from within the trigger.

The trigger works but the session is not terminated properly. It disconnects but tries to reconnect etc. Is there a cleaner way to exit the current session from within the trigger?

Here is the table content as well as the trigger definition:

insert into deny_login values ('vlad','OFF', 'n');
insert into deny_login values ('joe','OFF','y');
insert into deny_login values ('vlad@localhost','OFF', 'n');
insert into deny_login values ('joe@localhost','OFF','y');
insert into deny_login values ('joe@localhost','ON','n');

create table test (user varchar(20), deny integer, connection integer);

grant insert, update,delete on test to vlad;
grant insert, update,delete on test to joe;
grant insert, update,delete on test to bill;
grant insert, update,delete on deny_login to vlad;
grant insert, update,delete on deny_login to joe;
grant insert, update,delete on deny_login to bill;


DROP PROCEDURE IF EXISTS login_trigger;

DELIMITER //

CREATE PROCEDURE login_trigger()
SQL SECURITY DEFINER
BEGIN
  declare denied integer;
  select count(*) into denied from test.deny_login p, information_schema.global_status s where p.user=user() and user not
  like '%root%' and   s.variable_name='SLAVE_RUNNING' and s.variable_value=p.SLAVE_RUNNING and deny='y';
  insert into test values (user(),denied,connection_id());
commit;

if  denied = 1 then
-- signal sqlstate '45000' set message_text = 'forbidden';
-- kill( connection_id());
call Fail('forbidden');
end if;
END;

//
DELIMITER ;


REVOKE EXECUTE ON PROCEDURE test.login_trigger FROM 'vlad'@'%';
GRANT EXECUTE ON PROCEDURE test.login_trigger TO 'vlad'@'%';
REVOKE EXECUTE ON PROCEDURE test.login_trigger FROM 'joe'@'%';
GRANT EXECUTE ON PROCEDURE test.login_trigger TO 'joe'@'%';
REVOKE EXECUTE ON PROCEDURE test.login_trigger FROM 'bill'@'%';
GRANT EXECUTE ON PROCEDURE test.login_trigger TO 'bill'@'%';

SET GLOBAL init_connect="";
SET GLOBAL init_connect="CALL test.login_trigger()";

No correct solution

OTHER TIPS

On the slave, SET GLOBAL READ_ONLY = 1;. Then, users with write privileges will not be able to write to the slave... any attempt to change data will be met with an error that says, essentially, "the slave is running in read-only mode" and the writes will be denied.

Users with the SUPER privilege will retain write access (but they are immune to init_connect anyway) and the replication threads will not be affected.

For the users who don't need access to the master, drop their accounts from the master and add them directly to the slave. This does not cause a problem as long as you don't subsequently add the same user back to the master without dropping them from the slave first.

There's not a more elegant way to do what you're attempting, if the client is trying to reconnect on its own. You're doing a clean disconnect. The only alternative I can think of for you to try, instead of killing the connection, might be putting a ROLLBACK RELEASE; in the procedure, which also drops the client connection... though I suspect the client will still try to reconnect, if the disconnect is unexpected by the client.

https://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_read_only

Incidentally, in the current implementation, a simpler way to distinguish between master and slave would be to check the value of the @@SERVER_ID or @@HOSTNAME variables.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top