Skip to content

Commit

Permalink
let rpl_repl_slave_acl can use binlog
Browse files Browse the repository at this point in the history
Summary:
Context:
If we use SUPER_USER priv to replay binlog and write metadata to dst replicaset, SUPER_USER writing to read-only master can cause GTID not generated and therefore these events will not be replicated to slaves and will be ignored by promotion, causing a silent dataloss
Fix:
To fix this we extend REPL_SLAVE_ACL so that binlog can bypass it and run like SUPER_USER but without causing GTID not generated.

Reviewed By: yoshinorim

Differential Revision: D17004347

fbshipit-source-id: 1a67821
  • Loading branch information
zhichengzhu authored and facebook-github-bot committed Sep 13, 2019
1 parent 27ccf9e commit cfd9b93
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 9 deletions.
56 changes: 56 additions & 0 deletions mysql-test/suite/rpl/r/rpl_repl_slave_acl.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
include/master-slave.inc
Warnings:
Note #### Sending passwords in plain text without SSL/TLS is extremely insecure.
Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
[connection master]
create table tbl (id int primary key, value int);
insert into tbl values (1, 1);
update tbl set value=value+1 where id=1;
insert into tbl values (2, 10);
flush logs;
select id, value from tbl;
id value
1 2
2 10
drop table tbl;
include/sync_slave_sql_with_master.inc
show tables like 'tbl';
Tables_in_test (tbl)
create user 'olm'@'localhost' IDENTIFIED BY 'password';
grant all privileges on *.* to 'olm'@'localhost';
revoke super on *.* from 'olm'@'localhost';
show tables like 'tbl';
Tables_in_test (tbl)
create user 'normal'@'localhost' IDENTIFIED BY 'password';
grant all privileges on *.* to 'normal'@'localhost';
revoke super on *.* from 'normal'@'localhost';
revoke REPLICATION SLAVE on *.* from 'normal'@'localhost';
revoke Admin port on *.* from 'normal'@'localhost';
show tables like 'tbl';
Tables_in_test (tbl)
User without replication slave privilege can't replay binlog
select * from test.tbl;
ERROR 42S02: Table 'test.tbl' doesn't exist
grant REPLICATION SLAVE on *.* to 'normal'@'localhost';
User without replication slave privilege can't replay binlog
select * from test.tbl;
ERROR 42S02: Table 'test.tbl' doesn't exist
revoke REPLICATION SLAVE on *.* from 'normal'@'localhost';
grant Admin port on *.* to 'normal'@'localhost';
User without admin port privilege can't replay binlog
select * from test.tbl;
ERROR 42S02: Table 'test.tbl' doesn't exist
User user who has replication slave privilege and admin port can replay binlog
include/sync_slave_sql_with_master.inc
select * from test.tbl;
id value
1 2
2 10
select * from test.tbl;
id value
1 2
2 10
DROP user 'olm'@'localhost';
DROP user 'normal'@'localhost';
DROP table tbl;
include/rpl_end.inc
2 changes: 1 addition & 1 deletion mysql-test/suite/rpl/r/rpl_temporary.result
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ DROP TABLE t1;
include/sync_slave_sql_with_master.inc
SET @save_select_limit=@@session.sql_select_limit;
SET @@session.sql_select_limit=10, @@session.pseudo_thread_id=100;
ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
ERROR 42000: Access denied; you need (at least one of) the SUPER or (REPLICATION SLAVE AND ADMIN PORT) privilege(s) for this operation
SELECT @@session.sql_select_limit = @save_select_limit;
@@session.sql_select_limit = @save_select_limit
1
Expand Down
1 change: 1 addition & 0 deletions mysql-test/suite/rpl/t/rpl_repl_slave_acl-master.opt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--gtid_mode=ON --enforce_gtid_consistency --log_bin --log_slave_updates
1 change: 1 addition & 0 deletions mysql-test/suite/rpl/t/rpl_repl_slave_acl-slave.opt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--gtid_mode=ON --enforce_gtid_consistency --log_bin --log_slave_updates
85 changes: 85 additions & 0 deletions mysql-test/suite/rpl/t/rpl_repl_slave_acl.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
source include/have_gtid.inc;
source include/master-slave.inc;

connection master;
create table tbl (id int primary key, value int);
insert into tbl values (1, 1);
update tbl set value=value+1 where id=1;
insert into tbl values (2, 10);

let $MYSQLD_DATADIR= `select @@datadir;`;
let $BINLOG_NAME = query_get_value(show master status, File, 1);
--copy_file $MYSQLD_DATADIR/$BINLOG_NAME $MYSQLD_DATADIR/master-bin.saved

flush logs;
connection master;
select id, value from tbl;
drop table tbl;
--source include/sync_slave_sql_with_master.inc

connection slave;
show tables like 'tbl';

connection master;
create user 'olm'@'localhost' IDENTIFIED BY 'password';
grant all privileges on *.* to 'olm'@'localhost';
revoke super on *.* from 'olm'@'localhost';
show tables like 'tbl';

