Compare commits

..

37 Commits

Author SHA1 Message Date
Thomas Woerner
dd6dd1fd84 Update README-role.md
Change title of README-role to "Role module"
2020-07-29 10:18:15 +02:00
Rafael Guterres Jeffman
4e89da8536 Merge pull request #300 from t-woerner/oel-support
ipa[server,replica,client]: New OracleLinux vars files
2020-07-27 13:43:41 -03:00
Thomas Woerner
5b5cce1943 Merge pull request #327 from rjeffman/fix_ipagroup_nonposix_external
Add support for IPA CLI option `posix`.
2020-07-27 13:44:35 +02:00
Rafael Guterres Jeffman
8c889e9b0b Add support for IPA CLI option posix.
This patch adds suport for the IPA CLI option `posix` when modifying
an existing group. Also, enhances verification of `external` and
`posix/non-posix` groups to avoid unneded API failures (e.g. when
no change to the posix/external status is needed).

A new test was added:

    tests/group/test_group_external_nonposix.yml
2020-07-27 08:19:31 -03:00
Thomas Woerner
643b3f950d ipa[server,replica,client]: New OracleLinux vars files
These are links to the Red Hat Enterprise linux files.
2020-07-27 12:32:46 +02:00
Sergio Oliveira
0f3691979f Merge pull request #288 from rjeffman/iparole
New Role management module
2020-07-21 17:07:05 -03:00
Rafael Guterres Jeffman
b33c5a7bab New Role management module
There is a new role management module placed in the plugins folder:

    plugins/modules/iparole.py

The role module allows to ensure presence or absence of roles and
manage role members.

Here is the documentation for the module:

    README-role.md

New example playbooks have been added:

    playbooks/role/role-is-absent.yml
    playbooks/role/role-is-present.yml
    playbooks/role/role-member-group-absent.yml
    playbooks/role/role-member-group-present.yml
    playbooks/role/role-member-host-absent.yml
    playbooks/role/role-member-host-present.yml
    playbooks/role/role-member-hostgroup-absent.yml
    playbooks/role/role-member-hostgroup-present.yml
    playbooks/role/role-member-privilege-absent.yml
    playbooks/role/role-member-privilege-present.yml
    playbooks/role/role-member-service-absent.yml
    playbooks/role/role-member-service-present.yml
    playbooks/role/role-member-user-absent.yml
    playbooks/role/role-member-user-present.yml
    playbooks/role/role-members-absent.yml
    playbooks/role/role-members-present.yml
    playbooks/role/role-rename.yml

New tests for the module:

    tests/role/test_role.yml
    tests/role/test_role_service_member.yml
2020-07-21 10:33:34 -03:00
Thomas Woerner
97601ceb9a Merge pull request #323 from t-woerner/external_ca_password_with_FIPS
tests/external-signed-ca-../external-ca.sh: Password too weak in FIPS…
2020-07-21 14:15:58 +02:00
Thomas Woerner
ffba096dc5 tests/external-signed-ca-../external-ca.sh: Password too weak in FIPS mode
The password that is used in the script to generate the CA and also sign
the CSR is not strong enough in FIPS mode. In normal mode the password was
ok, though.

In FIPS mode the password needs to have at least one upper, lower, digit
and a special char.
2020-07-21 12:46:34 +02:00
Sergio Oliveira
5364cf8046 Merge pull request #305 from aryklein/master
Fix variable name error
2020-07-16 14:25:48 -03:00
Sergio Oliveira
ab1b24570f Merge pull request #316 from rjeffman/terminology
Terminology improvements: use allow list.
2020-07-16 14:14:58 -03:00
Rafael Guterres Jeffman
22ec1c505e Merge pull request #317 from t-woerner/ipareplica_fix_missing_parameters
ipareplica: Fix missing parameters for several modules
2020-07-09 14:20:25 -03:00
Thomas Woerner
7a2eaa6f53 ipareplica: Fix missing parameters for several modules
The parameters master_host_name, config_setup_ca, dirman_password have not
been set for some modules. Also there was no ldap2 connection within
ipareplica_setup_kra. All this resulted in improper configuration where
for example KRA deployment failed in the end.

A conversion warning in ipareplica_setup_adtrust has also been fixed for
the setup_ca parameter.

Fixes #314 (IPA replica installation failure - DS enabled SSL - second part)
2020-07-07 17:22:44 +02:00
Rafael Guterres Jeffman
c8ae3c3a02 Terminology improvements: use allow list.
Adhere to recent changes on FreeIPA CLI help messages.
Also, see: https://tools.ietf.org/id/draft-knodel-terminology-01.html
2020-07-07 11:09:22 -03:00
Rafael Guterres Jeffman
4d8a4a14e4 Merge pull request #313 from t-woerner/fix_ca_less_pkcs12_info_regressions
ipa[server,replica]: Fix pkcs12 info regressions introduced with CA-less
2020-07-02 07:43:42 -03:00
Thomas Woerner
8ce5fd147a ipa[server,replica]: Fix pkcs12 info regressions introduced with CA-less
With the CA-less patches the types for the pkcs12 infos have been changed
to lists in the modules. This is resulting in a bad conversion from None
to [''] for the parameters. Because of this a normal replica deployment is
failing as [''] is not a valid value.

The install.yml files for ipareplica and also ipaserver have been changed
in the way that the pkcs12 values are checked if they are None. The
parameter will simply be omitted in this case and the parameter in the
module will become None by default.
2020-07-02 12:10:17 +02:00
Varun Mylaraiah
ffa0c6eef8 Merge pull request #312 from t-woerner/ansible_action_module_discovered_python
action_plugins/ipaclient_get_otp: Discovered python needed in task_vars
2020-07-01 12:31:12 +05:30
Thomas Woerner
80aac15de9 action_plugins/ipaclient_get_otp: Discovered python needed in task_vars
Ansible is now also supporting discovered_python_interpreter for
action_plugins. task_vars needs to be non Null and contain a setting for
discovered_python_interpreter. The ipaclient_get_otp action_plugin
therefore needed to be adapted.
2020-06-30 17:36:09 +02:00
Sergio Oliveira
097a3426a6 Merge pull request #311 from rjeffman/service_fix_krb5_ticket
Fixes ipaservice disable tests.
2020-06-29 17:38:55 -03:00
Rafael Guterres Jeffman
957b5910b4 Fixes ipaservice disable tests.
Due to use of some shell commands that required a Kerberos ticket,
the ipaservice test test_service_disable would no work if a ticket
was not granted before it ran. This patch adresses this issue by
acquiring a ticket for the `admin` user before it is needed, and
destroying the tickets by the end of the test execution.
2020-06-29 17:32:07 -03:00
Thomas Woerner
464eae16a1 Merge pull request #310 from t-woerner/fail_on_duplicate_names
ipa[user,host]: Fail on duplucate names in the users and hosts lists
2020-06-29 18:52:13 +02:00
Rafael Guterres Jeffman
0303f15375 Merge pull request #309 from t-woerner/fix_membermanager_unknown_user_issue
ipa[host]group: Fix membermanager unknow user issue
2020-06-29 10:50:12 -03:00
Thomas Woerner
6132a947e6 ipa[host]group: Fix membermanager unknow user issue
If a unknown membermanager user presence will be ensured, the unknown user
error was ignored. This has been fixed in ipagroup. The code for the error
handling in ipagroup and ipahostgroup has been adapted because of this.

New tests for tests/[host]group/test_[host]group_membermnager.yml have been
added.
2020-06-29 15:38:18 +02:00
Sergio Oliveira
c97a15f8d4 Merge pull request #306 from rjeffman/vault_change_password
Add suppport for changing password of symmetric vaults.
2020-06-29 10:09:28 -03:00
Rafael Guterres Jeffman
78b635ae78 Add suppport for changing password of symmetric vaults.
Allows changing passwords of symmetric waults, using a new variable
`new_password` (or the file-base version, `new_password_file`). The
old password must be passed using the `password` or `password_file`
variables that also received new aliases `old_password` and
`old_password_file`, respectively.

Tests were modyfied to reflect the changes.
2020-06-29 10:04:36 -03:00
Thomas Woerner
1d7fb31b8b ipa[user,host]: Fail on duplucate names in the users and hosts lists
It was possible to have several entries for names with the hosts and users
lists. This resulted sometimes in errors but also unexpected changes. A new
check has been added to make sure that the names in the users and hosts
lists are unique.

New tests have been added to verify this in the existing files:
- tests/host/test_hosts.yml
- tests/user/test_users.yml
2020-06-29 14:50:56 +02:00
Sergio Oliveira
34f1a45641 Merge pull request #308 from rjeffman/ipaservice_fix_service_disable
Fixes service disable when service has no certificates attached.
2020-06-26 17:36:35 -03:00
Sergio Oliveira
9b69caff49 Merge pull request #304 from rjeffman/fix_forwardzone_issues
Fix forwardzone issues
2020-06-26 17:22:07 -03:00
Rafael Guterres Jeffman
8da6a69379 Change password values in README to keep consistency with other modules. 2020-06-26 13:10:48 -03:00
Rafael Guterres Jeffman
857fb82eb9 Allows modification of forward policy in existing DNS Forward Zone.
This patch allows the modification of the forward zone policy in
an existing DNS Forward Zone, and fixes some issues with `enable`
and `disable` state that prevented correct behavior of `forwardpolicy`.
2020-06-26 13:10:48 -03:00
Rafael Guterres Jeffman
bf864469a1 Add support for attribute permission on dnsforwardzone module.
Adds missing attribute `permission to dnsforwardzone module, that
enable setting `manageby` for the DNS Forwar Zone.
2020-06-26 13:10:48 -03:00
Rafael Guterres Jeffman
e57e4908f9 Fixes service disable when service has no certificates attached.
Services without certificates, but with keytabs were not being
disabled. This change allows execution of service_disable if
there is a certificate or if has_keytab is true.

A new test was added to verify the issue:

    tests/service/test_service_disable.yml
2020-06-25 17:05:15 -03:00
Ary Kleinerman
0165506514 Fix variable name 2020-06-21 20:49:14 -03:00
Rafael Guterres Jeffman
1d223c2b63 Add support for attributes ip_address and port to forwarders.
This patch modify the was forwarders are configured, using two attributes,
`ip_address` and `port`, instead of IPA API internal string representation
of `IP port PORT`.
2020-06-15 16:14:25 -03:00
Rafael Guterres Jeffman
3f785bc0e9 Fix error message when adding dnsforwardzone without forwarders. 2020-06-10 22:20:20 -03:00
Rafael Guterres Jeffman
f8ebca760d Allow processing of multiple names for deleting dnsforwardzones. 2020-06-10 22:14:27 -03:00
Rafael Guterres Jeffman
f0f933b463 Changed admin password on tests to match other modules.
Use of the same password on all module tests ease test automation,
and this change ensure that dnsforwardzone use the same password as
other modules.
2020-06-10 20:40:45 -03:00
66 changed files with 2553 additions and 267 deletions

View File

@@ -49,7 +49,7 @@ Example playbook to ensure presence of a forwardzone to ipa DNS:
tasks:
- name: ensure presence of forwardzone for DNS requests for example.com to 8.8.8.8
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
state: present
name: example.com
forwarders:
@@ -59,13 +59,13 @@ Example playbook to ensure presence of a forwardzone to ipa DNS:
- name: ensure the forward zone is disabled
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
name: example.com
state: disabled
- name: ensure presence of multiple upstream DNS servers for example.com
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
state: present
name: example.com
forwarders:
@@ -74,7 +74,7 @@ Example playbook to ensure presence of a forwardzone to ipa DNS:
- name: ensure presence of another forwarder to any existing ones for example.com
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
state: present
name: example.com
forwarders:
@@ -83,7 +83,7 @@ Example playbook to ensure presence of a forwardzone to ipa DNS:
- name: ensure the forwarder for example.com does not exists (delete it if needed)
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
name: example.com
state: absent
```
@@ -99,9 +99,12 @@ Variable | Description | Required
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
`name` \| `cn` | Zone name (FQDN). | yes if `state` == `present`
`forwarders` \| `idnsforwarders` | Per-zone conditional forwarding policy. Possible values are `only`, `first`, `none`) | no
`forwardpolicy` \| `idnsforwardpolicy` | Per-zone conditional forwarding policy. Set to "none" to disable forwarding to global forwarder for this zone. In that case, conditional zone forwarders are disregarded. | no
`forwarders` \| `idnsforwarders` | Per-zone forwarders. A custom port can be specified for each forwarder. Options | no
  | `ip_address`: The forwarder IP address. | yes
  | `port`: The forwarder IP port. | no
