diff --git a/roles/mongodb/README.md b/roles/mongodb/README.md new file mode 100644 index 00000000..81f5f290 --- /dev/null +++ b/roles/mongodb/README.md @@ -0,0 +1,63 @@ +Ansible Role: stackstorm.mongodb +================================ + +Installs MongoDB. If the `mongodb_auth_enable` boolean is enabled, then this also enables authentication, adds an admin +user, and adds any other users defined in `mongodb_users`. + +Requirements +------------ + +Enabling mongo auth requires the pymongo python module (requirement of the mongo_users ansible module). + +Role Variables +-------------- + +These default variables can be set in the inventory's group or host vars, or pass them in as vars in the playbook that +uses this role. An example of passing in some of these vars is shown in an example playbook below. + +* `mongodb_version`: The major.minor version to install (only 3.4 or 3.2 are supported). +* `mongodb_enable_auth`: Whether or not to enable auth in mongodb (default: no) +* `mongodb_host`: Login to this host to add users (default '127.0.0.1') +* `mongodb_port`: Login on this port to add users (default '27017') +* `mongodb_admin_username`: The admin's username (default 'admin') +* `mongodb_admin_password`: The admin's password (default: generate a random password and store it in a file) +* `mongodb_users`: A list of users to add (see example playbook below; default: []) +* `mongodb_creds_dir`: The directory that should hold any generated credentials like admin (default: '.') + +Dependencies +------------ + +On RedHat family distributions, this depends on the stackstorm.epel role. + +Example Playbook +---------------- + +This playbook installs mongo without enabling auth or adding any users: + + - hosts: localhost + roles: + - role: StackStorm.stackstorm/roles/mongodb + + +This playbook installs mongo, enables auth, and adds a stackstorm user, and force update the password if it already exists: + + - hosts: localhost + roles: + - role: StackStorm.stackstorm/roles/mongodb + vars: + mongodb_enable_auth: yes + mongodb_users: + - db: st2 + username: st2mongo + password: "{{ lookup('password', '{{ mongodb_creds_dir }}/mongodb-' + inventory_hostname + '-' + st2mongo_username + ' length=42' ) }}" + roles: readWrite + mongodb_force_password_update: yes + +Note that the `readWrite` mongo role is used by default, so `roles` can be ommitted for the above playbook. +You can use the roles attribute to add any other mongo roles to your user. + +License +------- + +Apache 2.0 + diff --git a/roles/mongodb/defaults/main.yml b/roles/mongodb/defaults/main.yml index d59081d8..46cfe493 100644 --- a/roles/mongodb/defaults/main.yml +++ b/roles/mongodb/defaults/main.yml @@ -2,3 +2,27 @@ # MongoDB version to install # Should be '3.2' or '3.4' mongodb_version: "3.4" + +# do not enable mongo auth by default. Make sure to add required users in mongodb_users if auth is enabled. +mongodb_enable_auth: no + +# when adding auth, the login credentials to use +mongodb_host: 127.0.0.1 +mongodb_port: 27017 +mongodb_admin_username: admin +# For production use - please change the admin password! +mongodb_admin_password: "{{ mongodb_default_admin_password }}" +# The default is separate so the st2 role can provide a default without overriding a user provided password. +mongodb_default_admin_password: "Ch@ngeMe" + +# Additional users to add. +mongodb_users: [] +# - db: st2 +# username: st2mongo +# password: "Ch@ngeMe" +# roles: readWrite + +# whether or not to force a password update for any users in mongodb_users +# Setting this to yes will result in 'changed' on every run, even if the password is the same. +# See the comment in tasks/mongodb_auth.yml for more details. +mongodb_force_update_password: no diff --git a/roles/mongodb/tasks/main.yml b/roles/mongodb/tasks/main.yml index d0fc890e..127405da 100644 --- a/roles/mongodb/tasks/main.yml +++ b/roles/mongodb/tasks/main.yml @@ -10,3 +10,8 @@ state: started enabled: yes tags: [databases, mongodb] + +- name: Enable mongodb authentication + when: mongodb_enable_auth + include: mongodb_auth.yml + tags: [databases, mongodb] diff --git a/roles/mongodb/tasks/mongodb_auth.yml b/roles/mongodb/tasks/mongodb_auth.yml new file mode 100644 index 00000000..4e611908 --- /dev/null +++ b/roles/mongodb/tasks/mongodb_auth.yml @@ -0,0 +1,136 @@ +--- +- name: Check for pip (used to install pymongo) + command: "{{ pip | default('pip') }} --version" + register: pip_check + changed_when: no + failed_when: no + +# It shouldn't matter if it's an old version of pip for installing pymongo, so use the system package instead of get-pip.py +- name: Install pip with system package (used to install pymongo) + when: pip_check.rc != 0 + become: yes + package: + name: 'python{% ansible_os_family == "RedHat" and ansible_distribution_major_version == "7" %}2{% endif %}-pip' + state: present + +- name: Ensure pymongo is installed for the mongodb_user ansible module + become: yes + pip: + name: pymongo + +- name: See if mongodb authorization is enabled and users are configured + command: 'mongo --eval "db.getUsers()" {{ mongodb_admin_db }}' + # this will fail (rc 252) if auth is enabled and users are configured + # With the localhost exception, this will succeed (rc 0) when users are not configured even if auth is enabled. + # see: + # - https://docs.mongodb.com/manual/core/security-users/#localhost-exception + # - https://stackoverflow.com/q/31949586/1134951 + # - https://github.com/ansible/ansible/issues/33832#issuecomment-358031733 + register: _mongo_authorization + changed_when: no # this does not change anything, it only checks + failed_when: no # The rc determines whether or not auth is required to create/update the admin user + +- name: Show mongodb auth check output + debug: + var: _mongo_authorization + verbosity: 2 + +- name: Warn about default credentials + when: "mongodb_admin_password == 'Ch@ngeMe'" + debug: + msg: "[WARNING] Using default admin credentials for mongodb admin account! Please change them!" + +- name: Add mongo admin + mongodb_user: + state: present + + # NOTE: on_create is idempotent - see comment below + update_password: on_create + + name: "{{ mongodb_admin_username }}" + password: "{{ mongodb_admin_password }}" + database: "{{ mongodb_admin_db }}" + roles: userAdminAnyDatabase + + login_host: "{{ mongodb_host }}" + login_port: "{{ mongodb_port }}" + login_user: "{{ (_mongo_authorization.rc == 0) | ternary(omit, mongodb_admin_username) }}" + login_password: "{{ (_mongo_authorization.rc == 0) | ternary(omit, mongodb_admin_password) }}" + login_database: "{{ (_mongo_authorization.rc == 0) | ternary(omit, mongodb_admin_db) }}" + +- name: Enable security section in mongod.conf + become: yes + lineinfile: + path: /etc/mongod.conf + regexp: |- + ^[#'"\s]*security['"]?\s*: + line: 'security:' + validate: | + {{ ansible_python_interpreter|default("/usr/bin/python") }} -c ' + import yaml, io + if "security" not in yaml.safe_load(io.open("%s")): + exit(1) + ' + +- name: Enable authentication in mongod.conf + become: yes + lineinfile: + path: /etc/mongod.conf + insertafter: '^security:' + # two space indentation (the default) assumed + line: ' authorization: enabled' + regexp: |- + ^[#'"\s]+authorization['"]?\s*: + validate: | + {{ ansible_python_interpreter|default("/usr/bin/python") }} -c ' + import yaml, io + if yaml.safe_load(io.open("%s"))["security"]["authorization"] != "enabled": + exit(1) + ' + register: _enable_mongo_auth + #notify: restart mongodb + +- name: Restart mongodb to enable auth before adding additional users + # This allows us to safely assume auth is already enabled when adding more users + when: + - mongodb_users + - _enable_mongo_auth is changed + become: yes + service: + name: mongod + state: restarted +# TODO: Use filtered flush_handlers once this is merged: https://github.com/ansible/ansible/pull/25573 +# As is, mongo may restart twice, once here and whenever the upgrade handlers get flushed. +# when: mongodb_users +# meta: +# name: flush_handlers +# filter: restart mongodb + +- name: Add additional mongo users + mongodb_user: + state: present + + # NOTE: on_create is idempotent, always is not. + # With `update_password: on_create`, mongodb_user checks to see if the user + # (a) exists on the db, and (b) has the same roles, + # and then it only adds the user if it's not there or the roles have changed. + # With `update_password: always`, mongodb_user cannot tell if the password + # needs to be changed without attempting a login with those credentials. + # But mongodb_user does not currently implement such a check. + # A comment in mongodb_user points to https://jira.mongodb.org/browse/SERVER-22848 + update_password: "{{ mongodb_force_update_password|ternary('always', 'on_create') }}" + + name: "{{ item.username }}" + password: "{{ item.password }}" + database: "{{ item.db }}" + roles: "{{ item.roles|default('readWrite') }}" + + login_host: "{{ mongodb_host }}" + login_port: "{{ mongodb_port }}" + login_user: "{{ mongodb_admin_username }}" + login_password: "{{ mongodb_admin_password }}" + login_database: "{{ mongodb_admin_db }}" + loop_control: + label: "{{ item.username }} on {{ item.db }}" + with_items: "{{ mongodb_users }}" + diff --git a/roles/mongodb/vars/main.yml b/roles/mongodb/vars/main.yml index 6f0c4579..11c1ecfb 100644 --- a/roles/mongodb/vars/main.yml +++ b/roles/mongodb/vars/main.yml @@ -4,3 +4,8 @@ mongodb_major_minor_version: "{{ (mongodb_version|string)[:3] }}" mongodb_apt_keys: "3.2": "42F3E95A2C4F08279C4960ADD68FA50FEA312927" "3.4": "0C49F3730359A14518585931BC711F9BA15703C6" + +# The admin database, used to add users +mongodb_admin_db: admin + +mongodb_python: "{{ ansible_python_interpreter | default( ansible_python.executable | default('python') ) }}"