connection master;
create user 'normal'@'localhost' IDENTIFIED BY 'password';
grant all privileges on *.* to 'normal'@'localhost';
revoke super on *.* from 'normal'@'localhost';
revoke REPLICATION SLAVE on *.* from 'normal'@'localhost';
revoke Admin port on *.* from 'normal'@'localhost';
show tables like 'tbl';

--echo User without replication slave privilege can't replay binlog
--disable_abort_on_error
--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.saved --skip-gtids --skip-empty-trans --database test | $MYSQL --user='normal' --password='password' --port=$MASTER_MYPORT --host=127.0.0.1
--enable_abort_on_error
connection master;
# Table 'test.tbl' doesn't exist
--error 1146
select * from test.tbl;

grant REPLICATION SLAVE on *.* to 'normal'@'localhost';
--echo User without replication slave privilege can't replay binlog
--disable_abort_on_error
--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.saved --skip-gtids --skip-empty-trans --database test | $MYSQL --user='normal' --password='password' --port=$MASTER_MYPORT --host=127.0.0.1
--enable_abort_on_error
connection master;
# Table 'test.tbl' doesn't exist
--error 1146
select * from test.tbl;
revoke REPLICATION SLAVE on *.* from 'normal'@'localhost';

grant Admin port on *.* to 'normal'@'localhost';
--echo User without admin port privilege can't replay binlog
--disable_abort_on_error
--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.saved --skip-gtids --skip-empty-trans --database test | $MYSQL --user='normal' --password='password' --port=$MASTER_MYPORT --host=127.0.0.1
--enable_abort_on_error
connection master;
# Table 'test.tbl' doesn't exist
--error 1146
select * from test.tbl;


--echo User user who has replication slave privilege and admin port can replay binlog
--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.saved --skip-gtids --skip-empty-trans --database test | $MYSQL --user='olm' --password='password' --port=$MASTER_MYPORT --host=127.0.0.1
--remove_file $MYSQLD_DATADIR/master-bin.saved

# with replication privilege, mysqlbinlog can generate binlog and gtid
--source include/sync_slave_sql_with_master.inc
connection master;
select * from test.tbl;

connection slave;
select * from test.tbl;

connection master;
DROP user 'olm'@'localhost';
DROP user 'normal'@'localhost';
DROP table tbl;
--source include/rpl_end.inc

8 changes: 6 additions & 2 deletions sql/sql_binlog.cc
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,13 @@ void mysql_client_binlog_statement(THD* thd)
(int) (thd->lex->comment.length < 2048 ?
thd->lex->comment.length : 2048),
thd->lex->comment.str));

if (check_global_access(thd, SUPER_ACL))
if (!(thd->security_ctx->master_access & SUPER_ACL) &&
!((thd->security_ctx->master_access & REPL_SLAVE_ACL) &&
(thd->security_ctx->master_access & ADMIN_PORT_ACL))) {
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0),
"SUPER or (REPLICATION SLAVE AND ADMIN PORT)");
DBUG_VOID_RETURN;
}

size_t coded_len= thd->lex->comment.length;
if (!coded_len)
Expand Down
28 changes: 22 additions & 6 deletions sql/sys_vars.cc
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,24 @@ static bool check_has_super(sys_var *self, THD *thd, set_var *var)
return false;
}

static bool check_has_super_or_repl_slave_and_admin_port(sys_var *self,
THD *thd,
set_var *var) {
// don't abuse check_has_super_or_repl_slave_and_admin_port()
DBUG_ASSERT(self->scope() != sys_var::GLOBAL);

#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (!(thd->security_ctx->master_access & SUPER_ACL) &&
!((thd->security_ctx->master_access & REPL_SLAVE_ACL) &&
(thd->security_ctx->master_access & ADMIN_PORT_ACL))) {
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0),
"SUPER or (REPLICATION SLAVE AND ADMIN PORT)");
return true;
}
#endif
return false;
}

#if defined(HAVE_GTID_NEXT_LIST) || defined(NON_DISABLED_GTID) || defined(HAVE_REPLICATION)
static bool check_top_level_stmt(sys_var *self, THD *thd, set_var *var)
{
Expand Down Expand Up @@ -2370,12 +2388,10 @@ static Sys_var_ulong Sys_metadata_locks_hash_instances(
BLOCK_SIZE(1));

static Sys_var_uint Sys_pseudo_thread_id(
"pseudo_thread_id",
"This variable is for internal server use",
SESSION_ONLY(pseudo_thread_id),
NO_CMD_LINE, VALID_RANGE(0, UINT_MAX32), DEFAULT(0),
BLOCK_SIZE(1), NO_MUTEX_GUARD, IN_BINLOG,
ON_CHECK(check_has_super));
"pseudo_thread_id", "This variable is for internal server use",
SESSION_ONLY(pseudo_thread_id), NO_CMD_LINE, VALID_RANGE(0, UINT_MAX32),
DEFAULT(0), BLOCK_SIZE(1), NO_MUTEX_GUARD, IN_BINLOG,
ON_CHECK(check_has_super_or_repl_slave_and_admin_port));

static bool fix_max_join_size(sys_var *self, THD *thd, enum_var_type type)
{
Expand Down

0 comments on commit cfd9b93

Please sign in to comment.