Add test to verify TLS requirements are removed (#26)

* Add test to verify TLS requirements are removed

* Fix cursor parsing

* small fixes

* Refactor TLS tests into their own file

* Fix TLS requirements parsing
This commit is contained in:
Jorge Rodriguez (A.K.A. Tiriel) 2020-09-21 07:39:08 +03:00 committed by GitHub
parent b3a22390f0
commit 6e040e1496
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 213 additions and 164 deletions

View file

@ -210,6 +210,11 @@ EXAMPLES = r'''
subject: '/CN=alice/O=MyDom, Inc./C=US/ST=Oregon/L=Portland'
cipher: 'ECDHE-ECDSA-AES256-SHA384'
- name: Modify user to no longer require SSL.
community.mysql.mysql_user:
name: bob
tls_requires:
- name: Ensure no user named 'sally'@'localhost' exists, also passing in the auth credentials.
community.mysql.mysql_user:
login_user: root
@ -424,12 +429,16 @@ def get_tls_requires(cursor, user, host):
query = "SHOW GRANTS for '%s'@'%s'" % (user, host)
cursor.execute(query)
require_list = list(filter(lambda x: "REQUIRE" in x, cursor.fetchall()))
require_list = [tuple[0] for tuple in filter(lambda x: "REQUIRE" in x[0], cursor.fetchall())]
require_line = require_list[0] if require_list else ""
pattern = r"(?<=\bREQUIRE\b)(.*?)(?=(?:\bPASSWORD\b|$))"
requires_match = re.search(pattern, require_line)
requires = requires_match.group().strip() if requires_match else ""
if len(requires.split()) > 1:
if any((requires.startswith(req) for req in ('SSL', 'X509', 'NONE'))):
requires = requires.split()[0]
if requires == 'NONE':
requires = None
else:
import shlex
items = iter(shlex.split(requires))
@ -514,6 +523,9 @@ def user_mod(cursor, user, host, host_all, password, encrypted,
msg = "User unchanged"
grant_option = False
# Determine what user management method server uses
old_user_mgmt = use_old_user_mgmt(cursor)
if host_all:
hostnames = user_get_hostnames(cursor, [user])
else:
@ -522,8 +534,6 @@ def user_mod(cursor, user, host, host_all, password, encrypted,
for host in hostnames:
# Handle clear text and hashed passwords.
if bool(password):
# Determine what user management method server uses
old_user_mgmt = use_old_user_mgmt(cursor)
# Get a list of valid columns in mysql.user table to check if Password and/or authentication_string exist
cursor.execute("""
@ -675,7 +685,7 @@ def user_mod(cursor, user, host, host_all, password, encrypted,
query = " ".join((pre_query, "%s@%s"))
cursor.execute(*mogrify_requires(query, (user, host), tls_requires))
else:
query = " ".join(pre_query, "%s@%s REQUIRE NONE")
query = " ".join((pre_query, "%s@%s REQUIRE NONE"))
cursor.execute(query, (user, host))
changed = True

View file

@ -129,165 +129,6 @@
- include: assert_no_user.yml user_name={{user_name_2}}
# ============================================================
# Create users with TLS requirements and verify requirements are assigned
#
- name: find out the database version
mysql_info:
<<: *mysql_params
filter: version
register: db_version
- name: create user with TLS requirements in check mode (expect changed=true)
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
password: "{{ user_password_1 }}"
tls_requires:
SSL:
check_mode: yes
register: result
- name: Assert check mode user create reports changed state
assert:
that:
- result is changed
- include: assert_no_user.yml user_name={{user_name_1}}
- name: create user with TLS requirements state=present (expect changed=true)
mysql_user:
<<: *mysql_params
name: '{{ item[0] }}'
password: '{{ user_password_1 }}'
tls_requires: '{{ item[1] }}'
with_together:
- [ '{{ user_name_1 }}', '{{ user_name_2 }}', '{{ user_name_3 }}']
-
- SSL:
- X509:
- subject: '/CN=alice/O=MyDom, Inc./C=US/ST=Oregon/L=Portland'
cipher: 'ECDHE-ECDSA-AES256-SHA384'
issuer: '/CN=org/O=MyDom, Inc./C=US/ST=Oregon/L=Portland'
- block:
- name: retrieve TLS requiremets for users in old database version
command: "{{ mysql_command }} -L -N -s -e \"SHOW GRANTS for '{{ item }}'@'localhost'\""
register: old_result
with_items: ['{{ user_name_1 }}', '{{ user_name_2 }}', '{{ user_name_3 }}']
- name: set old database separator
set_fact:
separator: '\n'
# Semantically: when mysql version <= 5.6 or MariaDB version <= 10.1
when: db_version.version.major <= 5 and db_version.version.minor <= 6 or db_version.version.major == 10 and db_version.version.minor < 2
- block:
- name: retrieve TLS requiremets for users in new database version
command: "{{ mysql_command }} -L -N -s -e \"SHOW CREATE USER '{{ item }}'@'localhost'\""
register: new_result
with_items: ['{{ user_name_1 }}', '{{ user_name_2 }}', '{{ user_name_3 }}']
- name: set new database separator
set_fact:
separator: 'PASSWORD'
# Semantically: when mysql version >= 5.7 or MariaDB version >= 10.2
when: db_version.version.major == 5 and db_version.version.minor >= 7 or db_version.version.major > 5 and db_version.version.major < 10 or db_version.version.major == 10 and db_version.version.minor >= 2
- block:
- name: assert user1 TLS requirements
assert:
that:
- "'SSL' in reqs"
vars:
- reqs: "{{((old_result.results[0] is skipped | ternary(new_result, old_result)).results | selectattr('item', 'contains', user_name_1) | first).stdout.split('REQUIRE')[1].split(separator)[0].strip()}}"
- name: assert user2 TLS requirements
assert:
that:
- "'X509' in reqs"
vars:
- reqs: "{{((old_result.results[0] is skipped | ternary(new_result, old_result)).results | selectattr('item', 'contains', user_name_2) | first).stdout.split('REQUIRE')[1].split(separator)[0].strip()}}"
- name: assert user3 TLS requirements
assert:
that:
- "'/CN=alice/O=MyDom, Inc./C=US/ST=Oregon/L=Portland' in (reqs | select('contains', 'SUBJECT') | first)"
- "'/CN=org/O=MyDom, Inc./C=US/ST=Oregon/L=Portland' in (reqs | select('contains', 'ISSUER') | first)"
- "'ECDHE-ECDSA-AES256-SHA384' in (reqs | select('contains', 'CIPHER') | first)"
vars:
- reqs: "{{((old_result.results[0] is skipped | ternary(new_result, old_result)).results | selectattr('item', 'contains', user_name_3) | first).stdout.split('REQUIRE')[1].split(separator)[0].replace(\"' \", \"':\").split(\":\")}}"
# CentOS 6 uses an older version of jinja that does not provide the selectattr filter.
when: ansible_distribution != 'CentOS' or ansible_distribution_major_version != '6'
- name: modify user with TLS requirements state=present in check mode (expect changed=true)
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
password: '{{ user_password_1 }}'
tls_requires:
X509:
check_mode: yes
register: result
- name: Assert check mode user update reports changed state
assert:
that:
- result is changed
- name: retrieve TLS requiremets for users in old database version
command: "{{ mysql_command }} -L -N -s -e \"SHOW GRANTS for '{{ user_name_1 }}'@'localhost'\""
register: old_result
when: db_version.version.major <= 5 and db_version.version.minor <= 6 or db_version.version.major == 10 and db_version.version.minor < 2
- name: retrieve TLS requiremets for users in new database version
command: "{{ mysql_command }} -L -N -s -e \"SHOW CREATE USER '{{ user_name_1 }}'@'localhost'\""
register: new_result
when: db_version.version.major == 5 and db_version.version.minor >= 7 or db_version.version.major > 5 and db_version.version.major < 10 or db_version.version.major == 10 and db_version.version.minor >= 2
- name: assert user1 TLS requirements was not changed
assert:
that: "'SSL' in reqs"
vars:
- reqs: "{{(old_result is skipped | ternary(new_result, old_result)).stdout.split('REQUIRE')[1].split(separator)[0].strip()}}"
- name: modify user with TLS requirements state=present (expect changed=true)
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
password: '{{ user_password_1 }}'
tls_requires:
X509:
- name: retrieve TLS requiremets for users in old database version
command: "{{ mysql_command }} -L -N -s -e \"SHOW GRANTS for '{{ user_name_1 }}'@'localhost'\""
register: old_result
when: db_version.version.major <= 5 and db_version.version.minor <= 6 or db_version.version.major == 10 and db_version.version.minor < 2
- name: retrieve TLS requiremets for users in new database version
command: "{{ mysql_command }} -L -N -s -e \"SHOW CREATE USER '{{ user_name_1 }}'@'localhost'\""
register: new_result
when: db_version.version.major == 5 and db_version.version.minor >= 7 or db_version.version.major > 5 and db_version.version.major < 10 or db_version.version.major == 10 and db_version.version.minor >= 2
- name: assert user1 TLS requirements
assert:
that: "'X509' in reqs"
vars:
- reqs: "{{(old_result is skipped | ternary(new_result, old_result)).stdout.split('REQUIRE')[1].split(separator)[0].strip()}}"
- include: remove_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }}
- include: remove_user.yml user_name={{user_name_2}} user_password={{ user_password_1 }}
- include: remove_user.yml user_name={{user_name_3}} user_password={{ user_password_1 }}
- include: assert_no_user.yml user_name={{user_name_1}}
- include: assert_no_user.yml user_name={{user_name_2}}
- include: assert_no_user.yml user_name={{user_name_3}}
# ============================================================
# Assert user has access to multiple databases
#
@ -393,6 +234,9 @@
# Tests for the priv parameter with dict value (https://github.com/ansible/ansible/issues/57533)
- include: test_priv_dict.yml
# Tests for the TLS requires dictionary
- include: tls_requirements.yml
- import_tasks: issue-29511.yaml
tags:
- issue-29511

View file

@ -0,0 +1,195 @@
---
- vars:
mysql_parameters: &mysql_params
login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}'
login_host: 127.0.0.1
login_port: '{{ mysql_primary_port }}'
block:
# ============================================================
- name: find out the database version
mysql_info:
<<: *mysql_params
filter: version
register: db_version
- name: Drop mysql user {{ item }} if exists
mysql_user:
<<: *mysql_params
name: '{{ item }}'
state: absent
with_items: ['{{ user_name_1 }}', '{{ user_name_2 }}', '{{ user_name_3 }}']
- name: create user with TLS requirements in check mode (expect changed=true)
mysql_user:
<<: *mysql_params
name: "{{ user_name_1 }}"
password: "{{ user_password_1 }}"
tls_requires:
SSL:
check_mode: yes
register: result
- name: Assert check mode user create reports changed state
assert:
that:
- result is changed
- include: assert_no_user.yml user_name={{user_name_1}}
- name: create user with TLS requirements state=present (expect changed=true)
mysql_user:
<<: *mysql_params
name: '{{ item[0] }}'
password: '{{ user_password_1 }}'
tls_requires: '{{ item[1] }}'
with_together:
- [ '{{ user_name_1 }}', '{{ user_name_2 }}', '{{ user_name_3 }}']
-
- SSL:
- X509:
- subject: '/CN=alice/O=MyDom, Inc./C=US/ST=Oregon/L=Portland'
cipher: 'ECDHE-ECDSA-AES256-SHA384'
issuer: '/CN=org/O=MyDom, Inc./C=US/ST=Oregon/L=Portland'
- block:
- name: retrieve TLS requiremets for users in old database version
command: "{{ mysql_command }} -L -N -s -e \"SHOW GRANTS for '{{ item }}'@'localhost'\""
register: old_result
with_items: ['{{ user_name_1 }}', '{{ user_name_2 }}', '{{ user_name_3 }}']
- name: set old database separator
set_fact:
separator: '\n'
# Semantically: when mysql version <= 5.6 or MariaDB version <= 10.1
when: db_version.version.major <= 5 and db_version.version.minor <= 6 or db_version.version.major == 10 and db_version.version.minor < 2
- block:
- name: retrieve TLS requiremets for users in new database version
command: "{{ mysql_command }} -L -N -s -e \"SHOW CREATE USER '{{ item }}'@'localhost'\""
register: new_result
with_items: ['{{ user_name_1 }}', '{{ user_name_2 }}', '{{ user_name_3 }}']
- name: set new database separator
set_fact:
separator: 'PASSWORD'
# Semantically: when mysql version >= 5.7 or MariaDB version >= 10.2
when: db_version.version.major == 5 and db_version.version.minor >= 7 or db_version.version.major > 5 and db_version.version.major < 10 or db_version.version.major == 10 and db_version.version.minor >= 2
- block:
- name: assert user1 TLS requirements
assert:
that:
- "'SSL' in reqs"
vars:
- reqs: "{{((old_result.results[0] is skipped | ternary(new_result, old_result)).results | selectattr('item', 'contains', user_name_1) | first).stdout.split('REQUIRE')[1].split(separator)[0].strip()}}"
- name: assert user2 TLS requirements
assert:
that:
- "'X509' in reqs"
vars:
- reqs: "{{((old_result.results[0] is skipped | ternary(new_result, old_result)).results | selectattr('item', 'contains', user_name_2) | first).stdout.split('REQUIRE')[1].split(separator)[0].strip()}}"
- name: assert user3 TLS requirements
assert:
that:
- "'/CN=alice/O=MyDom, Inc./C=US/ST=Oregon/L=Portland' in (reqs | select('contains', 'SUBJECT') | first)"
- "'/CN=org/O=MyDom, Inc./C=US/ST=Oregon/L=Portland' in (reqs | select('contains', 'ISSUER') | first)"
- "'ECDHE-ECDSA-AES256-SHA384' in (reqs | select('contains', 'CIPHER') | first)"
vars:
- reqs: "{{((old_result.results[0] is skipped | ternary(new_result, old_result)).results | selectattr('item', 'contains', user_name_3) | first).stdout.split('REQUIRE')[1].split(separator)[0].replace(\"' \", \"':\").split(\":\")}}"
# CentOS 6 uses an older version of jinja that does not provide the selectattr filter.
when: ansible_distribution != 'CentOS' or ansible_distribution_major_version != '6'
- name: modify user with TLS requirements state=present in check mode (expect changed=true)
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
password: '{{ user_password_1 }}'
tls_requires:
X509:
check_mode: yes
register: result
- name: Assert check mode user update reports changed state
assert:
that:
- result is changed
- name: retrieve TLS requiremets for users in old database version
command: "{{ mysql_command }} -L -N -s -e \"SHOW GRANTS for '{{ user_name_1 }}'@'localhost'\""
register: old_result
when: db_version.version.major <= 5 and db_version.version.minor <= 6 or db_version.version.major == 10 and db_version.version.minor < 2
- name: retrieve TLS requiremets for users in new database version
command: "{{ mysql_command }} -L -N -s -e \"SHOW CREATE USER '{{ user_name_1 }}'@'localhost'\""
register: new_result
when: db_version.version.major == 5 and db_version.version.minor >= 7 or db_version.version.major > 5 and db_version.version.major < 10 or db_version.version.major == 10 and db_version.version.minor >= 2
- name: assert user1 TLS requirements was not changed
assert:
that: "'SSL' in reqs"
vars:
- reqs: "{{(old_result is skipped | ternary(new_result, old_result)).stdout.split('REQUIRE')[1].split(separator)[0].strip()}}"
- name: modify user with TLS requirements state=present (expect changed=true)
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
password: '{{ user_password_1 }}'
tls_requires:
X509:
- name: retrieve TLS requiremets for users in old database version
command: "{{ mysql_command }} -L -N -s -e \"SHOW GRANTS for '{{ user_name_1 }}'@'localhost'\""
register: old_result
when: db_version.version.major <= 5 and db_version.version.minor <= 6 or db_version.version.major == 10 and db_version.version.minor < 2
- name: retrieve TLS requiremets for users in new database version
command: "{{ mysql_command }} -L -N -s -e \"SHOW CREATE USER '{{ user_name_1 }}'@'localhost'\""
register: new_result
when: db_version.version.major == 5 and db_version.version.minor >= 7 or db_version.version.major > 5 and db_version.version.major < 10 or db_version.version.major == 10 and db_version.version.minor >= 2
- name: assert user1 TLS requirements
assert:
that: "'X509' in reqs"
vars:
- reqs: "{{(old_result is skipped | ternary(new_result, old_result)).stdout.split('REQUIRE')[1].split(separator)[0].strip()}}"
- name: remove TLS requiremets from user (expect changed=true)
mysql_user:
<<: *mysql_params
name: '{{ user_name_1 }}'
password: '{{ user_password_1 }}'
tls_requires:
- name: retrieve TLS requiremets for users in old database version
command: "{{ mysql_command }} -L -N -s -e \"SHOW GRANTS for '{{ user_name_1 }}'@'localhost'\""
register: old_result
when: db_version.version.major <= 5 and db_version.version.minor <= 6 or db_version.version.major == 10 and db_version.version.minor < 2
- name: retrieve TLS requiremets for users in new database version
command: "{{ mysql_command }} -L -N -s -e \"SHOW CREATE USER '{{ user_name_1 }}'@'localhost'\""
register: new_result
when: db_version.version.major == 5 and db_version.version.minor >= 7 or db_version.version.major > 5 and db_version.version.major < 10 or db_version.version.major == 10 and db_version.version.minor >= 2
- name: assert user1 TLS requirements
assert:
that: "'NONE' in reqs"
vars:
- reqs: "{{(old_result is skipped | ternary(new_result, old_result)).stdout.split('REQUIRE')[1].split(separator)[0].strip() | default('NONE') }}"
- include: remove_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }}
- include: remove_user.yml user_name={{user_name_2}} user_password={{ user_password_1 }}
- include: remove_user.yml user_name={{user_name_3}} user_password={{ user_password_1 }}
- include: assert_no_user.yml user_name={{user_name_1}}
- include: assert_no_user.yml user_name={{user_name_2}}
- include: assert_no_user.yml user_name={{user_name_3}}