diff --git a/changelogs/fragments/feature_add_icingaweb2_users.yml b/changelogs/fragments/feature_add_icingaweb2_users.yml new file mode 100644 index 00000000..09d9279d --- /dev/null +++ b/changelogs/fragments/feature_add_icingaweb2_users.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - Add the ability to create additional Icinga Web 2 users - Thanks @losten-git diff --git a/doc/role-icingaweb2/role-icingaweb2.md b/doc/role-icingaweb2/role-icingaweb2.md index 97095f69..b42add46 100644 --- a/doc/role-icingaweb2/role-icingaweb2.md +++ b/doc/role-icingaweb2/role-icingaweb2.md @@ -32,6 +32,21 @@ icingaweb2_db: * `icingaweb2_admin_: string` * Set the username and password for the first admin user for Icinga Web 2. +* `icingaweb2_admin_recreate: boolean` + * Recreate can be used to change the password of the admin. **Default: False** + +In addition to the Icinga Web 2 Admin, other users can be configured by defining `icingaweb2_users`.
+The `recreate` parameter can be used to change passwords or to enable the user if he has been disabled. **Default: False** + +```yaml +icingaweb2_users: + - username: 'foo' + password: 'bar' + recreate: true + - username: webadmin + [...] +``` + ### Resources Besides the standard Icinga Web 2 database you may configure additional resources for IcingaDB or automated imports. diff --git a/roles/icingaweb2/defaults/main.yml b/roles/icingaweb2/defaults/main.yml index 3e9d913e..104014cf 100644 --- a/roles/icingaweb2/defaults/main.yml +++ b/roles/icingaweb2/defaults/main.yml @@ -41,3 +41,4 @@ icingaweb2_config: themes: default: Icinga icingaweb2_cli: icingacli +icingaweb2_users: [] diff --git a/roles/icingaweb2/tasks/main.yml b/roles/icingaweb2/tasks/main.yml index 9dd1ab00..45adaad6 100644 --- a/roles/icingaweb2/tasks/main.yml +++ b/roles/icingaweb2/tasks/main.yml @@ -40,8 +40,10 @@ ansible.builtin.include_tasks: "manage_icingaweb_config.yml" - name: Manage Icinga Web 2 DB - ansible.builtin.include_tasks: "manage_icingaweb_{{ icingaweb2_db.type }}_db.yml" - when: icingaweb2_db is defined + ansible.builtin.include_tasks: "manage_icingaweb_db.yml" + when: + - icingaweb2_db is defined + - (icingaweb2_db_import_schema | default(false)) or (icingaweb2_users is defined) or (icingaweb2_admin_username is defined) - name: Manage module states ansible.builtin.file: diff --git a/roles/icingaweb2/tasks/manage_icingaweb_db.yml b/roles/icingaweb2/tasks/manage_icingaweb_db.yml new file mode 100644 index 00000000..a6207ef7 --- /dev/null +++ b/roles/icingaweb2/tasks/manage_icingaweb_db.yml @@ -0,0 +1,27 @@ +--- + +- name: Prepare database + ansible.builtin.include_tasks: "{{ icingaweb2_db.type | default('mysql') }}/prepare_db.yml" + +- name: Import database schema + ansible.builtin.include_tasks: "{{ icingaweb2_db.type | default('mysql') }}/import_db.yml" + when: icingaweb2_db_import_schema | default(false) + +- name: Add admin to users list + ansible.builtin.set_fact: + icingaweb2_users: "{{ icingaweb2_users + [_current_user]}}" + vars: + _current_user: + username: "{{ icingaweb2_admin_username }}" + password: "{{ icingaweb2_admin_password }}" + recreate: "{{ icingaweb2_admin_recreate | default(false) }}" + when: + - icingaweb2_admin_username is defined + - icingaweb2_admin_password is defined + +- name: Add Icinga web 2 users + ansible.builtin.include_tasks: "{{ icingaweb2_db.type | default('mysql') }}/users_db.yml" + loop: "{{ icingaweb2_users }}" + loop_control: + loop_var: _current_user + when: icingaweb2_users | length > 0 diff --git a/roles/icingaweb2/tasks/manage_icingaweb_mysql_db.yml b/roles/icingaweb2/tasks/manage_icingaweb_mysql_db.yml deleted file mode 100644 index 89d992c4..00000000 --- a/roles/icingaweb2/tasks/manage_icingaweb_mysql_db.yml +++ /dev/null @@ -1,70 +0,0 @@ ---- - -- name: Check Database Credentials - ansible.builtin.assert: - that: - - icingaweb2_db['user'] is defined - - icingaweb2_db['password'] is defined - fail_msg: "No database credentials defined. Please set icingaweb2_db. or a privileged user with icingaweb2_priv_db_" - when: icingaweb2_priv_db_password is undefined and icingaweb2_priv_db_user is undefined - -- name: Set db user with admin privileges - ansible.builtin.set_fact: - _priv_db_user: "{{ icingaweb2_priv_db_user }}" - _priv_db_pass: "{{ icingaweb2_priv_db_password }}" - when: icingaweb2_priv_db_password is defined and icingaweb2_priv_db_user is defined - -- name: Ensure Icinga Web - block: - - name: Build mysql command - ansible.builtin.set_fact: - _tmp_mysqlcmd: >- - mysql {% if icingaweb2_db['host'] | default('localhost') != 'localhost' %} -h "{{ icingaweb2_db['host'] }}" {%- endif %} - {% if icingaweb2_db['port'] is defined %} -P "{{ icingaweb2_db['port'] }}" {%- endif %} - {% if icingaweb2_db['ssl_mode'] is defined %} --ssl-mode "{{ icingaweb2_db['ssl_mode'] }}" {%- endif %} - {% if icingaweb2_db['ssl_ca'] is defined %} --ssl-ca "{{ icingaweb2_db['ssl_ca'] }}" {%- endif %} - {% if icingaweb2_db['ssl_cert'] is defined %} --ssl-cert "{{ icingaweb2_db['ssl_cert'] }}" {%- endif %} - {% if icingaweb2_db['ssl_key'] is defined %} --ssl-key "{{ icingaweb2_db['ssl_key'] }}" {%- endif %} - {% if icingaweb2_db['ssl_cipher'] is defined %} --ssl-cipher "{{ icingaweb2_db['ssl_cipher'] }}" {%- endif %} - {% if icingaweb2_db['ssl_extra_options'] is defined %} {{ icingaweb2_db['ssl_extra_options'] }} {%- endif %} - -u "{{ icingaweb2_priv_db_user | default(icingaweb2_db['user']) }}" - -p"{{ icingaweb2_priv_db_password | default(icingaweb2_db['password']) }}" - "{{ icingaweb2_db['name'] }}" - - - name: MySQL check for icingaweb db schema - ansible.builtin.shell: > - {{ _tmp_mysqlcmd }} - -Ns -e "select * from icingaweb_user" - failed_when: false - changed_when: false - check_mode: false - register: _icingaweb2_db_schema - - - name: MySQL import icingaweb db schema - ansible.builtin.shell: > - {{ _tmp_mysqlcmd }} - < /usr/share/icingaweb2/schema/mysql.schema.sql - when: _icingaweb2_db_schema.rc != 0 - run_once: yes - when: icingaweb2_db_import_schema | default(False) - -- name: Ensure Icinga Web User - block: - - name: MySQL check for icingaweb db schema - ansible.builtin.shell: > - {{ _tmp_mysqlcmd }} - -Ns -e "select name from icingaweb_user where name like '{{ icingaweb2_admin_username }}'" - failed_when: false - changed_when: false - check_mode: false - register: _icingaweb2_db_user - - - name: Ensure admin user exists - ansible.builtin.shell: >- - echo "INSERT INTO icingaweb_user (name, active, password_hash) VALUES ('{{ icingaweb2_admin_username }}', 1, - '"`php -r 'echo password_hash("{{ icingaweb2_admin_password }}", PASSWORD_DEFAULT);'`"')" | {{ _tmp_mysqlcmd }} -Ns - when: _icingaweb2_db_user.stdout_lines | length <= 0 - run_once: yes - when: - - icingaweb2_admin_username is defined - - icingaweb2_admin_password is defined diff --git a/roles/icingaweb2/tasks/manage_icingaweb_pgsql_db.yml b/roles/icingaweb2/tasks/manage_icingaweb_pgsql_db.yml deleted file mode 100644 index 9a76a318..00000000 --- a/roles/icingaweb2/tasks/manage_icingaweb_pgsql_db.yml +++ /dev/null @@ -1,64 +0,0 @@ ---- - -- name: Check Database Credentials - ansible.builtin.assert: - that: - - icingaweb2_db['user'] is defined - - icingaweb2_db['password'] is defined - fail_msg: "No database credentials defined. Please set icingaweb2_db. or a privileged user with icingaweb2_priv_db_" - when: icingaweb2_priv_db_password is undefined and icingaweb2_priv_db_user is undefined - -- name: PostgreSQL import icingaweb db schema - block: - - name: Build psql command - ansible.builtin.set_fact: - _tmp_pgsqlcmd: >- - PGPASSWORD="{{ icingaweb2_priv_db_password | default(icingaweb2_db['password']) }}" - psql - "host={{ icingaweb2_db['host'] }} - {% if icingaweb2_db['port'] is defined %} port={{ icingaweb2_db['port'] }} {%- endif %} - user={{ icingaweb2_priv_db_user | default(icingaweb2_db['user']) }} - dbname={{ icingaweb2_db['name'] }} - {% if icingaweb2_db['ssl_mode'] is defined %} sslmode={{ icingaweb2_db['ssl_mode'] | default('require') }} {%- endif %} - {% if icingaweb2_db['ssl_cert'] is defined %} sslcert={{ icingaweb2_db['ssl_cert'] }} {%- endif %} - {% if icingaweb2_db['ssl_key'] is defined %} sslkey={{ icingaweb2_db['ssl_key'] }} {%- endif %} - {% if icingaweb2_db['ssl_extra_options'] is defined %} {{ icingaweb2_db['ssl_extra_options'] }} {%- endif %}" - - - name: PostgreSQL check for icingaweb db schema - ansible.builtin.shell: > - {{ _tmp_pgsqlcmd }} - -w -c "select * from icingaweb_user" - failed_when: false - changed_when: false - check_mode: false - register: _icingaweb2_db_schema - - - name: PostgreSQL import icingaweb db schema - ansible.builtin.shell: > - {{ _tmp_pgsqlcmd }} - -w -f /usr/share/icingaweb2/schema/pgsql.schema.sql - when: _icingaweb2_db_schema.rc != 0 - run_once: yes - when: icingaweb2_db_import_schema | default(False) - -- name: Ensure Icinga Web User - block: - - name: PostgreSQL check for icingaweb admin user - ansible.builtin.shell: > - LANG=C - {{ _tmp_pgsqlcmd }} - -w -c "select name from icingaweb_user where name like '{{ icingaweb2_admin_username }}'" - failed_when: false - changed_when: false - check_mode: false - register: _icingaweb2_db_user - - - name: Ensure admin user exists - ansible.builtin.shell: >- - echo "INSERT INTO icingaweb_user (name, active, password_hash) VALUES ('{{ icingaweb2_admin_username }}', 1, - '"`php -r 'echo password_hash("{{ icingaweb2_admin_password }}", PASSWORD_DEFAULT);'`"')" | {{ _tmp_pgsqlcmd }} -w - when: '"(0 rows)" in _icingaweb2_db_user.stdout_lines' - run_once: yes - when: - - icingaweb2_admin_username is defined - - icingaweb2_admin_password is defined diff --git a/roles/icingaweb2/tasks/manage_mysql_imports.yml b/roles/icingaweb2/tasks/manage_mysql_imports.yml deleted file mode 100644 index 14a9f9e2..00000000 --- a/roles/icingaweb2/tasks/manage_mysql_imports.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -- name: Check Database Credentials - ansible.builtin.assert: - that: - - _db['user'] is defined - - _db['password'] is defined - fail_msg: "No database credentials defined." - -- name: Build mysql command - ansible.builtin.set_fact: - _tmp_mysqlcmd: >- - mysql {% if _db['host'] | default('localhost') != 'localhost' %} -h "{{ _db['host'] }}" {%- endif %} - {% if _db['port'] is defined %} -P "{{ _db['port'] }}" {%- endif %} - {% if _db['ssl_mode'] is defined %} --ssl-mode "{{ _db['ssl_mode'] }}" {%- endif %} - {% if _db['ssl_ca'] is defined %} --ssl-ca "{{ _db['ssl_ca'] }}" {%- endif %} - {% if _db['ssl_cert'] is defined %} --ssl-cert "{{ _db['ssl_cert'] }}" {%- endif %} - {% if _db['ssl_key'] is defined %} --ssl-key "{{ _db['ssl_key'] }}" {%- endif %} - {% if _db['ssl_cipher'] is defined %} --ssl-cipher "{{ _db['ssl_cipher'] }}" {%- endif %} - {% if _db['ssl_extra_options'] is defined %} {{ _db['ssl_extra_options'] }} {%- endif %} - -u "{{ _db['user'] }}" - -p"{{ _db['password'] }}" - "{{ _db['name'] }}" - -- name: MySQL check for db schema - ansible.builtin.shell: > - {{ _tmp_mysqlcmd }} - -Ns -e "{{ _db['select_query'] }}" - failed_when: false - changed_when: false - check_mode: false - register: _db_schema - -- name: MySQL import db schema - ansible.builtin.shell: > - {{ _tmp_mysqlcmd }} - < {{ _db['schema_path_mysql'] }} - when: _db_schema.rc != 0 - run_once: yes diff --git a/roles/icingaweb2/tasks/mysql/import_db.yml b/roles/icingaweb2/tasks/mysql/import_db.yml new file mode 100644 index 00000000..6a5523cc --- /dev/null +++ b/roles/icingaweb2/tasks/mysql/import_db.yml @@ -0,0 +1,16 @@ +--- + +- name: MySQL check for icingaweb db schema + ansible.builtin.shell: > + {{ _tmp_mysqlcmd }} + -Ns -e "select * from icingaweb_user" + failed_when: false + changed_when: false + check_mode: false + register: _icingaweb2_db_schema + +- name: MySQL import icingaweb db schema + ansible.builtin.shell: > + {{ _tmp_mysqlcmd }} + < /usr/share/icingaweb2/schema/mysql.schema.sql + when: _icingaweb2_db_schema.rc != 0 diff --git a/roles/icingaweb2/tasks/mysql/prepare_db.yml b/roles/icingaweb2/tasks/mysql/prepare_db.yml new file mode 100644 index 00000000..d1b16d3a --- /dev/null +++ b/roles/icingaweb2/tasks/mysql/prepare_db.yml @@ -0,0 +1,30 @@ +--- + +- name: Check Database Credentials + ansible.builtin.assert: + that: + - icingaweb2_db['user'] is defined + - icingaweb2_db['password'] is defined + fail_msg: "No database credentials defined. Please set icingaweb2_db. or a privileged user with icingaweb2_priv_db_" + when: icingaweb2_priv_db_password is undefined and icingaweb2_priv_db_user is undefined + +- name: Set db user with admin privileges + ansible.builtin.set_fact: + _priv_db_user: "{{ icingaweb2_priv_db_user }}" + _priv_db_pass: "{{ icingaweb2_priv_db_password }}" + when: icingaweb2_priv_db_password is defined and icingaweb2_priv_db_user is defined + +- name: Build mysql command + ansible.builtin.set_fact: + _tmp_mysqlcmd: >- + mysql {% if icingaweb2_db['host'] | default('localhost') != 'localhost' %} -h "{{ icingaweb2_db['host'] }}" {%- endif %} + {% if icingaweb2_db['port'] is defined %} -P "{{ icingaweb2_db['port'] }}" {%- endif %} + {% if icingaweb2_db['ssl_mode'] is defined %} --ssl-mode "{{ icingaweb2_db['ssl_mode'] }}" {%- endif %} + {% if icingaweb2_db['ssl_ca'] is defined %} --ssl-ca "{{ icingaweb2_db['ssl_ca'] }}" {%- endif %} + {% if icingaweb2_db['ssl_cert'] is defined %} --ssl-cert "{{ icingaweb2_db['ssl_cert'] }}" {%- endif %} + {% if icingaweb2_db['ssl_key'] is defined %} --ssl-key "{{ icingaweb2_db['ssl_key'] }}" {%- endif %} + {% if icingaweb2_db['ssl_cipher'] is defined %} --ssl-cipher "{{ icingaweb2_db['ssl_cipher'] }}" {%- endif %} + {% if icingaweb2_db['ssl_extra_options'] is defined %} {{ icingaweb2_db['ssl_extra_options'] }} {%- endif %} + -u "{{ icingaweb2_priv_db_user | default(icingaweb2_db['user']) }}" + -p"{{ icingaweb2_priv_db_password | default(icingaweb2_db['password']) }}" + "{{ icingaweb2_db['name'] }}" diff --git a/roles/icingaweb2/tasks/mysql/users_db.yml b/roles/icingaweb2/tasks/mysql/users_db.yml new file mode 100644 index 00000000..ab4fe4ce --- /dev/null +++ b/roles/icingaweb2/tasks/mysql/users_db.yml @@ -0,0 +1,18 @@ +--- + +- name: MySQL check for icingaweb db schema + ansible.builtin.shell: > + {{ _tmp_mysqlcmd }} + -Ns -e "select name from icingaweb_user where name like '{{ _current_user.username }}'" + failed_when: false + changed_when: false + check_mode: false + register: _icingaweb2_db_user + +- name: Create user in Icinga Web (or reenable user / reset password) + run_once: true + ansible.builtin.shell: >- + echo "INSERT INTO icingaweb_user (name, active, password_hash) VALUES ('{{ _current_user.username }}', 1, + '"`php -r 'echo password_hash("{{ _current_user.password }}", PASSWORD_DEFAULT);'`"') + ON DUPLICATE KEY UPDATE active = 1, password_hash = '"`php -r 'echo password_hash("{{ _current_user.password }}", PASSWORD_DEFAULT);'`"'" | {{ _tmp_mysqlcmd }} -Ns + when: (_icingaweb2_db_user.stdout_lines | length <= 0) or (_current_user.recreate is true) diff --git a/roles/icingaweb2/tasks/pgsql/import_db.yml b/roles/icingaweb2/tasks/pgsql/import_db.yml new file mode 100644 index 00000000..e56be550 --- /dev/null +++ b/roles/icingaweb2/tasks/pgsql/import_db.yml @@ -0,0 +1,17 @@ +--- + +- name: PostgreSQL check for icingaweb db schema + ansible.builtin.shell: > + {{ _tmp_pgsqlcmd }} + -w -c "select * from icingaweb_user" + failed_when: false + changed_when: false + check_mode: false + register: _icingaweb2_db_schema + +- name: PostgreSQL import icingaweb db schema + ansible.builtin.shell: > + {{ _tmp_pgsqlcmd }} + -w -f /usr/share/icingaweb2/schema/pgsql.schema.sql + when: + - _icingaweb2_db_schema.rc != 0 diff --git a/roles/icingaweb2/tasks/pgsql/prepare_db.yml b/roles/icingaweb2/tasks/pgsql/prepare_db.yml new file mode 100644 index 00000000..99333f7a --- /dev/null +++ b/roles/icingaweb2/tasks/pgsql/prepare_db.yml @@ -0,0 +1,23 @@ +--- + +- name: Check Database Credentials + ansible.builtin.assert: + that: + - icingaweb2_db['user'] is defined + - icingaweb2_db['password'] is defined + fail_msg: "No database credentials defined. Please set icingaweb2_db. or a privileged user with icingaweb2_priv_db_" + when: icingaweb2_priv_db_password is undefined and icingaweb2_priv_db_user is undefined + +- name: Build psql command + ansible.builtin.set_fact: + _tmp_pgsqlcmd: >- + PGPASSWORD="{{ icingaweb2_priv_db_password | default(icingaweb2_db['password']) }}" + psql + "host={{ icingaweb2_db['host'] }} + {% if icingaweb2_db['port'] is defined %} port={{ icingaweb2_db['port'] }} {%- endif %} + user={{ icingaweb2_priv_db_user | default(icingaweb2_db['user']) }} + dbname={{ icingaweb2_db['name'] }} + {% if icingaweb2_db['ssl_mode'] is defined %} sslmode={{ icingaweb2_db['ssl_mode'] | default('require') }} {%- endif %} + {% if icingaweb2_db['ssl_cert'] is defined %} sslcert={{ icingaweb2_db['ssl_cert'] }} {%- endif %} + {% if icingaweb2_db['ssl_key'] is defined %} sslkey={{ icingaweb2_db['ssl_key'] }} {%- endif %} + {% if icingaweb2_db['ssl_extra_options'] is defined %} {{ icingaweb2_db['ssl_extra_options'] }} {%- endif %}" diff --git a/roles/icingaweb2/tasks/pgsql/users_db.yml b/roles/icingaweb2/tasks/pgsql/users_db.yml new file mode 100644 index 00000000..3ca8fd64 --- /dev/null +++ b/roles/icingaweb2/tasks/pgsql/users_db.yml @@ -0,0 +1,20 @@ +--- + +- name: PostgreSQL check for icingaweb admin user + ansible.builtin.shell: > + LANG=C + {{ _tmp_pgsqlcmd }} + -w -c "select name from icingaweb_user where name like '{{ _current_user.username }}'" + failed_when: false + changed_when: false + check_mode: false + register: _icingaweb2_db_user + +- name: Create user in Icinga Web (or reenable user / reset password) + run_once: true + ansible.builtin.shell: >- + echo "INSERT INTO icingaweb_user (name, active, password_hash) VALUES ('{{ _current_user.username }}', 1, + '"`php -r 'echo password_hash("{{ _current_user.password }}", PASSWORD_DEFAULT);'`"') + ON CONFLICT (name) DO UPDATE + SET active = 1, password_hash = '"`php -r 'echo password_hash("{{ _current_user.password }}", PASSWORD_DEFAULT);'`"'" | {{ _tmp_pgsqlcmd }} -w + when: ("(0 rows)" in _icingaweb2_db_user.stdout_lines) or (_current_user.recreate is true)