From 565e9f717aa4fc9c1087b67f1913a018344729a0 Mon Sep 17 00:00:00 2001 From: tcrivat Date: Sun, 17 Jul 2016 09:41:11 +0200 Subject: [PATCH] mysql: manager connects to the db using the root account This commit changes the behavior of the manager to make it use the root account (instead of the user's account) when connecting to the mysql database. This fixes a bug in which the two passwords become desynchronized and access is denied. So, now, the manager and agents only use the root password to connect the database and only store the root password, not the user's password. Access for the root account from the manager IP address is granted. The commit also renames the IP_WHITE_LIST parameter in the agent configuration with MANAGER_IP, to better reflect its content. Some logging messages are also improved and the root password is removed from the logs. --- .../cpsdirector/iaas/controller.py | 4 +- .../config/agent/default-agent.cfg | 3 +- .../scripts/agent/htc-agent-start | 2 +- .../scripts/agent/htcondor-agent-start | 2 +- conpaas-services/src/conpaas/core/ganglia.py | 2 +- .../src/conpaas/services/mysql/README.txt | 2 +- .../src/conpaas/services/mysql/agent/role.py | 39 ++++++++++++------- .../conpaas/services/mysql/manager/manager.py | 23 ++++++----- conpaas-services/src/tests/core/test_agent.py | 2 +- 9 files changed, 42 insertions(+), 37 deletions(-) diff --git a/conpaas-director/cpsdirector/iaas/controller.py b/conpaas-director/cpsdirector/iaas/controller.py index 515d39fb..81c5012d 100644 --- a/conpaas-director/cpsdirector/iaas/controller.py +++ b/conpaas-director/cpsdirector/iaas/controller.py @@ -104,11 +104,9 @@ def _set_clouds(self): #=========================================================================# # generate_context(self, service_name, replace, cloud) # #=========================================================================# - def generate_context(self, clouds, context_replacement={}, startup_script=None, ip_whitelist=None): + def generate_context(self, clouds, context_replacement={}, startup_script=None): """Generates the contextualization file for the given clouds. """ - # COMMENT (genc): what is ip_whitelist? - for cloud_name in clouds: cloud = self.get_cloud_by_name(cloud_name) diff --git a/conpaas-services/config/agent/default-agent.cfg b/conpaas-services/config/agent/default-agent.cfg index fe2a275e..6f729f76 100644 --- a/conpaas-services/config/agent/default-agent.cfg +++ b/conpaas-services/config/agent/default-agent.cfg @@ -17,8 +17,7 @@ VAR_CACHE = $VAR_CACHE VAR_RUN = $VAR_RUN CONPAAS_HOME = $CPS_HOME -# Will be filled in by the manager -IP_WHITE_LIST = $MANAGER_IP +MANAGER_IP = $MANAGER_IP IPOP_BASE_NAMESPACE = $IPOP_BASE_NAMESPACE diff --git a/conpaas-services/scripts/agent/htc-agent-start b/conpaas-services/scripts/agent/htc-agent-start index 6ebc0834..a7c5012f 100644 --- a/conpaas-services/scripts/agent/htc-agent-start +++ b/conpaas-services/scripts/agent/htc-agent-start @@ -22,7 +22,7 @@ grep CLOUD_MAX_VMS_ALL_CLOUDS $ROOT_DIR/config.cfg | sed 's/CLOUD_MAX_VMS_ALL_CL grep CLOUD_MAX_VMS.= $ROOT_DIR/config.cfg | sed 's/CLOUD_MAX_VMS = \(.*\)/CloudMaxVms = \1/' >> /etc/condor/condor_config.local echo 'STARTD_ATTRS = $(STARTD_ATTRS) CloudName CloudType CloudMachineType CloudCostPerTime CloudMaxVms CloudMaxVmsAllClouds' >> /etc/condor/condor_config.local -grep IP_WHITE_LIST $ROOT_DIR/config.cfg | sed 's/.*= //;s/$/ master.htc/' >> /etc/hosts +grep MANAGER_IP $ROOT_DIR/config.cfg | sed 's/.*= //;s/$/ master.htc/' >> /etc/hosts # TODO: run everything as user condor, for now make the execute directory writable for world chmod 777 /var/lib/condor/execute condor_restart diff --git a/conpaas-services/scripts/agent/htcondor-agent-start b/conpaas-services/scripts/agent/htcondor-agent-start index dbc9db03..b3f5310a 100644 --- a/conpaas-services/scripts/agent/htcondor-agent-start +++ b/conpaas-services/scripts/agent/htcondor-agent-start @@ -10,7 +10,7 @@ CONDOR_HOST = master.htc ALLOW_WRITE = *.htc " >> /etc/condor/condor_config.local -grep IP_WHITE_LIST $ROOT_DIR/config.cfg | sed 's/.*= //;s/$/ master.htc/' >> /etc/hosts +grep MANAGER_IP $ROOT_DIR/config.cfg | sed 's/.*= //;s/$/ master.htc/' >> /etc/hosts # TODO: run everything as user condor, for now make the execute directory writable for world chmod 777 /var/lib/condor/execute condor_restart diff --git a/conpaas-services/src/conpaas/core/ganglia.py b/conpaas-services/src/conpaas/core/ganglia.py index 0a736131..5b904fcf 100644 --- a/conpaas-services/src/conpaas/core/ganglia.py +++ b/conpaas-services/src/conpaas/core/ganglia.py @@ -147,5 +147,5 @@ def __init__(self, config_parser): """Same as for the base case, but with proper manager_ip""" BaseGanglia.__init__(self) - self.manager_ip = config_parser.get('agent', 'IP_WHITE_LIST') + self.manager_ip = config_parser.get('agent', 'MANAGER_IP') self.cps_home = config_parser.get('agent', 'CONPAAS_HOME') diff --git a/conpaas-services/src/conpaas/services/mysql/README.txt b/conpaas-services/src/conpaas/services/mysql/README.txt index 7fb212b8..8e6340bf 100644 --- a/conpaas-services/src/conpaas/services/mysql/README.txt +++ b/conpaas-services/src/conpaas/services/mysql/README.txt @@ -118,7 +118,7 @@ VAR_RUN = CONPAAS_HOME = /home/miha/projects/conpaas # Will be filled in by the manager -IP_WHITE_LIST = +MANAGER_IP = IPOP_BASE_NAMESPACE = $IPOP_BASE_NAMESPACE diff --git a/conpaas-services/src/conpaas/services/mysql/agent/role.py b/conpaas-services/src/conpaas/services/mysql/agent/role.py index 4b984213..86dd8eb9 100644 --- a/conpaas-services/src/conpaas/services/mysql/agent/role.py +++ b/conpaas-services/src/conpaas/services/mysql/agent/role.py @@ -49,7 +49,7 @@ def __init__(self, config, nodes, device_name): self.mkdir_cmd = "mkdir -p %s" % self.mount_point self.mount(True) except ConfigParser.Error: - sql_logger.exception('Could not mount the device') + sql_logger.warning('Could not mount the device') sql_logger.debug("Entering init MySQLServerConfiguration") try: #hostname = socket.gethostname() @@ -59,10 +59,11 @@ def __init__(self, config, nodes, device_name): sql_logger.debug("Trying to get params from configuration file ") self.conn_location = config.get('MySQL_root_connection', 'location') self.conn_username = config.get("MySQL_root_connection", "username") - self.conn_password = config.get("MySQL_root_connection", "password") + self.conn_password = config.get("MySQL_root_connection", "password") # root password sql_logger.debug("Got parameters for root connection to MySQL") # Get MySQL configuration parameters + self.manager_ip = config.get('agent', 'MANAGER_IP') self.mysqldump_path = config.get('agent', 'MYSQLDUMP_PATH') self.mycnf_filepath = config.get("MySQL_configuration", "my_cnf_file") self.path_mysql_ssr = config.get("MySQL_configuration", "path_mysql_ssr") @@ -125,14 +126,14 @@ def __init__(self, config, nodes, device_name): # Change root password when starting if is_first_node: os.system("mysqladmin -u root password " + self.conn_password) - #TODO: add user conn_userame and grant privileges to it from any host + self.allow_root_connections(self.manager_ip) self.add_user(self.conn_username, self.conn_password) self.add_user(self.wsrep_user, self.wsrep_password) except ConfigParser.Error: - sql_logger.exception('Could not read config file') + sql_logger.warning('Could not read config file') except IOError: - sql_logger.exception('Config file doesn\'t exist') + sql_logger.warning('Config file doesn\'t exist') # Creating a supervisor object sql_logger.debug("Leaving init MySQLServerConfiguration") @@ -148,7 +149,7 @@ def _load_dump(self, f): #os.system('rm /tmp/mysqldump.db') sql_logger.debug("Leaving load_dump") except Exception as e: - sql_logger.exception('Failed to load dump') + sql_logger.warning('Failed to load dump') raise e def _wait_daemon_started(self, is_first_node): @@ -164,7 +165,7 @@ def _wait_daemon_started(self, is_first_node): "-BN " "-e \"SHOW STATUS LIKE 'wsrep_local_state_comment';\"" % password) - sql_logger.debug("Polling mysql daemon: %s" % poll_cmd) + sql_logger.debug("Polling the MySQL daemon") #: %s" % poll_cmd) out, error, code = run_cmd_code(poll_cmd) if code != 0: wait_time = 5 @@ -191,7 +192,7 @@ def start(self, is_first_node): return_code = proc.wait() if return_code != 0: self.state = S_STOPPED - raise Exception('Failed to start MySQL Galera daemon: return code is %s.' % return_code) + raise Exception('Failed to start MySQL daemon: return code is %s.' % return_code) sql_logger.info('MySQL server started, checking if it\'s ready') self._wait_daemon_started(is_first_node) self.state = S_RUNNING @@ -208,12 +209,12 @@ def stop(self): self.state = S_STOPPED sql_logger.info('Daemon mysqld stopped') except Exception as e: - sql_logger.exception('Failed to stop MySQL daemon: %s' % e) + sql_logger.warning('Failed to stop MySQL daemon: %s' % e) raise e try: self.unmount() except Exception as e: - sql_logger.exception('Failed to unmount disk %s' % self.dev_name) + sql_logger.warning('Failed to unmount disk %s' % self.dev_name) raise e else: sql_logger.warning('Requested to stop MySQL daemon' @@ -285,6 +286,14 @@ def take_snapshot(self): db1.close() return ret + def allow_root_connections(self, manager_ip): + db = MySQLdb.connect(self.conn_location, 'root', self.conn_password) + exc = db.cursor() + exc.execute("create user 'root'@'" + manager_ip + "' identified by '" + self.conn_password + "'") + exc.execute("grant all privileges on *.* TO 'root'@'" + manager_ip + "' with grant option;") + exc.execute("flush privileges;") + db.close() + def add_user(self, new_username, new_password): db = MySQLdb.connect(self.conn_location, 'root', self.conn_password) exc = db.cursor() @@ -341,7 +350,7 @@ def mount(self, mkfs): dev_prefix = self.dev_name.split('/')[2][:-1] for attempt in range(1, 11): - sql_logger.info("Galera node waiting for block device %s" % self.dev_name) + sql_logger.info("MySQL node waiting for block device %s" % self.dev_name) if lexists(self.dev_name): dev_found = True break @@ -359,7 +368,7 @@ def mount(self, mkfs): run_cmd(self.mkdir_cmd) if dev_found: - sql_logger.info("Galera node has now access to %s" % self.dev_name) + sql_logger.info("MySQL node has now access to %s" % self.dev_name) # prepare block device if mkfs: @@ -395,14 +404,14 @@ def mount(self, mkfs): def unmount(self): # kill all processes still using the volume - sql_logger.info("Killing all processes using the Galera Disk") + sql_logger.info("Killing all processes using the storage volume") fuser_args = ['fuser', '-km', self.dev_name] fuser_cmd = ' '.join(fuser_args) sql_logger.debug("Running command '%s'" % fuser_cmd) run_cmd(fuser_cmd) # unmount - sql_logger.info("Trying to unmount the Galera Disk") + sql_logger.info("Trying to unmount the storage volume") unmount_args = ['umount', self.dev_name] unmount_cmd = ' '.join(unmount_args) sql_logger.debug("Running command '%s'" % unmount_cmd) @@ -410,7 +419,7 @@ def unmount(self): if err: sql_logger.critical('Failed to unmount storage device: %s' % err) else: - sql_logger.info("Galera node has succesfully unmounted %s" % self.dev_name) + sql_logger.info("MySQL node has succesfully unmounted %s" % self.dev_name) class GLBNode(object): diff --git a/conpaas-services/src/conpaas/services/mysql/manager/manager.py b/conpaas-services/src/conpaas/services/mysql/manager/manager.py index cfc2ad21..eb856354 100644 --- a/conpaas-services/src/conpaas/services/mysql/manager/manager.py +++ b/conpaas-services/src/conpaas/services/mysql/manager/manager.py @@ -91,7 +91,7 @@ def _start_mysqld(self, nodes): raise try: glb_nodes = self.config.get_glb_nodes() - self.logger.debug('MySQL Galera node already active: %s' % glb_nodes) + self.logger.debug('MySQL nodes already active: %s' % glb_nodes) nodesIp=[] nodesIp = ["%s:%s" % (node.ip, self.config.MYSQL_PORT) # FIXME: find real mysql port instead of default 3306 for node in nodes] @@ -99,7 +99,7 @@ def _start_mysqld(self, nodes): agent.add_glbd_nodes(glb.ip, self.config.AGENT_PORT, nodesIp) return True except Exception as ex: - self.logger.exception('Failed to configure new MySQL GLB: %s' % ex) + self.logger.exception('Failed to configure new GLB node: %s' % ex) raise def _start_glbd(self, new_glb_nodes): @@ -111,7 +111,7 @@ def _start_glbd(self, new_glb_nodes): self.logger.debug('create_glb_node for new_glb.ip = %s' % new_glb.ip) agent.start_glbd(new_glb.ip, self.config.AGENT_PORT, nodes) except AgentException: - self.logger.exception('Failed to start MySQL GLB Node at node %s' % new_glb.ip) + self.logger.exception('Failed to start GLB at node %s' % new_glb.ip) raise @expose('GET') @@ -230,8 +230,8 @@ def getMeanLoad(self, kwargs): inserts=[] insert=0 for node in nodes: - # self.logger.debug('connecting to: %s, using username: %s and pwd: %s' % (node.ip, 'mysqldb', self.root_pass)) - db = MySQLdb.connect(node.ip, 'mysqldb', self.root_pass) + # self.logger.debug('connecting to: %s, using username: %s and pwd: %s' % (node.ip, 'root', self.root_pass)) + db = MySQLdb.connect(node.ip, 'root', self.root_pass) exc = db.cursor() exc.execute("SHOW STATUS LIKE 'wsrep_local_recv_queue_avg';") localLoad=exc.fetchone()[1] @@ -441,7 +441,7 @@ def _do_migrate_nodes(self, migration_plan, delay): new_nodes = [] # TODO: make it parallel for cloud, count in new_vm_nb.iteritems(): - self.controller.add_context_replacement(dict(mysql_username='mysqldb', + self.controller.add_context_replacement(dict(mysql_username='root', mysql_password=self.root_pass), cloud=cloud) @@ -526,13 +526,12 @@ def set_password(self, kwargs): user, password = check_arguments(exp_params, kwargs) self.check_state([self.S_RUNNING]) except Exception as ex: - return HttpErrorResponse("Cannot set new password: %s." % ex) + return HttpErrorResponse("%s" % ex) one_node = self.config.get_nodes()[0] try: agent.set_password(one_node.ip, self.config.AGENT_PORT, user, password) - self.root_pass = password except Exception as ex: self.logger.exception() return HttpErrorResponse('Failed to set new password: %s.' % ex) @@ -596,10 +595,10 @@ def sqldump(self, kwargs): one_node = self.config.get_nodes()[0] # adding option '--skip-lock-tables' to avoid issue # mysqldump: Got error: 1142: SELECT,LOCK TABL command denied to user - # 'mysqldb'@'10.158.0.28' for table 'cond_instances' + # 'root'@'10.158.0.28' for table 'cond_instances' # when using LOCK TABLES # FIXME: is it MySQL 5.1 only? does it still occur with MySQL 5.5? - cmd = 'mysqldump -u mysqldb -h %s --password=%s -A --skip-lock-tables' \ + cmd = 'mysqldump -u root -h %s --password=%s -A --skip-lock-tables' \ % (one_node.ip, self.root_pass) out, error, return_code = run_cmd_code(cmd) @@ -673,13 +672,13 @@ def setMySqlParams(self, kwargs): self.check_state([self.S_RUNNING]) nodes = self.config.get_nodes() for node in nodes: - db = MySQLdb.connect(node.ip, 'mysqldb', self.root_pass) + db = MySQLdb.connect(node.ip, 'root', self.root_pass) exc = db.cursor() exc.execute('set global ' + variable + ' = ' + value + ';') '''glb_nodes = self.config.get_glb_nodes() n=len(nodes)*value for node in glb_nodes: - db = MySQLdb.connect(node.ip, 'mysqldb', self.root_pass,port=8010) + db = MySQLdb.connect(node.ip, 'root', self.root_pass,port=8010) exc = db.cursor() exc.execute('set global ' + variable + ' = ' + n + ';')''' except Exception as ex: diff --git a/conpaas-services/src/tests/core/test_agent.py b/conpaas-services/src/tests/core/test_agent.py index 4d2d5da7..4f0db296 100644 --- a/conpaas-services/src/tests/core/test_agent.py +++ b/conpaas-services/src/tests/core/test_agent.py @@ -16,7 +16,7 @@ def config_parser(): config_parser.set('agent', 'TYPE', 'test') config_parser.set('agent', 'USER_ID', '1') config_parser.set('agent', 'SERVICE_ID', '1') - config_parser.set('agent', 'IP_WHITE_LIST', '127.0.0.1') + config_parser.set('agent', 'MANAGER_IP', '127.0.0.1') config_parser.set('agent', 'CONPAAS_HOME', '/dev/null') return config_parser