diff --git a/.flake8 b/.flake8 index 1962f7e..c634542 100644 --- a/.flake8 +++ b/.flake8 @@ -3,7 +3,7 @@ # E221 multiple spaces before operator # E251 unexpected spaces around keyword / parameter equals -ignore = E221,E251 +# ignore = E221,E251 exclude = # No need to traverse our git directory diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..fdfb39e --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,25 @@ +# Changelog + +This file contains all significant changes to this Ansible Role. + +This file adheres to the guidelines of [http://keepachangelog.com/](http://keepachangelog.com/). +Versioning follows [Semantic Versioning](http://semver.org/). +"GH-X" refers to the X'th issue/pull request on the Github project. + +## 2.6.0 - 2024-09-10 + +There are quite a few breaking changes in this version, so update your playbooks! + +### Breaking changes + +- The variables `mariadb_root_home`, `mariadb_root_username`, `mariadb_root_password` and `mariadb_root_password_update` have been removed and replaced by the dictionary `mariadb_system_users`. +- The setting of the root password can be prevented + +### Added + +- The filter `system_user` has been added and can separate a user from the dictionary `mariadb_system_users`. + +### Modified + +- `mariadb_monitoring` must now be explicitly activated +- `mariadb_mysqltuner` must now be explicitly activated diff --git a/README.md b/README.md index c6d7519..d3bdb5b 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,30 @@ ansible-galaxy collection install --requirements-file collections.yml mariadb_datadir: /var/lib/mysql ``` +### create system users + +To create a `.my.cnf` on an instance, `mariadb_system_users` can be used. + +If no password is set, the associated task is skipped. + + +| variable | description | +| :--- | :----- | +| `username` | The user name for administrative access. | +| `password` | If no password is set, the associated task is skipped. | +| `home` | The home directory under which a `.my.cnf` is created. | +| `update` | Should the password be updated. | +| `ignore` | If the entire process is to be ignored, `ignore` must be set to `true`. | + +```yaml +mariadb_system_users: + - username: root + password: "" + home: /root + update: true + ignore: true +``` + ### create databases ```yaml @@ -57,7 +81,7 @@ mariadb_databases: encoding: utf8 ``` -### create users +### create database users ```yaml mariadb_users: @@ -179,21 +203,19 @@ mariadb_version: 10.4 mariadb_debian_repo: "http://mirror.netcologne.de/mariadb/repo" mariadb_monitoring: - enabled: true + enabled: false system_user: "nobody" username: 'monitoring' password: '8WOMmRWWYHPR' -mariadb_mysqltuner: true - -# The default root user installed by mysql - almost always root -mariadb_root_home: /root -mariadb_root_username: root -mariadb_root_password: root +mariadb_mysqltuner: false -# Set this to `true` to forcibly update the root password. -mariadb_root_password_update: true -mariadb_user_password_update: false +mariadb_system_users: + - username: root + password: "" + home: /root + update: true + ignore: true mariadb_enabled_on_startup: true diff --git a/archlinux.yml b/archlinux.yml deleted file mode 100644 index 394102a..0000000 --- a/archlinux.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- - -mariadb_dependencies: - - iproute2 - -mariadb_packages: - - mariadb - - mariadb-clients - - mariadb-libs - - python-mysqlclient - -mariadb_python_packages: [] - -mariadb_config_file: /etc/my.cnf - -mariadb_config_dir: /etc/my.cnf.d -mariadb_config_include_dir: "{{ mariadb_config_dir }}" - -# package name -mariadb_server: mariadb - -# service name -mariadb_service: mariadb - -... diff --git a/defaults/main.yml b/defaults/main.yml index cf7230f..c166659 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -7,7 +7,7 @@ mariadb_version: 10.6 mariadb_debian_repo: https://mirror.netcologne.de/mariadb/repo mariadb_monitoring: - enabled: true + enabled: false system_user: "nobody" username: 'monitoring' password: '8WOMmRWWYHPR' @@ -15,13 +15,19 @@ mariadb_monitoring: mariadb_mysqltuner: false # The default root user installed by mysql - almost always root -mariadb_root_home: /root -mariadb_root_username: root -mariadb_root_password: root - -# Set this to `true` to forcibly update the root password. -mariadb_root_password_update: true -mariadb_user_password_update: false +# mariadb_root_home: /root +# mariadb_root_username: root +# mariadb_root_password: root +# +# # Set this to `true` to forcibly update the root password. +# mariadb_root_password_update: true + +mariadb_system_users: + - username: root + password: "" + home: /root + update: true + ignore: true mariadb_enabled_on_startup: true diff --git a/filter_plugins/mariadb.py b/filter_plugins/mariadb.py index 5d2bbbe..9228522 100644 --- a/filter_plugins/mariadb.py +++ b/filter_plugins/mariadb.py @@ -6,7 +6,7 @@ import os import re -import json +# import json from ansible.utils.display import Display from ansible.module_utils.common._collections_compat import Mapping @@ -25,9 +25,8 @@ def filters(self): 'support_tls': self.support_tls, 'tls_directory': self.tls_directory, 'detect_galera': self.detect_galera, - 'wsrep_cluster_address': self.wsrep_cluster_address - - # 'galera_node_information': self.galera_node_information, + 'wsrep_cluster_address': self.wsrep_cluster_address, + 'system_user': self.system_user, } def support_tls(self, data): @@ -35,9 +34,9 @@ def support_tls(self, data): """ # display.vv(f"support_tls({data})") - ssl_ca = data.get("ssl-ca", None) + ssl_ca = data.get("ssl-ca", None) ssl_cert = data.get("ssl-cert", None) - ssl_key = data.get("ssl-key", None) + ssl_key = data.get("ssl-key", None) if ssl_ca and ssl_cert and ssl_key: return True @@ -51,9 +50,9 @@ def tls_directory(self, data): directory = [] - ssl_ca = data.get("ssl-ca", None) + ssl_ca = data.get("ssl-ca", None) ssl_cert = data.get("ssl-cert", None) - ssl_key = data.get("ssl-key", None) + ssl_key = data.get("ssl-key", None) if ssl_ca and ssl_cert and ssl_key: directory.append(os.path.dirname(ssl_ca)) @@ -70,10 +69,10 @@ def detect_galera(self, data, hostvars): """ display.vv(f"detect_galera({data}, hostvars)") result = dict( - galera = False, - cluster_members = [], - cluster_primary_node = "", - cluster_replica_nodes = [], + galera=False, + cluster_members=[], + cluster_primary_node="", + cluster_replica_nodes=[], # primary = False ) @@ -141,10 +140,10 @@ def detect_galera(self, data, hostvars): replica_nodes = [x for x, v in node_information.items() if v != primary_address] result = dict( - galera = True, - cluster_members = cluster_members, - cluster_primary_node = primary_node, - cluster_replica_nodes = replica_nodes, + galera=True, + cluster_members=cluster_members, + cluster_primary_node=primary_node, + cluster_replica_nodes=replica_nodes, # primary = primary ) @@ -195,3 +194,15 @@ def _galera_node_information(self, data): display.vv(f"= {result}") return result + + def system_user(self, data, username): + """ """ + display.vv(f"system_user({data}, {username})") + + result = [x for x in data if x.get('username') == username] + if len(result) == 1: + result = result[0] + + display.vv(f"= {result}") + + return result diff --git a/library/mariadb_bootstrap.py b/library/mariadb_bootstrap.py index 13a27d7..55fb497 100644 --- a/library/mariadb_bootstrap.py +++ b/library/mariadb_bootstrap.py @@ -156,7 +156,7 @@ def run(self): def main(): module = AnsibleModule( - argument_spec = dict( + argument_spec=dict( datadir=dict(required=False, type='path', default='/var/lib/mysql'), basedir=dict(required=False, type='path', default='/usr'), user=dict(required=False, type='str', default='mysql'), @@ -166,7 +166,7 @@ def main(): skip_name_resolve=dict(required=False, type='bool'), skip_test_db=dict(required=False, type='bool'), ), - supports_check_mode = False, + supports_check_mode=False, ) helper = MariadbBootstrap(module) diff --git a/library/mariadb_data_directory.py b/library/mariadb_data_directory.py index 0df1223..6b85247 100644 --- a/library/mariadb_data_directory.py +++ b/library/mariadb_data_directory.py @@ -86,9 +86,9 @@ def run(self): self.module.log(msg=" Directory not copied. Error: {}".format(e)) return dict( - changed = True, - failed = False, - msg = "directory {} synced to {}".format(self.source, self.destination) + changed=True, + failed=False, + msg="directory {} synced to {}".format(self.source, self.destination) ) # =========================================== @@ -98,11 +98,11 @@ def run(self): def main(): module = AnsibleModule( - argument_spec = dict( + argument_spec=dict( source=dict(required=False, type='path', default='/var/lib/mysql'), destination=dict(required=True, type='path'), ), - supports_check_mode = False, + supports_check_mode=False, ) helper = MariadbDataDirectories(module) diff --git a/library/mariadb_replication.py b/library/mariadb_replication.py index cecf0ad..b00aa3e 100644 --- a/library/mariadb_replication.py +++ b/library/mariadb_replication.py @@ -301,6 +301,7 @@ def __init__(self, module): self.login_password = self.module.params.get("login_password") self.login_username = self.module.params.get("login_user") + self.login_unix_socket = self.module.params.get("login_unix_socket") self.mode = module.params.get("mode") self.primary_host = module.params.get("primary_host") self.primary_user = module.params.get("primary_user") @@ -322,9 +323,10 @@ def __init__(self, module): self.ssl_key = module.params.get("client_key") self.ssl_ca = module.params.get("ca_cert") # check_hostname = module.params.get("check_hostname") - self.connect_timeout = module.params['connect_timeout'] - self.config_file = module.params['config_file'] - self.primary_delay = module.params['primary_delay'] + self.connect_timeout = module.params.get('connect_timeout') + self.config_file = module.params.get('config_file') + self.primary_delay = module.params.get('primary_delay') + if module.params.get("primary_use_gtid") == 'disabled': self.primary_use_gtid = 'no' else: @@ -339,7 +341,7 @@ def __init__(self, module): if self.primary_use_gtid == 'replica_pos': self.primary_use_gtid = 'slave_pos' - module.log(msg=" mode: {}".format(self.mode)) + # module.log(msg=" mode: {}".format(self.mode)) def run(self): """ @@ -362,70 +364,30 @@ def run(self): self.prepare(cursor) - if self.mode in ('get_primary'): + if self.mode == 'get_primary': """ get primary information """ result = self.get_primary(cursor) - elif self.mode in ("get_replica"): + elif self.mode == "get_replica": """ get replica state """ result = self.get_replica(cursor) - elif self.mode in ("change_primary"): + elif self.mode == "change_primary": """ - """ - chm = [] result = {} - if self.primary_host is not None: - chm.append("MASTER_HOST='%s'" % self.primary_host) - if self.primary_user is not None: - chm.append("MASTER_USER='%s'" % self.primary_user) - if self.primary_password is not None: - chm.append("MASTER_PASSWORD='%s'" % self.primary_password) - if self.primary_port is not None: - chm.append("MASTER_PORT=%s" % self.primary_port) - if self.primary_connect_retry is not None: - chm.append("MASTER_CONNECT_RETRY=%s" % self.primary_connect_retry) - if self.primary_log_file is not None: - chm.append("MASTER_LOG_FILE='%s'" % self.primary_log_file) - if self.primary_log_pos is not None: - chm.append("MASTER_LOG_POS=%s" % self.primary_log_pos) - if self.primary_delay is not None: - chm.append("MASTER_DELAY=%s" % self.primary_delay) - if self.relay_log_file is not None: - chm.append("RELAY_LOG_FILE='%s'" % self.relay_log_file) - if self.relay_log_pos is not None: - chm.append("RELAY_LOG_POS=%s" % self.relay_log_pos) - if self.primary_ssl: - chm.append("MASTER_SSL=1") - if self.primary_ssl_ca is not None: - chm.append("MASTER_SSL_CA='%s'" % self.primary_ssl_ca) - if self.primary_ssl_capath is not None: - chm.append("MASTER_SSL_CAPATH='%s'" % self.primary_ssl_capath) - if self.primary_ssl_cert is not None: - chm.append("MASTER_SSL_CERT='%s'" % self.primary_ssl_cert) - if self.primary_ssl_key is not None: - chm.append("MASTER_SSL_KEY='%s'" % self.primary_ssl_key) - if self.primary_ssl_cipher is not None: - chm.append("MASTER_SSL_CIPHER='%s'" % self.primary_ssl_cipher) - if self.primary_auto_position: - chm.append("MASTER_AUTO_POSITION=1") - if self.primary_use_gtid is not None: - chm.append("MASTER_USE_GTID=%s" % self.primary_use_gtid) - - self.module.log(msg=" chm: {}".format(chm)) try: - self.change_primary(cursor, chm) + self.change_primary(cursor) except mysql_driver.Warning as e: result['warning'] = to_native(e) except Exception as e: self.module.fail_json( - msg='{}. Query == CHANGE MASTER TO {}'.format(to_native(e), chm)) + msg=f"{to_native(e)}. Query == CHANGE MASTER TO ...") result['changed'] = True @@ -532,7 +494,7 @@ def prepare(self, cursor): version = cursor.fetchone()["version"].lower() - self.module.log(msg="- version: {}".format(LooseVersion(version))) + self.module.log(f"- version: {LooseVersion(version)}") if LooseVersion(version) >= LooseVersion('10.5.1'): # or LooseVersion(result) >= LooseVersion('8.0.22'): # self.primary_term = 'PRIMARY' @@ -542,8 +504,9 @@ def prepare(self, cursor): def get_primary(self, cursor): """ - """ + self.module.log("get_primary()") + result = dict() # if self.mode == 'getmaster': @@ -554,9 +517,9 @@ def get_primary(self, cursor): # TODO: when it's available to change on MySQL's side, # change MASTER to PRIMARY using the approach from # get_replica_status() function. Same for other functions. - query = "SHOW {} STATUS".format(self.primary_term) + query = f"SHOW {self.primary_term} STATUS" - self.module.log(msg="- query: {}".format(query)) + self.module.log(msg=f"- query: {query}") cursor.execute(query) primary_status = cursor.fetchone() @@ -582,19 +545,20 @@ def get_primary(self, cursor): def get_replica(self, cursor): """ - """ + self.module.log("get_replica()") + result = dict() - query = "SHOW {} STATUS".format(self.replica_term) + query = f"SHOW {self.replica_term} STATUS" if self.connection_name: - query = "SHOW {} '{}' STATUS".format(self.replica_term, self.connection_name) + query = f"SHOW {self.replica_term} '{self.connection_name}' STATUS" if self.channel: - query += " FOR CHANNEL '{}'".format(self.channel) + query += f" FOR CHANNEL '{self.channel}'" - self.module.log(msg="- query: {}".format(query)) + self.module.log(msg=f"- query: {query}") cursor.execute(query) replica_status = cursor.fetchone() @@ -758,18 +722,60 @@ def start_replica(self, cursor): return started - def change_primary(self, cursor, chm): + def change_primary(self, cursor): """ """ - query = 'CHANGE MASTER TO {}'.format(','.join(chm)) + chm = [] + if self.primary_host is not None: + chm.append(f"MASTER_HOST='{self.primary_host}'") + if self.primary_user is not None: + chm.append(f"MASTER_USER='{self.primary_user}'") + if self.primary_password is not None: + chm.append(f"MASTER_PASSWORD='{self.primary_password}'") + if self.primary_port is not None: + chm.append(f"MASTER_PORT={self.primary_port}") + if self.primary_connect_retry is not None: + chm.append(f"MASTER_CONNECT_RETRY={self.primary_connect_retry}") + if self.primary_log_file is not None: + chm.append(f"MASTER_LOG_FILE='{self.primary_log_file}'") + if self.primary_log_pos is not None: + chm.append(f"MASTER_LOG_POS={self.primary_log_pos}") + if self.primary_delay is not None: + chm.append(f"MASTER_DELAY={self.primary_delay}") + if self.relay_log_file is not None: + chm.append(f"RELAY_LOG_FILE='{self.relay_log_file}'") + if self.relay_log_pos is not None: + chm.append(f"RELAY_LOG_POS={self.relay_log_pos}") + if self.primary_ssl: + chm.append("MASTER_SSL=1") + if self.primary_ssl_ca is not None: + chm.append(f"MASTER_SSL_CA='{self.primary_ssl_ca}'") + if self.primary_ssl_capath is not None: + chm.append(f"MASTER_SSL_CAPATH='{self.primary_ssl_capath}'") + if self.primary_ssl_cert is not None: + chm.append(f"MASTER_SSL_CERT='{self.primary_ssl_cert}'") + if self.primary_ssl_key is not None: + chm.append(f"MASTER_SSL_KEY='{self.primary_ssl_key}'") + if self.primary_ssl_cipher is not None: + chm.append(f"MASTER_SSL_CIPHER='{self.primary_ssl_cipher}'") + if self.primary_auto_position: + chm.append("MASTER_AUTO_POSITION=1") + if self.primary_use_gtid is not None: + chm.append(f"MASTER_USE_GTID={self.primary_use_gtid}") + + self.module.log(msg=f" chm: {chm}") + + comma_chm = ','.join(chm) + + query = f"CHANGE MASTER TO {comma_chm}" if self.connection_name: - query = "CHANGE MASTER '{}' TO {}".format(self.connection_name, ','.join(chm)) + query = f"CHANGE MASTER '{self.connection_name}' TO {comma_chm}" if self.channel: - query += " FOR CHANNEL '{}'".format(self.channel) + query += f" FOR CHANNEL '{self.channel}'" - self.module.log(msg="- query: {}".format(query)) + self.module.log(msg=f"- query: {query}") self.executed_queries.append(query) cursor.execute(query) @@ -792,7 +798,10 @@ def _mysql_connect(self): if self.login_password is not None: config['passwd'] = self.login_password - # self.module.log(msg="config : {}".format(config)) + if self.login_unix_socket is not None and os.path.exists(self.login_unix_socket): + config['unix_socket'] = self.login_unix_socket + + # self.module.log(msg=f"config : {config}") if mysql_driver is None: self.module.fail_json(msg=mysql_driver_fail_msg) @@ -803,9 +812,8 @@ def _mysql_connect(self): except Exception as e: message = "unable to connect to database. " message += "check login_host, login_user and login_password are correct " - message += "or {0} has the credentials. " - message += "Exception message: {1}" - message = message.format(config_file, to_native(e)) + message += f"or {config_file} has the credentials. " + message += f"Exception message: {to_native(e)}" self.module.log(msg=message) diff --git a/library/mariadb_root_password.py b/library/mariadb_root_password.py index 22d94b4..bab0aaf 100644 --- a/library/mariadb_root_password.py +++ b/library/mariadb_root_password.py @@ -111,8 +111,9 @@ def run(self): args.append("--host") args.append(self.dba_bind_address) - args.append("password") - args.append(self.dba_root_password) + if self.dba_root_password: + args.append("password") + args.append(self.dba_root_password) self.module.log(msg=f" - args: {args}") diff --git a/library/mariadb_secure.py b/library/mariadb_secure.py index 4503195..1691964 100644 --- a/library/mariadb_secure.py +++ b/library/mariadb_secure.py @@ -59,6 +59,7 @@ def __init__(self, module): self.disallow_remote_root_login = module.params.get("disallow_remote_root_login") self.dba_root_username = module.params.get("dba_root_username") self.dba_root_password = module.params.get("dba_root_password") + self.dba_socket = module.params.get("dba_socket") self.mycnf_file = module.params.get("mycnf_file") self.db_connect_timeout = 30 @@ -76,44 +77,53 @@ def run(self): # args.append("status") # # rc, out, err = self._exec(args) + res = dict( + changed=False, + msg="all fine.", + ) - if self.disallow_remote_root_login: - state, error, error_message = self._remove_anonymous_users() + self.cursor, self.conn, error, message = self._mysql_connect() - self.module.log(msg=" - disallow remote root login: {}".format(state)) + if not error: - if error: - return dict( - failed=True, - msg=error_message - ) + if self.disallow_remote_root_login: + state, error, error_message = self._remove_anonymous_users() - if self.disallow_anonymous_users: - state, error, error_message = self._remove_remote_root_login() + self.module.log(msg=f" - disallow remote root login: {state}") - self.module.log(msg=" - disallow anonymous users: {}".format(state)) + if error: + return dict( + failed=True, + msg=error_message + ) - if error: - return dict( - failed=True, - msg=error_message - ) + if self.disallow_anonymous_users: + state, error, error_message = self._remove_remote_root_login() - if self.disallow_test_database: - state, error, error_message = self._remove_test_database() + self.module.log(msg=f" - disallow anonymous users: {state}") - self.module.log(msg=" - remove test database: {}".format(state)) + if error: + return dict( + failed=True, + msg=error_message + ) - if error: - return dict( - failed=True, - msg=error_message - ) + if self.disallow_test_database: + state, error, error_message = self._remove_test_database() - res = dict( - changed=False, - msg="all fine.", - ) + self.module.log(msg=f" - remove test database: {state}") + + if error: + return dict( + failed=True, + msg=error_message + ) + else: + res = dict( + changed=False, + msg=message + + ) return res @@ -219,7 +229,11 @@ def _exec(self, commands): def _mysql_connect(self): """ - + return: + cursor + conn + error + message """ config = {} @@ -232,10 +246,14 @@ def _mysql_connect(self): # config file if self.dba_root_username is not None: config['user'] = self.dba_root_username + if self.dba_root_password is not None: config['passwd'] = self.dba_root_password - # self.module.log(msg="config : {}".format(config)) + if self.dba_socket is not None and os.path.exists(self.dba_socket): + config['unix_socket'] = self.dba_socket + + # self.module.log(msg=f"config : {config}") if mysql_driver is None: self.module.fail_json(msg=mysql_driver_fail_msg) @@ -246,9 +264,8 @@ def _mysql_connect(self): except Exception as e: message = "unable to connect to database. " message += "check login_host, login_user and login_password are correct " - message += "or {0} has the credentials. " - message += "Exception message: {1}" - message = message.format(config_file, to_native(e)) + message += f"or {config_file} has the credentials. " + message += f"Exception message: {to_native(e)}" self.module.log(msg=message) @@ -268,15 +285,42 @@ def _parse_from_mysql_config_file(self, cnf): def main(): ''' ... ''' - module = AnsibleModule( - argument_spec=dict( - disallow_anonymous_users=dict(required=False, type='bool'), - disallow_test_database=dict(required=False, type='bool'), - disallow_remote_root_login=dict(required=False, type='bool'), - # dba_root_username=dict(required=False, type='str'), - # dba_root_password=dict(required=False, type='str', no_log=True), - mycnf_file=dict(required=False, type="str", default="/root/.my.cnf"), + + specs = dict( + disallow_anonymous_users=dict( + required=False, + type='bool' + ), + disallow_test_database=dict( + required=False, + type='bool' + ), + disallow_remote_root_login=dict( + required=False, + type='bool' + ), + dba_root_username=dict( + required=False, + type='str' ), + dba_root_password=dict( + required=False, + type='str', + no_log=True + ), + dba_socket=dict( + required=False, + type='str' + ), + mycnf_file=dict( + required=False, + type="str", + default="/root/.my.cnf" + ), + ) + + module = AnsibleModule( + argument_spec=specs, supports_check_mode=False, ) @@ -285,7 +329,7 @@ def main(): client = MariaDBSecure(module) result = client.run() - module.log(msg="= result: {}".format(result)) + module.log(msg=f"= result: {result}") module.log(msg="-------------------------------------------------------------") module.exit_json(**result) diff --git a/library/mariadb_tls_certificates.py b/library/mariadb_tls_certificates.py index 1162aa3..c8c5a9f 100644 --- a/library/mariadb_tls_certificates.py +++ b/library/mariadb_tls_certificates.py @@ -40,8 +40,8 @@ def __init__(self, module): self.group = module.params.get("group") self.ssl_cert = self.source.get("ssl_cert", None) - self.ssl_key = self.source.get("ssl_key", None) - self.ssl_ca = self.source.get("ssl_ca", None) + self.ssl_key = self.source.get("ssl_key", None) + self.ssl_ca = self.source.get("ssl_ca", None) self.ssl_files = [] if self.ssl_cert: @@ -132,7 +132,7 @@ def create_destination_directory(self): # Create the directory try: - os.makedirs(self.destination, exist_ok = True) + os.makedirs(self.destination, exist_ok=True) msg = f"Directory '{self.destination}' created successfully." shutil.chown(self.destination, self.owner, self.group) @@ -224,7 +224,7 @@ def __checksum(self, plaintext): def main(): module = AnsibleModule( - argument_spec = dict( + argument_spec=dict( source=dict( required=True, type='dict', @@ -244,7 +244,7 @@ def main(): default="mysql" ), ), - supports_check_mode = False, + supports_check_mode=False, ) helper = MariadbDataDirectories(module) diff --git a/molecule/configured/group_vars/all/vars.yml b/molecule/configured/group_vars/all/vars.yml index 207ed06..ff4be09 100644 --- a/molecule/configured/group_vars/all/vars.yml +++ b/molecule/configured/group_vars/all/vars.yml @@ -3,7 +3,12 @@ mariadb_use_external_repo: true mariadb_version: 10.4 -mariadb_root_password: "" # Y5FZfKXzCeOWGf4kBOiFSp6Il +mariadb_system_users: + - username: root + password: "" + home: /root + update: true + ignore: true mariadb_databases: - name: molecule diff --git a/molecule/custom-datadir/group_vars/all/vars.yml b/molecule/custom-datadir/group_vars/all/vars.yml index d7e6534..9f6073f 100644 --- a/molecule/custom-datadir/group_vars/all/vars.yml +++ b/molecule/custom-datadir/group_vars/all/vars.yml @@ -1,6 +1,11 @@ --- -mariadb_root_password: Y5FZfKXzCeOWGf4kBOiFSp6Il +mariadb_system_users: + - username: root + password: Y5FZfKXzCeOWGf4kBOiFSp6Il + home: /root + update: true + ignore: false mariadb_databases: - name: molecule diff --git a/molecule/galera-cluster/group_vars/all/vars.yml b/molecule/galera-cluster/group_vars/all/vars.yml index d5d22e1..1880f52 100644 --- a/molecule/galera-cluster/group_vars/all/vars.yml +++ b/molecule/galera-cluster/group_vars/all/vars.yml @@ -2,7 +2,14 @@ # mariadb_use_external_repo: false -mariadb_root_password: Y5FZfKXzCeOWGf4kBOiFSp6Il +mariadb_system_users: + - username: root + password: Y5FZfKXzCeOWGf4kBOiFSp6Il + home: /root + update: true + ignore: true + +# mariadb_root_password: mariadb_instances: "{{ groups['mariadb'] | map('extract', hostvars, ['ansible_default_ipv4', 'address']) | list }}" mariadb_hostnames: "{{ groups['mariadb'] | map('extract', hostname) | list }}" diff --git a/molecule/galera-cluster/tests/test_primary.py b/molecule/galera-cluster/tests/test_primary.py index 1e4a3d1..b1b74f4 100644 --- a/molecule/galera-cluster/tests/test_primary.py +++ b/molecule/galera-cluster/tests/test_primary.py @@ -69,7 +69,7 @@ def get_vars(host): ansible_vars.update(molecule_vars) # print(pp_json(ansible_vars)) - _ = ansible_vars.get("mariadb_config_galera",{}).pop("wsrep_cluster_address") + _ = ansible_vars.get("mariadb_config_galera", {}).pop("wsrep_cluster_address") templar = Templar(loader=DataLoader(), variables=ansible_vars) result = templar.template(ansible_vars, fail_on_undefined=False) diff --git a/molecule/galera-cluster/tests/test_replica_1.py b/molecule/galera-cluster/tests/test_replica_1.py index 467d030..4b8df61 100644 --- a/molecule/galera-cluster/tests/test_replica_1.py +++ b/molecule/galera-cluster/tests/test_replica_1.py @@ -69,7 +69,7 @@ def get_vars(host): ansible_vars.update(molecule_vars) # print(pp_json(ansible_vars)) - _ = ansible_vars.get("mariadb_config_galera",{}).pop("wsrep_cluster_address") + _ = ansible_vars.get("mariadb_config_galera", {}).pop("wsrep_cluster_address") templar = Templar(loader=DataLoader(), variables=ansible_vars) result = templar.template(ansible_vars, fail_on_undefined=False) diff --git a/molecule/galera-cluster/tests/test_replica_2.py b/molecule/galera-cluster/tests/test_replica_2.py index 78f7c1e..3a409a2 100644 --- a/molecule/galera-cluster/tests/test_replica_2.py +++ b/molecule/galera-cluster/tests/test_replica_2.py @@ -69,7 +69,7 @@ def get_vars(host): ansible_vars.update(molecule_vars) # print(pp_json(ansible_vars)) - _ = ansible_vars.get("mariadb_config_galera",{}).pop("wsrep_cluster_address") + _ = ansible_vars.get("mariadb_config_galera", {}).pop("wsrep_cluster_address") templar = Templar(loader=DataLoader(), variables=ansible_vars) result = templar.template(ansible_vars, fail_on_undefined=False) diff --git a/molecule/mariadb-10.4-without-root-password/group_vars/all/vars.yml b/molecule/mariadb-10.4-without-root-password/group_vars/all/vars.yml index 68d23c7..e5fdbf8 100644 --- a/molecule/mariadb-10.4-without-root-password/group_vars/all/vars.yml +++ b/molecule/mariadb-10.4-without-root-password/group_vars/all/vars.yml @@ -3,7 +3,12 @@ mariadb_use_external_repo: true mariadb_version: 10.4 -mariadb_root_password: "" +mariadb_system_users: + - username: root + password: "" + home: /root + update: true + ignore: true mariadb_monitoring: enabled: false diff --git a/molecule/mariadb-10.4/group_vars/all/vars.yml b/molecule/mariadb-10.4/group_vars/all/vars.yml index 2e87921..e8dde14 100644 --- a/molecule/mariadb-10.4/group_vars/all/vars.yml +++ b/molecule/mariadb-10.4/group_vars/all/vars.yml @@ -3,7 +3,12 @@ mariadb_use_external_repo: true mariadb_version: 10.4 -mariadb_root_password: "Y5FZfKXzCeOWGf4kBOiFSp6Il--XXX" +mariadb_system_users: + - username: root + password: Y5FZfKXzCeOWGf4kBOiFSp6Il--XXX + home: /root + update: true + ignore: false mariadb_monitoring: enabled: false diff --git a/molecule/mariadb-10.5/group_vars/all/vars.yml b/molecule/mariadb-10.5/group_vars/all/vars.yml index 2083ad7..6e7932b 100644 --- a/molecule/mariadb-10.5/group_vars/all/vars.yml +++ b/molecule/mariadb-10.5/group_vars/all/vars.yml @@ -3,7 +3,13 @@ mariadb_use_external_repo: true mariadb_version: 10.5 -mariadb_root_password: "QyJrytNhA7Mm2/x/oJFtAVC7KTyng5K9egKa3Ffm7_" + +mariadb_system_users: + - username: root + password: "QyJrytNhA7Mm2/x/oJFtAVC7KTyng5K9egKa3Ffm7_" + home: /root + update: true + ignore: false mariadb_monitoring: enabled: false diff --git a/molecule/with-replication/group_vars/all/vars.yml b/molecule/with-replication/group_vars/all/vars.yml index e49dbcc..e26086c 100644 --- a/molecule/with-replication/group_vars/all/vars.yml +++ b/molecule/with-replication/group_vars/all/vars.yml @@ -1,11 +1,14 @@ --- -# mariadb_use_external_repo: false - -mariadb_root_password: Y5FZfKXzCeOWGf4kBOiFSp6Il - mariadb_instances: "{{ groups['replica'] | map('extract', hostvars, ['ansible_default_ipv4', 'address']) | list }}" +mariadb_system_users: + - username: root + password: Y5FZfKXzCeOWGf4kBOiFSp6Il + home: /root + update: true + ignore: false + mariadb_databases: - name: molecule collation: utf8mb4_bin @@ -55,5 +58,7 @@ mariadb_replication: # The password must not be longer than 32 characters! password: "vkxHlCVMHAEtEFkEB9pspPB3N" encrypted: false + priv: + "*.*": "REPLICATION MASTER ADMIN,REPLICATION SLAVE ADMIN,REPLICATION SLAVE,REPLICATION CLIENT,REPLICA MONITOR,BINLOG MONITOR,BINLOG REPLAY" ... diff --git a/molecule/with-tls/group_vars/all/vars.yml b/molecule/with-tls/group_vars/all/vars.yml index 6043623..7c1f871 100644 --- a/molecule/with-tls/group_vars/all/vars.yml +++ b/molecule/with-tls/group_vars/all/vars.yml @@ -1,6 +1,11 @@ --- -mariadb_root_password: Y5FZfKXzCeOWGf4kBOiFSp6Il +mariadb_system_users: + - username: root + password: Y5FZfKXzCeOWGf4kBOiFSp6Il + home: /root + update: true + ignore: true mariadb_databases: - name: molecule @@ -28,7 +33,6 @@ mariadb_config_client: ## Allow only TLS encrypted connections ## ssl-verify-server-cert: on - mariadb_config_mysqld: bind-address: 127.0.0.1 socket: "{{ mariadb_socket }}" @@ -45,5 +49,5 @@ mariadb_config_mysqld: ## ..when MariaDB is compiled with OpenSSL: ssl-cipher: TLSv1.2 ## ..when MariaDB is compiled with YaSSL (default in Debian): - ssl: + ssl: true tls_version: TLSv1.2,TLSv1.3 diff --git a/tasks/databases.yml b/tasks/databases.yml index e31fafe..6a87bd8 100644 --- a/tasks/databases.yml +++ b/tasks/databases.yml @@ -9,6 +9,10 @@ collation: "{{ item.collation | default('utf8_general_ci') }}" encoding: "{{ item.encoding | default('utf8') }}" state: "{{ item.state | default('present') }}" + login_user: "{{ _mariadb_root_system_user.username }}" + login_password: "{{ _mariadb_root_system_user.password }}" + login_unix_socket: "{{ mariadb_socket | default(omit) }}" + config_file: "{{ _mariadb_root_system_user.home | default('/root') }}/.my.cnf" loop: '{{ mariadb_databases }}' loop_control: diff --git a/tasks/monitoring.yml b/tasks/monitoring.yml index 52a0bb8..ca51503 100644 --- a/tasks/monitoring.yml +++ b/tasks/monitoring.yml @@ -16,6 +16,10 @@ collation: 'utf8_general_ci' encoding: 'utf8' state: present + login_user: "{{ _mariadb_root_system_user.username }}" + login_password: "{{ _mariadb_root_system_user.password }}" + login_unix_socket: "{{ mariadb_socket | default(omit) }}" + config_file: "{{ _mariadb_root_system_user.home | default('/root') }}/.my.cnf" - name: create monitoring user community.mysql.mysql_user: @@ -27,6 +31,10 @@ append_privs: false encrypted: false update_password: on_create + login_user: "{{ _mariadb_root_system_user.username }}" + login_password: "{{ _mariadb_root_system_user.password }}" + login_unix_socket: "{{ mariadb_socket | default(omit) }}" + config_file: "{{ _mariadb_root_system_user.home | default('/root') }}/.my.cnf" no_log: "{{ not lookup('env', 'ANSIBLE_DEBUG') | bool }}" - name: create my.cnf file with password credentials diff --git a/tasks/prepare.yml b/tasks/prepare.yml index 8bd8065..e78c9e9 100644 --- a/tasks/prepare.yml +++ b/tasks/prepare.yml @@ -96,6 +96,10 @@ - "{{ hostname | default('') }}" - "{{ ansible_hostname | default('') }}" +- name: define root system userdata + ansible.builtin.set_fact: + _mariadb_root_system_user: "{{ mariadb_system_users | system_user('root') }}" + - name: define mariadb_galera and mariadb_galera_cluster ansible.builtin.set_fact: mariadb_galera_cluster: "{{ _mariadb_galera_cluster.galera }}" diff --git a/tasks/replication.yml b/tasks/replication.yml index 09ea183..c1d7851 100644 --- a/tasks/replication.yml +++ b/tasks/replication.yml @@ -21,6 +21,10 @@ encrypted: "{{ mariadb_replication.user.encrypted | default('true') }}" update_password: always state: present + login_user: "{{ _mariadb_root_system_user.username }}" + login_password: "{{ _mariadb_root_system_user.password }}" + login_unix_socket: "{{ mariadb_socket | default(omit) }}" + config_file: "{{ _mariadb_root_system_user.home | default('/root') }}/.my.cnf" run_once: true register: create_replication_user loop: @@ -28,7 +32,7 @@ loop_control: label: "{{ mariadb_replication.user.name }}@{{ item | default('%') }}, password encypted: {{ mariadb_replication.user.encrypted | default('true') }}" - no_log: "{{ not lookup('env', 'ANSIBLE_DEBUG') | bool }}" + # no_log: "{{ not lookup('env', 'ANSIBLE_DEBUG') | bool }}" when: - mariadb_replication.role == 'primary' - mariadb_replication.primary is defined @@ -42,6 +46,10 @@ - name: check replica replication status mariadb_replication: mode: get_replica + login_user: "{{ _mariadb_root_system_user.username }}" + login_password: "{{ _mariadb_root_system_user.password }}" + login_unix_socket: "{{ mariadb_socket | default(omit) }}" + config_file: "{{ _mariadb_root_system_user.home | default('/root') }}/.my.cnf" ignore_errors: true register: state_of_replica # no_log: true @@ -52,6 +60,10 @@ delegate_to: "{{ mariadb_replication.primary }}" mariadb_replication: mode: get_primary + login_user: "{{ _mariadb_root_system_user.username }}" + login_password: "{{ _mariadb_root_system_user.password }}" + login_unix_socket: "{{ mariadb_socket | default(omit) }}" + config_file: "{{ _mariadb_root_system_user.home | default('/root') }}/.my.cnf" register: state_of_primary when: - mariadb_replication.role == 'replica' @@ -65,6 +77,10 @@ primary_password: "{{ mariadb_replication.user.password }}" primary_log_file: "{{ state_of_primary.File }}" primary_log_pos: "{{ state_of_primary.Position }}" + login_user: "{{ _mariadb_root_system_user.username }}" + login_password: "{{ _mariadb_root_system_user.password }}" + login_unix_socket: "{{ mariadb_socket | default(omit) }}" + config_file: "{{ _mariadb_root_system_user.home | default('/root') }}/.my.cnf" # failed_when: true no_log: "{{ not lookup('env', 'ANSIBLE_DEBUG') | bool }}" register: configure_replication_on_replica @@ -83,6 +99,10 @@ - name: start replication mariadb_replication: mode: start_replica + login_user: "{{ _mariadb_root_system_user.username }}" + login_password: "{{ _mariadb_root_system_user.password }}" + login_unix_socket: "{{ mariadb_socket | default(omit) }}" + config_file: "{{ _mariadb_root_system_user.home | default('/root') }}/.my.cnf" register: start_replication when: - (state_of_replica.Is_Replica is defined and not state_of_replica.Is_Replica) or diff --git a/tasks/secure-installation.yml b/tasks/secure-installation.yml index 1d518a0..d0b2b54 100644 --- a/tasks/secure-installation.yml +++ b/tasks/secure-installation.yml @@ -1,15 +1,20 @@ --- +# - name: define root system userdata +# ansible.builtin.set_fact: +# _mariadb_root_system_user: "{{ mariadb_system_users | system_user('root') }}" + - name: validate custom root password when: - - mariadb_root_password | length == 0 + - not _mariadb_root_system_user.ignore | default('true') + - _mariadb_root_system_user.password | default('') | length == 0 block: - name: warn if a custom root password is not specified ansible.builtin.fail: msg: | - WARNING - The MariaDB root password was left empty. - Please set a custom password with role variable 'mariadb_root_password' + Please set a custom password with role variable 'mariadb_system_users' to secure your database server! ignore_errors: true @@ -19,18 +24,26 @@ timeout: 10 - name: set database root password + when: + - _mariadb_root_system_user.password | default('') | length > 0 + - _mariadb_root_system_user.update + - not _mariadb_root_system_user.ignore | default('true') mariadb_root_password: - dba_root_username: "{{ mariadb_root_username }}" - dba_root_password: "{{ mariadb_root_password }}" + dba_root_username: "{{ _mariadb_root_system_user.username }}" + dba_root_password: "{{ _mariadb_root_system_user.password }}" dba_bind_address: "{{ mariadb_bind_address | default(omit) }}" dba_socket: "{{ mariadb_socket | default(omit) }}" dba_config_directory: "{{ mariadb_config_dir }}" - mycnf_file: "{{ mariadb_root_home }}/.my.cnf" + mycnf_file: "{{ _mariadb_root_system_user.home | default('/root') }}/.my.cnf" - name: secure database (remove anonymous user, disallow remote root login, ...) when: - not mariadb_galera_cluster or (mariadb_galera_cluster and mariadb_galera_primary_node == ansible_hostname) mariadb_secure: + dba_root_username: "{{ _mariadb_root_system_user.username }}" + dba_root_password: "{{ _mariadb_root_system_user.password }}" + mycnf_file: "{{ _mariadb_root_system_user.home | default('/root') }}/.my.cnf" + dba_socket: "{{ mariadb_socket | default(omit) }}" disallow_anonymous_users: true disallow_test_database: true disallow_remote_root_login: true diff --git a/tasks/users.yml b/tasks/users.yml index 01b070b..1ee0b64 100644 --- a/tasks/users.yml +++ b/tasks/users.yml @@ -12,6 +12,10 @@ state: "{{ item.state | default('present') }}" append_privs: "{{ item.append_privs | default('false') }}" encrypted: "{{ item.encrypted | default('true') }}" + login_user: "{{ _mariadb_root_system_user.username }}" + login_password: "{{ _mariadb_root_system_user.password }}" + login_unix_socket: "{{ mariadb_socket | default(omit) }}" + config_file: "{{ _mariadb_root_system_user.home | default('/root') }}/.my.cnf" loop: '{{ mariadb_users }}' loop_control: