Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #223: Generate a random default root password #465

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
7 changes: 5 additions & 2 deletions defaults/main.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
---
# Set a random password.
mysql_autogenerated_password: "{{ lookup('password', '/dev/null length=20 chars=ascii_letters') }}"

# Set this to the user ansible is logging in as - should have root
# or sudo access
mysql_user_home: /root
mysql_user_name: root
mysql_user_password: root
mysql_user_password: "{{ mysql_autogenerated_password }}"

# The default root user installed by mysql - almost always root
mysql_root_home: /root
mysql_root_username: root
mysql_root_password: root
mysql_root_password: "{{ mysql_autogenerated_password }}"

# Set this to `true` to forcibly update the root password.
mysql_root_password_update: false
Expand Down
81 changes: 58 additions & 23 deletions tasks/secure-installation.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,41 @@
---
- name: Ensure default user is present.
mysql_user:
name: "{{ mysql_user_name }}"
host: 'localhost'
password: "{{ mysql_user_password }}"
priv: '*.*:ALL,GRANT'
state: present
when: mysql_user_name != mysql_root_username
- name: Set the user's .my.cnf file path.
set_fact:
mysql_user_cnf_path: "{{ mysql_user_home }}/.my.cnf"

# Has to be after the password assignment, for idempotency.
- name: Copy user-my.cnf file with password credentials.
- name: Write the user's .my.cnf file with password credentials.
template:
src: "user-my.cnf.j2"
dest: "{{ mysql_user_home }}/.my.cnf"
dest: "{{ mysql_user_cnf_path }}"
owner: "{{ mysql_user_name }}"
mode: 0600
when: >
mysql_user_name != mysql_root_username
and (mysql_install_packages | bool or mysql_user_password_update)

- name: Fetch contents of the user's .my.cnf file
slurp:
src: "{{ mysql_user_cnf_path }}"
register: mysql_user_cnf_file
when: mysql_user_name != mysql_root_username

# It would be cleaner to use the `ini` lookup plugin, but that only works
# locally so we'd have to copy the file first, which we'd rather not do because
# it contains secrets.
- name: Extract the user password from .my.cnf
set_fact:
mysql_user_password_written: "{{ mysql_user_cnf_file['content'] | b64decode | regex_findall('password=\"(.+)\"') | first }}"
when: mysql_user_name != mysql_root_username

- name: Ensure default user is present.
mysql_user:
name: "{{ mysql_user_name }}"
host: 'localhost'
password: "{{ mysql_user_password_written }}"
priv: '*.*:ALL,GRANT'
state: present
when: mysql_user_name != mysql_root_username

- name: Disallow root login remotely
command: 'mysql -NBe "{{ item }}"'
with_items:
Expand All @@ -36,38 +53,56 @@
check_mode: false
when: mysql_install_packages | bool or mysql_root_password_update

- name: Set root's .my.cnf file path.
set_fact:
mysql_root_cnf_path: "{{ mysql_root_home }}/.my.cnf"

- name: Write root's .my.cnf file with password credentials.
template:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In terms of backwards compatibility, could this break that for existing installs?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I think you're mostly concerned about here, and rightly so, is if there's a password already there (for example, the old default one). I tested that by changing it (and leaving the rest of the file alone). Both <5.7 and 5.7+ were skipped so we're all good there. force: no is the default so I think we're good. If the file's already there, it won't touch it. It'll just use what's there for running operations, which will just work.

src: "root-my.cnf.j2"
dest: "{{ mysql_root_cnf_path }}"
owner: root
group: root
mode: 0600
when: mysql_install_packages | bool or mysql_root_password_update
register: mysql_root_password_setting

- name: Fetch contents of root's .my.cnf file
slurp:
src: "{{ mysql_root_cnf_path }}"
register: mysql_root_cnf_file

# It would be cleaner to use the `ini` lookup plugin, but that only works
# locally so we'd have to copy the file first, which we'd rather not do because
# it contains secrets.
- name: Extract the root password from .my.cnf
set_fact:
mysql_root_password_written: "{{ mysql_root_cnf_file['content'] | b64decode | regex_findall('password=\"(.+)\"') | first }}"

# Note: We do not use mysql_user for this operation, as it doesn't always update
# the root password correctly. See: https://goo.gl/MSOejW
# Set root password for MySQL >= 5.7.x.
- name: Update MySQL root password for localhost root account (5.7.x).
shell: >
mysql -u root -NBe
'ALTER USER "{{ mysql_root_username }}"@"{{ item }}"
IDENTIFIED WITH mysql_native_password BY "{{ mysql_root_password }}"; FLUSH PRIVILEGES;'
IDENTIFIED WITH mysql_native_password BY "{{ mysql_root_password_written }}"; FLUSH PRIVILEGES;'
with_items: "{{ mysql_root_hosts.stdout_lines|default([]) }}"
when: >
((mysql_install_packages | bool) or mysql_root_password_update)
and ('5.7.' in mysql_cli_version.stdout or '8.0.' in mysql_cli_version.stdout)
and mysql_root_password_setting.changed

# Set root password for MySQL < 5.7.x.
- name: Update MySQL root password for localhost root account (< 5.7.x).
shell: >
mysql -NBe
'SET PASSWORD FOR "{{ mysql_root_username }}"@"{{ item }}" = PASSWORD("{{ mysql_root_password }}"); FLUSH PRIVILEGES;'
'SET PASSWORD FOR "{{ mysql_root_username }}"@"{{ item }}" = PASSWORD("{{ mysql_root_password_written }}"); FLUSH PRIVILEGES;'
with_items: "{{ mysql_root_hosts.stdout_lines|default([]) }}"
when: >
((mysql_install_packages | bool) or mysql_root_password_update)
and ('5.7.' not in mysql_cli_version.stdout and '8.0.' not in mysql_cli_version.stdout)

# Has to be after the root password assignment, for idempotency.
- name: Copy .my.cnf file with root password credentials.
template:
src: "root-my.cnf.j2"
dest: "{{ mysql_root_home }}/.my.cnf"
owner: root
group: root
mode: 0600
when: mysql_install_packages | bool or mysql_root_password_update
and mysql_root_password_setting.changed

- name: Get list of hosts for the anonymous user.
command: mysql -NBe 'SELECT Host FROM mysql.user WHERE User = ""'
Expand Down