`forwardpolicy` \| `idnsforwardpolicy` | Per-zone conditional forwarding policy. Possible values are `only`, `first`, `none`. Set to "none" to disable forwarding to global forwarder for this zone. In that case, conditional zone forwarders are disregarded. | no
`skip_overlap_check` | Force DNS zone creation even if it will overlap with an existing zone. Defaults to False. | no
`permission` | Allow DNS Forward Zone to be managed. (bool) | no
`action` | Work on group or member level. It can be on of `member` or `dnsforwardzone` and defaults to `dnsforwardzone`. | no
`state` | The state to ensure. It can be one of `present`, `absent`, `enabled` or `disabled`, default: `present`. | yes

View File

@@ -137,6 +137,7 @@ Variable | Description | Required
`name` \| `cn` | The list of group name strings. | no
`description` | The group description string. | no
`gid` \| `gidnumber` | The GID integer. | no
`posix` | Create a non-POSIX group or change a non-POSIX to a posix group. (bool) | no
`nonposix` | Create as a non-POSIX group. (bool) | no
`external` | Allow adding external non-IPA members from trusted domains. (bool) | no
`nomembers` | Suppress processing of membership attributes. (bool) | no

View File

@@ -355,7 +355,7 @@ Variable | Description | Required
`mac_address` \| `macaddress` | List of hardware MAC addresses. | no
`sshpubkey` \| `ipasshpubkey` | List of SSH public keys | no
`userclass` \| `class` | Host category (semantics placed on this attribute are for local interpretation) | no
`auth_ind` \| `krbprincipalauthind` | Defines a whitelist for Authentication Indicators. Use 'otp' to allow OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA authentications. Use empty string to reset auth_ind to the initial value. Other values may be used for custom configurations. choices: ["radius", "otp", "pkinit", "hardened", ""] | no
`auth_ind` \| `krbprincipalauthind` | Defines an allow list for Authentication Indicators. Use 'otp' to allow OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA authentications. Use empty string to reset auth_ind to the initial value. Other values may be used for custom configurations. choices: ["radius", "otp", "pkinit", "hardened", ""] | no
`requires_pre_auth` \| `ipakrbrequirespreauth` | Pre-authentication is required for the service (bool) | no
`ok_as_delegate` \| `ipakrbokasdelegate` | Client credentials may be delegated to the service (bool) | no
`ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client (bool) | no

264
README-role.md Normal file
View File

@@ -0,0 +1,264 @@
Role module
===========
Description
-----------
The role module allows to ensure presence, absence of roles and members of roles.
The role module is as compatible as possible to the Ansible upstream `ipa_role` module, but additionally offers role member management.
Features
--------
* Role management
Supported FreeIPA Versions
--------------------------
FreeIPA versions 4.4.0 and up are supported by the iparole module.
Requirements
------------
**Controller**
* Ansible version: 2.8+
**Node**
* Supported FreeIPA version (see above)
Usage
=====
Example inventory file
```ini
[ipaserver]
ipaserver.test.local
```
Example playbook to make sure role is present with all members:
```yaml
---
- name: Playbook to manage IPA role with members.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
user:
- pinky
group:
- group01
host:
- host01.example.com
hostgroup:
- hostgroup01
privilege:
- Group Administrators
- User Administrators
service:
- service01
```
Example playbook to rename a role:
```yaml
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
rename: anotherrole
```
Example playbook to make sure role is absent:
```yaml
---
- name: Playbook to manage IPA role.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
state: absent
```
Example playbook to ensure a user is a member of a role:
```yaml
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
user:
- pinky
action: member
```
Example playbook to ensure a group is a member of a role:
```yaml
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
host:
- host01.example.com
action: member
```
Example playbook to ensure a host is a member of a role:
```yaml
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
host:
- host01.example.com
action: member
```
Example playbook to ensure a hostgroup is a member of a role:
```yaml
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
hostgroup:
- hostgroup01
action: member
```
Example playbook to ensure a service is a member of a role:
```yaml
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
service:
- service01
action: member
```
Example playbook to ensure a privilege is a member of a role:
```yaml
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
privilege:
- Group Administrators
- User Administrators
action: member
```
Example playbook to ensure that different members are not associated with a role.
```yaml
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
user:
- pinky
group:
- group01
host:
- host01.example.com
hostgroup:
- hostgroup01
privilege:
- Group Administrators
- User Administrators
service:
- service01
action: member
state: absent
```
Variables
---------
iparole
-------
Variable | Description | Required
-------- | ----------- | --------
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
`name` \| `cn` | The list of role name strings. | yes
`description` | A description for the role. | no
`rename` | Rename the role object. | no
`privileges` | Privileges associated to this role. | no
`user` | List of users to be assigned or not assigned to the role. | no
`group` | List of groups to be assigned or not assigned to the role. | no
`host` | List of hosts to be assigned or not assigned to the role. | no
`hostgroup` | List of hostgroups to be assigned or not assigned to the role. | no
`service` | List of services to be assigned or not assigned to the role. | no
`action` | Work on role or member level. It can be on of `member` or `role` and defaults to `role`. | no
`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. | no
Authors
=======
Rafael Jeffman

View File

@@ -294,7 +294,7 @@ Variable | Description | Required
`name` \| `service` | The list of service name strings. | yes
`certificate` \| `usercertificate` | Base-64 encoded service certificate. | no
`pac_type` \| `ipakrbauthzdata` | Supported PAC type. It can be one of `MS-PAC`, `PAD`, or `NONE`. | no
`auth_ind` \| `krbprincipalauthind` | Defines a whitelist for Authentication Indicators. It can be any of `otp`, `radius`, `pkinit`, or `hardened`. | no
`auth_ind` \| `krbprincipalauthind` | Defines an allow list for Authentication Indicators. It can be any of `otp`, `radius`, `pkinit`, or `hardened`. | no
`requires_pre_auth` \| `ipakrbrequirespreauth` | Pre-authentication is required for the service. Default to true. (bool) | no
`ok_as_delegate` \| `ipakrbokasdelegate` | Client credentials may be delegated to the service. Default to false. (bool) | no
`ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client. Default to false. (bool) | no

View File

@@ -165,6 +165,22 @@ Example playbook to make sure vault data is absent in a symmetric vault:
state: absent
```
Example playbook to change the password of a symmetric:
```yaml
---
- name: Playbook to handle vaults
hosts: ipaserver
become: true
tasks:
- ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
old_password: SomeVAULTpassword
new_password: SomeNEWpassword
```
Example playbook to make sure vault is absent:
```yaml
@@ -197,8 +213,11 @@ Variable | Description | Required
`name` \| `cn` | The list of vault name strings. | yes
`description` | The vault description string. | no
`nomembers` | Suppress processing of membership attributes. (bool) | no
`password ` \| `vault_password` \| `ipavaultpassword` | Vault password. | no
`public_key ` \| `vault_public_key` \| `ipavaultpublickey` | Base64 encoded vault public key. | no
`password` \| `vault_password` \| `ipavaultpassword` \| `old_password`| Vault password. | no
`password_file` \| `vault_password_file` \| `old_password_file`| File containing Base64 encoded Vault password. | no
`new_password` | Vault new password. | no
`new_password_file` | File containing Base64 encoded new Vault password. | no
`public_key ` \| `vault_public_key` \| `old_password_file` | Base64 encoded vault public key. | no
`public_key_file` \| `vault_public_key_file` | Path to file with public key. | no
`private_key `\| `vault_private_key` | Base64 encoded vault private key. Used only to retrieve data. | no
`private_key_file` \| `vault_private_key_file` | Path to file with private key. Used only to retrieve data. | no

View File

@@ -21,6 +21,7 @@ Features
* Modules for host management
* Modules for hostgroup management
* Modules for pwpolicy management
* Modules for role management
* Modules for service management
* Modules for sudocmd management
* Modules for sudocmdgroup management
@@ -421,6 +422,7 @@ Modules in plugin/modules
* [ipahost](README-host.md)
* [ipahostgroup](README-hostgroup.md)
* [ipapwpolicy](README-pwpolicy.md)
* [iparole](README-role.md)
* [ipaservice](README-service.md)
* [ipasudocmd](README-sudocmd.md)
* [ipasudocmdgroup](README-sudocmdgroup.md)

View File

@@ -0,0 +1,11 @@
---
- name: Playbook to manage IPA role.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
state: absent

View File

@@ -0,0 +1,11 @@
---
- name: Playbook to manage IPA role.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
description: A role in IPA.

View File

@@ -0,0 +1,14 @@
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
group:
- group01
action: member
state: absent

View File

@@ -0,0 +1,13 @@
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
group:
- group01
action: member

View File

@@ -0,0 +1,14 @@
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
host:
- host01.example.com
action: member
state: absent

View File

@@ -0,0 +1,13 @@
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
host:
- host01.example.com
action: member

View File

@@ -0,0 +1,14 @@
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
hostgroup:
- hostgroup01
action: member
state: absent

View File

@@ -0,0 +1,13 @@
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
hostgroup:
- hostgroup01
action: member

View File

@@ -0,0 +1,15 @@
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
privilege:
- Group Administrators
- User Administrators
action: member
state: absent

View File

@@ -0,0 +1,14 @@
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
privilege:
- Group Administrators
- User Administrators
action: member

View File

@@ -0,0 +1,14 @@
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
service:
- http/www.example.com
action: member
state: absent

View File

@@ -0,0 +1,13 @@
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
service:
- service01
action: member

View File

@@ -0,0 +1,14 @@
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
user:
- pinky
action: member
state: absent

View File

@@ -0,0 +1,13 @@
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
user:
- pinky
action: member

View File

@@ -0,0 +1,25 @@
---
- name: Playbook to manage IPA role member.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
user:
- pinky
group:
- group01
host:
- host01.example.com
hostgroup:
- hostgroup01
privilege:
- Group Administrators
- User Administrators
service:
- service01
action: member
state: absent

View File

@@ -0,0 +1,23 @@
---
- name: Playbook to manage IPA role with members.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
user:
- pinky
group:
- group01
host:
- host01.example.com
hostgroup:
- hostgroup01
privilege:
- Group Administrators
- User Administrators
service:
- service01

View File

@@ -0,0 +1,11 @@
---
- name: Playbook to manage IPA role.
hosts: ipaserver
become: yes
gather_facts: no
tasks:
- iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
rename: anotherrole

View File

@@ -10,7 +10,7 @@
ipaadmin_password: SomeADMINpassword
name: symvault
password: SomeVAULTpassword
- name: Change vault passord.
- name: Change vault password.
ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault

View File

@@ -54,9 +54,16 @@ options:
forwarders:
description:
- List of the DNS servers to forward to
required: true
type: list
aliases: ["idnsforwarders"]
options:
ip_address:
description: Forwarder IP address (either IPv4 or IPv6).
required: false
type: string
port:
description: Forwarder port.
required: false
type: int
forwardpolicy:
description: Per-zone conditional forwarding policy
required: false
@@ -68,6 +75,11 @@ options:
- Force DNS zone creation even if it will overlap with an existing zone.
required: false
default: false
permission:
description:
- Allow DNS Forward Zone to be managed.
required: false
type: bool
'''
EXAMPLES = '''
@@ -128,20 +140,41 @@ def gen_args(forwarders, forwardpolicy, skip_overlap_check):
return _args
def forwarder_list(forwarders):
"""Convert the forwarder dict into a list compatible with IPA API."""
if forwarders is None:
return None
fwd_list = []
for forwarder in forwarders:
if forwarder.get('port', None) is not None:
formatter = "{ip_address} port {port}"
else:
formatter = "{ip_address}"
fwd_list.append(formatter.format(**forwarder))
return fwd_list
def main():
ansible_module = AnsibleModule(
argument_spec=dict(
# general
ipaadmin_principal=dict(type="str", default="admin"),
ipaadmin_password=dict(type="str", required=False, no_log=True),
name=dict(type="str", aliases=["cn"], default=None,
name=dict(type="list", aliases=["cn"], default=None,
required=True),
forwarders=dict(type='list', aliases=["idnsforwarders"],
required=False),
forwarders=dict(type="list", default=None, required=False,
aliases=["idnsforwarders"], elements='dict',
options=dict(
ip_address=dict(type='str', required=True),
port=dict(type='int', required=False,
default=None),
)),
forwardpolicy=dict(type='str', aliases=["idnsforwardpolicy"],
required=False,
choices=['only', 'first', 'none']),
skip_overlap_check=dict(type='bool', required=False),
permission=dict(type='bool', required=False,
aliases=['managedby']),
action=dict(type="str", default="dnsforwardzone",
choices=["member", "dnsforwardzone"]),
# state
@@ -158,14 +191,22 @@ def main():
"ipaadmin_principal")
ipaadmin_password = module_params_get(ansible_module,
"ipaadmin_password")
name = module_params_get(ansible_module, "name")
names = module_params_get(ansible_module, "name")
action = module_params_get(ansible_module, "action")
forwarders = module_params_get(ansible_module, "forwarders")
forwarders = forwarder_list(
module_params_get(ansible_module, "forwarders"))
forwardpolicy = module_params_get(ansible_module, "forwardpolicy")
skip_overlap_check = module_params_get(ansible_module,
"skip_overlap_check")
permission = module_params_get(ansible_module, "permission")
state = module_params_get(ansible_module, "state")
if state == 'present' and len(names) != 1:
ansible_module.fail_json(
msg="Only one dnsforwardzone can be added at a time.")
if state == 'absent' and len(names) < 1:
ansible_module.fail_json(msg="No name given.")
# absent stae means delete if the action is NOT member but update if it is
# if action is member then update an exisiting resource
# and if action is not member then create a resource
@@ -176,18 +217,30 @@ def main():
else:
operation = "add"
if state == "disabled":
wants_enable = False
else:
wants_enable = True
if operation == "del":
invalid = ["forwarders", "forwardpolicy", "skip_overlap_check"]
if state in ["enabled", "disabled"]:
if action == "member":
ansible_module.fail_json(
msg="Action `member` cannot be used with state `%s`"
% (state))
invalid = [
"forwarders", "forwardpolicy", "skip_overlap_check", "permission"
]
for x in invalid:
if vars()[x] is not None:
ansible_module.fail_json(
msg="Argument '%s' can not be used with action "
"'%s'" % (x, action))
"'%s', state `%s`" % (x, action, state))
wants_enable = (state == "enabled")
if operation == "del":
invalid = [
"forwarders", "forwardpolicy", "skip_overlap_check", "permission"
]
for x in invalid:
if vars()[x] is not None:
ansible_module.fail_json(
msg="Argument '%s' can not be used with action "
"'%s', state `%s`" % (x, action, state))
changed = False
exit_args = {}
@@ -207,99 +260,116 @@ def main():
ipaadmin_password)
api_connect()
# Make sure forwardzone exists
existing_resource = find_dnsforwardzone(ansible_module, name)
if existing_resource is None and operation == "update":
# does not exist and is updating
# trying to update something that doesn't exist, so error
ansible_module.fail_json(msg="""dnsforwardzone '%s' is not
valid""" % (name))
elif existing_resource is None and operation == "del":
# does not exists and should be absent
# set command
for name in names:
commands = []
command = None
# enabled or disabled?
is_enabled = "IGNORE"
elif existing_resource is not None and operation == "del":
# exists but should be absent
# set command
command = "dnsforwardzone_del"
# enabled or disabled?
is_enabled = "IGNORE"
elif forwarders is None:
# forwarders are not defined its not a delete, update state?
# set command
command = None
# enabled or disabled?
if existing_resource is not None:
is_enabled = existing_resource["idnszoneactive"][0]
else:
is_enabled = "IGNORE"
elif existing_resource is not None and operation == "update":
# exists and is updating
# calculate the new forwarders and mod
# determine args
if state != "absent":
forwarders = list(set(existing_resource["idnsforwarders"]
+ forwarders))
else:
forwarders = list(set(existing_resource["idnsforwarders"])
- set(forwarders))
args = gen_args(forwarders, forwardpolicy,
skip_overlap_check)
if skip_overlap_check is not None:
del args['skip_overlap_check']
# command
if not compare_args_ipa(ansible_module, args, existing_resource):
command = "dnsforwardzone_mod"
else:
command = None
# Make sure forwardzone exists
existing_resource = find_dnsforwardzone(ansible_module, name)
# enabled or disabled?
is_enabled = existing_resource["idnszoneactive"][0]
# validate parameters
if state == 'present':
if existing_resource is None and not forwarders:
ansible_module.fail_json(msg='No forwarders specified.')
elif existing_resource is None and operation == "add":
# does not exist but should be present
# determine args
args = gen_args(forwarders, forwardpolicy,
skip_overlap_check)
# set command
command = "dnsforwardzone_add"
# enabled or disabled?
is_enabled = "TRUE"
if existing_resource is None:
if operation == "add":
# does not exist but should be present
# determine args
args = gen_args(forwarders, forwardpolicy,
skip_overlap_check)
# set command
command = "dnsforwardzone_add"
# enabled or disabled?
elif existing_resource is not None and operation == "add":
# exists and should be present, has it changed?
# determine args
args = gen_args(forwarders, forwardpolicy, skip_overlap_check)
if skip_overlap_check is not None:
del args['skip_overlap_check']
elif operation == "update":
# does not exist and is updating
# trying to update something that doesn't exist, so error
ansible_module.fail_json(
msg="dnsforwardzone '%s' not found." % (name))
# set command
if not compare_args_ipa(ansible_module, args, existing_resource):
command = "dnsforwardzone_mod"
else:
command = None
elif operation == "del":
# there's nothnig to do.
continue
# enabled or disabled?
is_enabled = existing_resource["idnszoneactive"][0]
else: # existing_resource is not None
if state != "absent":
if forwarders:
forwarders = list(
set(existing_resource["idnsforwarders"]
+ forwarders))
else:
if forwarders:
forwarders = list(
set(existing_resource["idnsforwarders"])
- set(forwarders))
# if command is set then run it with the args
if command is not None:
api_command(ansible_module, command, name, args)
changed = True
if operation == "add":
# exists and should be present, has it changed?
# determine args
args = gen_args(
forwarders, forwardpolicy, skip_overlap_check)
if 'skip_overlap_check' in args:
del args['skip_overlap_check']
# does the enabled state match what we want (if we care)
if is_enabled != "IGNORE":
if wants_enable and is_enabled != "TRUE":
api_command(ansible_module, "dnsforwardzone_enable",
name, {})
changed = True
elif not wants_enable and is_enabled != "FALSE":
api_command(ansible_module, "dnsforwardzone_disable",
name, {})
# set command
if not compare_args_ipa(
ansible_module, args, existing_resource):
command = "dnsforwardzone_mod"
elif operation == "del":
# exists but should be absent
# set command
command = "dnsforwardzone_del"
args = {}
elif operation == "update":
# exists and is updating
# calculate the new forwarders and mod
args = gen_args(
forwarders, forwardpolicy, skip_overlap_check)
if "skip_overlap_check" in args:
del args['skip_overlap_check']
# command
if not compare_args_ipa(
ansible_module, args, existing_resource):
command = "dnsforwardzone_mod"
if state in ['enabled', 'disabled']:
if existing_resource is not None:
is_enabled = existing_resource["idnszoneactive"][0]
else:
ansible_module.fail_json(
msg="dnsforwardzone '%s' not found." % (name))
# does the enabled state match what we want (if we care)
if is_enabled != "IGNORE":
if wants_enable and is_enabled != "TRUE":
commands.append([name, "dnsforwardzone_enable", {}])
elif not wants_enable and is_enabled != "FALSE":
commands.append([name, "dnsforwardzone_disable", {}])
# if command is set...
if command is not None:
commands.append([name, command, args])
if permission is not None:
if existing_resource is None:
managedby = None
else:
managedby = existing_resource.get('managedby', None)
if permission and managedby is None:
commands.append(
[name, 'dnsforwardzone_add_permission', {}]
)
elif not permission and managedby is not None:
commands.append(
[name, 'dnsforwardzone_remove_permission', {}]
)
for name, command, args in commands:
api_command(ansible_module, command, name, args)
changed = True
except Exception as e:

View File

@@ -57,6 +57,11 @@ options:
description: Allow adding external non-IPA members from trusted domains
required: false
type: bool
posix:
description:
Create a non-POSIX group or change a non-POSIX to a posix group.
required: false
type: bool
nomembers:
description: Suppress processing of membership attributes
required: false
@@ -140,10 +145,23 @@ EXAMPLES = """
- sysops
- appops
# Remove goups sysops, appops and ops
# Create a non-POSIX group
- ipagroup:
ipaadmin_password: SomeADMINpassword
name: sysops,appops,ops
name: nongroup
nonposix: yes
# Turn a non-POSIX group into a POSIX group.
- ipagroup:
ipaadmin_password: SomeADMINpassword
name: nonposix
posix: yes
# Remove goups sysops, appops, ops and nongroup
- ipagroup:
ipaadmin_password: SomeADMINpassword
name: sysops,appops,ops, nongroup
state: absent
"""
@@ -173,16 +191,12 @@ def find_group(module, name):
return None
def gen_args(description, gid, nonposix, external, nomembers):
def gen_args(description, gid, nomembers):
_args = {}
if description is not None:
_args["description"] = description
if gid is not None:
_args["gidnumber"] = gid
if nonposix is not None:
_args["nonposix"] = nonposix
if external is not None:
_args["external"] = external
if nomembers is not None:
_args["nomembers"] = nomembers
@@ -201,6 +215,41 @@ def gen_member_args(user, group, service):
return _args
def check_objectclass_args(module, res_find, nonposix, posix, external):
if res_find and 'posixgroup' in res_find['objectclass']:
if (
(posix is not None and posix is False)
or nonposix
or external
):
module.fail_json(
msg="Cannot change `POSIX` status of a group "
"to `non-POSIX` or `external`.")
# Can't change an existing external group
if res_find and 'ipaexternalgroup' in res_find['objectclass']:
if (
posix
or (nonposix is not None and nonposix is False)
or (external is not None and external is False)
):
module.fail_json(
msg="Cannot change `external` status of group "
"to `POSIX` or `non-external`.")
def should_modify_group(module, res_find, args, nonposix, posix, external):
if not compare_args_ipa(module, args, res_find):
return True
if any([posix, nonposix]):
set_posix = posix or (nonposix is not None and not nonposix)
if set_posix and 'posixgroup' not in res_find['objectclass']:
return True
if 'ipaexternalgroup' not in res_find['objectclass'] and external:
if 'posixgroup' not in res_find['objectclass']:
return True
return False
def main():
ansible_module = AnsibleModule(
argument_spec=dict(
@@ -215,6 +264,7 @@ def main():
gid=dict(type="int", aliases=["gidnumber"], default=None),
nonposix=dict(required=False, type='bool', default=None),
external=dict(required=False, type='bool', default=None),
posix=dict(required=False, type='bool', default=None),
nomembers=dict(required=False, type='bool', default=None),
user=dict(required=False, type='list', default=None),
group=dict(required=False, type='list', default=None),
@@ -228,6 +278,7 @@ def main():
state=dict(type="str", default="present",
choices=["present", "absent"]),
),
mutually_exclusive=[['posix', 'nonposix']],
supports_check_mode=True,
)
@@ -248,6 +299,7 @@ def main():
gid = module_params_get(ansible_module, "gid")
nonposix = module_params_get(ansible_module, "nonposix")
external = module_params_get(ansible_module, "external")
posix = module_params_get(ansible_module, "posix")
nomembers = module_params_get(ansible_module, "nomembers")
user = module_params_get(ansible_module, "user")
group = module_params_get(ansible_module, "group")
@@ -267,7 +319,7 @@ def main():
ansible_module.fail_json(
msg="Only one group can be added at a time.")
if action == "member":
invalid = ["description", "gid", "nonposix", "external",
invalid = ["description", "gid", "posix", "nonposix", "external",
"nomembers"]
for x in invalid:
if vars()[x] is not None:
@@ -279,7 +331,8 @@ def main():
if len(names) < 1:
ansible_module.fail_json(
msg="No name given.")
invalid = ["description", "gid", "nonposix", "external", "nomembers"]
invalid = ["description", "gid", "posix", "nonposix", "external",
"nomembers"]
if action == "group":
invalid.extend(["user", "group", "service"])
for x in invalid:
@@ -322,9 +375,12 @@ def main():
# Create command
if state == "present":
# Can't change an existing posix group
check_objectclass_args(ansible_module, res_find, nonposix,
posix, external)
# Generate args
args = gen_args(description, gid, nonposix, external,
nomembers)
args = gen_args(description, gid, nomembers)
if action == "group":
# Found the group
@@ -332,10 +388,21 @@ def main():
# For all settings is args, check if there are
# different settings in the find result.
# If yes: modify
if not compare_args_ipa(ansible_module, args,
res_find):
if should_modify_group(ansible_module, res_find, args,
nonposix, posix, external):
if (
posix
or (nonposix is not None and not nonposix)
):
args['posix'] = True
if external:
args['external'] = True
commands.append([name, "group_mod", args])
else:
if nonposix or (posix is not None and not posix):
args['nonposix'] = True
if external:
args['external'] = True
commands.append([name, "group_add", args])
# Set res_find to empty dict for next step
res_find = {}
@@ -507,16 +574,15 @@ def main():
# All "already a member" and "not a member" failures in the
# result are ignored. All others are reported.
errors = []
if "failed" in result and len(result["failed"]) > 0:
for item in result["failed"]:
failed_item = result["failed"][item]
for member_type in failed_item:
for member, failure in failed_item[member_type]:
if "already a member" in failure \
or "not a member" in failure:
continue
errors.append("%s: %s %s: %s" % (
command, member_type, member, failure))
for failed_item in result.get("failed", []):
failed = result["failed"][failed_item]
for member_type in failed:
for member, failure in failed[member_type]:
if "already a member" in failure \
or "not a member" in failure:
continue
errors.append("%s: %s %s: %s" % (
command, member_type, member, failure))
if len(errors) > 0:
ansible_module.fail_json(msg=", ".join(errors))

View File

@@ -799,10 +799,15 @@ def main():
server_realm = api_get_realm()
commands = []
host_set = set()
for host in names:
if isinstance(host, dict):
name = host.get("name")
if name in host_set:
ansible_module.fail_json(
msg="host '%s' is used more than once" % name)
host_set.add(name)
description = host.get("description")
locality = host.get("locality")
location = host.get("location")
@@ -1337,6 +1342,8 @@ def main():
else:
ansible_module.fail_json(msg="Unkown state '%s'" % state)
del host_set
# Execute commands
errors = []

View File

@@ -423,14 +423,15 @@ def main():
# All "already a member" and "not a member" failures in the
# result are ignored. All others are reported.
errors = []
if "failed" in result and "member" in result["failed"]:
failed = result["failed"]["member"]
for failed_item in result.get("failed", []):
failed = result["failed"][failed_item]
for member_type in failed:
for member, failure in failed[member_type]:
if "already a member" not in failure \
and "not a member" not in failure:
errors.append("%s: %s %s: %s" % (
command, member_type, member, failure))
if "already a member" in failure \
or "not a member" in failure:
continue
errors.append("%s: %s %s: %s" % (
command, member_type, member, failure))
if len(errors) > 0:
ansible_module.fail_json(msg=", ".join(errors))

485
plugins/modules/iparole.py Normal file
View File

@@ -0,0 +1,485 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""ansible-freeipa iparole module implementation."""
# Authors:
# Rafael Guterres Jeffman <rjeffman@redhat.com>
#
# Copyright (C) 2020 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
"status": ["preview"],
}
DOCUMENTATION = """
---
module: ipaservice
short description: Manage FreeIPA service
description: Manage FreeIPA service
options:
ipaadmin_principal:
description: The admin principal.
default: admin
ipaadmin_password:
description: The admin password.
required: false
role:
description: The list of role name strings.
required: true
aliases: ["cn"]
description:
descrpition: A description for the role.
required: false
rename:
descrpition: Rename the role object.
required: false
user:
description: List of users.
required: false
group:
description: List of groups.
required: false
host:
description: List of hosts.
required: false
hostgroup:
description: List of hostgroups.
required: false
service:
description: List of services.
required: false
action:
description: Work on service or member level.
choices: ["role", "member"]
default: role
required: false
state:
description: The state to ensure.
choices: ["present", "absent"]
default: present
required: true
"""
EXAMPLES = """
- name: Ensure a role named `somerole` is present.
iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
- name: Ensure user `pinky` is a memmer of role `somerole`.
iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
user:
- pinky
action: member
- name: Ensure a role named `somerole` is absent.
iparole:
ipaadmin_password: SomeADMINpassword
name: somerole
state: absent
"""
# pylint: disable=wrong-import-position
# pylint: disable=import-error
# pylint: disable=no-name-in-module
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_text
from ansible.module_utils.ansible_freeipa_module import \
temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \
gen_add_del_lists, compare_args_ipa, module_params_get, api_get_realm
import six
if six.PY3:
unicode = str
def find_role(module, name):
"""Find if a role with the given name already exist."""
try:
_result = api_command(module, "role_show", name, {"all": True})
except Exception: # pylint: disable=broad-except
# An exception is raised if role name is not found.
return None
else:
return _result["result"]
def gen_args(module):
"""Generate arguments for executing commands."""
arg_map = {
"description": "description",
"rename": "rename",
}
args = {}
for param, arg in arg_map.items():
value = module_params_get(module, param)
if value is not None:
args[arg] = value
return args
def check_parameters(module):
"""Check if parameters passed for module processing are valid."""
action = module_params_get(module, "action")
state = module_params_get(module, "state")
invalid = []
if state == "present":
if action == "member":
invalid.extend(['description', 'rename'])
if state == "absent":
invalid.extend(['description', 'rename'])
if action != "member":
invalid.extend(['privilege'])
for arg in invalid:
if module_params_get(module, arg) is not None:
module.fail_json(
msg="Argument '%s' can not be used with action '%s'" %
(arg, state))
def verify_credentials(module):
"""Ensure there are valid Kerberos credentials."""
ccache_dir = None
ccache_name = None
ipaadmin_principal = module_params_get(module, "ipaadmin_principal")
ipaadmin_password = module_params_get(module, "ipaadmin_password")
if not valid_creds(module, ipaadmin_principal):
ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
ipaadmin_password)
return (ccache_dir, ccache_name)
def member_intersect(module, attr, memberof, res_find):
"""Filter member arguments from role found by intersection."""
params = module_params_get(module, attr)
if not res_find:
return params
filtered = []
if params:
existing = res_find.get(memberof, [])
filtered = list(set(params) & set(existing))
return filtered
def member_difference(module, attr, memberof, res_find):
"""Filter member arguments from role found by difference."""
params = module_params_get(module, attr)
if not res_find:
return params
filtered = []
if params:
existing = res_find.get(memberof, [])
filtered = list(set(params) - set(existing))
return filtered
def ensure_absent_state(module, name, action, res_find):
"""Define commands to ensure absent state."""
commands = []
if action == "role":
commands.append([name, 'role_del', {}])
if action == "member":
members = member_intersect(
module, 'privilege', 'memberof_privilege', res_find)
if members:
commands.append([name, "role_remove_privilege",
{"privilege": members}])
member_args = {}
for key in ['user', 'group', 'host', 'hostgroup']:
items = member_intersect(
module, key, 'member_%s' % key, res_find)
if items:
member_args[key] = items
_services = filter_service(module, res_find,
lambda res, svc: res.startswith(svc))
if _services:
member_args['service'] = _services
# Only add remove command if there's at least one member no manage.
if member_args:
commands.append([name, "role_remove_member", member_args])
return commands
def filter_service(module, res_find, predicate):
"""
Filter service based on predicate.
Compare service name with existing ones matching
at least until `@` from principal name.
Predicate is a callable that accepts the existing service, and the
modified service to be compared to.
"""
_services = []
service = module_params_get(module, 'service')
if service:
existing = [to_text(x) for x in res_find.get('member_service', [])]
for svc in service:
svc = svc if '@' in svc else ('%s@' % svc)
found = [x for x in existing if predicate(x, svc)]
_services.extend(found)
return _services
def ensure_role_with_members_is_present(module, name, res_find):
"""Define commands to ensure member are present for action `role`."""
commands = []
privilege_add, privilege_del = gen_add_del_lists(
module_params_get(module, "privilege"),
res_find.get('memberof_privilege', []))
if privilege_add:
commands.append([name, "role_add_privilege",
{"privilege": privilege_add}])
if privilege_del:
commands.append([name, "role_remove_privilege",
{"privilege": privilege_del}])
add_members = {}
del_members = {}
for key in ["user", "group", "host", "hostgroup"]:
add_list, del_list = gen_add_del_lists(
module_params_get(module, key),
res_find.get('member_%s' % key, [])
)
if add_list:
add_members[key] = add_list
if del_list:
del_members[key] = [to_text(item) for item in del_list]
service = [
to_text(svc) if '@' in svc else ('%s@%s' % (svc, api_get_realm()))
for svc in (module_params_get(module, 'service') or [])
]
existing = [str(svc) for svc in res_find.get('member_service', [])]
add_list, del_list = gen_add_del_lists(service, existing)
if add_list:
add_members['service'] = add_list
if del_list:
del_members['service'] = [to_text(item) for item in del_list]
if add_members:
commands.append([name, "role_add_member", add_members])
if del_members:
commands.append([name, "role_remove_member", del_members])
return commands
def ensure_members_are_present(module, name, res_find):
"""Define commands to ensure members are present for action `member`."""
commands = []
members = member_difference(
module, 'privilege', 'memberof_privilege', res_find)
if members:
commands.append([name, "role_add_privilege",
{"privilege": members}])
member_args = {}
for key in ['user', 'group', 'host', 'hostgroup']:
items = member_difference(
module, key, 'member_%s' % key, res_find)
if items:
member_args[key] = items
_services = filter_service(module, res_find,
lambda res, svc: not res.startswith(svc))
if _services:
member_args['service'] = _services
if member_args:
commands.append([name, "role_add_member", member_args])
return commands
def process_command_failures(command, result):
"""Process the result of a command, looking for errors."""
# Get all errors
# All "already a member" and "not a member" failures in the
# result are ignored. All others are reported.
errors = []
if "failed" in result and len(result["failed"]) > 0:
for item in result["failed"]:
failed_item = result["failed"][item]
for member_type in failed_item:
for member, failure in failed_item[member_type]:
if "already a member" in failure \
or "not a member" in failure:
continue
errors.append("%s: %s %s: %s" % (
command, member_type, member, failure))
return errors
def process_commands(module, commands):
"""Process the list of IPA API commands."""
errors = []
exit_args = {}
changed = False
for name, command, args in commands:
try:
result = api_command(module, command, name, args)
if "completed" in result:
if result["completed"] > 0:
changed = True
else:
changed = True
errors = process_command_failures(command, result)
except Exception as exception: # pylint: disable=broad-except
module.fail_json(
msg="%s: %s: %s" % (command, name, str(exception)))
if errors:
module.fail_json(msg=", ".join(errors))
return changed, exit_args
def role_commands_for_name(module, state, action, name):
"""Define commands for the Role module."""
commands = []
rename = module_params_get(module, "rename")
res_find = find_role(module, name)
if state == "present":
args = gen_args(module)
if action == "role":
if res_find is None:
if rename is not None:
module.fail_json(msg="Cannot `rename` inexistent role.")
commands.append([name, 'role_add', args])
res_find = {}
else:
if not compare_args_ipa(module, args, res_find):
commands.append([name, 'role_mod', args])
if action == "member":
if res_find is None:
module.fail_json(msg="No role '%s'" % name)
cmds = ensure_role_with_members_is_present(module, name, res_find)
commands.extend(cmds)
if state == "absent" and res_find is not None:
cmds = ensure_absent_state(module, name, action, res_find)
commands.extend(cmds)
return commands
def create_module():
"""Create module description."""
ansible_module = AnsibleModule(
argument_spec=dict(
# generalgroups
ipaadmin_principal=dict(type="str", default="admin"),
ipaadmin_password=dict(type="str", required=False, no_log=True),
name=dict(type="list", aliases=["cn"], default=None,
required=True),
# present
description=dict(required=False, type="str", default=None),
rename=dict(required=False, type="str", default=None),
# members
privilege=dict(required=False, type='list', default=None),
user=dict(required=False, type='list', default=None),
group=dict(required=False, type='list', default=None),
host=dict(required=False, type='list', default=None),
hostgroup=dict(required=False, type='list', default=None),
service=dict(required=False, type='list', default=None),
# state
action=dict(type="str", default="role",
choices=["role", "member"]),
state=dict(type="str", default="present",
choices=["present", "absent"]),
),
supports_check_mode=True,
mutually_exclusive=[],
required_one_of=[]
)
ansible_module._ansible_debug = True # pylint: disable=protected-access
return ansible_module
def main():
"""Process role module script."""
ansible_module = create_module()
check_parameters(ansible_module)
# Init
ccache_dir = None
ccache_name = None
try:
ccache_dir, ccache_name = verify_credentials(ansible_module)
api_connect()
state = module_params_get(ansible_module, "state")
action = module_params_get(ansible_module, "action")
names = module_params_get(ansible_module, "name")
commands = []
for name in names:
cmds = role_commands_for_name(ansible_module, state, action, name)
commands.extend(cmds)
changed, exit_args = process_commands(ansible_module, commands)
except Exception as exception: # pylint: disable=broad-except
ansible_module.fail_json(msg=str(exception))
finally:
temp_kdestroy(ccache_dir, ccache_name)
# Done
ansible_module.exit_json(changed=changed, **exit_args)
if __name__ == "__main__":
main()

View File

@@ -812,9 +812,11 @@ def main():
elif state == "disabled":
if action == "service":
if res_find is not None and \
len(res_find.get('usercertificate', [])) > 0:
commands.append([name, 'service_disable', {}])
if res_find is not None:
has_cert = bool(res_find.get('usercertificate'))
has_keytab = res_find.get('has_keytab', False)
if has_cert or has_keytab:
commands.append([name, 'service_disable', {}])
else:
ansible_module.fail_json(
msg="Invalid action '%s' for state '%s'" %

View File

@@ -958,10 +958,15 @@ def main():
# commands
commands = []
user_set = set()
for user in names:
if isinstance(user, dict):
name = user.get("name")
if name in user_set:
ansible_module.fail_json(
msg="user '%s' is used more than once" % name)
user_set.add(name)
# present
first = user.get("first")
last = user.get("last")
@@ -1370,6 +1375,8 @@ def main():
else:
ansible_module.fail_json(msg="Unkown state '%s'" % state)
del user_set
# Execute commands
errors = []

View File

@@ -69,12 +69,20 @@ options:
description: password to be used on symmetric vault.
required: false
type: string
aliases: ["ipavaultpassword", "vault_password"]
aliases: ["ipavaultpassword", "vault_password", "old_password"]
password_file:
description: file with password to be used on symmetric vault.
required: false
type: string
aliases: ["vault_password_file"]
aliases: ["vault_password_file", "old_password_file"]
new_password:
description: new password to be used on symmetric vault.
required: false
type: string
new_password_file:
description: file with new password to be used on symmetric vault.
required: false
type: string
salt:
description: Vault salt.
required: false
@@ -235,7 +243,15 @@ EXAMPLES = """
state: retrieved
register: result
- debug:
msg: "{{ result.data | b64decode }}"
msg: "{{ result.data }}"
# Change password of a symmetric vault
- ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
username: admin
old_password: SomeVAULTpassword
new_password: SomeNEWpassword
# Ensure vault symvault is absent
- ipavault:
@@ -416,18 +432,29 @@ def check_parameters(module, state, action, description, username, service,
shared, users, groups, services, owners, ownergroups,
ownerservices, vault_type, salt, password, password_file,
public_key, public_key_file, private_key,
private_key_file, vault_data, datafile_in, datafile_out):
private_key_file, vault_data, datafile_in, datafile_out,
new_password, new_password_file):
invalid = []
if state == "present":
invalid = ['private_key', 'private_key_file', 'datafile_out']
if all([password, password_file]) \
or all([new_password, new_password_file]):
module.fail_json(msg="Password specified multiple times.")
if any([new_password, new_password_file]) \
and not any([password, password_file]):
module.fail_json(
msg="Either `password` or `password_file` must be provided to "
"change symmetric vault password.")
if action == "member":
invalid.extend(['description'])
elif state == "absent":
invalid = ['description', 'salt', 'vault_type', 'private_key',
'private_key_file', 'datafile_in', 'datafile_out',
'vault_data']
'vault_data', 'new_password', 'new_password_file']
if action == "vault":
invalid.extend(['users', 'groups', 'services', 'owners',
@@ -437,7 +464,7 @@ def check_parameters(module, state, action, description, username, service,
elif state == "retrieved":
invalid = ['description', 'salt', 'datafile_in', 'users', 'groups',
'owners', 'ownergroups', 'public_key', 'public_key_file',
'vault_data']
'vault_data', 'new_password', 'new_password_file']
if action == 'member':
module.fail_json(
msg="State `retrieved` do not support action `member`.")
@@ -458,11 +485,17 @@ def check_parameters(module, state, action, description, username, service,
def check_encryption_params(module, state, action, vault_type, salt,
password, password_file, public_key,
public_key_file, private_key, private_key_file,
vault_data, datafile_in, datafile_out, res_find):
vault_data, datafile_in, datafile_out,
new_password, new_password_file, res_find):
vault_type_invalid = []
if res_find is not None:
vault_type = res_find['ipavaulttype']
if vault_type == "standard":
vault_type_invalid = ['public_key', 'public_key_file', 'password',
'password_file', 'salt']
'password_file', 'salt', 'new_password',
'new_password_file']
if vault_type is None or vault_type == "symmetric":
vault_type_invalid = ['public_key', 'public_key_file',
@@ -473,8 +506,14 @@ def check_encryption_params(module, state, action, vault_type, salt,
msg="Symmetric vault requires password or password_file "
"to store data or change `salt`.")
if any([new_password, new_password_file]) and res_find is None:
module.fail_json(
msg="Cannot modify password of inexistent vault.")
if vault_type == "asymmetric":
vault_type_invalid = ['password', 'password_file']
vault_type_invalid = [
'password', 'password_file', 'new_password', 'new_password_file'
]
if not any([public_key, public_key_file]) and res_find is None:
module.fail_json(
msg="Assymmetric vault requires public_key "
@@ -487,6 +526,43 @@ def check_encryption_params(module, state, action, vault_type, salt,
(param, vault_type or 'symmetric'))
def change_password(module, res_find, password, password_file, new_password,
new_password_file):
"""
Change the password of a symmetric vault.
To change the password of a vault, it is needed to retrieve the stored
data with the current password, and store the data again, with the new
password, forcing it to override the old one.
"""
# verify parameters.
if not any([new_password, new_password_file]):
return []
if res_find["ipavaulttype"][0] != "symmetric":
module.fail_json(msg="Cannot change password of `%s` vault."
% res_find["ipavaulttype"])
# prepare arguments to retrieve data.
name = res_find["cn"][0]
args = {}
if password:
args["password"] = password
if password_file:
args["password"] = password_file
# retrieve current stored data
result = api_command(module, 'vault_retrieve', name, args)
args['data'] = result['result']['data']
# modify arguments to store data with new password.
if password:
args["password"] = new_password
if password_file:
args["password"] = new_password_file
args["override_password"] = True
# return the command to store data with the new password.
return [(name, "vault_archive", args)]
def main():
ansible_module = AnsibleModule(
argument_spec=dict(
@@ -533,10 +609,18 @@ def main():
datafile_out=dict(type="str", required=False, default=None,
aliases=['out']),
vault_password=dict(type="str", required=False, default=None,
aliases=['ipavaultpassword', 'password'],
no_log=True),
no_log=True,
aliases=['ipavaultpassword', 'password',
"old_password"]),
vault_password_file=dict(type="str", required=False, default=None,
no_log=False, aliases=['password_file']),
no_log=False,
aliases=[
'password_file', "old_password_file"
]),
new_password=dict(type="str", required=False, default=None,
no_log=True),
new_password_file=dict(type="str", required=False, default=None,
no_log=False),
# state
action=dict(type="str", default="vault",
choices=["vault", "data", "member"]),
@@ -546,6 +630,7 @@ def main():
supports_check_mode=True,
mutually_exclusive=[['username', 'service', 'shared'],
['datafile_in', 'vault_data'],
['new_password', 'new_password_file'],
['vault_password', 'vault_password_file'],
['vault_public_key', 'vault_public_key_file']],
)
@@ -576,6 +661,8 @@ def main():
salt = module_params_get(ansible_module, "vault_salt")
password = module_params_get(ansible_module, "vault_password")
password_file = module_params_get(ansible_module, "vault_password_file")
new_password = module_params_get(ansible_module, "new_password")
new_password_file = module_params_get(ansible_module, "new_password_file")
public_key = module_params_get(ansible_module, "vault_public_key")
public_key_file = module_params_get(ansible_module,
"vault_public_key_file")
@@ -614,7 +701,8 @@ def main():
service, shared, users, groups, services, owners,
ownergroups, ownerservices, vault_type, salt, password,
password_file, public_key, public_key_file, private_key,
private_key_file, vault_data, datafile_in, datafile_out)
private_key_file, vault_data, datafile_in, datafile_out,
new_password, new_password_file)
# Init
changed = False
@@ -660,7 +748,7 @@ def main():
ansible_module, state, action, vault_type, salt, password,
password_file, public_key, public_key_file, private_key,
private_key_file, vault_data, datafile_in, datafile_out,
res_find)
new_password, new_password_file, res_find)
# Found the vault
if action == "vault":
@@ -721,7 +809,6 @@ def main():
owner_add_args = gen_member_args(
args, owner_add, ownergroups_add, ownerservice_add)
if owner_add_args is not None:
# ansible_module.warn("OWNER ADD: %s" % owner_add_args)
commands.append(
[name, 'vault_add_owner', owner_add_args])
@@ -729,7 +816,6 @@ def main():
owner_del_args = gen_member_args(
args, owner_del, ownergroups_del, ownerservice_del)
if owner_del_args is not None:
# ansible_module.warn("OWNER DEL: %s" % owner_del_args)
commands.append(
[name, 'vault_remove_owner', owner_del_args])
@@ -758,19 +844,22 @@ def main():
if any([vault_data, datafile_in]):
commands.append([name, "vault_archive", pwdargs])
cmds = change_password(
ansible_module, res_find, password, password_file,
new_password, new_password_file)
commands.extend(cmds)
elif state == "retrieved":
if res_find is None:
ansible_module.fail_json(
msg="Vault `%s` not found to retrieve data." % name)
vault_type = res_find['cn']
# verify data encription args
check_encryption_params(
ansible_module, state, action, vault_type, salt, password,
password_file, public_key, public_key_file, private_key,
private_key_file, vault_data, datafile_in, datafile_out,
res_find)
new_password, new_password_file, res_find)
pwdargs = data_storage_args(
args, vault_data, password, password_file, private_key,
@@ -813,7 +902,6 @@ def main():
errors = []
for name, command, args in commands:
try:
# ansible_module.warn("RUN: %s %s %s" % (command, name, args))
result = api_command(ansible_module, command, name, args)
if command == 'vault_archive':
@@ -829,7 +917,6 @@ def main():
raise Exception("No data retrieved.")
changed = False
else:
# ansible_module.warn("RESULT: %s" % (result))
if "completed" in result:
if result["completed"] > 0:
changed = True

View File

@@ -164,7 +164,8 @@ class ActionModule(ActionBase):
return result
data = self._execute_module(module_name='ipaclient_get_facts',
module_args=dict(), task_vars=None)
module_args=dict(), task_vars=task_vars)
try:
domain = data['ansible_facts']['ipa']['domain']
realm = data['ansible_facts']['ipa']['realm']
@@ -245,4 +246,3 @@ class ActionModule(ActionBase):
finally:
# delete the local temp directory
shutil.rmtree(local_temp_dir, ignore_errors=True)
run_cmd(['/usr/bin/kdestroy', '-c', tmp_ccache])

View File

@@ -134,7 +134,6 @@
"Password cannot be set on enrolled host" not
in result_ipaclient_get_otp.msg
delegate_to: "{{ result_ipaclient_test.servers[0] }}"
delegate_facts: yes
ignore_errors: yes
- name: Install - Report error for OTP generation

View File

@@ -0,0 +1 @@
RedHat-7.yml

View File

@@ -0,0 +1 @@
RedHat-8.yml

View File

@@ -95,7 +95,7 @@ ipareplica1.example.com
ipareplica2.example.com
[ipareplicas:vars]
ipaclient_domain=example.com
ipareplica_domain=example.com
ipaadmin_principal=admin
ipaadmin_password=MySecretPassword123
ipadm_password=MySecretPassword456

View File

@@ -262,6 +262,7 @@ def main():
config.subject_base = options.subject_base
config.dirman_password = dirman_password
config.ca_host_name = ca_host_name
config.setup_ca = options.setup_ca
remote_api = gen_remote_api(master_host_name, paths.ETC_IPA)
installer._remote_api = remote_api

View File

@@ -177,6 +177,7 @@ def main():
config = gen_ReplicaConfig()
config.dirman_password = dirman_password
config.subject_base = options.subject_base
config.master_host_name = master_host_name
remote_api = gen_remote_api(master_host_name, paths.ETC_IPA)

View File

@@ -173,6 +173,7 @@ def main():
config = gen_ReplicaConfig()
config.dirman_password = dirman_password
config.subject_base = options.subject_base
config.master_host_name = master_host_name
remote_api = gen_remote_api(master_host_name, paths.ETC_IPA)
# installer._remote_api = remote_api

View File

@@ -110,7 +110,7 @@ def main():
# additional
ccache=dict(required=True),
_top_dir=dict(required=True),
setup_ca=dict(required=True),
setup_ca=dict(required=True, type='bool'),
config_master_host_name=dict(required=True),
),
supports_check_mode=True,

View File

@@ -169,6 +169,7 @@ def main():
config.promote = installer.promote
config.kra_enabled = kra_enabled
config.kra_host_name = kra_host_name
config.setup_ca = options.setup_ca
remote_api = gen_remote_api(master_host_name, paths.ETC_IPA)

View File

@@ -164,7 +164,7 @@ def main():
config.subject_base = options.subject_base
config.dirman_password = dirman_password
config.setup_ca = options.setup_ca
# config.master_host_name = master_host_name
config.master_host_name = master_host_name
config.ca_host_name = ca_host_name
config.promote = installer.promote

View File

@@ -120,6 +120,9 @@ options:
_subject_base:
description: The installer _subject_base setting
required: no
dirman_password:
description: Directory Manager (master) password
required: no
author:
- Thomas Woerner
'''
@@ -173,10 +176,12 @@ def main():
_ca_enabled=dict(required=False, type='bool'),
_kra_enabled=dict(required=False, type='bool'),
_kra_host_name=dict(required=False),
_ca_host_name=dict(required=False),
_top_dir=dict(required=True),
_add_to_ipaservers=dict(required=True, type='bool'),
_ca_subject=dict(required=True),
_subject_base=dict(required=True),
dirman_password=dict(required=True, no_log=True),
),
supports_check_mode=True,
)
@@ -233,6 +238,7 @@ def main():
ca_enabled = ansible_module.params.get('_ca_enabled')
kra_enabled = ansible_module.params.get('_kra_enabled')
kra_host_name = ansible_module.params.get('_kra_host_name')
ca_host_name = ansible_module.params.get('_ca_host_name')
options.subject_base = ansible_module.params.get('subject_base')
if options.subject_base is not None:
@@ -243,6 +249,7 @@ def main():
options._ca_subject = ansible_module.params.get('_ca_subject')
options._subject_base = ansible_module.params.get('_subject_base')
dirman_password = ansible_module.params.get('dirman_password')
# init #
@@ -254,14 +261,25 @@ def main():
constants.DEFAULT_CONFIG)
api_bootstrap_finalize(env)
config = gen_ReplicaConfig()
config.dirman_password = dirman_password
config.subject_base = options.subject_base
config.promote = installer.promote
config.kra_enabled = kra_enabled
config.kra_host_name = kra_host_name
config.ca_host_name = ca_host_name
config.master_host_name = master_host_name
remote_api = gen_remote_api(master_host_name, paths.ETC_IPA)
installer._remote_api = remote_api
conn = remote_api.Backend.ldap2
ccache = os.environ['KRB5CCNAME']
# There is a api.Backend.ldap2.connect call somewhere in ca, ds, dns or
# ntpinstance
api.Backend.ldap2.connect()
conn.connect(ccache=ccache)
with redirect_stdout(ansible_log):
ansible_log.debug("-- INSTALL KRA --")

View File

@@ -63,6 +63,9 @@ options:
_top_dir:
description: The installer _top_dir setting
required: no
dirman_password:
description: Directory Manager (master) password
required: no
author:
- Thomas Woerner
'''
@@ -98,6 +101,7 @@ def main():
ccache=dict(required=True),
_pkinit_pkcs12_info=dict(required=False, type='list'),
_top_dir=dict(required=True),
dirman_password=dict(required=True, no_log=True),
),
supports_check_mode=True,
)
@@ -126,6 +130,7 @@ def main():
'_pkinit_pkcs12_info')
options._top_dir = ansible_module.params.get('_top_dir')
dirman_password = ansible_module.params.get('dirman_password')
# init #
@@ -141,8 +146,10 @@ def main():
constants.DEFAULT_CONFIG)
api_bootstrap_finalize(env)
config = gen_ReplicaConfig()
config.dirman_password = dirman_password
config.master_host_name = config_master_host_name
config.subject_base = options.subject_base
config.setup_ca = options.setup_ca
ccache = os.environ['KRB5CCNAME']

View File

@@ -226,6 +226,8 @@
setup_adtrust: "{{ result_ipareplica_test.setup_adtrust }}"
setup_kra: "{{ result_ipareplica_test.setup_kra }}"
setup_dns: "{{ ipareplica_setup_dns }}"
### server ###
setup_ca: "{{ ipareplica_setup_ca }}"
### ssl certificate ###
dirsrv_cert_files: "{{ ipareplica_dirsrv_cert_files | default([]) }}"
### client ###
@@ -281,7 +283,7 @@
ccache: "{{ result_ipareplica_prepare.ccache }}"
installer_ccache: "{{ result_ipareplica_prepare.installer_ccache }}"
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
_dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info }}"
_dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info if result_ipareplica_prepare._dirsrv_pkcs12_info != None else omit }}"
subject_base: "{{ result_ipareplica_prepare.subject_base }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
_add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
@@ -332,6 +334,7 @@
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
dirman_password: "{{ ipareplica_dirman_password }}"
setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
- name: Install - Setup KRB
ipareplica_setup_krb:
@@ -345,8 +348,9 @@
config_master_host_name:
"{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
ccache: "{{ result_ipareplica_prepare.ccache }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
# We need to point to the master in ipa default conf when certmonger
# asks for HTTP certificate in newer ipa versions. In these versions
@@ -388,6 +392,7 @@
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
dirman_password: "{{ ipareplica_dirman_password }}"
setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
master:
"{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
when: result_ipareplica_test.change_master_for_certmonger
@@ -407,8 +412,8 @@
ccache: "{{ result_ipareplica_prepare.ccache }}"
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
_dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
_dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info if result_ipareplica_prepare._dirsrv_pkcs12_info != None else omit }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
ds_ca_subject: "{{ result_ipareplica_setup_ds.ds_ca_subject }}"
@@ -429,7 +434,7 @@
ccache: "{{ result_ipareplica_prepare.ccache }}"
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
_http_pkcs12_info: "{{ result_ipareplica_prepare._http_pkcs12_info }}"
_http_pkcs12_info: "{{ result_ipareplica_prepare._http_pkcs12_info if result_ipareplica_prepare._http_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
@@ -471,6 +476,7 @@
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
dirman_password: "{{ ipareplica_dirman_password }}"
setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
when: result_ipareplica_test.change_master_for_certmonger
- name: Install - Setup otpd
@@ -507,7 +513,7 @@
_kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}"
_kra_host_name: "{{ result_ipareplica_prepare.config_kra_host_name }}"
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
@@ -529,7 +535,7 @@
_kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}"
_kra_host_name: "{{ result_ipareplica_prepare.config_kra_host_name }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
config_setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
@@ -554,7 +560,7 @@
ccache: "{{ result_ipareplica_prepare.ccache }}"
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
@@ -574,7 +580,7 @@
ccache: "{{ result_ipareplica_prepare.ccache }}"
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
ds_ca_subject: "{{ result_ipareplica_setup_ds.ds_ca_subject }}"
@@ -611,10 +617,12 @@
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
_kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}"
_kra_host_name: "{{ result_ipareplica_prepare.config_kra_host_name }}"
_ca_host_name: "{{ result_ipareplica_prepare.config_ca_host_name }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
_add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
dirman_password: "{{ ipareplica_dirman_password }}"
when: result_ipareplica_test.setup_kra
- name: Install - Restart KDC

View File

@@ -0,0 +1 @@
RedHat-7.yml

View File

@@ -0,0 +1 @@
RedHat-8.yml

View File

@@ -203,7 +203,7 @@
# no_host_dns: "{{ result_ipaserver_test.no_host_dns }}"
dirsrv_config_file: "{{ ipaserver_dirsrv_config_file | default(omit) }}"
dirsrv_cert_files: "{{ ipaserver_dirsrv_cert_files | default(omit) }}"
_dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info }}"
_dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info if result_ipaserver_test._dirsrv_pkcs12_info != None else omit }}"
external_cert_files:
"{{ ipaserver_external_cert_files | default(omit) }}"
subject_base: "{{ result_ipaserver_prepare.subject_base }}"
@@ -240,7 +240,7 @@
no_hbac_allow: "{{ ipaserver_no_hbac_allow }}"
idstart: "{{ result_ipaserver_test.idstart }}"
idmax: "{{ result_ipaserver_test.idmax }}"
_pkinit_pkcs12_info: "{{ result_ipaserver_test._pkinit_pkcs12_info }}"
_pkinit_pkcs12_info: "{{ result_ipaserver_test._pkinit_pkcs12_info if result_ipaserver_test._pkinit_pkcs12_info != None else omit }}"
- name: Install - Setup custodia
ipaserver_setup_custodia:
@@ -270,7 +270,7 @@
no_pkinit: "{{ result_ipaserver_test.no_pkinit }}"
dirsrv_config_file: "{{ ipaserver_dirsrv_config_file | default(omit) }}"
dirsrv_cert_files: "{{ ipaserver_dirsrv_cert_files | default([]) }}"
_dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info }}"
_dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info if result_ipaserver_test._dirsrv_pkcs12_info != None else omit }}"
external_ca: "{{ ipaserver_external_ca }}"
external_ca_type: "{{ ipaserver_external_ca_type | default(omit) }}"
external_ca_profile:
@@ -334,7 +334,7 @@
idmax: "{{ result_ipaserver_test.idmax }}"
http_cert_files: "{{ ipaserver_http_cert_files | default([]) }}"
no_ui_redirect: "{{ ipaserver_no_ui_redirect }}"
_http_pkcs12_info: "{{ result_ipaserver_test._http_pkcs12_info }}"
_http_pkcs12_info: "{{ result_ipaserver_test._http_pkcs12_info if result_ipaserver_test._http_pkcs12_info != None else omit }}"
- name: Install - Setup KRA
ipaserver_setup_kra:
@@ -394,7 +394,7 @@
idstart: "{{ result_ipaserver_test.idstart }}"
idmax: "{{ result_ipaserver_test.idmax }}"
dirsrv_config_file: "{{ ipaserver_dirsrv_config_file | default(omit) }}"
_dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info }}"
_dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info if result_ipaserver_test._dirsrv_pkcs12_info != None else omit }}"
- name: Install - Setup client
include_role:

View File

@@ -0,0 +1 @@
RedHat-7.yml

View File

@@ -0,0 +1 @@
RedHat-8.yml

View File

@@ -5,19 +5,21 @@
gather_facts: false
tasks:
- name: ensure forwardzone example.com is absent - prep
- name: ensure test forwardzones are absent
ipadnsforwardzone:
ipaadmin_password: password01
name: example.com
ipaadmin_password: SomeADMINpassword
name:
- example.com
- newfailzone.com
state: absent
- name: ensure forwardzone example.com is created
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
state: present
name: example.com
forwarders:
- 8.8.8.8
- ip_address: 8.8.8.8
forwardpolicy: first
skip_overlap_check: true
register: result
@@ -25,11 +27,11 @@
- name: ensure forwardzone example.com is present again
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
state: present
name: example.com
forwarders:
- 8.8.8.8
- ip_address: 8.8.8.8
forwardpolicy: first
skip_overlap_check: true
register: result
@@ -37,12 +39,13 @@
- name: ensure forwardzone example.com has two forwarders
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
state: present
name: example.com
forwarders:
- 8.8.8.8
- 4.4.4.4
- ip_address: 8.8.8.8
- ip_address: 4.4.4.4
port: 8053
forwardpolicy: first
skip_overlap_check: true
register: result
@@ -50,165 +53,246 @@
- name: ensure forwardzone example.com has one forwarder again
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
name: example.com
forwarders:
- 8.8.8.8
- ip_address: 8.8.8.8
forwardpolicy: first
skip_overlap_check: true
state: present
register: result
failed_when: not result.changed
failed_when: result.changed
- name: skip_overlap_check can only be set on creation so change nothing
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
name: example.com
forwarders:
- 8.8.8.8
- ip_address: 8.8.8.8
forwardpolicy: first
skip_overlap_check: false
state: present
register: result
failed_when: result.changed
- name: change all the things at once
- name: ensure forwardzone example.com is absent.
ipadnsforwardzone:
ipaadmin_password: password01
state: present
ipaadmin_password: SomeADMINpassword
name: example.com
forwarders:
- 8.8.8.8
- 4.4.4.4
forwardpolicy: only
skip_overlap_check: false
state: absent
register: result
failed_when: not result.changed
- name: ensure forwardzone example.com is absent for next testset
- name: ensure forwardzone example.com is absent, again.
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
name: example.com
state: absent
register: result
failed_when: result.changed
- name: change all the things at once
ipadnsforwardzone:
ipaadmin_password: SomeADMINpassword
state: present
name: example.com
forwarders:
- ip_address: 8.8.8.8
- ip_address: 4.4.4.4
port: 8053
forwardpolicy: only
skip_overlap_check: true
permission: yes
register: result
failed_when: not result.changed
- name: change zone forward policy
ipadnsforwardzone:
ipaadmin_password: SomeADMINpassword
name: example.com
forwardpolicy: first
register: result
failed_when: not result.changed
- name: change zone forward policy, again
ipadnsforwardzone:
ipaadmin_password: SomeADMINpassword
name: example.com
forwardpolicy: first
register: result
failed_when: result.changed
- name: ensure forwardzone example.com is absent.
ipadnsforwardzone:
ipaadmin_password: SomeADMINpassword
name: example.com
state: absent
- name: ensure forwardzone example.com is created with minimal args
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
state: present
name: example.com
skip_overlap_check: true
forwarders:
- 8.8.8.8
- ip_address: 8.8.8.8
register: result
failed_when: not result.changed
- name: add a forwarder to any existing ones
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
state: present
name: example.com
forwarders:
- 4.4.4.4
- ip_address: 4.4.4.4
port: 8053
action: member
register: result
failed_when: not result.changed
- name: check the list of forwarders is what we expect
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
state: present
name: example.com
forwarders:
- 4.4.4.4
- 8.8.8.8
- ip_address: 4.4.4.4
port: 8053
- ip_address: 8.8.8.8
action: member
register: result
failed_when: result.changed
- name: remove a single forwarder
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
state: absent
name: example.com
forwarders:
- 8.8.8.8
- ip_address: 8.8.8.8
action: member
register: result
failed_when: not result.changed
- name: check the list of forwarders is what we expect now
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
state: present
name: example.com
forwarders:
- 4.4.4.4
- ip_address: 4.4.4.4
port: 8053
action: member
register: result
failed_when: result.changed
- name: ensure forwardzone example.com is absent again
- name: Add a permission for per-forward zone access delegation.
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
name: example.com
state: absent
- name: try to create a new forwarder with action=member
ipadnsforwardzone:
ipaadmin_password: password01
state: present
name: example.com
forwarders:
- 4.4.4.4
permission: yes
action: member
skip_overlap_check: true
register: result
failed_when: result.changed
- name: ensure forwardzone example.com is absent - tidy up
ipadnsforwardzone:
ipaadmin_password: password01
name: example.com
state: absent
- name: try to create a new forwarder is disabled state
ipadnsforwardzone:
ipaadmin_password: password01
state: disabled
name: example.com
forwarders:
- 4.4.4.4
skip_overlap_check: true
register: result
failed_when: not result.changed
- name: enable the forwarder
- name: Add a permission for per-forward zone access delegation, again.
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
name: example.com
state: enabled
permission: yes
action: member
register: result
failed_when: result.changed
- name: Remove a permission for per-forward zone access delegation.
ipadnsforwardzone:
ipaadmin_password: SomeADMINpassword
name: example.com
permission: no
action: member
register: result
failed_when: not result.changed
- name: Remove a permission for per-forward zone access delegation, again.
ipadnsforwardzone:
ipaadmin_password: SomeADMINpassword
name: example.com
permission: no
action: member
register: result
failed_when: result.changed
- name: disable the forwarder
ipadnsforwardzone:
ipaadmin_password: SomeADMINpassword
name: example.com
state: disabled
register: result
failed_when: not result.changed
- name: disable the forwarder again
ipadnsforwardzone:
ipaadmin_password: password01
name: example.com
state: disabled
action: member
register: result
failed_when: not result.changed
- name: ensure it stays disabled
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
name: example.com
state: disabled
register: result
failed_when: result.changed
- name: ensure forwardzone example.com is absent - tidy up
- name: enable the forwarder
ipadnsforwardzone:
ipaadmin_password: password01
ipaadmin_password: SomeADMINpassword
name: example.com
state: enabled
register: result
failed_when: not result.changed
- name: enable the forwarder, again
ipadnsforwardzone:
ipaadmin_password: SomeADMINpassword
name: example.com
state: enabled
register: result
failed_when: result.changed
- name: ensure forwardzone example.com is absent again
ipadnsforwardzone:
ipaadmin_password: SomeADMINpassword
name: example.com
state: absent
- name: try to create a new forwarder with action=member
ipadnsforwardzone:
ipaadmin_password: SomeADMINpassword
state: present
name: example.com
forwarders:
- ip_address: 4.4.4.4
port: 8053
action: member
skip_overlap_check: true
register: result
failed_when: not result.failed or "not found" not in result.msg
- name: try to create a new forwarder with disabled state
ipadnsforwardzone:
ipaadmin_password: SomeADMINpassword
name: example.com
state: disabled
register: result
failed_when: not result.failed or "not found" not in result.msg
- name: Ensure forwardzone is not added without forwarders, with correct message.
ipadnsforwardzone:
ipaadmin_password: SomeADMINpassword
name: newfailzone.com
register: result
failed_when: not result.failed or "No forwarders specified" not in result.msg
- name: ensure forwardzone example.com is absent - tidy up
ipadnsforwardzone:
ipaadmin_password: SomeADMINpassword
name:
- example.com
- newfailzone.com
state: absent

View File

@@ -8,7 +8,7 @@ if [ -z "$master" ]; then
exit 0;
fi
PASSWORD="SomeCApassword"
PASSWORD="SomeCApassword.123"
DBDIR="${master}-nssdb"
PWDFILE="$DBDIR/pwdfile.txt"
NOISE="$DBDIR/noise.txt"

View File

@@ -0,0 +1,133 @@
---
- name: Test group
hosts: ipaserver
become: yes
gather_facts: yes
tasks:
- name: Remove testing groups.
ipagroup:
ipaadmin_password: SomeADMINpassword
name:
- extgroup
- nonposixgroup
- posixgroup
state: absent
- name: Add nonposix group.
ipagroup:
ipaadmin_password: SomeADMINpassword
name: extgroup
nonposix: yes
register: result
failed_when: result.failed or not result.changed
- name: Add nonposix group, again.
ipagroup:
ipaadmin_password: SomeADMINpassword
name: extgroup
nonposix: yes
register: result
failed_when: result.failed or result.changed
- name: Set group to be external
ipagroup:
ipaadmin_password: SomeADMINpassword
name: extgroup
external: yes
register: result
failed_when: result.failed or not result.changed
- name: Set group to be external, again.
ipagroup:
ipaadmin_password: SomeADMINpassword
name: extgroup
external: yes
register: result
failed_when: result.failed or result.changed
- name: Set external group to be non-external.
ipagroup:
ipaadmin_password: SomeADMINpassword
name: extgroup
external: no
register: result
failed_when: not result.failed or "Cannot change `external` status of group" not in result.msg
- name: Set external group to be posix.
ipagroup:
ipaadmin_password: SomeADMINpassword
name: extgroup
posix: yes
register: result
failed_when: not result.failed or "Cannot change `external` status of group" not in result.msg
- name: Add nonposix group.
ipagroup:
ipaadmin_password: SomeADMINpassword
name: posixgroup
nonposix: yes
register: result
failed_when: result.failed or not result.changed
- name: Set group to be posix
ipagroup:
ipaadmin_password: SomeADMINpassword
name: posixgroup
posix: yes
register: result
failed_when: result.failed or not result.changed
- name: Set group to be posix, again.
ipagroup:
ipaadmin_password: SomeADMINpassword
name: posixgroup
posix: yes
register: result
failed_when: result.failed or result.changed
- name: Set posix group to be external.
ipagroup:
ipaadmin_password: SomeADMINpassword
name: posixgroup
external: yes
register: result
failed_when: not result.failed or "Cannot change `POSIX` status of a group" not in result.msg
- name: Set posix group to be non-POSIX.
ipagroup:
ipaadmin_password: SomeADMINpassword
name: posixgroup
posix: no
register: result
failed_when: not result.failed or "Cannot change `POSIX` status of a group" not in result.msg
- name: Set posix group to be non-POSIX.
ipagroup:
ipaadmin_password: SomeADMINpassword
name: posixgroup
nonposix: yes
register: result
failed_when: not result.failed or "Cannot change `POSIX` status of a group" not in result.msg
- name: Add nonposix group.
ipagroup:
ipaadmin_password: SomeADMINpassword
name: nonposixgroup
posix: no
register: result
failed_when: result.failed or not result.changed
- name: Add nonposix group, again.
ipagroup:
ipaadmin_password: SomeADMINpassword
name: nonposixgroup
nonposix: yes
register: result
failed_when: result.failed or result.changed
- name: Remove testing groups.
ipagroup:
ipaadmin_password: SomeADMINpassword
name: extgroup,nonposixgroup,posixgroup
state: absent

View File

@@ -8,7 +8,7 @@
- name: Ensure user manangeruser1 and manageruser2 is absent
ipauser:
ipaadmin_password: SomeADMINpassword
name: manageruser1,manageruser2
name: manageruser1,manageruser2,unknown_user
state: absent
- name: Ensure group testgroup, managergroup1 and managergroup2 are absent
@@ -185,6 +185,15 @@
register: result
failed_when: not result.changed
- name: Ensure unknown membermanager_user member failure
ipagroup:
ipaadmin_password: SomeADMINpassword
name: testgroup
membermanager_user: unknown_user
action: member
register: result
failed_when: result.changed or "no such entry" not in result.msg
- name: Ensure group testgroup, managergroup1 and managergroup2 are absent
ipagroup:
ipaadmin_password: SomeADMINpassword

View File

@@ -96,3 +96,18 @@
state: absent
register: result
failed_when: result.changed
- name: Duplicate names in hosts failure test
ipahost:
ipaadmin_password: SomeADMINpassword
hosts:
- name: "{{ host1_fqdn }}"
force: yes
- name: "{{ host2_fqdn }}"
force: yes
- name: "{{ host3_fqdn }}"
force: yes
- name: "{{ host3_fqdn }}"
force: yes
register: result
failed_when: result.changed or "is used more than once" not in result.msg

View File

@@ -15,7 +15,7 @@
- name: Ensure user manangeruser1 and manageruser2 is absent
ipauser:
ipaadmin_password: SomeADMINpassword
name: manageruser1,manageruser2
name: manageruser1,manageruser2,unknown_user
state: absent
- name: Ensure group managergroup1 and managergroup2 are absent
@@ -200,6 +200,15 @@
register: result
failed_when: not result.changed
- name: Ensure unknown membermanager_user member failure
ipahostgroup:
ipaadmin_password: SomeADMINpassword
name: testhostgroup
membermanager_user: unknown_user
action: member
register: result
failed_when: result.changed or "no such entry" not in result.msg
- name: Ensure host-group testhostgroup is absent
ipahostgroup:
ipaadmin_password: SomeADMINpassword

View File

@@ -0,0 +1,38 @@
---
- name: Ensure test user is absent.
ipauser:
ipaadmin_password: SomeADMINpassword
name: user01
state: absent
- name: Ensure test group is absent.
ipagroup:
ipaadmin_password: SomeADMINpassword
name: group01
state: absent
- name: Ensure test hostgroup is absent.
ipahostgroup:
ipaadmin_password: SomeADMINpassword
name: hostgroup01
state: absent
- name: Ensure test host is absent.
ipahost:
ipaadmin_password: SomeADMINpassword
name: "{{ host1_fqdn }}"
state: absent
- name: Ensure test service is absent.
ipaservice:
ipaadmin_password: SomeADMINpassword
name: "service01/{{ host1_fqdn }}"
state: absent
- name: Ensure test roles are absent.
iparole:
ipaadmin_password: SomeADMINpassword
name:
- renamerole
- testrole
state: absent

14
tests/role/env_facts.yml Normal file
View File

@@ -0,0 +1,14 @@
---
- name: Get Domain from server name
set_fact:
ipaserver_domain: "{{ ansible_fqdn | join ('.') }}"
when: ipaserver_domain is not defined
- name: Set fact for realm name
set_fact:
ipaserver_realm: "{{ ipaserver_domain }} | upper"
when: ipaserver_domain is not defined
- name: Create FQDN for host01
set_fact:
host1_fqdn: "host01.{{ ipaserver_domain }}"

34
tests/role/env_setup.yml Normal file
View File

@@ -0,0 +1,34 @@
---
- name: Cleanup environment.
import_tasks: env_cleanup.yml
- name: Ensure test user is present.
ipauser:
ipaadmin_password: SomeADMINpassword
name: user01
first: First
last: Last
- name: Ensure test group is present.
ipagroup:
ipaadmin_password: SomeADMINpassword
name: group01
- name: Ensure test host is present.
ipahost:
ipaadmin_password: SomeADMINpassword
name: "{{ host1_fqdn }}"
force: yes
- name: Ensure test hostgroup is present.
ipahostgroup:
ipaadmin_password: SomeADMINpassword
name: hostgroup01
host:
- "{{ host1_fqdn }}"
- name: Ensure test service is present.
ipaservice:
ipaadmin_password: SomeADMINpassword
name: "service01/{{ host1_fqdn }}"
force: yes

388
tests/role/test_role.yml Normal file
View File

@@ -0,0 +1,388 @@
---
- name: Test role module
hosts: ipaserver
become: yes
gather_facts: yes
tasks:
- name: Set environment facts.
import_tasks: env_facts.yml
- name: Setup environment.
import_tasks: env_setup.yml
# tests
- name: Ensure role is present.
iparole:
ipaadmin_password: SomeADMINpassword
name: renamerole
description: A role in IPA.
register: result
failed_when: not result.changed
- name: Ensure role is present, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: renamerole
description: A role in IPA.
register: result
failed_when: result.changed
- name: Rename role.
iparole:
ipaadmin_password: SomeADMINpassword
name: renamerole
rename: testrole
register: result
failed_when: not result.changed
- name: Rename role, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: renamerole
rename: testrole
register: result
failed_when: result.changed
- name: Ensure role has member has privileges.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
privilege:
- DNS Servers
- Host Administrators
action: member
register: result
failed_when: not result.changed
- name: Ensure role has member has privileges, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
privilege:
- DNS Servers
- Host Administrators
action: member
register: result
failed_when: result.changed
- name: Ensure role has less privileges.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
privilege:
- Host Administrators
action: member
state: absent
register: result
failed_when: not result.changed
- name: Ensure role has less privileges, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
privilege:
- Host Administrators
action: member
state: absent
register: result
failed_when: result.changed
- name: Ensure role has member has privileges restored.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
privilege:
- DNS Servers
- Host Administrators
action: member
register: result
failed_when: not result.changed
- name: Ensure role has member has privileges restored, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
privilege:
- DNS Servers
- Host Administrators
action: member
register: result
failed_when: result.changed
- name: Ensure role member privileges are absent.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
privilege:
- DNS Servers
- Host Administrators
action: member
state: absent
register: result
failed_when: not result.changed
- name: Ensure role member privileges are absent, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
privilege:
- DNS Servers
- Host Administrators
action: member
state: absent
register: result
failed_when: result.changed
- name: Ensure invalid privileged is not assigned to role.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
privilege: Invalid Privilege
action: member
register: result
failed_when: not result.failed or "privilege not found" not in result.msg
- name: Ensure role has member user present.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- user01
action: member
register: result
failed_when: not result.changed
- name: Ensure role has member user present, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- user01
action: member
register: result
failed_when: result.changed
- name: Ensure role has member user absent.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- user01
action: member
state: absent
register: result
failed_when: not result.changed
- name: Ensure role has member user absent, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- user01
action: member
state: absent
register: result
failed_when: result.changed
- name: Ensure role has member group present.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
group:
- group01
action: member
register: result
failed_when: not result.changed
- name: Ensure role has member group present, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
group:
- group01
action: member
register: result
failed_when: result.changed
- name: Ensure role has member group absent.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
group:
- group01
action: member
state: absent
register: result
failed_when: not result.changed
- name: Ensure role has member group absent, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
group:
- group01
action: member
state: absent
register: result
failed_when: result.changed
- name: Ensure role has member host present.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
host:
- "{{ host1_fqdn }}"
action: member
register: result
failed_when: not result.changed
- name: Ensure role has member host present, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
host:
- "{{ host1_fqdn }}"
action: member
register: result
failed_when: result.changed
- name: Ensure role has member host absent.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
host:
- "{{ host1_fqdn }}"
action: member
state: absent
register: result
failed_when: not result.changed
- name: Ensure role has member host absent, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
host:
- "{{ host1_fqdn }}"
action: member
state: absent
register: result
failed_when: result.changed
- name: Ensure role has member hostgroup present.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
hostgroup:
- hostgroup01
action: member
register: result
failed_when: not result.changed
- name: Ensure role has member hostgroup present, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
hostgroup:
- hostgroup01
action: member
register: result
failed_when: result.changed
- name: Ensure role has member hostgroup absent.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
hostgroup:
- hostgroup01
action: member
state: absent
register: result
failed_when: not result.changed
- name: Ensure role has member hostgroup absent, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
hostgroup:
- hostgroup01
action: member
state: absent
register: result
failed_when: result.changed
- name: Ensure role is absent.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
state: absent
register: result
failed_when: not result.changed
- name: Ensure role is absent, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
state: absent
register: result
failed_when: result.changed
- name: Ensure role with members is present.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- user01
group:
- group01
host:
- "{{ host1_fqdn }}"
hostgroup:
- hostgroup01
privilege:
- Group Administrators
- User Administrators
service:
- "service01/{{ host1_fqdn }}"
register: result
failed_when: not result.changed
- name: Ensure role with members is present, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
user:
- user01
group:
- group01
host:
- "{{ host1_fqdn }}"
hostgroup:
- hostgroup01
privilege:
- Group Administrators
- User Administrators
service:
- "service01/{{ host1_fqdn }}"
register: result
failed_when: result.changed
- name: Ensure role is absent.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
state: absent
register: result
failed_when: not result.changed
- name: Ensure role is absent, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
state: absent
register: result
failed_when: result.changed
# cleanup
- name: Cleanup environment.
include_tasks: env_cleanup.yml

View File

@@ -0,0 +1,95 @@
---
- name: Test service member in role module.
hosts: ipaserver
become: yes
gather_facts: yes
tasks:
- name: Set environment facts.
import_tasks: env_facts.yml
- name: Setup environment.
import_tasks: env_setup.yml
# tests
- name: Ensure role with member service is present.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
service:
- "service01/{{ host1_fqdn }}"
register: result
failed_when: not result.changed
- name: Ensure role with member service is present, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
service:
- "service01/{{ host1_fqdn }}"
action: member
register: result
failed_when: result.changed
- name: Ensure role has member service absent.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
service:
- "service01/{{ host1_fqdn }}"
action: member
state: absent
register: result
failed_when: not result.changed
- name: Ensure role has member service absent, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
service:
- "service01/{{ host1_fqdn }}"
action: member
state: absent
register: result
failed_when: result.changed
- name: Ensure role has member service with principal name.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
service:
- "service01/{{ host1_fqdn }}@{{ ipaserver_realm }}"
action: member
register: result
failed_when: not result.changed
- name: Ensure role has member service with principal name, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
service:
- "service01/{{ host1_fqdn }}@{{ ipaserver_realm }}"
action: member
register: result
failed_when: result.changed
- name: Ensure role is absent.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
state: absent
register: result
failed_when: not result.changed
- name: Ensure role is absent, again.
iparole:
ipaadmin_password: SomeADMINpassword
name: testrole
state: absent
register: result
failed_when: result.changed
# cleanup
- name: Cleanup environment.
include_tasks: env_cleanup.yml

View File

@@ -0,0 +1,83 @@
# Due to not having some Ansible modules for IPA, some tasks are executed
# in this playbook using the `shell` module, as a Kerberos tikcket is needed
# for these tasks.
# The Kerberos cache is cleaned in the end, so you might need to `kinit` on
# the testing target after running this playbook.
---
- name: Playbook to manage IPA service.
hosts: ipaserver
become: yes
gather_facts: yes
environment:
KRB5CCNAME: test_service_disable_ccache
tasks:
- name: Get Kerberos ticket for `admin`.
shell: echo SomeADMINpassword | kinit -c ${KRB5CCNAME} admin
- name: Ensure service is absent
ipaservice:
ipaadmin_password: SomeADMINpassword
name: "mysvc1/{{ ansible_fqdn }}"
state: absent
- name: Ensure service is present
ipaservice:
ipaadmin_password: SomeADMINpassword
name: "mysvc1/{{ ansible_fqdn }}"
certificate:
- MIIC/zCCAeegAwIBAgIUMNHIbn+hhrOVew/2WbkteisV29QwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEdGVzdDAeFw0yMDAyMDQxNDQxMDhaFw0zMDAyMDExNDQxMDhaMA8xDTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+XVVGFYpHVkcDfVnNInE1Y/pFciegdzqTjMwUWlRL4Zt3u96GhaMLRbtk+OfEkzLUAhWBOwEraELJzMLJOMvjYF3C+TiGO7dStFLikZmccuSsSIXjnzIPwBXa8KvgRVRyGLoVvGbLJvmjfMXp0nIToTx/i74KF9S++WEes9H5ErJ99CDhLKFgq0amnvsgparYXhypHaRLnikn0vQINt55YoEd1s4KrvEcD2VdZkIMPbLRu2zFvMprF3cjQQG4LT9ggfEXNIPZ1nQWAnAsu7OJEkNF+E4Mkmpcxj9aGUVt5bsq1D+Tzj3GsidSX0nSNcZ2JltXRnL/5v63g5cZyE+nAgMBAAGjUzBRMB0GA1UdDgQWBBRV0j7JYukuH/r/t9+QeNlRLXDlEDAfBgNVHSMEGDAWgBRV0j7JYukuH/r/t9+QeNlRLXDlEDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCgVy1+1kNwHs5y1Zp0WjMWGCJC6/zw7FDG4OW5r2GJiCXZYdJ0UonY9ZtoVLJPrp2/DAv1m5DtnDhBYqicuPgLzEkOS1KdTi20Otm/J4yxLLrZC5W4x0XOeSVPXOJuQWfwQ5pPvKkn6WxYUYkGwIt1OH2nSMngkbami3CbSmKZOCpgQIiSlQeDJ8oGjWFMLDymYSHoVOIXHwNoooyEiaio3693l6noobyGv49zyCVLVR1DC7i6RJ186ql0av+D4vPoiF5mX7+sKC2E8xEj9uKQ5GTWRh59VnRBVC/SiMJ/H78tJnBAvoBwXxSEvj8Z3Kjm/BQqZfv4IBsA5yqV7MVq
force: no
register: result
failed_when: not result.changed
- name: Obtain keytab
shell: ipa-getkeytab -s "{{ ansible_fqdn }}" -p "mysvc1/{{ ansible_fqdn }}" -k mysvc1.keytab
- name: Verify keytab
shell: ipa service-find "mysvc1/{{ ansible_fqdn }}"
register: result
failed_when: result.failed or result.stdout | regex_search(" Keytab. true")
- name: Ensure service is disabled
ipaservice:
ipaadmin_password: SomeADMINpassword
name: "mysvc1/{{ ansible_fqdn }}"
state: disabled
register: result
failed_when: not result.changed
- name: Verify keytab
shell: ipa service-find "mysvc1/{{ ansible_fqdn }}"
register: result
failed_when: result.failed or result.stdout | regex_search(" Keytab. true")
- name: Obtain keytab
shell: ipa-getkeytab -s "{{ ansible_fqdn }}" -p "mysvc1/{{ ansible_fqdn }}" -k mysvc1.keytab
- name: Verify keytab
shell: ipa service-find "mysvc1/{{ ansible_fqdn }}"
register: result
failed_when: result.failed or result.stdout | regex_search(" Keytab. true")
- name: Ensure service is disabled
ipaservice:
ipaadmin_password: SomeADMINpassword
name: "mysvc1/{{ ansible_fqdn }}"
state: disabled
register: result
failed_when: not result.changed
- name: Verify keytab
shell: ipa service-find "mysvc1/{{ ansible_fqdn }}"
register: result
failed_when: result.failed or result.stdout | regex_search(" Keytab. true")
- name: Ensure service is absent
ipaservice:
ipaadmin_password: SomeADMINpassword
name: "mysvc1/{{ ansible_fqdn }}"
- name: Destroy Kerberos tickets.
shell: kdestroy -A -q -c ${KRB5CCNAME}

View File

@@ -85,6 +85,25 @@
register: result
failed_when: result.changed
- name: Duplicate names in users failure test
ipauser:
ipaadmin_password: SomeADMINpassword
users:
- name: user1
givenname: user1
last: Last
- name: user2
first: user2
last: Last
- name: user3
first: user3
last: Last
- name: user3
first: user3
last: Last
register: result
failed_when: result.changed or "is used more than once" not in result.msg
- name: Remove test users
ipauser:
ipaadmin_password: SomeADMINpassword

View File

@@ -178,6 +178,61 @@
register: result
failed_when: result.data != 'Hello World.' or result.changed
- name: Change vault password.
ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
password: SomeVAULTpassword
new_password: SomeNEWpassword
register: result
failed_when: not result.changed
- name: Retrieve data from symmetric vault, with wrong password.
ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
password: SomeVAULTpassword
state: retrieved
register: result
failed_when: not result.failed or "Invalid credentials" not in result.msg
- name: Change vault password, with wrong `old_password`.
ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
password: SomeVAULTpassword
new_password: SomeNEWpassword
register: result
failed_when: not result.failed or "Invalid credentials" not in result.msg
- name: Retrieve data from symmetric vault, with new password.
ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
password: SomeNEWpassword
state: retrieved
register: result
failed_when: result.data != 'Hello World.' or result.changed
- name: Try to add vault with multiple passwords.
ipavault:
ipaadmin_password: SomeADMINpassword
name: inexistentvault
password: SomeVAULTpassword
password_file: "{{ ansible_env.HOME }}/password.txt"
register: result
failed_when: not result.failed or "parameters are mutually exclusive" not in result.msg
- name: Try to add vault with multiple new passwords.
ipavault:
ipaadmin_password: SomeADMINpassword
name: inexistentvault
password: SomeVAULTpassword
new_password: SomeVAULTpassword
new_password_file: "{{ ansible_env.HOME }}/password.txt"
register: result
failed_when: not result.failed or "parameters are mutually exclusive" not in result.msg
- name: Ensure symmetric vault is absent
ipavault:
ipaadmin_password: SomeADMINpassword
@@ -194,5 +249,14 @@
register: result
failed_when: result.changed
- name: Try to change password of inexistent vault.
ipavault:
ipaadmin_password: SomeADMINpassword
name: inexistentvault
password: SomeVAULTpassword
new_password: SomeNEWpassword
register: result
failed_when: not result.failed or "Cannot modify password of inexistent vault" not in result.msg
- name: Cleanup testing environment.
import_tasks: env_cleanup.yml