mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-03-30 23:33:06 +00:00
Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6a95e7649 | ||
|
|
6b2b9ea787 | ||
|
|
3487efcf9f | ||
|
|
695ad6307d | ||
|
|
cf54d139c2 | ||
|
|
ae471de0bd | ||
|
|
927329326c | ||
|
|
26444b42b0 | ||
|
|
1d196bca67 | ||
|
|
d73b6e3920 | ||
|
|
b80d6b061d | ||
|
|
5a290565f3 | ||
|
|
40048c781a | ||
|
|
f7ca62e52b | ||
|
|
da87f1648e | ||
|
|
0bcb4eaf0f | ||
|
|
0456424821 | ||
|
|
ff03b3153b | ||
|
|
0abfe8ab90 | ||
|
|
89ba344a0b | ||
|
|
c49fa4e899 | ||
|
|
66936d1afa | ||
|
|
c26b9c27b1 | ||
|
|
ad139256df | ||
|
|
d3b0fcebda | ||
|
|
19b117a71c | ||
|
|
02705c9e47 | ||
|
|
10e7b4094d | ||
|
|
0acf576d99 | ||
|
|
fd7eb4f85f | ||
|
|
2e7df27fe3 | ||
|
|
561cd4fb98 | ||
|
|
4ad1033685 | ||
|
|
3981dafd7b | ||
|
|
1cf251baf8 | ||
|
|
c9210ca2d1 | ||
|
|
d7a3b7533c | ||
|
|
46caacd0ae | ||
|
|
5406c60157 | ||
|
|
341078ed5d | ||
|
|
95d90ef31f | ||
|
|
cf0b710047 | ||
|
|
bf9024f79f | ||
|
|
f44e33c6b3 | ||
|
|
6b5f034912 | ||
|
|
bf0b1ed75f | ||
|
|
a052160cc9 | ||
|
|
851c6a9f39 | ||
|
|
59cb7eebd9 | ||
|
|
55e86c924f | ||
|
|
56b1368441 | ||
|
|
4ada6e1d24 | ||
|
|
b48b81a030 | ||
|
|
09fefbb2d4 | ||
|
|
8e6d433df8 | ||
|
|
578d08c796 | ||
|
|
2408a9b7c6 | ||
|
|
0372fec0e3 | ||
|
|
07d7e2fa86 | ||
|
|
4221213f1e | ||
|
|
05a1aaed53 | ||
|
|
5b53862871 | ||
|
|
7ca6c15fee | ||
|
|
44af47d93a | ||
|
|
89bc267d98 | ||
|
|
583d46b020 | ||
|
|
315f93c09a | ||
|
|
91094ce4d4 | ||
|
|
848959ca6a | ||
|
|
c236fe3d62 | ||
|
|
bf15351c07 | ||
|
|
ac61f597d5 | ||
|
|
fdcdad2c7e | ||
|
|
6a69bbeafb | ||
|
|
571cc210b5 | ||
|
|
a432c3ff50 | ||
|
|
14d4502019 | ||
|
|
b0a067d5d5 | ||
|
|
f1c733d867 | ||
|
|
e36961f35e | ||
|
|
e8317b281a | ||
|
|
60c8be19a5 | ||
|
|
1f1762bd25 | ||
|
|
2b084e6d15 | ||
|
|
b3d5b32e31 | ||
|
|
67261c3dcd | ||
|
|
84d8fc0cf3 | ||
|
|
791c4703b1 | ||
|
|
457050c6ac | ||
|
|
703ee1c9cd | ||
|
|
efbc50b257 | ||
|
|
cf1fe72616 | ||
|
|
6b0cf1e777 | ||
|
|
0677af0714 | ||
|
|
5d7c0ec3d9 | ||
|
|
5643cfc20d | ||
|
|
4155f2f3ac | ||
|
|
871cce5258 | ||
|
|
5e734e847e | ||
|
|
9d348cb368 |
149
README-config.md
Normal file
149
README-config.md
Normal file
@@ -0,0 +1,149 @@
|
||||
Config module
|
||||
===========
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The config module allows the setting of global config parameters within IPA. If no parameters are specified it returns the list of all current parameters.
|
||||
|
||||
The config module is as compatible as possible to the Ansible upstream `ipa_config` module, but adds many additional parameters
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
* IPA server configuration management
|
||||
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipaconfig 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 read config options:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to handle global config options
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: return current values of the global configuration options
|
||||
ipaconfig:
|
||||
ipaadmin_password: password
|
||||
register: result
|
||||
- name: display default login shell
|
||||
debug:
|
||||
msg: '{{result.config.defaultlogin }}'
|
||||
|
||||
- name: ensure defaultloginshell and maxusernamelength are set as required
|
||||
ipaconfig:
|
||||
ipaadmin_password: password
|
||||
defaultlogin: /bin/bash
|
||||
maxusername: 64
|
||||
```
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to ensure some config options are set
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: set defaultlogin and maxusername
|
||||
ipaconfig:
|
||||
ipaadmin_password: password
|
||||
defaultlogin: /bin/bash
|
||||
maxusername: 64
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipauser
|
||||
-------
|
||||
|
||||
**General Variables:**
|
||||
|
||||
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
|
||||
`maxusername` \| `ipamaxusernamelength` | Set the maximum username length (1 to 255) | no
|
||||
`maxhostname` \| `ipamaxhostnamelength` | Set the maximum hostname length between 64-255 | no
|
||||
`homedirectory` \| `ipahomesrootdir` | Set the default location of home directories | no
|
||||
`defaultshell` \| `ipadefaultloginshell` | Set the default shell for new users | no
|
||||
`defaultgroup` \| `ipadefaultprimarygroup` | Set the default group for new users | no
|
||||
`emaildomain`\| `ipadefaultemaildomain` | Set the default e-mail domain | false
|
||||
`searchtimelimit` \| `ipasearchtimelimit` | Set maximum amount of time (seconds) for a search -1 to 2147483647 (-1 or 0 is unlimited) | no
|
||||
`searchrecordslimit` \| `ipasearchrecordslimit` | Set maximum number of records to search -1 to 2147483647 (-1 or 0 is unlimited) | no
|
||||
`usersearch` \| `ipausersearchfields` | Set list of fields to search when searching for users | no
|
||||
`groupsearch` \| `ipagroupsearchfields` | Set list of fields to search in when searching for groups | no
|
||||
`enable_migration` \| `ipamigrationenabled` | Enable migration mode (choices: True, False ) | no
|
||||
`groupobjectclasses` \| `ipagroupobjectclasses` | Set default group objectclasses (list) | no
|
||||
`userobjectclasses` \| `ipauserobjectclasses` | Set default user objectclasses (list) | no
|
||||
`pwdexpnotify` \| `ipapwdexpadvnotify` | Set number of days's notice of impending password expiration (0 to 2147483647) | no
|
||||
`configstring` \| `ipaconfigstring` | Set extra hashes to generate in password plug-in (choices:`AllowNThash`, `KDC:Disable Last Success`, `KDC:Disable Lockout`, `KDC:Disable Default Preauth for SPNs`). Use `""` to clear this variable. | no
|
||||
`selinuxusermaporder` \| `ipaselinuxusermaporder`| Set ordered list in increasing priority of SELinux users | no
|
||||
`selinuxusermapdefault`\| `ipaselinuxusermapdefault` | Set default SELinux user when no match is found in SELinux map rule | no
|
||||
`pac_type` \| `ipakrbauthzdata` | set default types of PAC supported for services (choices: `MS-PAC`, `PAD`, `nfs:NONE`). Use `""` to clear this variable. | no
|
||||
`user_auth_type` \| `ipauserauthtype` | set default types of supported user authentication (choices: `password`, `radius`, `otp`, `disabled`). Use `""` to clear this variable. | no
|
||||
`domain_resolution_order` \| `ipadomainresolutionorder` | Set list of domains used for short name qualification | no
|
||||
`ca_renewal_master_server` \| `ipacarenewalmasterserver`| Renewal master for IPA certificate authority. | no
|
||||
|
||||
|
||||
Return Values
|
||||
=============
|
||||
|
||||
Variable | Description | Returned When
|
||||
-------- | ----------- | -------------
|
||||
`config` | config dict <br />Fields: | No values to configure are specified
|
||||
| `maxusername` |
|
||||
| `maxhostname` |
|
||||
| `homedirectory` |
|
||||
| `defaultshell` |
|
||||
| `defaultgroup` |
|
||||
| `emaildomain` |
|
||||
| `searchtimelimit` |
|
||||
| `searchrecordslimit` |
|
||||
| `usersearch` |
|
||||
| `groupsearch` |
|
||||
| `enable_migration` |
|
||||
| `groupobjectclasses` |
|
||||
| `userobjectclasses` |
|
||||
| `pwdexpnotify` |
|
||||
| `configstring` |
|
||||
| `selinuxusermapdefault` |
|
||||
| `selinuxusermaporder` |
|
||||
| `pac_type` |
|
||||
| `user_auth_type` |
|
||||
| `domain_resolution_order` |
|
||||
| `ca_renewal_master_server` |
|
||||
|
||||
All returned fields take the same form as their namesake input parameters
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Chris Procter
|
||||
357
README-dnsrecord.md
Normal file
357
README-dnsrecord.md
Normal file
@@ -0,0 +1,357 @@
|
||||
DNSRecord module
|
||||
================
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The dnsrecord module allows management of DNS records and is as compatible as possible with the Ansible upstream `ipa_dnsrecord` module, but provide some other features like multiple record management in one execution and support for more DNS record types.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
* DNS record management.
|
||||
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipadnsrecord module.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.example.com
|
||||
```
|
||||
|
||||
Example playbook to ensure an AAAA record is present:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: example.com
|
||||
record_type: 'AAAA'
|
||||
record_value: '::1'
|
||||
```
|
||||
|
||||
Example playbook to ensure an AAAA record is present, with a TTL of 300:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: example.com
|
||||
record_type: 'AAAA'
|
||||
record_value: '::1'
|
||||
record_ttl: 300
|
||||
```
|
||||
|
||||
Example playbook to ensure an AAAA record is present, with a reverse PTR record:
|
||||
```yaml
|
||||
---
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host02
|
||||
zone_name: example.com
|
||||
record_type: 'AAAA'
|
||||
record_value: 'fd00::0002'
|
||||
create_reverse: yes
|
||||
```
|
||||
|
||||
Example playbook to ensure a LOC record is present, given its individual attributes:
|
||||
```yaml
|
||||
---
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: example.com
|
||||
name: host03
|
||||
loc_lat_deg: 52
|
||||
loc_lat_min: 22
|
||||
loc_lat_sec: 23.000
|
||||
loc_lat_dir: N
|
||||
loc_lon_deg: 4
|
||||
loc_lon_min: 53
|
||||
loc_lon_sec: 32.00
|
||||
loc_lon_dir: E
|
||||
loc_altitude: -2.00
|
||||
loc_size: 1.00
|
||||
loc_h_precision: 10000
|
||||
loc_v_precision: 10
|
||||
```
|
||||
|
||||
Example playbook to ensure multiple DNS records are present:
|
||||
|
||||
```yaml
|
||||
---
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
records:
|
||||
- name: host02
|
||||
zone_name: example.com
|
||||
record_type: A
|
||||
record_value:
|
||||
- "{{ ipv4_prefix }}.112"
|
||||
- "{{ ipv4_prefix }}.122"
|
||||
- name: host02
|
||||
zone_name: example.com
|
||||
record_type: AAAA
|
||||
record_value: ::1
|
||||
```
|
||||
|
||||
Example playbook to ensure multiple CNAME records are present:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Ensure that 'host03' and 'host04' have CNAME records.
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: example.com
|
||||
records:
|
||||
- name: host03
|
||||
cname_hostname: host03.example.com
|
||||
- name: host04
|
||||
cname_hostname: host04.example.com
|
||||
```
|
||||
|
||||
Example playbook to ensure NS record is absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: example.com
|
||||
name: host04
|
||||
ns_hostname: host04
|
||||
state: absent
|
||||
```
|
||||
|
||||
Example playbook to ensure LOC record is present, with fields:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: example.com
|
||||
name: host04
|
||||
loc_lat_deg: 52
|
||||
loc_lat_min: 22
|
||||
loc_lat_sec: 23.000
|
||||
loc_lat_dir: N
|
||||
loc_lon_deg: 4
|
||||
loc_lon_min: 53
|
||||
loc_lon_sec: 32.000
|
||||
loc_lon_dir: E
|
||||
loc_altitude: -2.00
|
||||
loc_size: 0.00
|
||||
loc_h_precision: 10000
|
||||
loc_v_precision: 10
|
||||
```
|
||||
|
||||
Change value of an existing LOC record:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: example.com
|
||||
name: host04
|
||||
loc_size: 1.00
|
||||
loc_rec: 52 22 23 N 4 53 32 E -2 0 10000 10
|
||||
```
|
||||
|
||||
Example playbook to ensure multiple A records are present:
|
||||
|
||||
```yaml
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: example.com
|
||||
name: host04
|
||||
a_rec:
|
||||
- 192.168.122.221
|
||||
- 192.168.122.222
|
||||
- 192.168.122.223
|
||||
- 192.168.122.224
|
||||
```
|
||||
|
||||
Example playbook to ensure A and AAAA records are present, with reverse records (PTR):
|
||||
```yaml
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: example.com
|
||||
name: host01
|
||||
a_rec:
|
||||
- 192.168.122.221
|
||||
- 192.168.122.222
|
||||
aaaa_rec:
|
||||
- fd00:;0001
|
||||
- fd00::0002
|
||||
create_reverse: yes
|
||||
```
|
||||
|
||||
Example playbook to ensure multiple A and AAAA records are present, but only A records have reverse records:
|
||||
```yaml
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: example.com
|
||||
name: host01
|
||||
a_ip_address: 192.168.122.221
|
||||
aaaa_ip_address: fd00::0001
|
||||
a_create_reverse: yes
|
||||
```
|
||||
|
||||
Example playbook to ensure multiple DNS records are absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: example.com
|
||||
records:
|
||||
- name: host01
|
||||
del_all: yes
|
||||
- name: host02
|
||||
del_all: yes
|
||||
- name: host03
|
||||
del_all: yes
|
||||
- name: host04
|
||||
del_all: yes
|
||||
- name: _ftp._tcp
|
||||
del_all: yes
|
||||
- name: _sip._udp
|
||||
del_all: yes
|
||||
state: absent
|
||||
```
|
||||
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipadnsrecord
|
||||
------------
|
||||
|
||||
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
|
||||
`zone_name` \| `dnszone` | The DNS zone name to which DNS record needs to be managed. You can use one global zone name for multiple records. | no
|
||||
required: true
|
||||
`records` | The list of dns records dicts. Each `records` dict entry can contain **record variables**. | no
|
||||
| **Record variables** | no
|
||||
**Record variables** | Used when defining a single record. | no
|
||||
`state` | The state to ensure. It can be one of `present` or `absent`, and defaults to `present`. | yes
|
||||
|
||||
|
||||
**Record Variables:**
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`zone_name` \| `dnszone` | The DNS zone name to which DNS record needs to be managed. You can use one global zone name for multiple records. When used on a `records` dict, overrides the global `zone_name`. | yes
|
||||
`name` \| `record_name` | The DNS record name to manage. | yes
|
||||
`record_type` | The type of DNS record. Supported values are `A`, `AAAA`, `A6`, `AFSDB`, `CERT`, `CNAME`, `DLV`, `DNAME`, `DS`, `KX`, `LOC`, `MX`, `NAPTR`, `NS`, `PTR`, `SRV`, `SSHFP`, `TLSA`, `TXT`, `URI`, and defaults to `A`. | no
|
||||
`record_value` | Manage DNS record name with this values. | no
|
||||
`record_ttl` | Set the TTL for the record. (int) | no
|
||||
`del_all` | Delete all associated records. (bool) | no
|
||||
`a_rec` \| `a_record` | Raw A record. | no
|
||||
`aaaa_rec` \| `aaaa_record` | Raw AAAA record. | no
|
||||
`a6_rec` \| `a6_record` | Raw A6 record data. | no
|
||||
`afsdb_rec` \| `afsdb_record` | Raw AFSDB record. | no
|
||||
`cert_rec` \| `cert_record` | Raw CERT record. | no
|
||||
`cname_rec` \| `cname_record` | Raw CNAME record. | no
|
||||
`dlv_rec` \| `dlv_record` | Raw DLV record. | no
|
||||
`dname_rec` \| `dname_record` | Raw DNAM record. | no
|
||||
`ds_rec` \| `ds_record` | Raw DS record. | no
|
||||
`kx_rec` \| `kx_record` | Raw KX record. | no
|
||||
`loc_rec` \| `loc_record` | Raw LOC record. | no
|
||||
`mx_rec` \| `mx_record` | Raw MX record. | no
|
||||
`naptr_rec` \| `naptr_record` | Raw NAPTR record. | no
|
||||
`ns_rec` \| `ns_record` | Raw NS record. | no
|
||||
`ptr_rec` \| `ptr_record` | Raw PTR record. | no
|
||||
`srv_rec` \| `srv_record` | Raw SRV record. | no
|
||||
`sshfp_rec` \| `sshfp_record` | Raw SSHFP record. | no
|
||||
`tlsa_rec` \| `tlsa_record` | Raw TLSA record. | no
|
||||
`txt_rec` \| `txt_record` | Raw TXT record. | no
|
||||
`uri_rec` \| `uri_record` | Raw URI record. | no
|
||||
`ip_address` | IP adress for A or AAAA records. Set `record_type` to `A` or `AAAA`. | no
|
||||
`create_reverse` \| `reverse` | Create reverse records for `A` and `AAAA` record types. There is no equivalent to remove reverse records. (bool) | no
|
||||
`a_ip_address` | IP adress for A records. Set `record_type` to `A`. | no
|
||||
`a_create_reverse` | Create reverse records only for `A` records. There is no equivalent to remove reverse records. (bool) | no
|
||||
`aaaa_ip_address` | IP adress for AAAA records. Set `record_type` `AAAA`. | no
|
||||
`aaaa_create_reverse` | Create reverse records only for `AAAA` record types. There is no equivalent to remove reverse records. (bool) | no
|
||||
`a6_data` | A6 record. Set `record_type` to `A6`. | no
|
||||
`afsdb_subtype` | AFSDB Subtype. Set `record_type` to `AFSDB`. (int) | no
|
||||
`afsdb_hostname` | AFSDB Hostname. Set `record_type` to `AFSDB`. | no
|
||||
`cert_type` | CERT Certificate Type. Set `record_type` to `CERT`. (int) | no
|
||||
`cert_key_tag` | CERT Key Tag. Set `record_type` to `CERT`. (int) | no
|
||||
`cert_algorithm` | CERT Algorithm. Set `record_type` to `CERT`. (int) | no
|
||||
`cert_certificate_or_crl` | CERT Certificate or Certificate Revocation List (CRL). Set `record_type` to `CERT`. | no
|
||||
`cname_hostname` | A hostname which this alias hostname points to. Set `record_type` to `CNAME`. | no
|
||||
`dlv_key_tag` | DS Key Tag. Set `record_type` to `DLV`. (int) | no
|
||||
`dlv_algorithm` | DLV Algorithm. Set `record_type` to `DLV`. (int) | no
|
||||
`dlv_digest_type` | DLV Digest Type. Set `record_type` to `DLV`. (int) | no
|
||||
`dlv_digest` | DLV Digest. Set `record_type` to `DLV`. | no
|
||||
`dname_target` | DNAME Target. Set `record_type` to `DNAME`. | no
|
||||
`ds_key_tag` | DS Key Tag. Set `record_type` to `DS`. (int) | no
|
||||
`ds_algorithm` | DS Algorithm. Set `record_type` to `DS`. (int) | no
|
||||
`ds_digest_type` | DS Digest Type. Set `record_type` to `DS`. (int) | no
|
||||
`ds_digest` | DS Digest. Set `record_type` to `DS`. | no
|
||||
`kx_preference` | Preference given to this exchanger. Lower values are more preferred. Set `record_type` to `KX`. (int) | no
|
||||
`kx_exchanger` | A host willing to act as a key exchanger. Set `record_type` to `KX`. | no
|
||||
`loc_lat_deg` | LOC Degrees Latitude. Set `record_type` to `LOC`. (int) | no
|
||||
`loc_lat_min` | LOC Minutes Latitude. Set `record_type` to `LOC`. (int) | no
|
||||
`loc_lat_sec` | LOC Seconds Latitude. Set `record_type` to `LOC`. (float) | no
|
||||
`loc_lat_dir` | LOC Direction Latitude. Valid values are `N` or `S`. Set `record_type` to `LOC`. (int) | no
|
||||
`loc_lon_deg` | LOC Degrees Longitude. Set `record_type` to `LOC`. (int) | no
|
||||
`loc_lon_min` | LOC Minutes Longitude. Set `record_type` to `LOC`. (int) | no
|
||||
`loc_lon_sec` | LOC Seconds Longitude. Set `record_type` to `LOC`. (float) | no
|
||||
`loc_lon_dir` | LOC Direction Longitude. Valid values are `E` or `W`. Set `record_type` to `LOC`. (int) | no
|
||||
`loc_altitude` | LOC Altitude. Set `record_type` to `LOC`. (float) | no
|
||||
`loc_size` | LOC Size. Set `record_type` to `LOC`. (float) | no
|
||||
`loc_h_precision` | LOC Horizontal Precision. Set `record_type` to `LOC`. (float) | no
|
||||
`loc_v_precision` | LOC Vertical Precision. Set `record_type` to `LOC`. (float) | no
|
||||
`mx_preference` | Preference given to this exchanger. Lower values are more preferred. Set `record_type` to `MX`. (int) | no
|
||||
`mx_exchanger` | A host willing to act as a mail exchanger. Set `record_type` to `LOC`. | no
|
||||
`naptr_order` | NAPTR Order. Set `record_type` to `NAPTR`. (int) | no
|
||||
`naptr_preference` | NAPTR Preference. Set `record_type` to `NAPTR`. (int) | no
|
||||
`naptr_flags` | NAPTR Flags. Set `record_type` to `NAPTR`. | no
|
||||
`naptr_service` | NAPTR Service. Set `record_type` to `NAPTR`. | no
|
||||
`naptr_regexp` | NAPTR Regular Expression. Set `record_type` to `NAPTR`. | no
|
||||
`naptr_replacement` | NAPTR Replacement. Set `record_type` to `NAPTR`. | no
|
||||
`ns_hostname` | NS Hostname. Set `record_type` to `NS`. | no
|
||||
`ptr_hostname` | The hostname this reverse record points to. . Set `record_type` to `PTR`. | no
|
||||
`srv_priority` | Lower number means higher priority. Clients will attempt to contact the server with the lowest-numbered priority they can reach. Set `record_type` to `SRV`. (int) | no
|
||||
`srv_weight` | Relative weight for entries with the same priority. Set `record_type` to `SRV`. (int) | no
|
||||
`srv_port` | SRV Port. Set `record_type` to `SRV`. (int) | no
|
||||
`srv_target` | The domain name of the target host or '.' if the service is decidedly not available at this domain. Set `record_type` to `SRV`. | no
|
||||
`sshfp_algorithm` | SSHFP Algorithm. Set `record_type` to `SSHFP`. (int) | no
|
||||
`sshfp_fp_type` | SSHFP Fingerprint Type. Set `record_type` to `SSHFP`. (int) | no
|
||||
`sshfp_fingerprint`| SSHFP Fingerprint. Set `record_type` to `SSHFP`. (int) | no
|
||||
`txt_data` | TXT Text Data. Set `record_type` to `TXT`. | no
|
||||
`tlsa_cert_usage` | TLSA Certificate Usage. Set `record_type` to `TLSA`. (int) | no
|
||||
`tlsa_selector` | TLSA Selector. Set `record_type` to `TLSA`. (int) | no
|
||||
`tlsa_matching_type` | TLSA Matching Type. Set `record_type` to `TLSA`. (int) | no
|
||||
`tlsa_cert_association_data` | TLSA Certificate Association Data. Set `record_type` to `TLSA`. | no
|
||||
`uri_target` | Target Uniform Resource Identifier according to RFC 3986. Set `record_type` to `URI`. | no
|
||||
`uri_priority` | Lower number means higher priority. Clients will attempt to contact the URI with the lowest-numbered priority they can reach. Set `record_type` to `URI`. (int) | no
|
||||
`uri_weight` | Relative weight for entries with the same priority. Set `record_type` to `URI`. (int) | no
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Rafael Guterres Jeffman
|
||||
@@ -143,6 +143,8 @@ Variable | Description | Required
|
||||
`user` | List of user name strings assigned to this group. | no
|
||||
`group` | List of group name strings assigned to this group. | no
|
||||
`service` | List of service name strings assigned to this group. Only usable with IPA versions 4.7 and up. | no
|
||||
`membermanager_user` | List of member manager users assigned to this group. Only usable with IPA versions 4.8.4 and up. | no
|
||||
`membermanager_group` | List of member manager groups assigned to this group. Only usable with IPA versions 4.8.4 and up. | no
|
||||
`action` | Work on group or member level. It can be on of `member` or `group` and defaults to `group`. | no
|
||||
`state` | The state to ensure. It can be one of `present` or `absent`, default: `present`. | yes
|
||||
|
||||
|
||||
@@ -138,9 +138,9 @@ Variable | Description | Required
|
||||
`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 hbacrule name strings. | yes
|
||||
`description` | The hbacrule description string. | no
|
||||
`usercategory` \| `usercat` | User category the rule applies to. Choices: ["all"] | no
|
||||
`hostcategory` \| `hostcat` | Host category the rule applies to. Choices: ["all"] | no
|
||||
`servicecategory` \| `servicecat` | HBAC service category the rule applies to. Choices: ["all"] | no
|
||||
`usercategory` \| `usercat` | User category the rule applies to. Choices: ["all", ""] | no
|
||||
`hostcategory` \| `hostcat` | Host category the rule applies to. Choices: ["all", ""] | no
|
||||
`servicecategory` \| `servicecat` | HBAC service category the rule applies to. Choices: ["all", ""] | no
|
||||
`nomembers` | Suppress processing of membership attributes. (bool) | no
|
||||
`host` | List of host name strings assigned to this hbacrule. | no
|
||||
`hostgroup` | List of host group name strings assigned to this hbacrule. | no
|
||||
|
||||
@@ -173,14 +173,14 @@ Example playbook to ensure host presence with a random password:
|
||||
name: host01.example.com
|
||||
random: yes
|
||||
force: yes
|
||||
update_password: on_create
|
||||
register: ipahost
|
||||
|
||||
- name: Print generated random password
|
||||
debug:
|
||||
var: ipahost.host.randompassword
|
||||
```
|
||||
Please remember that the `force` tag will also force the generation of a new random password even if the host already exists and if `update_password` is limited to `on_create`.
|
||||
|
||||
Please remember that a new random password will be generated for an existing but not enrolled host if `update_password` is not limited to `on_create`. For an already enrolled host the task will fail with `update_password` default setting `always`.
|
||||
|
||||
Example playbook to ensure presence of several hosts with a random password:
|
||||
|
||||
@@ -198,9 +198,11 @@ Example playbook to ensure presence of several hosts with a random password:
|
||||
- name: host01.example.com
|
||||
random: yes
|
||||
force: yes
|
||||
update_password: on_create
|
||||
- name: host02.example.com
|
||||
random: yes
|
||||
force: yes
|
||||
update_password: on_create
|
||||
register: ipahost
|
||||
|
||||
- name: Print generated random password for host01.example.com
|
||||
@@ -211,7 +213,7 @@ Example playbook to ensure presence of several hosts with a random password:
|
||||
debug:
|
||||
var: ipahost.host["host02.example.com"].randompassword
|
||||
```
|
||||
Please remember that the `force` tag will also force the generation of a new random password even if the host alreay exists and if `update_password` is limited to `on_create`.
|
||||
Please remember that a new random password will be generated for an existing but not enrolled host if `update_password` is not limited to `on_create`. For an already enrolled host the task will fail with `update_password` default setting `always`.
|
||||
|
||||
|
||||
Example playbook to ensure presence of host member principal:
|
||||
@@ -337,8 +339,8 @@ Variable | Description | Required
|
||||
`location` \| `ns_host_location` | Host location (e.g. "Lab 2"). | no
|
||||
`platform` \| `ns_hardware_platform` | Host hardware platform (e.g. "Lenovo T61"). | no
|
||||
`os` \| `ns_os_version` | Host operating system and version (e.g. "Fedora 9"). | no
|
||||
`password` \| `user_password` \| `userpassword` | Password used in bulk enrollment. | no
|
||||
`random` \| `random_password` | Initiate the generation of a random password to be used in bulk enrollment. | no
|
||||
`password` \| `user_password` \| `userpassword` | Password used in bulk enrollment for absent or not enrolled hosts. | no
|
||||
`random` \| `random_password` | Initiate the generation of a random password to be used in bulk enrollment for absent or not enrolled hosts. | no
|
||||
`certificate` \| `usercertificate` | List of base-64 encoded host certificates | no
|
||||
`managedby` \| `principalname` \| `krbprincipalname` | List of hosts that can manage this host | no
|
||||
`principal` \| `principalname` \| `krbprincipalname` | List of principal aliases for this host | no
|
||||
|
||||
@@ -137,6 +137,8 @@ Variable | Description | Required
|
||||
`nomembers` | Suppress processing of membership attributes. (bool) | no
|
||||
`host` | List of host name strings assigned to this hostgroup. | no
|
||||
`hostgroup` | List of hostgroup name strings assigned to this hostgroup. | no
|
||||
`membermanager_user` | List of member manager users assigned to this hostgroup. Only usable with IPA versions 4.8.4 and up. | no
|
||||
`membermanager_group` | List of member manager groups assigned to this hostgroup. Only usable with IPA versions 4.8.4 and up. | no
|
||||
`action` | Work on hostgroup or member level. It can be on of `member` or `hostgroup` and defaults to `hostgroup`. | no
|
||||
`state` | The state to ensure. It can be one of `present` or `absent`, default: `present`. | no
|
||||
|
||||
|
||||
@@ -310,6 +310,7 @@ Variable | Description | Required
|
||||
`allow_retrieve_keytab_group` \| `ipaallowedtoperform_read_keys_group` | Groups allowed to retrieve a keytab of this host. | no
|
||||
`allow_retrieve_keytab_host` \| `ipaallowedtoperform_read_keys_host` | Hosts allowed to retrieve a keytab from of host. | no
|
||||
`allow_retrieve_keytab_hostgroup` \| `ipaallowedtoperform_read_keys_hostgroup` | Host groups allowed to retrieve a keytab of this host. | no
|
||||
`continue` | Continuous mode: don't stop on errors. Valid only if `state` is `absent`. Default: `no` (bool) | no
|
||||
`action` | Work on service or member level. It can be on of `member` or `service` and defaults to `service`. | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent`, or `disabled`, default: `present`. | no
|
||||
|
||||
|
||||
@@ -122,11 +122,11 @@ Variable | Description | Required
|
||||
`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 sudorule name strings. | yes
|
||||
`description` | The sudorule description string. | no
|
||||
`usercategory` | User category the rule applies to. Choices: ["all"] | no
|
||||
`hostcategory` | Host category the rule applies to. Choices: ["all"] | no
|
||||
`cmdcategory` | Command category the rule applies to. Choices: ["all"] | no
|
||||
`runasusercategory` | RunAs User category the rule applies to. Choices: ["all"] | no
|
||||
`runasgroupcategory` | RunAs Group category the rule applies to. Choices: ["all"] | no
|
||||
`usercategory` \| `usercat` | User category the rule applies to. Choices: ["all", ""] | no
|
||||
`hostcategory` \| `hostcat` | Host category the rule applies to. Choices: ["all", ""] | no
|
||||
`cmdcategory` \| `cmdcat` | Command category the rule applies to. Choices: ["all", ""] | no
|
||||
`runasusercategory` \| `rusasusercat` | RunAs User category the rule applies to. Choices: ["all", ""] | no
|
||||
`runasgroupcategory` \| `runasgroupcat` | RunAs Group category the rule applies to. Choices: ["all", ""] | no
|
||||
`nomembers` | Suppress processing of membership attributes. (bool) | no
|
||||
`host` | List of host name strings assigned to this sudorule. | no
|
||||
`hostgroup` | List of host group name strings assigned to this sudorule. | no
|
||||
|
||||
@@ -417,10 +417,11 @@ Variable | Description | Required
|
||||
`employeetype` | Employee Type | no
|
||||
`preferredlanguage` | Preferred Language | no
|
||||
`certificate` | List of base-64 encoded user certificates. | no
|
||||
`certmapdata` | List of certificate mappings. Either `certificate` or `issuer` together with `subject` need to be specified. <br>Options: | no
|
||||
| `certificate` - Base-64 encoded user certificate | no
|
||||
| `issuer` - Issuer of the certificate | no
|
||||
| `subject` - Subject of the certificate | no
|
||||
`certmapdata` | List of certificate mappings. Either `data` or `certificate` or `issuer` together with `subject` need to be specified. Only usable with IPA versions 4.5 and up. <br>Options: | no
|
||||
| `certificate` - Base-64 encoded user certificate, not usable with other certmapdata options. | no
|
||||
| `issuer` - Issuer of the certificate, only usable together with `usbject` option. | no
|
||||
| `subject` - Subject of the certificate, only usable together with `issuer` option. | no
|
||||
| `data` - Certmap data, not usable with other certmapdata options. | no
|
||||
`noprivate` | Do not create user private group. (bool) | no
|
||||
`nomembers` | Suppress processing of membership attributes. (bool) | no
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ Example inventory file
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
Example playbook to make sure vault is present:
|
||||
Example playbook to make sure vault is present (by default, vault type is `symmetric`):
|
||||
|
||||
```yaml
|
||||
---
|
||||
@@ -53,8 +53,7 @@ Example playbook to make sure vault is present:
|
||||
- ipavault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: symvault
|
||||
username: admin
|
||||
vault_password: MyVaultPassword123
|
||||
password: SomeVAULTpassword
|
||||
description: A standard private vault.
|
||||
```
|
||||
|
||||
@@ -124,13 +123,30 @@ Example playbook to make sure vault data is present in a symmetric vault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: symvault
|
||||
username: admin
|
||||
vault_password: MyVaultPassword123
|
||||
vault_data: >
|
||||
password: SomeVAULTpassword
|
||||
data: >
|
||||
Data archived.
|
||||
More data archived.
|
||||
action: member
|
||||
```
|
||||
|
||||
Example playbook to retrieve vault data from a symmetric vault:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to handle vaults
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- ipavault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: symvault
|
||||
username: admin
|
||||
password: SomeVAULTpassword
|
||||
state: retrieved
|
||||
```
|
||||
|
||||
Example playbook to make sure vault data is absent in a symmetric vault:
|
||||
|
||||
```yaml
|
||||
@@ -144,7 +160,7 @@ Example playbook to make sure vault data is absent in a symmetric vault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: symvault
|
||||
username: admin
|
||||
vault_password: MyVaultPassword123
|
||||
password: SomeVAULTpassword
|
||||
action: member
|
||||
state: absent
|
||||
```
|
||||
@@ -163,6 +179,9 @@ Example playbook to make sure vault is absent:
|
||||
name: symvault
|
||||
username: admin
|
||||
state: absent
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.data }}"
|
||||
```
|
||||
|
||||
Variables
|
||||
@@ -178,17 +197,37 @@ 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
|
||||
`vault_public_key` \| `ipavaultpublickey` | Vault public key. | no
|
||||
`vault_salt` \| `ipavaultsalt` | Vault salt. | no
|
||||
`password ` \| `vault_password` \| `ipavaultpassword` | Vault password. | no
|
||||
`public_key ` \| `vault_public_key` \| `ipavaultpublickey` | 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
|
||||
`salt` \| `vault_salt` \| `ipavaultsalt` | Vault salt. | no
|
||||
`vault_type` \| `ipavaulttype` | Vault types are based on security level. It can be one of `standard`, `symmetric` or `asymmetric`, default: `symmetric` | no
|
||||
`user` \| `username` | Any user can own one or more user vaults. | no
|
||||
`service` | Any service can own one or more service vaults. | no
|
||||
`user` | Any user can own one or more user vaults. | no
|
||||
`shared` | Vault is shared. Default to false. (bool) | no
|
||||
`users` | Users that are members of the vault. | no
|
||||
`groups` | Groups that are member of the vault. | no
|
||||
`vault_data` \| `ipavaultdata` | Data to be stored in the vault. | no
|
||||
`services` | Services that are member of the vault. | no
|
||||
`data` \|`vault_data` \| `ipavaultdata` | Data to be stored in the vault. | no
|
||||
`in` \| `datafile_in` | Path to file with data to be stored in the vault. | no
|
||||
`out` \| `datafile_out` | Path to file to store data retrieved from the vault. | no
|
||||
`action` | Work on vault or member level. It can be on of `member` or `vault` and defaults to `vault`. | no
|
||||
`state` | The state to ensure. It can be one of `present` or `absent`, default: `present`. | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent` or `retrieved`, default: `present`. | no
|
||||
|
||||
|
||||
Return Values
|
||||
=============
|
||||
|
||||
ipavault
|
||||
--------
|
||||
|
||||
There is only a return value if `state` is `retrieved`.
|
||||
|
||||
Variable | Description | Returned When
|
||||
-------- | ----------- | -------------
|
||||
`data` | The data stored in the vault. | If `state` is `retrieved`.
|
||||
|
||||
|
||||
Notes
|
||||
|
||||
@@ -12,6 +12,7 @@ Features
|
||||
* One-time-password (OTP) support for client installation
|
||||
* Repair mode for clients
|
||||
* Modules for dns forwarder management
|
||||
* Modules for dns record management
|
||||
* Modules for dns zone management
|
||||
* Modules for group management
|
||||
* Modules for hbacrule management
|
||||
@@ -411,6 +412,7 @@ Modules in plugin/modules
|
||||
|
||||
* [ipadnsconfig](README-dnsconfig.md)
|
||||
* [ipadnsforwardzone](README-dnsforwardzone.md)
|
||||
* [ipadnsrecord](README-dnsrecord.md)
|
||||
* [ipadnszone](README-dnszone.md)
|
||||
* [ipagroup](README-group.md)
|
||||
* [ipahbacrule](README-hbacrule.md)
|
||||
|
||||
22
azure-pipelines.yml
Normal file
22
azure-pipelines.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
trigger:
|
||||
- master
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-18.04'
|
||||
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '3.6'
|
||||
|
||||
- script: python -m pip install --upgrade pip setuptools wheel
|
||||
displayName: Install tools
|
||||
|
||||
- script: pip install pydocstyle flake8
|
||||
displayName: Install dependencies
|
||||
|
||||
- script: flake8 .
|
||||
displayName: Run flake8 checks
|
||||
|
||||
- script: pydocstyle .
|
||||
displayName: Verify docstings
|
||||
@@ -13,7 +13,6 @@ issues: "https://github.com/freeipa/ansible-freeipa/issues"
|
||||
|
||||
readme: "README.md"
|
||||
license: "GPL-3.0-or-later"
|
||||
license_file: "COPYING"
|
||||
|
||||
dependencies:
|
||||
|
||||
|
||||
14
playbooks/config/retrieve-config.yml
Normal file
14
playbooks/config/retrieve-config.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
- name: Playbook to handle global DNS configuration
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
gather_facts: no
|
||||
|
||||
tasks:
|
||||
- name: Query IPA global configuration
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
register: serverconfig
|
||||
|
||||
- debug:
|
||||
msg: "{{ serverconfig }}"
|
||||
11
playbooks/config/set-ca-renewal-master-server.yml
Normal file
11
playbooks/config/set-ca-renewal-master-server.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Playbook to handle global DNS configuration
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
gather_facts: no
|
||||
|
||||
tasks:
|
||||
- name: set ca_renewal_master_server
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ca_renewal_master_server: carenewal.example.com
|
||||
18
playbooks/dnsrecord/ensure-A-and-AAAA-records-are-absent.yml
Normal file
18
playbooks/dnsrecord/ensure-A-and-AAAA-records-are-absent.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
- name: Test PTR Record is present.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure a PTR record is present
|
||||
- name: Ensure that 'host04' has A and AAAA records.
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: ipatest.local
|
||||
records:
|
||||
- name: host04
|
||||
a_ip_address: 192.168.122.104
|
||||
- name: host04
|
||||
aaaa_ip_address: ::1
|
||||
state: absent
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
- name: Test PTR Record is present.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure a PTR record is present
|
||||
- name: Ensure that 'host04' has A and AAAA records.
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: ipatest.local
|
||||
records:
|
||||
- name: host04
|
||||
a_ip_address: 192.168.122.104
|
||||
- name: host04
|
||||
aaaa_ip_address: ::1
|
||||
13
playbooks/dnsrecord/ensure-CNAME-record-is-absent.yml
Normal file
13
playbooks/dnsrecord/ensure-CNAME-record-is-absent.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: Test CNAME Record is present.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure that 'host04' has CNAME, with cname_hostname
|
||||
- ipadnsrecord:
|
||||
zone_name: example.com
|
||||
name: host04
|
||||
cname_hostname: host04.example.com
|
||||
state: absent
|
||||
12
playbooks/dnsrecord/ensure-CNAME-record-is-present.yml
Normal file
12
playbooks/dnsrecord/ensure-CNAME-record-is-present.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Test CNAME Record is present.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure that 'host04' has CNAME, with cname_hostname
|
||||
- ipadnsrecord:
|
||||
zone_name: example.com
|
||||
name: host04
|
||||
cname_hostname: host04.example.com
|
||||
15
playbooks/dnsrecord/ensure-MX-record-is-present.yml
Normal file
15
playbooks/dnsrecord/ensure-MX-record-is-present.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Ensure MX Record is present.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure an MX record is absent
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: '@'
|
||||
record_type: 'MX'
|
||||
record_value: '1 mailserver.example.com'
|
||||
zone_name: example.com
|
||||
state: present
|
||||
15
playbooks/dnsrecord/ensure-PTR-record-is-present.yml
Normal file
15
playbooks/dnsrecord/ensure-PTR-record-is-present.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Test PTR Record is present.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure a PTR record is present
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: 5
|
||||
record_type: 'PTR'
|
||||
record_value: 'internal.ipa.example.com'
|
||||
zone_name: 2.168.192.in-addr.arpa
|
||||
state: present
|
||||
15
playbooks/dnsrecord/ensure-SRV-record-is-present.yml
Normal file
15
playbooks/dnsrecord/ensure-SRV-record-is-present.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Test SRV Record is present.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure a SRV record is present
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: _kerberos._udp.example.com
|
||||
record_type: 'SRV'
|
||||
record_value: '10 50 88 ipa.example.com'
|
||||
zone_name: example.com
|
||||
state: present
|
||||
16
playbooks/dnsrecord/ensure-SSHFP-record-is-present.yml
Normal file
16
playbooks/dnsrecord/ensure-SSHFP-record-is-present.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
- name: Test SSHFP Record is present.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure a SSHFP record is present
|
||||
# SSHFP fingerprint generated with `ssh-keygen -r host04.testzone.local`
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: example.com
|
||||
name: host04
|
||||
sshfp_algorithm: 1
|
||||
sshfp_fp_type: 1
|
||||
sshfp_fingerprint: d21802c61733e055b8d16296cbce300efb8a167a
|
||||
16
playbooks/dnsrecord/ensure-TLSA-record-is-present.yml
Normal file
16
playbooks/dnsrecord/ensure-TLSA-record-is-present.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
- name: Test SSHFP Record is present.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure a SSHFP record is present
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: example.com
|
||||
name: host04
|
||||
tlsa_cert_usage: 3
|
||||
tlsa_selector: 1
|
||||
tlsa_matching_type: 1
|
||||
tlsa_cert_association_data: 9c0ad776dbeae8d9d55b0ad42899d30235c114d5f918fd69746e4279e47bdaa2
|
||||
15
playbooks/dnsrecord/ensure-TXT-record-is-present.yml
Normal file
15
playbooks/dnsrecord/ensure-TXT-record-is-present.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Test TXT Record is present.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure a TXT record is absent
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: _kerberos
|
||||
record_type: 'TXT'
|
||||
record_value: 'EXAMPLE.COM'
|
||||
zone_name: example.com
|
||||
state: present
|
||||
17
playbooks/dnsrecord/ensure-URI-record-is-present.yml
Normal file
17
playbooks/dnsrecord/ensure-URI-record-is-present.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
- name: Test URI Record is present.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure a URI record is absent
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: _ftp._tcp
|
||||
record_type: 'URI'
|
||||
uri_priority: 10
|
||||
uri_weight: 1
|
||||
uri_target: ftp://ftp.example.com/public
|
||||
zone_name: example.com
|
||||
state: present
|
||||
15
playbooks/dnsrecord/ensure-dnsrecord-is-absent.yml
Normal file
15
playbooks/dnsrecord/ensure-dnsrecord-is-absent.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Test DNS Record is absent.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure that dns record is absent
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: example.com
|
||||
record_type: 'AAAA'
|
||||
record_value: '::1'
|
||||
state: absent
|
||||
15
playbooks/dnsrecord/ensure-dnsrecord-is-present.yml
Normal file
15
playbooks/dnsrecord/ensure-dnsrecord-is-present.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Test DNS Record is present.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure that dns record is present
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: example.com
|
||||
record_type: 'AAAA'
|
||||
record_value: '::1'
|
||||
state: present
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Test DNS Record is present.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure that dns record is present
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: example.com
|
||||
ip_address: 192.160.123.45
|
||||
create_reverse: yes
|
||||
state: present
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
- name: Playbook to manage DNS records.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
gather_facts: no
|
||||
|
||||
tasks:
|
||||
- name: Ensure that 'host04' has multiple A records.
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: ipatest.local
|
||||
name: host01
|
||||
a_rec:
|
||||
- 192.168.122.221
|
||||
- 192.168.122.222
|
||||
- 192.168.122.223
|
||||
- 192.168.122.224
|
||||
21
playbooks/dnsrecord/ensure-presence-multiple-records.yml
Normal file
21
playbooks/dnsrecord/ensure-presence-multiple-records.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
- name: Test multiple DNS Records are present.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure that multiple dns records are present
|
||||
- ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
records:
|
||||
- name: host01
|
||||
zone_name: example.com
|
||||
record_type: A
|
||||
record_value:
|
||||
- 192.168.122.112
|
||||
- 192.168.122.122
|
||||
- name: host01
|
||||
zone_name: testzone.local
|
||||
record_type: AAAA
|
||||
record_value: ::1
|
||||
18
playbooks/vault/change-password-symmetric-vault.yml
Normal file
18
playbooks/vault/change-password-symmetric-vault.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
- name: Playbook to change password of symmetric vault.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
gather_facts: no
|
||||
|
||||
tasks:
|
||||
- name: Create vault.
|
||||
ipavault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: symvault
|
||||
password: SomeVAULTpassword
|
||||
- name: Change vault passord.
|
||||
ipavault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: symvault
|
||||
password: SomeVAULTpassword
|
||||
new_password: SomeNEWpassword
|
||||
@@ -9,6 +9,6 @@
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: symvault
|
||||
username: admin
|
||||
vault_password: MyVaultPassword123
|
||||
vault_password: SomeVAULTpassword
|
||||
vault_data: The world of π is half rounded.
|
||||
action: member
|
||||
|
||||
@@ -9,5 +9,4 @@
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: symvault
|
||||
username: admin
|
||||
vault_password: MyVaultPassword123
|
||||
vault_type: symmetric
|
||||
vault_password: SomeVAULTpassword
|
||||
|
||||
1
playbooks/vault/password.txt
Normal file
1
playbooks/vault/password.txt
Normal file
@@ -0,0 +1 @@
|
||||
SomeVAULTpassword
|
||||
27
playbooks/vault/private.pem
Normal file
27
playbooks/vault/private.pem
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEArM5/f6dd/YIm/a9eoGVTW8jobEgrf9PXRA3aHsA7kJo6fB18
|
||||
HD4+RVUwx/lqlkPYbUi9bXV/rJAkUwAEDOnJeqXESZ+gVCVmigRzmKWK2ad9agmY
|
||||
SiqyyNxFIJvZAo0dG4CAWjYK27tLg4Ih6oGsZIDG+WVES5W89K+L0bwVjq4tshhe
|
||||
DMO57unvmIKEmaBE0ewPfvkdZh5k8Gts9H4fh0fGk5tbIYa0bhwMUpL+WHOm6nbd
|
||||
+n7BbaVc820TgZDO/rSYtnuXaIc6Wx0U9LXZkUmk3apMnzknNaTqguAQdTn79G8P
|
||||
qrGqmyWd/E1cH2b5jzIxiGo8psL5sxWVY7WJdwIDAQABAoIBAA6e9iit14UAgx4J
|
||||
vX7is9fbOtcWkB+jo94NMfxSFXgZpIMl139oQMqK97KjxsHqAaDVe7mMLH5EP96J
|
||||
7M3O5g4rgl0cVWtpMrDQyZsLvqDFzBWxtCHqVPAruumUZhsSJ3lROQro8ag/w5bf
|
||||
5tC5ogVq4+rsB4hBphgp1jGrsUM+E8O7DXXFH68F8WgBi725WvcjnbI9irkb0Gcq
|
||||
1bCPJwN3fA1i2VWiRwVYWbNTWnDoNM9ZdYYxK0kuUkD+QtreycWPf9V49lvUi1Vp
|
||||
FVNmBUDvGK3K1MwbgXRwOXhacY7Ptjkdvaeb2Qcu5RjTkruGhzUYsOP3p/cw+wKV
|
||||
vzQqceECgYEA5Wz7V2SlRa2r//z+ETQkJfENJ0KDnCb0pMClCQh3jTNPA6DbhiMk
|
||||
FTkcoNbqcpTiVSlvhh6TKscSgqYQUjQ/OqyG7SkjKVjQ72j5beQLxiLTtUyj1OmP
|
||||
Xh9cWJXx8iQ+45cPon+kMOAIiTwiB3mmFRfQjIGve1DPUo9J+NZ4XdECgYEAwNKg
|
||||
OdGYxxKtCrXVz1mdg6PDlV8qh7nxxZbPch+aMIQl1+oTCgSiw8oOYEd8g0HOdV6t
|
||||
1G+IWhvPxiiWy3/AE0QhgoKk2GUsSjWSMLcJbaUzDoEHFjTLjecRlqdzo7qxRXqB
|
||||
meN4L5WJYKnLC482K7hvufS+uo5fB5qwPmt13McCgYAe4TVPRP+tyjttYCr+O8tl
|
||||
w/UmRKCcQu4Iwtkzxwz4V2CaN2t0uYQgyygcSfESbRGtrr8RCUp7poHKTfnCZr/f
|
||||
8NrUTwYpiYfNwY5ZCSnAiG2AaIlgnfMrEwOF9OC028YPMgTrtUxvO6hKeGqIIQqG
|
||||
qkbqsoXhDjZpgVnOgWeAEQKBgGuiZ0w/IqAlXbC31fUb2iBMfvXXnJ8M/dfFGmFj
|
||||
IKfqbFF9WUljUxQlqya1YNzIFB5STohiBeP+2FmN+Lb5xdc7VdVLZgdhWnrGMqe8
|
||||
1Kd+6uQyxCjyKZo5nQjSymtf4GqfOs8TOdieCYSK40u9koiPONa9tuXeaU+OWslN
|
||||
JQqrAoGBAJ3MKOvsnQzuZVP2vz0ZqLwIE3XjRiFGveVpizq4hwOVeuNsV08JvA0t
|
||||
pueNIy9klPScFc9OUdiZWkEX09BwJkVIrOHotuSB8AStO5UAntNnuyWLJEFC4Uq4
|
||||
GpB8lbj9jkxSKaU7X3Gac23K9JL8euLh7E7rPuZRYa6mYN4nbKqu
|
||||
-----END RSA PRIVATE KEY-----
|
||||
9
playbooks/vault/public.pem
Normal file
9
playbooks/vault/public.pem
Normal file
@@ -0,0 +1,9 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArM5/f6dd/YIm/a9eoGVT
|
||||
W8jobEgrf9PXRA3aHsA7kJo6fB18HD4+RVUwx/lqlkPYbUi9bXV/rJAkUwAEDOnJ
|
||||
eqXESZ+gVCVmigRzmKWK2ad9agmYSiqyyNxFIJvZAo0dG4CAWjYK27tLg4Ih6oGs
|
||||
ZIDG+WVES5W89K+L0bwVjq4tshheDMO57unvmIKEmaBE0ewPfvkdZh5k8Gts9H4f
|
||||
h0fGk5tbIYa0bhwMUpL+WHOm6nbd+n7BbaVc820TgZDO/rSYtnuXaIc6Wx0U9LXZ
|
||||
kUmk3apMnzknNaTqguAQdTn79G8PqrGqmyWd/E1cH2b5jzIxiGo8psL5sxWVY7WJ
|
||||
dwIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
17
playbooks/vault/retrive-data-asymmetric-vault.yml
Normal file
17
playbooks/vault/retrive-data-asymmetric-vault.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
- name: Tests
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
gather_facts: no
|
||||
|
||||
tasks:
|
||||
- name: Retrieve data from assymetric vault with a private key file.
|
||||
ipavault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: asymvault
|
||||
username: user01
|
||||
private_key_file: private.pem
|
||||
state: retrieved
|
||||
register: result
|
||||
- debug:
|
||||
msg: "Data: {{ result.data }}"
|
||||
17
playbooks/vault/retrive-data-symmetric-vault.yml
Normal file
17
playbooks/vault/retrive-data-symmetric-vault.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
- name: Tests
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
gather_facts: no
|
||||
|
||||
tasks:
|
||||
- name: Retrieve data from symmetric vault.
|
||||
ipavault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: symvault
|
||||
username: admin
|
||||
password: SomeVAULTpassword
|
||||
state: retrieved
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.data | b64decode }}"
|
||||
22
playbooks/vault/vault-is-present-with-password-file.yml
Normal file
22
playbooks/vault/vault-is-present-with-password-file.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
- name: Tests
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: True
|
||||
|
||||
tasks:
|
||||
- copy:
|
||||
src: "{{ playbook_dir }}/password.txt"
|
||||
dest: "{{ ansible_env.HOME }}/password.txt"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: 0600
|
||||
- ipavault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: symvault
|
||||
username: admin
|
||||
vault_type: symmetric
|
||||
vault_password_file: "{{ ansible_env.HOME }}/password.txt"
|
||||
- file:
|
||||
path: "{{ ansible_env.HOME }}/password.txt"
|
||||
state: absent
|
||||
27
playbooks/vault/vault-is-present-with-public-key-file.yml
Normal file
27
playbooks/vault/vault-is-present-with-public-key-file.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
#
|
||||
# Example keys for this playbook were generated with the commands:
|
||||
# $ openssl genrsa -out private.pem 2048
|
||||
# $ openssl rsa -in private.pem -pubout > public.pem
|
||||
#
|
||||
- name: Tests
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: True
|
||||
|
||||
tasks:
|
||||
- copy:
|
||||
src: "{{ playbook_dir }}/public.pem"
|
||||
dest: "{{ ansible_env.HOME }}/public.pem"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: 0600
|
||||
- ipavault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: asymvault
|
||||
username: admin
|
||||
vault_type: asymmetric
|
||||
vault_public_key_file: "{{ ansible_env.HOME }}/public.pem"
|
||||
- file:
|
||||
path: "{{ ansible_env.HOME }}/public.pem"
|
||||
state: absent
|
||||
@@ -39,6 +39,7 @@ try:
|
||||
except ImportError:
|
||||
from ipapython.ipautil import kinit_password, kinit_keytab
|
||||
from ipapython.ipautil import run
|
||||
from ipapython.dn import DN
|
||||
from ipaplatform.paths import paths
|
||||
from ipalib.krb_utils import get_credentials_if_valid
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
@@ -48,6 +49,13 @@ try:
|
||||
from ipalib.x509 import Encoding
|
||||
except ImportError:
|
||||
from cryptography.hazmat.primitives.serialization import Encoding
|
||||
|
||||
try:
|
||||
from ipalib.x509 import load_pem_x509_certificate
|
||||
except ImportError:
|
||||
from ipalib.x509 import load_certificate
|
||||
load_pem_x509_certificate = None
|
||||
|
||||
import socket
|
||||
import base64
|
||||
import six
|
||||
@@ -63,9 +71,7 @@ if six.PY3:
|
||||
|
||||
|
||||
def valid_creds(module, principal): # noqa
|
||||
"""
|
||||
Get valid credintials matching the princial, try GSSAPI first
|
||||
"""
|
||||
"""Get valid credentials matching the princial, try GSSAPI first."""
|
||||
if "KRB5CCNAME" in os.environ:
|
||||
ccache = os.environ["KRB5CCNAME"]
|
||||
module.debug('KRB5CCNAME set to %s' % ccache)
|
||||
@@ -103,9 +109,7 @@ def valid_creds(module, principal): # noqa
|
||||
|
||||
|
||||
def temp_kinit(principal, password):
|
||||
"""
|
||||
kinit with password using a temporary ccache
|
||||
"""
|
||||
"""Kinit with password using a temporary ccache."""
|
||||
if not password:
|
||||
raise RuntimeError("The password is not set")
|
||||
if not principal:
|
||||
@@ -119,22 +123,27 @@ def temp_kinit(principal, password):
|
||||
except RuntimeError as e:
|
||||
raise RuntimeError("Kerberos authentication failed: {}".format(e))
|
||||
|
||||
os.environ["KRB5CCNAME"] = ccache_name
|
||||
return ccache_dir, ccache_name
|
||||
|
||||
|
||||
def temp_kdestroy(ccache_dir, ccache_name):
|
||||
"""
|
||||
Destroy temporary ticket and remove temporary ccache
|
||||
"""
|
||||
"""Destroy temporary ticket and remove temporary ccache."""
|
||||
if ccache_name is not None:
|
||||
run([paths.KDESTROY, '-c', ccache_name], raiseonerr=False)
|
||||
del os.environ['KRB5CCNAME']
|
||||
if ccache_dir is not None:
|
||||
shutil.rmtree(ccache_dir, ignore_errors=True)
|
||||
|
||||
|
||||
def api_connect(context=None):
|
||||
"""
|
||||
Create environment, initialize api and connect to ldap2
|
||||
Initialize IPA API with the provided context.
|
||||
|
||||
`context` can be any of:
|
||||
* `server` (default)
|
||||
* `ansible-freeipa`
|
||||
* `cli_installer`
|
||||
"""
|
||||
env = Env()
|
||||
env._bootstrap()
|
||||
@@ -153,32 +162,33 @@ def api_connect(context=None):
|
||||
backend = api.Backend.rpcclient
|
||||
|
||||
if not backend.isconnected():
|
||||
backend.connect()
|
||||
backend.connect(ccache=os.environ.get('KRB5CCNAME', None))
|
||||
|
||||
|
||||
def api_command(module, command, name, args):
|
||||
"""
|
||||
Call ipa.Command
|
||||
"""
|
||||
"""Call ipa.Command."""
|
||||
return api.Command[command](name, **args)
|
||||
|
||||
|
||||
def api_command_no_name(module, command, args):
|
||||
"""
|
||||
Call ipa.Command without a name.
|
||||
"""
|
||||
"""Call ipa.Command without a name."""
|
||||
return api.Command[command](**args)
|
||||
|
||||
|
||||
def api_check_command(command):
|
||||
"""Return if command exists in command list."""
|
||||
return command in api.Command
|
||||
|
||||
|
||||
def api_check_param(command, name):
|
||||
"""
|
||||
Return if param exists in command param list
|
||||
"""
|
||||
"""Check if param exists in command param list."""
|
||||
return name in api.Command[command].params
|
||||
|
||||
|
||||
def execute_api_command(module, principal, password, command, name, args):
|
||||
"""
|
||||
Execute an API command.
|
||||
|
||||
Get KRB ticket if not already there, initialize api, connect,
|
||||
execute command and destroy ticket again if it has been created also.
|
||||
"""
|
||||
@@ -300,10 +310,11 @@ def api_get_realm():
|
||||
|
||||
|
||||
def gen_add_del_lists(user_list, res_list):
|
||||
"""
|
||||
Generate the lists for the addition and removal of members using the
|
||||
provided user and ipa settings
|
||||
"""
|
||||
"""Generate the lists for the addition and removal of members."""
|
||||
# The user list is None, therefore the parameter should not be touched
|
||||
if user_list is None:
|
||||
return [], []
|
||||
|
||||
add_list = list(set(user_list or []) - set(res_list or []))
|
||||
del_list = list(set(res_list or []) - set(user_list or []))
|
||||
|
||||
@@ -312,8 +323,9 @@ def gen_add_del_lists(user_list, res_list):
|
||||
|
||||
def encode_certificate(cert):
|
||||
"""
|
||||
Encode a certificate using base64 with also taking FreeIPA and Python
|
||||
versions into account
|
||||
Encode a certificate using base64.
|
||||
|
||||
It also takes FreeIPA and Python versions into account.
|
||||
"""
|
||||
if isinstance(cert, (str, unicode, bytes)):
|
||||
encoded = base64.b64encode(cert)
|
||||
@@ -324,6 +336,30 @@ def encode_certificate(cert):
|
||||
return encoded
|
||||
|
||||
|
||||
def load_cert_from_str(cert):
|
||||
cert = cert.strip()
|
||||
if not cert.startswith("-----BEGIN CERTIFICATE-----"):
|
||||
cert = "-----BEGIN CERTIFICATE-----\n" + cert
|
||||
if not cert.endswith("-----END CERTIFICATE-----"):
|
||||
cert += "\n-----END CERTIFICATE-----"
|
||||
|
||||
if load_pem_x509_certificate is not None:
|
||||
cert = load_pem_x509_certificate(cert.encode('utf-8'))
|
||||
else:
|
||||
cert = load_certificate(cert.encode('utf-8'))
|
||||
return cert
|
||||
|
||||
|
||||
def DN_x500_text(text):
|
||||
if hasattr(DN, "x500_text"):
|
||||
return DN(text).x500_text()
|
||||
else:
|
||||
# Emulate x500_text
|
||||
dn = DN(text)
|
||||
dn.rdns = reversed(dn.rdns)
|
||||
return str(dn)
|
||||
|
||||
|
||||
def is_valid_port(port):
|
||||
if not isinstance(port, int):
|
||||
return False
|
||||
@@ -335,9 +371,7 @@ def is_valid_port(port):
|
||||
|
||||
|
||||
def is_ipv4_addr(ipaddr):
|
||||
"""
|
||||
Test if figen IP address is a valid IPv4 address
|
||||
"""
|
||||
"""Test if given IP address is a valid IPv4 address."""
|
||||
try:
|
||||
socket.inet_pton(socket.AF_INET, ipaddr)
|
||||
except socket.error:
|
||||
@@ -346,9 +380,7 @@ def is_ipv4_addr(ipaddr):
|
||||
|
||||
|
||||
def is_ipv6_addr(ipaddr):
|
||||
"""
|
||||
Test if figen IP address is a valid IPv6 address
|
||||
"""
|
||||
"""Test if given IP address is a valid IPv6 address."""
|
||||
try:
|
||||
socket.inet_pton(socket.AF_INET6, ipaddr)
|
||||
except socket.error:
|
||||
|
||||
479
plugins/modules/ipaconfig.py
Normal file
479
plugins/modules/ipaconfig.py
Normal file
@@ -0,0 +1,479 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
# Chris Procter <cprocter@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: ipa_config
|
||||
author: chris procter
|
||||
short_description: Modify IPA global config options
|
||||
description:
|
||||
- Modify IPA global config options
|
||||
options:
|
||||
ipaadmin_principal:
|
||||
description: The admin principal
|
||||
default: admin
|
||||
ipaadmin_password:
|
||||
description: The admin password
|
||||
required: false
|
||||
maxusername:
|
||||
description: Set the maximum username length between 1-255
|
||||
required: false
|
||||
aliases: ['ipamaxusernamelength']
|
||||
maxhostname:
|
||||
description: Set the maximum hostname length between 64-255
|
||||
required: false
|
||||
aliases: ['ipamaxhostnamelength']
|
||||
homedirectory:
|
||||
description: Set the default location of home directories
|
||||
required: false
|
||||
aliases: ['ipahomesrootdir']
|
||||
defaultshell:
|
||||
description: Set the default shell for new users
|
||||
required: false
|
||||
aliases: ['ipadefaultloginshell', 'loginshell']
|
||||
defaultgroup:
|
||||
description: Set the default group for new users
|
||||
required: false
|
||||
aliases: ['ipadefaultprimarygroup']
|
||||
emaildomain:
|
||||
description: Set the default e-mail domain
|
||||
required: false
|
||||
aliases: ['ipadefaultemaildomain']
|
||||
searchtimelimit:
|
||||
description:
|
||||
- Set maximum amount of time (seconds) for a search
|
||||
- values -1 to 2147483647 (-1 or 0 is unlimited)
|
||||
required: false
|
||||
aliases: ['ipasearchtimelimit']
|
||||
searchrecordslimit:
|
||||
description:
|
||||
- Set maximum number of records to search
|
||||
- values -1 to 2147483647 (-1 or 0 is unlimited)
|
||||
required: false
|
||||
aliases: ['ipasearchrecordslimit']
|
||||
usersearch:
|
||||
description:
|
||||
- Set comma-separated list of fields to search for user search
|
||||
required: false
|
||||
aliases: ['ipausersearchfields']
|
||||
groupsearch:
|
||||
description:
|
||||
- Set comma-separated list of fields to search for group search
|
||||
required: false
|
||||
aliases: ['ipagroupsearchfields']
|
||||
enable_migration:
|
||||
description: Enable migration mode
|
||||
type: bool
|
||||
required: false
|
||||
aliases: ['ipamigrationenabled']
|
||||
groupobjectclasses:
|
||||
description: Set default group objectclasses (comma-separated list)
|
||||
required: false
|
||||
type: list
|
||||
aliases: ['ipagroupobjectclasses']
|
||||
userobjectclasses:
|
||||
description: Set default user objectclasses (comma-separated list)
|
||||
required: false
|
||||
type: list
|
||||
aliases: ['ipauserobjectclasses']
|
||||
pwdexpnotify:
|
||||
description:
|
||||
- Set number of days's notice of impending password expiration
|
||||
- values 0 to 2147483647
|
||||
required: false
|
||||
aliases: ['ipapwdexpadvnotify']
|
||||
configstring:
|
||||
description: Set extra hashes to generate in password plug-in
|
||||
required: false
|
||||
type: list
|
||||
choices:
|
||||
- "AllowNThash"
|
||||
- "KDC:Disable Last Success"
|
||||
- "KDC:Disable Lockout"
|
||||
- "KDC:Disable Default Preauth for SPNs"
|
||||
- ""
|
||||
aliases: ['ipaconfigstring']
|
||||
selinuxusermaporder:
|
||||
description: Set order in increasing priority of SELinux users
|
||||
required: false
|
||||
type: list
|
||||
aliases: ['ipaselinuxusermaporder']
|
||||
selinuxusermapdefault:
|
||||
description: Set default SELinux user when no match found in map rule
|
||||
required: false
|
||||
aliases: ['ipaselinuxusermapdefault']
|
||||
pac_type:
|
||||
description: set default types of PAC supported for services
|
||||
required: false
|
||||
type: list
|
||||
choices: ["MS-PAC", "PAD", "nfs:NONE", ""]
|
||||
aliases: ["ipakrbauthzdata"]
|
||||
user_auth_type:
|
||||
description: set default types of supported user authentication
|
||||
required: false
|
||||
type: list
|
||||
choices: ["password", "radius", "otp", "disabled", ""]
|
||||
aliases: ["ipauserauthtype"]
|
||||
ca_renewal_master_server:
|
||||
description: Renewal master for IPA certificate authority.
|
||||
required: false
|
||||
type: string
|
||||
domain_resolution_order:
|
||||
description: set list of domains used for short name qualification
|
||||
required: false
|
||||
type: list
|
||||
aliases: ["ipadomainresolutionorder"]
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
---
|
||||
- name: Playbook to handle global configuration options
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
tasks:
|
||||
- name: return current values of the global configuration options
|
||||
ipaconfig:
|
||||
ipaadmin_password: password
|
||||
register: result
|
||||
- name: display default login shell
|
||||
debug:
|
||||
msg: '{{result.config.defaultshell[0] }}'
|
||||
|
||||
- name: set defaultshell and maxusername
|
||||
ipaconfig:
|
||||
ipaadmin_password: password
|
||||
defaultshell: /bin/bash
|
||||
maxusername: 64
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
config:
|
||||
description: Dict of all global config options
|
||||
returned: When no options are set
|
||||
type: dict
|
||||
options:
|
||||
maxusername:
|
||||
description: maximum username length
|
||||
returned: always
|
||||
maxhostname:
|
||||
description: maximum hostname length
|
||||
returned: always
|
||||
homedirectory:
|
||||
description: default location of home directories
|
||||
returned: always
|
||||
defaultshell:
|
||||
description: default shell for new users
|
||||
returned: always
|
||||
defaultgroup:
|
||||
description: default group for new users
|
||||
returned: always
|
||||
emaildomain:
|
||||
description: default e-mail domain
|
||||
returned: always
|
||||
searchtimelimit:
|
||||
description: maximum amount of time (seconds) for a search
|
||||
returned: always
|
||||
searchrecordslimit:
|
||||
description: maximum number of records to search
|
||||
returned: always
|
||||
usersearch:
|
||||
description: comma-separated list of fields to search in user search
|
||||
type: list
|
||||
returned: always
|
||||
groupsearch:
|
||||
description: comma-separated list of fields to search in group search
|
||||
type: list
|
||||
returned: always
|
||||
enable_migration:
|
||||
description: Enable migration mode
|
||||
type: bool
|
||||
returned: always
|
||||
groupobjectclasses:
|
||||
description: default group objectclasses (comma-separated list)
|
||||
type: list
|
||||
returned: always
|
||||
userobjectclasses:
|
||||
description: default user objectclasses (comma-separated list)
|
||||
type: list
|
||||
returned: always
|
||||
pwdexpnotify:
|
||||
description: number of days's notice of impending password expiration
|
||||
returned: always
|
||||
configstring:
|
||||
description: extra hashes to generate in password plug-in
|
||||
type: list
|
||||
returned: always
|
||||
selinuxusermaporder:
|
||||
description: order in increasing priority of SELinux users
|
||||
returned: always
|
||||
selinuxusermapdefault:
|
||||
description: default SELinux user when no match is found in map rule
|
||||
returned: always
|
||||
pac_type:
|
||||
description: default types of PAC supported for services
|
||||
type: list
|
||||
returned: always
|
||||
user_auth_type:
|
||||
description: default types of supported user authentication
|
||||
returned: always
|
||||
ca_renewal_master_server:
|
||||
description: master for IPA certificate authority.
|
||||
returned: always
|
||||
domain_resolution_order:
|
||||
description: list of domains used for short name qualification
|
||||
returned: always
|
||||
'''
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
|
||||
temp_kdestroy, valid_creds, api_connect, api_command_no_name, \
|
||||
compare_args_ipa, module_params_get
|
||||
import ipalib.errors
|
||||
|
||||
|
||||
def config_show(module):
|
||||
_result = api_command_no_name(module, "config_show", {})
|
||||
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_args(params):
|
||||
_args = {}
|
||||
for k, v in params.items():
|
||||
if v is not None:
|
||||
_args[k] = v
|
||||
|
||||
return _args
|
||||
|
||||
|
||||
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),
|
||||
maxusername=dict(type="int", required=False,
|
||||
aliases=['ipamaxusernamelength']),
|
||||
maxhostname=dict(type="int", required=False,
|
||||
aliases=['ipamaxhostnamelength']),
|
||||
homedirectory=dict(type="str", required=False,
|
||||
aliases=['ipahomesrootdir']),
|
||||
defaultshell=dict(type="str", required=False,
|
||||
aliases=['ipadefaultloginshell',
|
||||
'loginshell']),
|
||||
defaultgroup=dict(type="str", required=False,
|
||||
aliases=['ipadefaultprimarygroup']),
|
||||
emaildomain=dict(type="str", required=False,
|
||||
aliases=['ipadefaultemaildomain']),
|
||||
searchtimelimit=dict(type="int", required=False,
|
||||
aliases=['ipasearchtimelimit']),
|
||||
searchrecordslimit=dict(type="int", required=False,
|
||||
aliases=['ipasearchrecordslimit']),
|
||||
usersearch=dict(type="list", required=False,
|
||||
aliases=['ipausersearchfields']),
|
||||
groupsearch=dict(type="list", required=False,
|
||||
aliases=['ipagroupsearchfields']),
|
||||
enable_migration=dict(type="bool", required=False,
|
||||
aliases=['ipamigrationenabled']),
|
||||
groupobjectclasses=dict(type="list", required=False,
|
||||
aliases=['ipagroupobjectclasses']),
|
||||
userobjectclasses=dict(type="list", required=False,
|
||||
aliases=['ipauserobjectclasses']),
|
||||
pwdexpnotify=dict(type="int", required=False,
|
||||
aliases=['ipapwdexpadvnotify']),
|
||||
configstring=dict(type="list", required=False,
|
||||
aliases=['ipaconfigstring'],
|
||||
choices=["AllowNThash",
|
||||
"KDC:Disable Last Success",
|
||||
"KDC:Disable Lockout",
|
||||
"KDC:Disable Default Preauth for SPNs",
|
||||
""]), # noqa E128
|
||||
selinuxusermaporder=dict(type="list", required=False,
|
||||
aliases=['ipaselinuxusermaporder']),
|
||||
selinuxusermapdefault=dict(type="str", required=False,
|
||||
aliases=['ipaselinuxusermapdefault']),
|
||||
pac_type=dict(type="list", required=False,
|
||||
aliases=["ipakrbauthzdata"],
|
||||
choices=["MS-PAC", "PAD", "nfs:NONE", ""]),
|
||||
user_auth_type=dict(type="list", required=False,
|
||||
choices=["password", "radius", "otp",
|
||||
"disabled", ""],
|
||||
aliases=["ipauserauthtype"]),
|
||||
ca_renewal_master_server=dict(type="str", required=False),
|
||||
domain_resolution_order=dict(type="list", required=False,
|
||||
aliases=["ipadomainresolutionorder"])
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
ansible_module._ansible_debug = True
|
||||
|
||||
# Get parameters
|
||||
|
||||
# general
|
||||
ipaadmin_principal = module_params_get(ansible_module,
|
||||
"ipaadmin_principal")
|
||||
ipaadmin_password = module_params_get(ansible_module,
|
||||
"ipaadmin_password")
|
||||
|
||||
field_map = {
|
||||
"maxusername": "ipamaxusernamelength",
|
||||
"maxhostname": "ipamaxhostnamelength",
|
||||
"homedirectory": "ipahomesrootdir",
|
||||
"defaultshell": "ipadefaultloginshell",
|
||||
"defaultgroup": "ipadefaultprimarygroup",
|
||||
"emaildomain": "ipadefaultemaildomain",
|
||||
"searchtimelimit": "ipasearchtimelimit",
|
||||
"searchrecordslimit": "ipasearchrecordslimit",
|
||||
"usersearch": "ipausersearchfields",
|
||||
"groupsearch": "ipagroupsearchfields",
|
||||
"enable_migration": "ipamigrationenabled",
|
||||
"groupobjectclasses": "ipagroupobjectclasses",
|
||||
"userobjectclasses": "ipauserobjectclasses",
|
||||
"pwdexpnotify": "ipapwdexpadvnotify",
|
||||
"configstring": "ipaconfigstring",
|
||||
"selinuxusermaporder": "ipaselinuxusermaporder",
|
||||
"selinuxusermapdefault": "ipaselinuxusermapdefault",
|
||||
"pac_type": "ipakrbauthzdata",
|
||||
"user_auth_type": "ipauserauthtype",
|
||||
"ca_renewal_master_server": "ca_renewal_master_server",
|
||||
"domain_resolution_order": "ipadomainresolutionorder"
|
||||
}
|
||||
reverse_field_map = {v: k for k, v in field_map.items()}
|
||||
|
||||
params = {}
|
||||
for x in field_map.keys():
|
||||
val = module_params_get(ansible_module, x)
|
||||
|
||||
if val is not None:
|
||||
params[field_map.get(x, x)] = val
|
||||
|
||||
if params.get("ipamigrationenabled") is not None:
|
||||
params["ipamigrationenabled"] = \
|
||||
str(params["ipamigrationenabled"]).upper()
|
||||
|
||||
if params.get("ipaselinuxusermaporder", None):
|
||||
params["ipaselinuxusermaporder"] = \
|
||||
"$".join(params["ipaselinuxusermaporder"])
|
||||
|
||||
if params.get("ipadomainresolutionorder", None):
|
||||
params["ipadomainresolutionorder"] = \
|
||||
":".join(params["ipadomainresolutionorder"])
|
||||
|
||||
if params.get("ipausersearchfields", None):
|
||||
params["ipausersearchfields"] = \
|
||||
",".join(params["ipausersearchfields"])
|
||||
|
||||
if params.get("ipagroupsearchfields", None):
|
||||
params["ipagroupsearchfields"] = \
|
||||
",".join(params["ipagroupsearchfields"])
|
||||
|
||||
# verify limits on INT values.
|
||||
args_with_limits = [
|
||||
("ipamaxusernamelength", 1, 255),
|
||||
("ipamaxhostnamelength", 64, 255),
|
||||
("ipasearchtimelimit", -1, 2147483647),
|
||||
("ipasearchrecordslimit", -1, 2147483647),
|
||||
("ipapwdexpadvnotify", 0, 2147483647),
|
||||
]
|
||||
for arg, min, max in args_with_limits:
|
||||
if arg in params and (params[arg] > max or params[arg] < min):
|
||||
ansible_module.fail_json(
|
||||
msg="Argument '%s' must be between %d and %d."
|
||||
% (arg, min, max))
|
||||
|
||||
changed = False
|
||||
exit_args = {}
|
||||
ccache_dir = None
|
||||
ccache_name = None
|
||||
res_show = None
|
||||
try:
|
||||
if not valid_creds(ansible_module, ipaadmin_principal):
|
||||
ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
|
||||
ipaadmin_password)
|
||||
api_connect()
|
||||
if params:
|
||||
res_show = config_show(ansible_module)
|
||||
params = {
|
||||
k: v for k, v in params.items()
|
||||
if k not in res_show or res_show[k] != v
|
||||
}
|
||||
if params \
|
||||
and not compare_args_ipa(ansible_module, params, res_show):
|
||||
changed = True
|
||||
api_command_no_name(ansible_module, "config_mod", params)
|
||||
|
||||
else:
|
||||
rawresult = api_command_no_name(ansible_module, "config_show", {})
|
||||
result = rawresult['result']
|
||||
del result['dn']
|
||||
for key, v in result.items():
|
||||
k = reverse_field_map.get(key, key)
|
||||
if ansible_module.argument_spec.get(k):
|
||||
if k == 'ipaselinuxusermaporder':
|
||||
exit_args['ipaselinuxusermaporder'] = \
|
||||
result.get(key)[0].split('$')
|
||||
elif k == 'domain_resolution_order':
|
||||
exit_args['domain_resolution_order'] = \
|
||||
result.get(key)[0].split('$')
|
||||
elif k == 'usersearch':
|
||||
exit_args['usersearch'] = \
|
||||
result.get(key)[0].split(',')
|
||||
elif k == 'groupsearch':
|
||||
exit_args['groupsearch'] = \
|
||||
result.get(key)[0].split(',')
|
||||
elif isinstance(v, str) and \
|
||||
ansible_module.argument_spec[k]['type'] == "list":
|
||||
exit_args[k] = [v]
|
||||
elif isinstance(v, list) and \
|
||||
ansible_module.argument_spec[k]['type'] == "str":
|
||||
exit_args[k] = ",".join(v)
|
||||
elif isinstance(v, list) and \
|
||||
ansible_module.argument_spec[k]['type'] == "int":
|
||||
exit_args[k] = ",".join(v)
|
||||
elif isinstance(v, list) and \
|
||||
ansible_module.argument_spec[k]['type'] == "bool":
|
||||
exit_args[k] = (v[0] == "TRUE")
|
||||
else:
|
||||
exit_args[k] = v
|
||||
except ipalib.errors.EmptyModlist:
|
||||
changed = False
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(msg="%s %s" % (params, str(e)))
|
||||
|
||||
finally:
|
||||
temp_kdestroy(ccache_dir, ccache_name)
|
||||
|
||||
# Done
|
||||
ansible_module.exit_json(changed=changed, config=exit_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -97,11 +97,10 @@ RETURN = """
|
||||
"""
|
||||
|
||||
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, \
|
||||
temp_kdestroy, valid_creds, api_connect, \
|
||||
api_command_no_name, compare_args_ipa, module_params_get, \
|
||||
gen_add_del_lists, is_ipv4_addr, is_ipv6_addr, ipalib_errors
|
||||
is_ipv4_addr, is_ipv6_addr
|
||||
|
||||
|
||||
def find_dnsconfig(module):
|
||||
@@ -116,7 +115,7 @@ def find_dnsconfig(module):
|
||||
_result["result"]['idnsforwarders'] = ['']
|
||||
return _result["result"]
|
||||
else:
|
||||
module.fail("Could not retrieve current DNS configuration.")
|
||||
module.fail_json(msg="Could not retrieve current DNS configuration.")
|
||||
return None
|
||||
|
||||
|
||||
@@ -130,7 +129,7 @@ def gen_args(module, state, dnsconfig, forwarders, forward_policy,
|
||||
ip_address = forwarder.get('ip_address')
|
||||
port = forwarder.get('port')
|
||||
if not (is_ipv4_addr(ip_address) or is_ipv6_addr(ip_address)):
|
||||
module.fail(
|
||||
module.fail_json(
|
||||
msg="Invalid IP for DNS forwarder: %s" % ip_address)
|
||||
if port is None:
|
||||
_forwarders.append(ip_address)
|
||||
@@ -154,7 +153,7 @@ def gen_args(module, state, dnsconfig, forwarders, forward_policy,
|
||||
|
||||
else:
|
||||
# shouldn't happen, but let's be paranoid.
|
||||
module.fail(msg="Invalid state: %s" % state)
|
||||
module.fail_json(msg="Invalid state: %s" % state)
|
||||
|
||||
if forward_policy is not None:
|
||||
_args['idnsforwardpolicy'] = forward_policy
|
||||
|
||||
1509
plugins/modules/ipadnsrecord.py
Normal file
1509
plugins/modules/ipadnsrecord.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -75,6 +75,18 @@ options:
|
||||
- Only usable with IPA versions 4.7 and up.
|
||||
required: false
|
||||
type: list
|
||||
membermanager_user:
|
||||
description:
|
||||
- List of member manager users assigned to this group.
|
||||
- Only usable with IPA versions 4.8.4 and up.
|
||||
required: false
|
||||
type: list
|
||||
membermanager_group:
|
||||
description:
|
||||
- List of member manager groups assigned to this group.
|
||||
- Only usable with IPA versions 4.8.4 and up.
|
||||
required: false
|
||||
type: list
|
||||
action:
|
||||
description: Work on group or member level
|
||||
default: group
|
||||
@@ -141,7 +153,7 @@ RETURN = """
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
|
||||
temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
|
||||
api_check_param, module_params_get
|
||||
api_check_param, module_params_get, gen_add_del_lists, api_check_command
|
||||
|
||||
|
||||
def find_group(module, name):
|
||||
@@ -207,6 +219,9 @@ def main():
|
||||
user=dict(required=False, type='list', default=None),
|
||||
group=dict(required=False, type='list', default=None),
|
||||
service=dict(required=False, type='list', default=None),
|
||||
membermanager_user=dict(required=False, type='list', default=None),
|
||||
membermanager_group=dict(required=False, type='list',
|
||||
default=None),
|
||||
action=dict(type="str", default="group",
|
||||
choices=["member", "group"]),
|
||||
# state
|
||||
@@ -221,7 +236,10 @@ def main():
|
||||
# Get parameters
|
||||
|
||||
# general
|
||||
ipaadmin_principal = module_params_get(ansible_module, "ipaadmin_principal")
|
||||
ipaadmin_principal = module_params_get(
|
||||
ansible_module,
|
||||
"ipaadmin_principal",
|
||||
)
|
||||
ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
|
||||
names = module_params_get(ansible_module, "name")
|
||||
|
||||
@@ -234,6 +252,10 @@ def main():
|
||||
user = module_params_get(ansible_module, "user")
|
||||
group = module_params_get(ansible_module, "group")
|
||||
service = module_params_get(ansible_module, "service")
|
||||
membermanager_user = module_params_get(ansible_module,
|
||||
"membermanager_user")
|
||||
membermanager_group = module_params_get(ansible_module,
|
||||
"membermanager_group")
|
||||
action = module_params_get(ansible_module, "action")
|
||||
# state
|
||||
state = module_params_get(ansible_module, "state")
|
||||
@@ -284,6 +306,14 @@ def main():
|
||||
msg="Managing a service as part of a group is not supported "
|
||||
"by your IPA version")
|
||||
|
||||
has_add_membermanager = api_check_command("group_add_member_manager")
|
||||
if ((membermanager_user is not None or
|
||||
membermanager_group is not None) and not has_add_membermanager):
|
||||
ansible_module.fail_json(
|
||||
msg="Managing a membermanager user or group is not supported "
|
||||
"by your IPA version"
|
||||
)
|
||||
|
||||
commands = []
|
||||
|
||||
for name in names:
|
||||
@@ -314,24 +344,14 @@ def main():
|
||||
if not compare_args_ipa(ansible_module, member_args,
|
||||
res_find):
|
||||
# Generate addition and removal lists
|
||||
user_add = list(
|
||||
set(user or []) -
|
||||
set(res_find.get("member_user", [])))
|
||||
user_del = list(
|
||||
set(res_find.get("member_user", [])) -
|
||||
set(user or []))
|
||||
group_add = list(
|
||||
set(group or []) -
|
||||
set(res_find.get("member_group", [])))
|
||||
group_del = list(
|
||||
set(res_find.get("member_group", [])) -
|
||||
set(group or []))
|
||||
service_add = list(
|
||||
set(service or []) -
|
||||
set(res_find.get("member_service", [])))
|
||||
service_del = list(
|
||||
set(res_find.get("member_service", [])) -
|
||||
set(service or []))
|
||||
user_add, user_del = gen_add_del_lists(
|
||||
user, res_find.get("member_user"))
|
||||
|
||||
group_add, group_del = gen_add_del_lists(
|
||||
group, res_find.get("member_group"))
|
||||
|
||||
service_add, service_del = gen_add_del_lists(
|
||||
service, res_find.get("member_service"))
|
||||
|
||||
if has_add_member_service:
|
||||
# Add members
|
||||
@@ -367,6 +387,41 @@ def main():
|
||||
"user": user_del,
|
||||
"group": group_del,
|
||||
}])
|
||||
|
||||
membermanager_user_add, membermanager_user_del = \
|
||||
gen_add_del_lists(
|
||||
membermanager_user,
|
||||
res_find.get("membermanager_user")
|
||||
)
|
||||
|
||||
membermanager_group_add, membermanager_group_del = \
|
||||
gen_add_del_lists(
|
||||
membermanager_group,
|
||||
res_find.get("membermanager_group")
|
||||
)
|
||||
|
||||
if has_add_membermanager:
|
||||
# Add membermanager users and groups
|
||||
if len(membermanager_user_add) > 0 or \
|
||||
len(membermanager_group_add) > 0:
|
||||
commands.append(
|
||||
[name, "group_add_member_manager",
|
||||
{
|
||||
"user": membermanager_user_add,
|
||||
"group": membermanager_group_add,
|
||||
}]
|
||||
)
|
||||
# Remove member manager
|
||||
if len(membermanager_user_del) > 0 or \
|
||||
len(membermanager_group_del) > 0:
|
||||
commands.append(
|
||||
[name, "group_remove_member_manager",
|
||||
{
|
||||
"user": membermanager_user_del,
|
||||
"group": membermanager_group_del,
|
||||
}]
|
||||
)
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(msg="No group '%s'" % name)
|
||||
@@ -384,6 +439,18 @@ def main():
|
||||
"group": group,
|
||||
}])
|
||||
|
||||
if has_add_membermanager:
|
||||
# Add membermanager users and groups
|
||||
if membermanager_user is not None or \
|
||||
membermanager_group is not None:
|
||||
commands.append(
|
||||
[name, "group_add_member_manager",
|
||||
{
|
||||
"user": membermanager_user,
|
||||
"group": membermanager_group,
|
||||
}]
|
||||
)
|
||||
|
||||
elif state == "absent":
|
||||
if action == "group":
|
||||
if res_find is not None:
|
||||
@@ -393,12 +460,32 @@ def main():
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(msg="No group '%s'" % name)
|
||||
|
||||
commands.append([name, "group_remove_member",
|
||||
{
|
||||
"user": user,
|
||||
"group": group,
|
||||
"service": service,
|
||||
}])
|
||||
if has_add_member_service:
|
||||
commands.append([name, "group_remove_member",
|
||||
{
|
||||
"user": user,
|
||||
"group": group,
|
||||
"service": service,
|
||||
}])
|
||||
else:
|
||||
commands.append([name, "group_remove_member",
|
||||
{
|
||||
"user": user,
|
||||
"group": group,
|
||||
}])
|
||||
|
||||
if has_add_membermanager:
|
||||
# Remove membermanager users and groups
|
||||
if membermanager_user is not None or \
|
||||
membermanager_group is not None:
|
||||
commands.append(
|
||||
[name, "group_remove_member_manager",
|
||||
{
|
||||
"user": membermanager_user,
|
||||
"group": membermanager_group,
|
||||
}]
|
||||
)
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
|
||||
@@ -49,17 +49,17 @@ options:
|
||||
description: User category the rule applies to
|
||||
required: false
|
||||
aliases: ["usercat"]
|
||||
choices: ["all"]
|
||||
choices: ["all", ""]
|
||||
hostcategory:
|
||||
description: Host category the rule applies to
|
||||
required: false
|
||||
aliases: ["hostcat"]
|
||||
choices: ["all"]
|
||||
choices: ["all", ""]
|
||||
servicecategory:
|
||||
description: Service category the rule applies to
|
||||
required: false
|
||||
aliases: ["servicecat"]
|
||||
choices: ["all"]
|
||||
choices: ["all", ""]
|
||||
nomembers:
|
||||
description: Suppress processing of membership attributes
|
||||
required: false
|
||||
@@ -159,7 +159,7 @@ RETURN = """
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
|
||||
temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
|
||||
module_params_get
|
||||
module_params_get, gen_add_del_lists
|
||||
|
||||
|
||||
def find_hbacrule(module, name):
|
||||
@@ -208,11 +208,11 @@ def main():
|
||||
# present
|
||||
description=dict(type="str", default=None),
|
||||
usercategory=dict(type="str", default=None,
|
||||
aliases=["usercat"], choices=["all"]),
|
||||
aliases=["usercat"], choices=["all", ""]),
|
||||
hostcategory=dict(type="str", default=None,
|
||||
aliases=["hostcat"], choices=["all"]),
|
||||
aliases=["hostcat"], choices=["all", ""]),
|
||||
servicecategory=dict(type="str", default=None,
|
||||
aliases=["servicecat"], choices=["all"]),
|
||||
aliases=["servicecat"], choices=["all", ""]),
|
||||
nomembers=dict(required=False, type='bool', default=None),
|
||||
host=dict(required=False, type='list', default=None),
|
||||
hostgroup=dict(required=False, type='list', default=None),
|
||||
@@ -270,6 +270,16 @@ def main():
|
||||
ansible_module.fail_json(
|
||||
msg="Argument '%s' can not be used with action "
|
||||
"'%s'" % (x, action))
|
||||
else:
|
||||
if hostcategory == 'all' and any([host, hostgroup]):
|
||||
ansible_module.fail_json(
|
||||
msg="Hosts cannot be added when host category='all'")
|
||||
if usercategory == 'all' and any([user, group]):
|
||||
ansible_module.fail_json(
|
||||
msg="Users cannot be added when user category='all'")
|
||||
if servicecategory == 'all' and any([hbacsvc, hbacsvcgroup]):
|
||||
ansible_module.fail_json(
|
||||
msg="Services cannot be added when service category='all'")
|
||||
|
||||
elif state == "absent":
|
||||
if len(names) < 1:
|
||||
@@ -342,44 +352,24 @@ def main():
|
||||
res_find = {}
|
||||
|
||||
# Generate addition and removal lists
|
||||
host_add = list(
|
||||
set(host or []) -
|
||||
set(res_find.get("memberhost_host", [])))
|
||||
host_del = list(
|
||||
set(res_find.get("memberhost_host", [])) -
|
||||
set(host or []))
|
||||
hostgroup_add = list(
|
||||
set(hostgroup or []) -
|
||||
set(res_find.get("memberhost_hostgroup", [])))
|
||||
hostgroup_del = list(
|
||||
set(res_find.get("memberhost_hostgroup", [])) -
|
||||
set(hostgroup or []))
|
||||
host_add, host_del = gen_add_del_lists(
|
||||
host, res_find.get("memberhost_host"))
|
||||
|
||||
hbacsvc_add = list(
|
||||
set(hbacsvc or []) -
|
||||
set(res_find.get("memberservice_hbacsvc", [])))
|
||||
hbacsvc_del = list(
|
||||
set(res_find.get("memberservice_hbacsvc", [])) -
|
||||
set(hbacsvc or []))
|
||||
hbacsvcgroup_add = list(
|
||||
set(hbacsvcgroup or []) -
|
||||
set(res_find.get("memberservice_hbacsvcgroup", [])))
|
||||
hbacsvcgroup_del = list(
|
||||
set(res_find.get("memberservice_hbacsvcgroup", [])) -
|
||||
set(hbacsvcgroup or []))
|
||||
hostgroup_add, hostgroup_del = gen_add_del_lists(
|
||||
hostgroup, res_find.get("memberhost_hostgroup"))
|
||||
|
||||
user_add = list(
|
||||
set(user or []) -
|
||||
set(res_find.get("memberuser_user", [])))
|
||||
user_del = list(
|
||||
set(res_find.get("memberuser_user", [])) -
|
||||
set(user or []))
|
||||
group_add = list(
|
||||
set(group or []) -
|
||||
set(res_find.get("memberuser_group", [])))
|
||||
group_del = list(
|
||||
set(res_find.get("memberuser_group", [])) -
|
||||
set(group or []))
|
||||
hbacsvc_add, hbacsvc_del = gen_add_del_lists(
|
||||
hbacsvc, res_find.get("memberservice_hbacsvc"))
|
||||
|
||||
hbacsvcgroup_add, hbacsvcgroup_del = gen_add_del_lists(
|
||||
hbacsvcgroup,
|
||||
res_find.get("memberservice_hbacsvcgroup"))
|
||||
|
||||
user_add, user_del = gen_add_del_lists(
|
||||
user, res_find.get("memberuser_user"))
|
||||
|
||||
group_add, group_del = gen_add_del_lists(
|
||||
group, res_find.get("memberuser_group"))
|
||||
|
||||
# Add hosts and hostgroups
|
||||
if len(host_add) > 0 or len(hostgroup_add) > 0:
|
||||
|
||||
@@ -104,7 +104,8 @@ RETURN = """
|
||||
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, compare_args_ipa
|
||||
temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
|
||||
gen_add_del_lists
|
||||
|
||||
|
||||
def find_hbacsvcgroup(module, name):
|
||||
@@ -249,12 +250,8 @@ def main():
|
||||
if not compare_args_ipa(ansible_module, member_args,
|
||||
res_find):
|
||||
# Generate addition and removal lists
|
||||
hbacsvc_add = list(
|
||||
set(hbacsvc or []) -
|
||||
set(res_find.get("member_hbacsvc", [])))
|
||||
hbacsvc_del = list(
|
||||
set(res_find.get("member_hbacsvc", [])) -
|
||||
set(hbacsvc or []))
|
||||
hbacsvc_add, hbacsvc_del = gen_add_del_lists(
|
||||
hbacsvc, res_find.get("member_hbacsvc"))
|
||||
|
||||
# Add members
|
||||
if len(hbacsvc_add) > 0:
|
||||
|
||||
@@ -420,23 +420,22 @@ if six.PY3:
|
||||
def find_host(module, name):
|
||||
_args = {
|
||||
"all": True,
|
||||
"fqdn": to_text(name),
|
||||
}
|
||||
|
||||
_result = api_command(module, "host_find", to_text(name), _args)
|
||||
try:
|
||||
_result = api_command(module, "host_show", to_text(name), _args)
|
||||
except ipalib_errors.NotFound as e:
|
||||
msg = str(e)
|
||||
if "host not found" in msg:
|
||||
return None
|
||||
module.fail_json(msg="host_show failed: %s" % msg)
|
||||
|
||||
if len(_result["result"]) > 1:
|
||||
module.fail_json(
|
||||
msg="There is more than one host '%s'" % (name))
|
||||
elif len(_result["result"]) == 1:
|
||||
_res = _result["result"][0]
|
||||
certs = _res.get("usercertificate")
|
||||
if certs is not None:
|
||||
_res["usercertificate"] = [encode_certificate(cert) for
|
||||
cert in certs]
|
||||
return _res
|
||||
else:
|
||||
return None
|
||||
_res = _result["result"]
|
||||
certs = _res.get("usercertificate")
|
||||
if certs is not None:
|
||||
_res["usercertificate"] = [encode_certificate(cert) for
|
||||
cert in certs]
|
||||
return _res
|
||||
|
||||
|
||||
def find_dnsrecord(module, name):
|
||||
@@ -445,24 +444,19 @@ def find_dnsrecord(module, name):
|
||||
|
||||
_args = {
|
||||
"all": True,
|
||||
"idnsname": to_text(host_name),
|
||||
"idnsname": to_text(host_name)
|
||||
}
|
||||
|
||||
_result = api_command(module, "dnsrecord_find", to_text(domain_name),
|
||||
_args)
|
||||
try:
|
||||
_result = api_command(module, "dnsrecord_show", to_text(domain_name),
|
||||
_args)
|
||||
except ipalib_errors.NotFound as e:
|
||||
msg = str(e)
|
||||
if "record not found" in msg or "zone not found" in msg:
|
||||
return None
|
||||
module.fail_json(msg="dnsrecord_show failed: %s" % msg)
|
||||
|
||||
if len(_result["result"]) > 1:
|
||||
module.fail_json(
|
||||
msg="There is more than one host '%s'" % (name))
|
||||
elif len(_result["result"]) == 1:
|
||||
_res = _result["result"][0]
|
||||
certs = _res.get("usercertificate")
|
||||
if certs is not None:
|
||||
_res["usercertificate"] = [encode_certificate(cert) for
|
||||
cert in certs]
|
||||
return _res
|
||||
else:
|
||||
return None
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def show_host(module, name):
|
||||
@@ -875,9 +869,11 @@ def main():
|
||||
res_find_dnsrecord = find_dnsrecord(ansible_module, name)
|
||||
except ipalib_errors.NotFound as e:
|
||||
msg = str(e)
|
||||
if ip_address is None and \
|
||||
("DNS is not configured" in msg or \
|
||||
"DNS zone not found" in msg):
|
||||
dns_not_configured = "DNS is not configured" in msg
|
||||
dns_zone_not_found = "DNS zone not found" in msg
|
||||
if ip_address is None and (
|
||||
dns_not_configured or dns_zone_not_found
|
||||
):
|
||||
# IP address(es) not given and no DNS support in IPA
|
||||
# -> Ignore failure
|
||||
# IP address(es) not given and DNS zone is not found
|
||||
@@ -901,9 +897,25 @@ def main():
|
||||
# Found the host
|
||||
if res_find is not None:
|
||||
# Ignore password with update_password == on_create
|
||||
if update_password == "on_create" and \
|
||||
"userpassword" in args:
|
||||
del args["userpassword"]
|
||||
if update_password == "on_create":
|
||||
# Ignore userpassword and random for existing
|
||||
# host if update_password is "on_create"
|
||||
if "userpassword" in args:
|
||||
del args["userpassword"]
|
||||
if "random" in args:
|
||||
del args["random"]
|
||||
elif "userpassword" in args or "random" in args:
|
||||
# Allow an existing OTP to be reset but don't
|
||||
# allow a OTP or to be added to an enrolled host.
|
||||
# Also do not allow to change the password for an
|
||||
# enrolled host.
|
||||
|
||||
if not res_find["has_password"] and \
|
||||
res_find["has_keytab"]:
|
||||
ansible_module.fail_json(
|
||||
msg="%s: Password cannot be set on "
|
||||
"enrolled host." % host
|
||||
)
|
||||
|
||||
# Ignore force, ip_address and no_reverse for mod
|
||||
for x in ["force", "ip_address", "no_reverse"]:
|
||||
@@ -951,7 +963,7 @@ def main():
|
||||
principal_add, principal_del = gen_add_del_lists(
|
||||
principal, res_find.get("principal"))
|
||||
# Principals are not returned as utf8 for IPA using
|
||||
# python2 using host_find, therefore we need to
|
||||
# python2 using host_show, therefore we need to
|
||||
# convert the principals that we should remove.
|
||||
principal_del = [to_text(x) for x in principal_del]
|
||||
|
||||
|
||||
@@ -58,6 +58,18 @@ options:
|
||||
description: List of hostgroup names assigned to this hostgroup.
|
||||
required: false
|
||||
type: list
|
||||
membermanager_user:
|
||||
description:
|
||||
- List of member manager users assigned to this hostgroup.
|
||||
- Only usable with IPA versions 4.8.4 and up.
|
||||
required: false
|
||||
type: list
|
||||
membermanager_group:
|
||||
description:
|
||||
- List of member manager groups assigned to this hostgroup.
|
||||
- Only usable with IPA versions 4.8.4 and up.
|
||||
required: false
|
||||
type: list
|
||||
action:
|
||||
description: Work on hostgroup or member level
|
||||
default: hostgroup
|
||||
@@ -117,7 +129,7 @@ RETURN = """
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
|
||||
temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
|
||||
module_params_get
|
||||
module_params_get, gen_add_del_lists, api_check_command
|
||||
|
||||
|
||||
def find_hostgroup(module, name):
|
||||
@@ -171,6 +183,9 @@ def main():
|
||||
nomembers=dict(required=False, type='bool', default=None),
|
||||
host=dict(required=False, type='list', default=None),
|
||||
hostgroup=dict(required=False, type='list', default=None),
|
||||
membermanager_user=dict(required=False, type='list', default=None),
|
||||
membermanager_group=dict(required=False, type='list',
|
||||
default=None),
|
||||
action=dict(type="str", default="hostgroup",
|
||||
choices=["member", "hostgroup"]),
|
||||
# state
|
||||
@@ -196,6 +211,10 @@ def main():
|
||||
nomembers = module_params_get(ansible_module, "nomembers")
|
||||
host = module_params_get(ansible_module, "host")
|
||||
hostgroup = module_params_get(ansible_module, "hostgroup")
|
||||
membermanager_user = module_params_get(ansible_module,
|
||||
"membermanager_user")
|
||||
membermanager_group = module_params_get(ansible_module,
|
||||
"membermanager_group")
|
||||
action = module_params_get(ansible_module, "action")
|
||||
# state
|
||||
state = module_params_get(ansible_module, "state")
|
||||
@@ -239,6 +258,15 @@ def main():
|
||||
ipaadmin_password)
|
||||
api_connect()
|
||||
|
||||
has_add_membermanager = api_check_command(
|
||||
"hostgroup_add_member_manager")
|
||||
if ((membermanager_user is not None or
|
||||
membermanager_group is not None) and not has_add_membermanager):
|
||||
ansible_module.fail_json(
|
||||
msg="Managing a membermanager user or group is not supported "
|
||||
"by your IPA version"
|
||||
)
|
||||
|
||||
commands = []
|
||||
|
||||
for name in names:
|
||||
@@ -268,18 +296,11 @@ def main():
|
||||
if not compare_args_ipa(ansible_module, member_args,
|
||||
res_find):
|
||||
# Generate addition and removal lists
|
||||
host_add = list(
|
||||
set(host or []) -
|
||||
set(res_find.get("member_host", [])))
|
||||
host_del = list(
|
||||
set(res_find.get("member_host", [])) -
|
||||
set(host or []))
|
||||
hostgroup_add = list(
|
||||
set(hostgroup or []) -
|
||||
set(res_find.get("member_hostgroup", [])))
|
||||
hostgroup_del = list(
|
||||
set(res_find.get("member_hostgroup", [])) -
|
||||
set(hostgroup or []))
|
||||
host_add, host_del = gen_add_del_lists(
|
||||
host, res_find.get("member_host"))
|
||||
|
||||
hostgroup_add, hostgroup_del = gen_add_del_lists(
|
||||
hostgroup, res_find.get("member_hostgroup"))
|
||||
|
||||
# Add members
|
||||
if len(host_add) > 0 or len(hostgroup_add) > 0:
|
||||
@@ -295,6 +316,41 @@ def main():
|
||||
"host": host_del,
|
||||
"hostgroup": hostgroup_del,
|
||||
}])
|
||||
|
||||
membermanager_user_add, membermanager_user_del = \
|
||||
gen_add_del_lists(
|
||||
membermanager_user,
|
||||
res_find.get("membermanager_user")
|
||||
)
|
||||
|
||||
membermanager_group_add, membermanager_group_del = \
|
||||
gen_add_del_lists(
|
||||
membermanager_group,
|
||||
res_find.get("membermanager_group")
|
||||
)
|
||||
|
||||
if has_add_membermanager:
|
||||
# Add membermanager users and groups
|
||||
if len(membermanager_user_add) > 0 or \
|
||||
len(membermanager_group_add) > 0:
|
||||
commands.append(
|
||||
[name, "hostgroup_add_member_manager",
|
||||
{
|
||||
"user": membermanager_user_add,
|
||||
"group": membermanager_group_add,
|
||||
}]
|
||||
)
|
||||
# Remove member manager
|
||||
if len(membermanager_user_del) > 0 or \
|
||||
len(membermanager_group_del) > 0:
|
||||
commands.append(
|
||||
[name, "hostgroup_remove_member_manager",
|
||||
{
|
||||
"user": membermanager_user_del,
|
||||
"group": membermanager_group_del,
|
||||
}]
|
||||
)
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(
|
||||
@@ -306,6 +362,19 @@ def main():
|
||||
"host": host,
|
||||
"hostgroup": hostgroup,
|
||||
}])
|
||||
|
||||
if has_add_membermanager:
|
||||
# Add membermanager users and groups
|
||||
if membermanager_user is not None or \
|
||||
membermanager_group is not None:
|
||||
commands.append(
|
||||
[name, "hostgroup_add_member_manager",
|
||||
{
|
||||
"user": membermanager_user,
|
||||
"group": membermanager_group,
|
||||
}]
|
||||
)
|
||||
|
||||
elif state == "absent":
|
||||
if action == "hostgroup":
|
||||
if res_find is not None:
|
||||
@@ -322,6 +391,19 @@ def main():
|
||||
"host": host,
|
||||
"hostgroup": hostgroup,
|
||||
}])
|
||||
|
||||
if has_add_membermanager:
|
||||
# Remove membermanager users and groups
|
||||
if membermanager_user is not None or \
|
||||
membermanager_group is not None:
|
||||
commands.append(
|
||||
[name, "hostgroup_remove_member_manager",
|
||||
{
|
||||
"user": membermanager_user,
|
||||
"group": membermanager_group,
|
||||
}]
|
||||
)
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
|
||||
@@ -90,6 +90,14 @@ options:
|
||||
required: false
|
||||
type: list
|
||||
aliases: ["krbprincipalname"]
|
||||
smb:
|
||||
description: Add a SMB service. Can only be used with new services.
|
||||
required: false
|
||||
type: bool
|
||||
netbiosname:
|
||||
description: NETBIOS name for the SMB service.
|
||||
required: false
|
||||
type: str
|
||||
host:
|
||||
description: Host that can manage the service.
|
||||
required: false
|
||||
@@ -135,6 +143,12 @@ options:
|
||||
required: false
|
||||
type: list
|
||||
aliases: ["ipaallowedtoperform_read_keys_hostgroup"]
|
||||
continue:
|
||||
description:
|
||||
Continuous mode. Don't stop on errors. Valid only if `state` is `absent`.
|
||||
required: false
|
||||
default: True
|
||||
type: bool
|
||||
action:
|
||||
description: Work on service or member level
|
||||
default: service
|
||||
@@ -142,7 +156,7 @@ options:
|
||||
state:
|
||||
description: State to ensure
|
||||
default: present
|
||||
choices: ["present", "absent", "enabled", "disabled"]
|
||||
choices: ["present", "absent", "disabled"]
|
||||
author:
|
||||
- Rafael Jeffman
|
||||
"""
|
||||
@@ -217,20 +231,31 @@ from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
|
||||
temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
|
||||
encode_certificate, gen_add_del_lists, module_params_get, to_text, \
|
||||
api_check_param
|
||||
import ipalib.errors
|
||||
|
||||
|
||||
def find_service(module, name):
|
||||
def find_service(module, name, netbiosname):
|
||||
_args = {
|
||||
"all": True,
|
||||
}
|
||||
|
||||
_result = api_command(module, "service_find", to_text(name), _args)
|
||||
# Search for a SMB/cifs service.
|
||||
if netbiosname is not None:
|
||||
_result = api_command(
|
||||
module, "service_find", to_text(netbiosname), _args)
|
||||
|
||||
if len(_result["result"]) > 1:
|
||||
module.fail_json(
|
||||
msg="There is more than one service '%s'" % (name))
|
||||
elif len(_result["result"]) == 1:
|
||||
_res = _result["result"][0]
|
||||
for _res_find in _result.get('result', []):
|
||||
for uid in _res_find.get('uid', []):
|
||||
if uid.startswith("%s$@" % netbiosname):
|
||||
return _res_find
|
||||
|
||||
try:
|
||||
_result = api_command(module, "service_show", to_text(name), _args)
|
||||
except ipalib.errors.NotFound:
|
||||
return None
|
||||
|
||||
if "result" in _result:
|
||||
_res = _result["result"]
|
||||
certs = _res.get("usercertificate")
|
||||
if certs is not None:
|
||||
_res["usercertificate"] = [encode_certificate(cert) for
|
||||
@@ -268,7 +293,7 @@ def check_parameters(module, state, action, names, parameters):
|
||||
# invalid parameters for everything but state 'present', action 'service'.
|
||||
invalid = ['pac_type', 'auth_ind', 'skip_host_check',
|
||||
'force', 'requires_pre_auth', 'ok_as_delegate',
|
||||
'ok_to_auth_as_delegate']
|
||||
'ok_to_auth_as_delegate', 'smb', 'netbiosname']
|
||||
|
||||
# invalid parameters when not handling service members.
|
||||
invalid_not_member = \
|
||||
@@ -283,7 +308,19 @@ def check_parameters(module, state, action, names, parameters):
|
||||
module.fail_json(msg="Only one service can be added at a time.")
|
||||
|
||||
if action == 'service':
|
||||
invalid = []
|
||||
invalid = ['delete_continue']
|
||||
|
||||
if parameters.get('smb', False):
|
||||
invalid.extend(['force', 'auth_ind', 'skip_host_check',
|
||||
'requires_pre_auth', 'auth_ind', 'pac_type'])
|
||||
|
||||
for _invalid in invalid:
|
||||
if parameters.get(_invalid, False):
|
||||
module.fail_json(
|
||||
msg="Argument '%s' can not be used with SMB "
|
||||
"service." % _invalid)
|
||||
else:
|
||||
invalid.append('delete_continue')
|
||||
|
||||
elif state == 'absent':
|
||||
if len(names) < 1:
|
||||
@@ -291,9 +328,12 @@ def check_parameters(module, state, action, names, parameters):
|
||||
|
||||
if action == "service":
|
||||
invalid.extend(invalid_not_member)
|
||||
else:
|
||||
invalid.extend('delete_continue')
|
||||
|
||||
elif state == 'disabled':
|
||||
invalid.extend(invalid_not_member)
|
||||
invalid.append('delete_continue')
|
||||
if action != "service":
|
||||
module.fail_json(
|
||||
msg="Invalid action '%s' for state '%s'" % (action, state))
|
||||
@@ -302,10 +342,10 @@ def check_parameters(module, state, action, names, parameters):
|
||||
module.fail_json(msg="Invalid state '%s'" % (state))
|
||||
|
||||
for _invalid in invalid:
|
||||
if parameters[_invalid] is not None:
|
||||
if _invalid in parameters and parameters[_invalid] is not None:
|
||||
module.fail_json(
|
||||
msg="Argument '%s' can not be used with state '%s'" %
|
||||
(_invalid, state))
|
||||
msg="Argument '%s' can not be used with state '%s', "
|
||||
"action '%s'" % (_invalid, state, action))
|
||||
|
||||
|
||||
def init_ansible_module():
|
||||
@@ -322,11 +362,13 @@ def init_ansible_module():
|
||||
default=None, required=False),
|
||||
principal=dict(type="list", aliases=["krbprincipalname"],
|
||||
default=None),
|
||||
smb=dict(type="bool", required=False),
|
||||
netbiosname=dict(type="str", required=False),
|
||||
pac_type=dict(type="list", aliases=["ipakrbauthzdata"],
|
||||
choices=["MS-PAC", "PAD", "NONE"]),
|
||||
auth_ind=dict(type="str",
|
||||
auth_ind=dict(type="list",
|
||||
aliases=["krbprincipalauthind"],
|
||||
choices=["otp", "radius", "pkinit", "hardened"]),
|
||||
choices=["otp", "radius", "pkinit", "hardened", ""]),
|
||||
skip_host_check=dict(type="bool"),
|
||||
force=dict(type="bool"),
|
||||
requires_pre_auth=dict(
|
||||
@@ -359,13 +401,14 @@ def init_ansible_module():
|
||||
allow_retrieve_keytab_hostgroup=dict(
|
||||
type="list", required=False,
|
||||
aliases=['ipaallowedtoperform_read_keys_hostgroup']),
|
||||
delete_continue=dict(type="bool", required=False,
|
||||
aliases=['continue']),
|
||||
# action
|
||||
action=dict(type="str", default="service",
|
||||
choices=["member", "service"]),
|
||||
# state
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent",
|
||||
"enabled", "disabled"]),
|
||||
choices=["present", "absent", "disabled"]),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
@@ -398,6 +441,9 @@ def main():
|
||||
ok_to_auth_as_delegate = module_params_get(ansible_module,
|
||||
"ok_to_auth_as_delegate")
|
||||
|
||||
smb = module_params_get(ansible_module, "smb")
|
||||
netbiosname = module_params_get(ansible_module, "netbiosname")
|
||||
|
||||
host = module_params_get(ansible_module, "host")
|
||||
|
||||
allow_create_keytab_user = module_params_get(
|
||||
@@ -417,6 +463,7 @@ def main():
|
||||
ansible_module, "allow_create_keytab_host")
|
||||
allow_retrieve_keytab_hostgroup = module_params_get(
|
||||
ansible_module, "allow_retrieve_keytab_hostgroup")
|
||||
delete_continue = module_params_get(ansible_module, "delete_continue")
|
||||
|
||||
# action
|
||||
action = module_params_get(ansible_module, "action")
|
||||
@@ -447,9 +494,11 @@ def main():
|
||||
commands = []
|
||||
|
||||
for name in names:
|
||||
res_find = find_service(ansible_module, name)
|
||||
res_find = find_service(ansible_module, name, netbiosname)
|
||||
|
||||
if state == "present":
|
||||
# if service exists, 'smb' cannot be used.
|
||||
|
||||
if action == "service":
|
||||
args = gen_args(
|
||||
pac_type, auth_ind, skip_host_check, force,
|
||||
@@ -459,7 +508,12 @@ def main():
|
||||
del args['skip_host_check']
|
||||
|
||||
if res_find is None:
|
||||
commands.append([name, 'service_add', args])
|
||||
if smb:
|
||||
if netbiosname is not None:
|
||||
args['ipantflatname'] = netbiosname
|
||||
commands.append([name, 'service_add_smb', args])
|
||||
else:
|
||||
commands.append([name, 'service_add', args])
|
||||
|
||||
certificate_add = certificate or []
|
||||
certificate_del = []
|
||||
@@ -699,7 +753,8 @@ def main():
|
||||
elif state == "absent":
|
||||
if action == "service":
|
||||
if res_find is not None:
|
||||
commands.append([name, 'service_del', {}])
|
||||
args = {'continue': True if delete_continue else False}
|
||||
commands.append([name, 'service_del', args])
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
|
||||
@@ -110,7 +110,8 @@ RETURN = """
|
||||
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, compare_args_ipa
|
||||
temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
|
||||
gen_add_del_lists
|
||||
|
||||
|
||||
def find_sudocmdgroup(module, name):
|
||||
@@ -257,12 +258,10 @@ def main():
|
||||
if not compare_args_ipa(ansible_module, member_args,
|
||||
res_find):
|
||||
# Generate addition and removal lists
|
||||
sudocmdgroup_add = list(
|
||||
set(sudocmdgroup or []) -
|
||||
set(res_find.get("member_sudocmdgroup", [])))
|
||||
sudocmdgroup_del = list(
|
||||
set(res_find.get("member_sudocmdgroup", [])) -
|
||||
set(sudocmdgroup or []))
|
||||
sudocmdgroup_add, sudocmdgroup_del = \
|
||||
gen_add_del_lists(
|
||||
sudocmdgroup,
|
||||
res_find.get("member_sudocmdgroup"))
|
||||
|
||||
# Add members
|
||||
if len(sudocmdgroup_add) > 0:
|
||||
|
||||
@@ -51,18 +51,21 @@ options:
|
||||
usercategory:
|
||||
description: User category the sudo rule applies to
|
||||
required: false
|
||||
choices: ["all"]
|
||||
choices: ["all", ""]
|
||||
aliases: ["usercat"]
|
||||
usergroup:
|
||||
description: List of user groups assigned to the sudo rule.
|
||||
required: false
|
||||
runasgroupcategory:
|
||||
description: RunAs Group category applied to the sudo rule.
|
||||
required: false
|
||||
choices: ["all"]
|
||||
choices: ["all", ""]
|
||||
aliases: ["runasgroupcat"]
|
||||
runasusercategory:
|
||||
description: RunAs User category applied to the sudorule.
|
||||
required: false
|
||||
choices: ["all"]
|
||||
choices: ["all", ""]
|
||||
aliases: ["runasusercat"]
|
||||
nomembers:
|
||||
description: Suppress processing of membership attributes
|
||||
required: false
|
||||
@@ -78,7 +81,8 @@ options:
|
||||
hostcategory:
|
||||
description: Host category the sudo rule applies to.
|
||||
required: false
|
||||
choices: ["all"]
|
||||
choices: ["all", ""]
|
||||
aliases: ["hostcat"]
|
||||
allow_sudocmd:
|
||||
description: List of allowed sudocmds assigned to this sudorule.
|
||||
required: false
|
||||
@@ -98,7 +102,8 @@ options:
|
||||
cmdcategory:
|
||||
description: Command category the sudo rule applies to
|
||||
required: false
|
||||
choices: ["all"]
|
||||
choices: ["all", ""]
|
||||
aliases: ["cmdcat"]
|
||||
order:
|
||||
description: Order to apply this rule.
|
||||
required: false
|
||||
@@ -241,9 +246,9 @@ def main():
|
||||
# present
|
||||
description=dict(required=False, type="str", default=None),
|
||||
usercategory=dict(required=False, type="str", default=None,
|
||||
choices=["all"]),
|
||||
choices=["all", ""], aliases=['usercat']),
|
||||
hostcategory=dict(required=False, type="str", default=None,
|
||||
choices=["all"]),
|
||||
choices=["all", ""], aliases=['hostcat']),
|
||||
nomembers=dict(required=False, type='bool', default=None),
|
||||
host=dict(required=False, type='list', default=None),
|
||||
hostgroup=dict(required=False, type='list', default=None),
|
||||
@@ -254,11 +259,13 @@ def main():
|
||||
allow_sudocmdgroup=dict(required=False, type="list", default=None),
|
||||
deny_sudocmdgroup=dict(required=False, type="list", default=None),
|
||||
cmdcategory=dict(required=False, type="str", default=None,
|
||||
choices=["all"]),
|
||||
choices=["all", ""], aliases=['cmdcat']),
|
||||
runasusercategory=dict(required=False, type="str", default=None,
|
||||
choices=["all"]),
|
||||
choices=["all", ""],
|
||||
aliases=['runasusercat']),
|
||||
runasgroupcategory=dict(required=False, type="str", default=None,
|
||||
choices=["all"]),
|
||||
choices=["all", ""],
|
||||
aliases=['runasgroupcat']),
|
||||
runasuser=dict(required=False, type="list", default=None),
|
||||
runasgroup=dict(required=False, type="list", default=None),
|
||||
order=dict(type="int", required=False, aliases=['sudoorder']),
|
||||
@@ -332,6 +339,17 @@ def main():
|
||||
ansible_module.fail_json(
|
||||
msg="Argument '%s' can not be used with action "
|
||||
"'%s'" % (arg, action))
|
||||
else:
|
||||
if hostcategory == 'all' and any([host, hostgroup]):
|
||||
ansible_module.fail_json(
|
||||
msg="Hosts cannot be added when host category='all'")
|
||||
if usercategory == 'all' and any([user, group]):
|
||||
ansible_module.fail_json(
|
||||
msg="Users cannot be added when user category='all'")
|
||||
if cmdcategory == 'all' \
|
||||
and any([allow_sudocmd, allow_sudocmdgroup]):
|
||||
ansible_module.fail_json(
|
||||
msg="Commands cannot be added when command category='all'")
|
||||
|
||||
elif state == "absent":
|
||||
if len(names) < 1:
|
||||
|
||||
@@ -186,7 +186,9 @@ options:
|
||||
description: List of base-64 encoded user certificates
|
||||
required: false
|
||||
certmapdata:
|
||||
description: List of certificate mappings
|
||||
description:
|
||||
- List of certificate mappings
|
||||
- Only usable with IPA versions 4.5 and up.
|
||||
options:
|
||||
certificate:
|
||||
description: Base-64 encoded user certificate
|
||||
@@ -197,6 +199,9 @@ options:
|
||||
subject:
|
||||
description: Subject of the certificate
|
||||
required: false
|
||||
data:
|
||||
description: Certmap data
|
||||
required: false
|
||||
required: false
|
||||
noprivate:
|
||||
description: Don't create user private group
|
||||
@@ -346,7 +351,9 @@ options:
|
||||
description: List of base-64 encoded user certificates
|
||||
required: false
|
||||
certmapdata:
|
||||
description: List of certificate mappings
|
||||
description:
|
||||
- List of certificate mappings
|
||||
- Only usable with IPA versions 4.5 and up.
|
||||
options:
|
||||
certificate:
|
||||
description: Base-64 encoded user certificate
|
||||
@@ -357,6 +364,9 @@ options:
|
||||
subject:
|
||||
description: Subject of the certificate
|
||||
required: false
|
||||
data:
|
||||
description: Certmap data
|
||||
required: false
|
||||
required: false
|
||||
noprivate:
|
||||
description: Don't create user private group
|
||||
@@ -467,7 +477,8 @@ 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, date_format, \
|
||||
compare_args_ipa, module_params_get, api_check_param, api_get_realm, \
|
||||
api_command_no_name
|
||||
api_command_no_name, gen_add_del_lists, encode_certificate, \
|
||||
load_cert_from_str, DN_x500_text, api_check_command
|
||||
import six
|
||||
|
||||
|
||||
@@ -497,6 +508,11 @@ def find_user(module, name, preserved=False):
|
||||
for x in _result["krbprincipalname"]:
|
||||
_list.append(str(x))
|
||||
_result["krbprincipalname"] = _list
|
||||
certs = _result.get("usercertificate")
|
||||
if certs is not None:
|
||||
_result["usercertificate"] = [encode_certificate(x)
|
||||
for x in certs]
|
||||
|
||||
return _result
|
||||
else:
|
||||
return None
|
||||
@@ -640,13 +656,21 @@ def check_parameters(module, state, action,
|
||||
certificate = x.get("certificate")
|
||||
issuer = x.get("issuer")
|
||||
subject = x.get("subject")
|
||||
data = x.get("data")
|
||||
|
||||
if data is not None:
|
||||
if certificate is not None or issuer is not None or \
|
||||
subject is not None:
|
||||
module.fail_json(
|
||||
msg="certmapdata: data can not be used with "
|
||||
"certificate, issuer or subject")
|
||||
check_certmapdata(data)
|
||||
if certificate is not None \
|
||||
and (issuer is not None or subject is not None):
|
||||
module.fail_json(
|
||||
msg="certmapdata: certificate can not be used with "
|
||||
"issuer or subject")
|
||||
if certificate is None:
|
||||
if data is None and certificate is None:
|
||||
if issuer is None:
|
||||
module.fail_json(msg="certmapdata: issuer is missing")
|
||||
if subject is None:
|
||||
@@ -655,25 +679,54 @@ def check_parameters(module, state, action,
|
||||
|
||||
def extend_emails(email, default_email_domain):
|
||||
if email is not None:
|
||||
return [ "%s@%s" % (_email, default_email_domain)
|
||||
if "@" not in _email else _email
|
||||
for _email in email]
|
||||
return ["%s@%s" % (_email, default_email_domain)
|
||||
if "@" not in _email else _email
|
||||
for _email in email]
|
||||
return email
|
||||
|
||||
|
||||
def gen_certmapdata_args(certmapdata):
|
||||
certificate = certmapdata.get("certificate")
|
||||
issuer = certmapdata.get("issuer")
|
||||
subject = certmapdata.get("subject")
|
||||
def convert_certmapdata(certmapdata):
|
||||
if certmapdata is None:
|
||||
return None
|
||||
|
||||
_args = {}
|
||||
if certificate is not None:
|
||||
_args["certificate"] = certificate
|
||||
if issuer is not None:
|
||||
_args["issuer"] = issuer
|
||||
if subject is not None:
|
||||
_args["subject"] = subject
|
||||
return _args
|
||||
_result = []
|
||||
for x in certmapdata:
|
||||
certificate = x.get("certificate")
|
||||
issuer = x.get("issuer")
|
||||
subject = x.get("subject")
|
||||
data = x.get("data")
|
||||
|
||||
if data is None:
|
||||
if issuer is None and subject is None:
|
||||
cert = load_cert_from_str(certificate)
|
||||
issuer = cert.issuer
|
||||
subject = cert.subject
|
||||
|
||||
_result.append("X509:<I>%s<S>%s" % (DN_x500_text(issuer),
|
||||
DN_x500_text(subject)))
|
||||
else:
|
||||
_result.append(data)
|
||||
|
||||
return _result
|
||||
|
||||
|
||||
def check_certmapdata(data):
|
||||
if not data.startswith("X509:"):
|
||||
return False
|
||||
|
||||
i = data.find("<I>", 4)
|
||||
s = data.find("<S>", i)
|
||||
issuer = data[i+3:s]
|
||||
subject = data[s+3:]
|
||||
|
||||
if i < 0 or s < 0 or "CN" not in issuer or "CN" not in subject:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def gen_certmapdata_args(certmapdata):
|
||||
return {"ipacertmapdata": to_text(certmapdata)}
|
||||
|
||||
|
||||
def main():
|
||||
@@ -735,7 +788,8 @@ def main():
|
||||
# Here certificate is a simple string
|
||||
certificate=dict(type="str", default=None),
|
||||
issuer=dict(type="str", default=None),
|
||||
subject=dict(type="str", default=None)
|
||||
subject=dict(type="str", default=None),
|
||||
data=dict(type="str", default=None)
|
||||
),
|
||||
elements='dict', required=False),
|
||||
noprivate=dict(type='bool', default=None),
|
||||
@@ -763,7 +817,7 @@ def main():
|
||||
preserve=dict(required=False, type='bool', default=None),
|
||||
|
||||
# mod
|
||||
update_password=dict(type='str', default=None,
|
||||
update_password=dict(type='str', default=None, no_log=False,
|
||||
choices=['always', 'on_create']),
|
||||
|
||||
# general
|
||||
@@ -870,6 +924,7 @@ def main():
|
||||
departmentnumber, employeenumber, employeetype, preferredlanguage,
|
||||
certificate, certmapdata, noprivate, nomembers, preserve,
|
||||
update_password)
|
||||
certmapdata = convert_certmapdata(certmapdata)
|
||||
|
||||
# Use users if names is None
|
||||
if users is not None:
|
||||
@@ -967,6 +1022,7 @@ def main():
|
||||
employeetype, preferredlanguage, certificate,
|
||||
certmapdata, noprivate, nomembers, preserve,
|
||||
update_password)
|
||||
certmapdata = convert_certmapdata(certmapdata)
|
||||
|
||||
# Extend email addresses
|
||||
|
||||
@@ -996,6 +1052,16 @@ def main():
|
||||
msg="The use of passwordexpiration is not supported by "
|
||||
"your IPA version")
|
||||
|
||||
# Check certmapdata availability.
|
||||
# We need the connected API for this test, therefore it can not
|
||||
# be part of check_parameters as this is used also before the
|
||||
# connection to the API has been established.
|
||||
if certmapdata is not None and \
|
||||
not api_check_command("user_add_certmapdata"):
|
||||
ansible_module.fail_json(
|
||||
msg="The use of certmapdata is not supported by "
|
||||
"your IPA version")
|
||||
|
||||
# Make sure user exists
|
||||
res_find = find_user(ansible_module, name)
|
||||
# Also search for preserved user if the user could not be found
|
||||
@@ -1063,36 +1129,21 @@ def main():
|
||||
# certmapdata
|
||||
if res_find is not None:
|
||||
# Generate addition and removal lists
|
||||
manager_add = list(
|
||||
set(manager or []) -
|
||||
set(res_find.get("manager", [])))
|
||||
manager_del = list(
|
||||
set(res_find.get("manager", [])) -
|
||||
set(manager or []))
|
||||
principal_add = list(
|
||||
set(principal or []) -
|
||||
set(res_find.get("krbprincipalname", [])))
|
||||
principal_del = list(
|
||||
set(res_find.get("krbprincipalname", [])) -
|
||||
set(principal or []))
|
||||
manager_add, manager_del = gen_add_del_lists(
|
||||
manager, res_find.get("manager"))
|
||||
|
||||
principal_add, principal_del = gen_add_del_lists(
|
||||
principal, res_find.get("krbprincipalname"))
|
||||
# Principals are not returned as utf8 for IPA using
|
||||
# python2 using user_find, therefore we need to
|
||||
# convert the principals that we should remove.
|
||||
principal_del = [to_text(x) for x in principal_del]
|
||||
|
||||
certificate_add = list(
|
||||
set(certificate or []) -
|
||||
set(res_find.get("certificate", [])))
|
||||
certificate_del = list(
|
||||
set(res_find.get("certificate", [])) -
|
||||
set(certificate or []))
|
||||
certmapdata_add = list(
|
||||
set(certmapdata or []) -
|
||||
set(res_find.get("ipaCertMapData", [])))
|
||||
certmapdata_del = list(
|
||||
set(res_find.get("ipaCertMapData", [])) -
|
||||
set(certmapdata or []))
|
||||
certificate_add, certificate_del = gen_add_del_lists(
|
||||
certificate, res_find.get("usercertificate"))
|
||||
|
||||
certmapdata_add, certmapdata_del = gen_add_del_lists(
|
||||
certmapdata, res_find.get("ipacertmapdata"))
|
||||
|
||||
else:
|
||||
# Use given managers and principals
|
||||
@@ -1179,7 +1230,7 @@ def main():
|
||||
# Remove certmapdata
|
||||
if len(certmapdata_del) > 0:
|
||||
for _data in certmapdata_del:
|
||||
commands.append([name, "user_add_certmapdata",
|
||||
commands.append([name, "user_remove_certmapdata",
|
||||
gen_certmapdata_args(_data)])
|
||||
|
||||
elif action == "member":
|
||||
@@ -1376,7 +1427,6 @@ def main():
|
||||
temp_kdestroy(ccache_dir, ccache_name)
|
||||
|
||||
# Done
|
||||
|
||||
ansible_module.exit_json(changed=changed, user=exit_args)
|
||||
|
||||
|
||||
|
||||
@@ -45,21 +45,41 @@ options:
|
||||
description:
|
||||
description: The vault description
|
||||
required: false
|
||||
vault_public_key:
|
||||
description: Base64 encoded public key.
|
||||
public_key:
|
||||
description: Base64 encode public key.
|
||||
required: false
|
||||
type: list
|
||||
aliases: ["ipavaultpublickey"]
|
||||
vault_salt:
|
||||
description: Vault salt.
|
||||
type: string
|
||||
aliases: ["ipavaultpublickey", "vault_public_key"]
|
||||
public_key_file:
|
||||
description: Path to file with public key.
|
||||
required: false
|
||||
type: list
|
||||
aliases: ["ipavaultsalt"]
|
||||
vault_password:
|
||||
type: string
|
||||
aliases: ["vault_public_key_file"]
|
||||
private_key:
|
||||
description: Base64 encode private key.
|
||||
required: false
|
||||
type: string
|
||||
aliases: ["ipavaultprivatekey", "vault_private_key"]
|
||||
private_key_file:
|
||||
description: Path to file with private key.
|
||||
required: false
|
||||
type: string
|
||||
aliases: ["vault_private_key_file"]
|
||||
password:
|
||||
description: password to be used on symmetric vault.
|
||||
required: false
|
||||
type: string
|
||||
aliases: ["ipavaultpassword"]
|
||||
aliases: ["ipavaultpassword", "vault_password"]
|
||||
password_file:
|
||||
description: file with password to be used on symmetric vault.
|
||||
required: false
|
||||
type: string
|
||||
aliases: ["vault_password_file"]
|
||||
salt:
|
||||
description: Vault salt.
|
||||
required: false
|
||||
type: list
|
||||
aliases: ["ipavaultsalt", "vault_salt"]
|
||||
vault_type:
|
||||
description: Vault types are based on security level.
|
||||
required: true
|
||||
@@ -79,23 +99,45 @@ options:
|
||||
description: Vault is shared.
|
||||
required: false
|
||||
type: boolean
|
||||
vault_data:
|
||||
description: Data to be stored in the vault.
|
||||
required: false
|
||||
type: string
|
||||
aliases: ["ipavaultdata"]
|
||||
owners:
|
||||
description: Users that are owners of the container.
|
||||
required: false
|
||||
type: list
|
||||
users:
|
||||
description: Users that are member of the container.
|
||||
description: Users that are member of the vault.
|
||||
required: false
|
||||
type: list
|
||||
groups:
|
||||
description: Groups that are member of the container.
|
||||
description: Groups that are member of the vault.
|
||||
required: false
|
||||
type: list
|
||||
owners:
|
||||
description: Users that are owners of the vault.
|
||||
required: false
|
||||
type: list
|
||||
ownergroups:
|
||||
description: Groups that are owners of the vault.
|
||||
required: false
|
||||
type: list
|
||||
ownerservices:
|
||||
description: Services that are owners of the vault.
|
||||
required: false
|
||||
type: list
|
||||
services:
|
||||
description: Services that are member of the container.
|
||||
required: false
|
||||
type: list
|
||||
data:
|
||||
description: Data to be stored in the vault.
|
||||
required: false
|
||||
type: string
|
||||
aliases: ["ipavaultdata", "vault_data"]
|
||||
in:
|
||||
description: Path to file with data to be stored in the vault.
|
||||
required: false
|
||||
type: string
|
||||
aliases: ["datafile_in"]
|
||||
out:
|
||||
description: Path to file to store data retrieved from the vault.
|
||||
required: false
|
||||
type: string
|
||||
aliases: ["datafile_out"]
|
||||
action:
|
||||
description: Work on vault or member level.
|
||||
default: vault
|
||||
@@ -103,7 +145,7 @@ options:
|
||||
state:
|
||||
description: State to ensure
|
||||
default: present
|
||||
choices: ["present", "absent"]
|
||||
choices: ["present", "absent", "retrieved"]
|
||||
author:
|
||||
- Rafael Jeffman
|
||||
"""
|
||||
@@ -114,9 +156,9 @@ EXAMPLES = """
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: symvault
|
||||
username: admin
|
||||
vault_password: MyVaultPassword123
|
||||
vault_salt: MTIzNDU2Nzg5MAo=
|
||||
vault_type: symmetric
|
||||
password: SomeVAULTpassword
|
||||
salt: MTIzNDU2Nzg5MAo=
|
||||
|
||||
# Ensure group ipausers is a vault member.
|
||||
- ipavault:
|
||||
@@ -178,12 +220,23 @@ EXAMPLES = """
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: symvault
|
||||
username: admin
|
||||
vault_password: MyVaultPassword123
|
||||
vault_data: >
|
||||
password: SomeVAULTpassword
|
||||
data: >
|
||||
Data archived.
|
||||
More data archived.
|
||||
action: member
|
||||
|
||||
# Retrieve data archived from a symmetric vault
|
||||
- ipavault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: symvault
|
||||
username: admin
|
||||
password: SomeVAULTpassword
|
||||
state: retrieved
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.data | b64decode }}"
|
||||
|
||||
# Ensure vault symvault is absent
|
||||
- ipavault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
@@ -198,7 +251,7 @@ EXAMPLES = """
|
||||
username: user01
|
||||
description: An asymmetric vault
|
||||
vault_type: asymmetric
|
||||
vault_public_key:
|
||||
public_key:
|
||||
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTR
|
||||
HTkFEQ0JpUUtCZ1FDdGFudjRkK3ptSTZ0T3ova1RXdGowY3AxRAowUENoYy8vR0pJMTUzTi
|
||||
9CN3UrN0h3SXlRVlZoNUlXZG1UcCtkWXYzd09yeVpPbzYvbHN5eFJaZ2pZRDRwQ3VGCjlxM
|
||||
@@ -211,11 +264,19 @@ EXAMPLES = """
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: asymvault
|
||||
username: admin
|
||||
vault_data: >
|
||||
data: >
|
||||
Data archived.
|
||||
More data archived.
|
||||
action: member
|
||||
|
||||
# Retrive data archived in an asymmetric vault, using a private key file.
|
||||
- ipavault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: asymvault
|
||||
username: admin
|
||||
private_key_file: private.pem
|
||||
state: retrieved
|
||||
|
||||
# Ensure asymmetric vault is absent.
|
||||
- ipavault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
@@ -226,9 +287,14 @@ EXAMPLES = """
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
user:
|
||||
description: The vault data.
|
||||
returned: If state is retrieved.
|
||||
type: string
|
||||
"""
|
||||
|
||||
import os
|
||||
from base64 import b64decode
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
|
||||
temp_kdestroy, valid_creds, api_connect, api_command, \
|
||||
@@ -261,7 +327,8 @@ def find_vault(module, name, username, service, shared):
|
||||
|
||||
|
||||
def gen_args(description, username, service, shared, vault_type, salt,
|
||||
public_key, vault_data):
|
||||
password, password_file, public_key, public_key_file, vault_data,
|
||||
datafile_in, datafile_out):
|
||||
_args = {}
|
||||
|
||||
if description is not None:
|
||||
@@ -277,14 +344,16 @@ def gen_args(description, username, service, shared, vault_type, salt,
|
||||
if salt is not None:
|
||||
_args['ipavaultsalt'] = salt
|
||||
if public_key is not None:
|
||||
_args['ipavaultpublickey'] = public_key
|
||||
if vault_data is not None:
|
||||
_args['data'] = vault_data.encode('utf-8')
|
||||
_args['ipavaultpublickey'] = b64decode(public_key.encode('utf-8'))
|
||||
if public_key_file is not None:
|
||||
with open(public_key_file, 'r') as keyfile:
|
||||
keydata = keyfile.read()
|
||||
_args['ipavaultpublickey'] = keydata.strip().encode('utf-8')
|
||||
|
||||
return _args
|
||||
|
||||
|
||||
def gen_member_args(args, users, groups):
|
||||
def gen_member_args(args, users, groups, services):
|
||||
_args = args.copy()
|
||||
|
||||
for arg in ['ipavaulttype', 'description', 'ipavaultpublickey',
|
||||
@@ -292,13 +361,21 @@ def gen_member_args(args, users, groups):
|
||||
if arg in _args:
|
||||
del _args[arg]
|
||||
|
||||
_args['user'] = users
|
||||
_args['group'] = groups
|
||||
if any([users, groups, services]):
|
||||
if users is not None:
|
||||
_args['user'] = users
|
||||
if groups is not None:
|
||||
_args['group'] = groups
|
||||
if services is not None:
|
||||
_args['services'] = services
|
||||
|
||||
return _args
|
||||
return _args
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def data_storage_args(args, data, password):
|
||||
def data_storage_args(args, data, password, password_file, private_key,
|
||||
private_key_file, datafile_in, datafile_out):
|
||||
_args = {}
|
||||
|
||||
if 'username' in args:
|
||||
@@ -310,53 +387,104 @@ def data_storage_args(args, data, password):
|
||||
|
||||
if password is not None:
|
||||
_args['password'] = password
|
||||
if password_file is not None:
|
||||
_args['password_file'] = password_file
|
||||
|
||||
_args['data'] = data
|
||||
if private_key is not None:
|
||||
_args['private_key'] = private_key
|
||||
if private_key_file is not None:
|
||||
_args['private_key_file'] = private_key_file
|
||||
|
||||
if datafile_in is not None:
|
||||
_args['in'] = datafile_in
|
||||
else:
|
||||
if data is None:
|
||||
_args['data'] = b''
|
||||
else:
|
||||
_args['data'] = data.encode('utf-8')
|
||||
|
||||
if datafile_out is not None:
|
||||
_args['out'] = datafile_out
|
||||
|
||||
if private_key_file is not None:
|
||||
_args['private_key_file'] = private_key_file
|
||||
|
||||
return _args
|
||||
|
||||
|
||||
def check_parameters(module, state, action, description, username, service,
|
||||
shared, users, groups, owners, ownergroups, vault_type,
|
||||
salt, password, public_key, vault_data):
|
||||
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):
|
||||
invalid = []
|
||||
if state == "present":
|
||||
if action == "member":
|
||||
invalid = ['description', 'public_key', 'salt']
|
||||
invalid = ['private_key', 'private_key_file', 'datafile_out']
|
||||
|
||||
for param in invalid:
|
||||
if vars()[param] is not None:
|
||||
module.fail_json(
|
||||
msg="Argument '%s' can not be used with action '%s'" %
|
||||
(param, action))
|
||||
if action == "member":
|
||||
invalid.extend(['description'])
|
||||
|
||||
elif state == "absent":
|
||||
invalid = ['description', 'salt']
|
||||
invalid = ['description', 'salt', 'vault_type', 'private_key',
|
||||
'private_key_file', 'datafile_in', 'datafile_out',
|
||||
'vault_data']
|
||||
|
||||
if action == "vault":
|
||||
invalid.extend(['users', 'groups', 'owners', 'ownergroups',
|
||||
'password', 'public_key'])
|
||||
invalid.extend(['users', 'groups', 'services', 'owners',
|
||||
'ownergroups', 'ownerservices', 'password',
|
||||
'password_file', 'public_key', 'public_key_file'])
|
||||
|
||||
for arg in invalid:
|
||||
if vars()[arg] is not None:
|
||||
module.fail_json(
|
||||
msg="Argument '%s' can not be used with action '%s'" %
|
||||
(arg, state))
|
||||
elif state == "retrieved":
|
||||
invalid = ['description', 'salt', 'datafile_in', 'users', 'groups',
|
||||
'owners', 'ownergroups', 'public_key', 'public_key_file',
|
||||
'vault_data']
|
||||
if action == 'member':
|
||||
module.fail_json(
|
||||
msg="State `retrieved` do not support action `member`.")
|
||||
|
||||
for arg in invalid:
|
||||
if vars()[arg] is not None:
|
||||
module.fail_json(
|
||||
msg="Argument '%s' can not be used with state '%s', "
|
||||
"action '%s'" % (arg, state, action))
|
||||
|
||||
for arg in invalid:
|
||||
if vars()[arg] is not None:
|
||||
module.fail_json(
|
||||
msg="Argument '%s' can not be used with state '%s', "
|
||||
"action '%s'" % (arg, state, action))
|
||||
|
||||
|
||||
def check_encryption_params(module, state, vault_type, password, public_key,
|
||||
vault_data, res_find):
|
||||
if state == "present":
|
||||
if vault_type == "symmetric":
|
||||
if password is None \
|
||||
and (vault_data is not None or res_find is None):
|
||||
module.fail_json(
|
||||
msg="Vault password required for symmetric vault.")
|
||||
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_type_invalid = []
|
||||
if vault_type == "standard":
|
||||
vault_type_invalid = ['public_key', 'public_key_file', 'password',
|
||||
'password_file', 'salt']
|
||||
|
||||
if vault_type == "asymmetric":
|
||||
if public_key is None and res_find is None:
|
||||
module.fail_json(
|
||||
msg="Public Key required for asymmetric vault.")
|
||||
if vault_type is None or vault_type == "symmetric":
|
||||
vault_type_invalid = ['public_key', 'public_key_file',
|
||||
'private_key', 'private_key_file']
|
||||
|
||||
if password is None and password_file is None and action != 'member':
|
||||
module.fail_json(
|
||||
msg="Symmetric vault requires password or password_file "
|
||||
"to store data or change `salt`.")
|
||||
|
||||
if vault_type == "asymmetric":
|
||||
vault_type_invalid = ['password', 'password_file']
|
||||
if not any([public_key, public_key_file]) and res_find is None:
|
||||
module.fail_json(
|
||||
msg="Assymmetric vault requires public_key "
|
||||
"or public_key_file to store data.")
|
||||
|
||||
for param in vault_type_invalid:
|
||||
if vars()[param] is not None:
|
||||
module.fail_json(
|
||||
msg="Argument '%s' cannot be used with vault type '%s'" %
|
||||
(param, vault_type or 'symmetric'))
|
||||
|
||||
|
||||
def main():
|
||||
@@ -369,16 +497,23 @@ def main():
|
||||
name=dict(type="list", aliases=["cn"], default=None,
|
||||
required=True),
|
||||
|
||||
# present
|
||||
|
||||
description=dict(required=False, type="str", default=None),
|
||||
vault_type=dict(type="str", aliases=["ipavaulttype"],
|
||||
default=None, required=False,
|
||||
choices=["standard", "symmetric", "asymmetric"]),
|
||||
vault_public_key=dict(type="str", required=False, default=None,
|
||||
aliases=['ipavaultpublickey']),
|
||||
aliases=['ipavaultpublickey', 'public_key']),
|
||||
vault_public_key_file=dict(type="str", required=False,
|
||||
default=None,
|
||||
aliases=['public_key_file']),
|
||||
vault_private_key=dict(
|
||||
type="str", required=False, default=None, no_log=True,
|
||||
aliases=['ipavaultprivatekey', 'private_key']),
|
||||
vault_private_key_file=dict(type="str", required=False,
|
||||
default=None,
|
||||
aliases=['private_key_file']),
|
||||
vault_salt=dict(type="str", required=False, default=None,
|
||||
aliases=['ipavaultsalt']),
|
||||
aliases=['ipavaultsalt', 'salt']),
|
||||
username=dict(type="str", required=False, default=None,
|
||||
aliases=['user']),
|
||||
service=dict(type="str", required=False, default=None),
|
||||
@@ -386,23 +521,33 @@ def main():
|
||||
|
||||
users=dict(required=False, type='list', default=None),
|
||||
groups=dict(required=False, type='list', default=None),
|
||||
owners=dict(required=False, type='list', default=None),
|
||||
services=dict(required=False, type='list', default=None),
|
||||
owners=dict(required=False, type='list', default=None,
|
||||
aliases=['ownerusers']),
|
||||
ownergroups=dict(required=False, type='list', default=None),
|
||||
|
||||
ownerservices=dict(required=False, type='list', default=None),
|
||||
vault_data=dict(type="str", required=False, default=None,
|
||||
aliases=['ipavaultdata']),
|
||||
no_log=True, aliases=['ipavaultdata', 'data']),
|
||||
datafile_in=dict(type="str", required=False, default=None,
|
||||
aliases=['in']),
|
||||
datafile_out=dict(type="str", required=False, default=None,
|
||||
aliases=['out']),
|
||||
vault_password=dict(type="str", required=False, default=None,
|
||||
no_log=True, aliases=['ipavaultpassword']),
|
||||
|
||||
aliases=['ipavaultpassword', 'password'],
|
||||
no_log=True),
|
||||
vault_password_file=dict(type="str", required=False, default=None,
|
||||
no_log=False, aliases=['password_file']),
|
||||
# state
|
||||
action=dict(type="str", default="vault",
|
||||
choices=["vault", "data", "member"]),
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent"]),
|
||||
choices=["present", "absent", "retrieved"]),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=[['username', 'service', 'shared']],
|
||||
required_one_of=[['username', 'service', 'shared']]
|
||||
mutually_exclusive=[['username', 'service', 'shared'],
|
||||
['datafile_in', 'vault_data'],
|
||||
['vault_password', 'vault_password_file'],
|
||||
['vault_public_key', 'vault_public_key_file']],
|
||||
)
|
||||
|
||||
ansible_module._ansible_debug = True
|
||||
@@ -422,18 +567,28 @@ def main():
|
||||
|
||||
users = module_params_get(ansible_module, "users")
|
||||
groups = module_params_get(ansible_module, "groups")
|
||||
services = module_params_get(ansible_module, "services")
|
||||
owners = module_params_get(ansible_module, "owners")
|
||||
ownergroups = module_params_get(ansible_module, "ownergroups")
|
||||
ownerservices = module_params_get(ansible_module, "ownerservices")
|
||||
|
||||
vault_type = module_params_get(ansible_module, "vault_type")
|
||||
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")
|
||||
public_key = module_params_get(ansible_module, "vault_public_key")
|
||||
public_key_file = module_params_get(ansible_module,
|
||||
"vault_public_key_file")
|
||||
private_key = module_params_get(ansible_module, "vault_private_key")
|
||||
private_key_file = module_params_get(ansible_module,
|
||||
"vault_private_key_file")
|
||||
|
||||
vault_data = module_params_get(ansible_module, "vault_data")
|
||||
|
||||
datafile_in = module_params_get(ansible_module, "datafile_in")
|
||||
datafile_out = module_params_get(ansible_module, "datafile_out")
|
||||
|
||||
action = module_params_get(ansible_module, "action")
|
||||
# state
|
||||
state = module_params_get(ansible_module, "state")
|
||||
|
||||
# Check parameters
|
||||
@@ -447,12 +602,19 @@ def main():
|
||||
if len(names) < 1:
|
||||
ansible_module.fail_json(msg="No name given.")
|
||||
|
||||
elif state == "retrieved":
|
||||
if len(names) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Only one vault can be retrieved at a time.")
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Invalid state '%s'" % state)
|
||||
|
||||
check_parameters(ansible_module, state, action, description, username,
|
||||
service, shared, users, groups, owners, ownergroups,
|
||||
vault_type, salt, password, public_key, vault_data)
|
||||
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)
|
||||
# Init
|
||||
|
||||
changed = False
|
||||
@@ -463,6 +625,9 @@ def main():
|
||||
if not valid_creds(ansible_module, ipaadmin_principal):
|
||||
ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
|
||||
ipaadmin_password)
|
||||
# Need to set krb5 ccache name, due to context='ansible-freeipa'
|
||||
if ccache_name is not None:
|
||||
os.environ["KRB5CCNAME"] = ccache_name
|
||||
|
||||
api_connect(context='ansible-freeipa')
|
||||
|
||||
@@ -475,7 +640,10 @@ def main():
|
||||
|
||||
# Generate args
|
||||
args = gen_args(description, username, service, shared, vault_type,
|
||||
salt, public_key, vault_data)
|
||||
salt, password, password_file, public_key,
|
||||
public_key_file, vault_data, datafile_in,
|
||||
datafile_out)
|
||||
pwdargs = None
|
||||
|
||||
# Set default vault_type if needed.
|
||||
if vault_type is None and vault_data is not None:
|
||||
@@ -485,12 +653,14 @@ def main():
|
||||
else:
|
||||
args['ipavaulttype'] = vault_type = "symmetric"
|
||||
|
||||
# verify data encription args
|
||||
check_encryption_params(ansible_module, state, vault_type,
|
||||
password, public_key, vault_data, res_find)
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
# 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)
|
||||
|
||||
# Found the vault
|
||||
if action == "vault":
|
||||
@@ -501,16 +671,13 @@ def main():
|
||||
if not compare_args_ipa(ansible_module, args,
|
||||
res_find):
|
||||
commands.append([name, "vault_mod_internal", args])
|
||||
else:
|
||||
if 'ipavaultsault' not in args:
|
||||
args['ipavaultsalt'] = os.urandom(32)
|
||||
commands.append([name, "vault_add_internal", args])
|
||||
# archive empty data to set password
|
||||
pwdargs = data_storage_args(
|
||||
args, args.get('data', ''), password)
|
||||
commands.append([name, "vault_archive", pwdargs])
|
||||
|
||||
# Set res_find to empty dict for next step # noqa
|
||||
else:
|
||||
commands.append([name, "vault_add_internal", args])
|
||||
if vault_type != 'standard' and vault_data is None:
|
||||
vault_data = ''
|
||||
|
||||
# Set res_find to empty dict for next steps
|
||||
res_find = {}
|
||||
|
||||
# Generate adittion and removal lists
|
||||
@@ -520,54 +687,98 @@ def main():
|
||||
group_add, group_del = \
|
||||
gen_add_del_lists(groups,
|
||||
res_find.get('member_group', []))
|
||||
service_add, service_del = \
|
||||
gen_add_del_lists(services,
|
||||
res_find.get('member_service', []))
|
||||
|
||||
owner_add, owner_del = \
|
||||
gen_add_del_lists(owners,
|
||||
res_find.get('owner_user', []))
|
||||
|
||||
ownergroups_add, ownergroups_del = \
|
||||
gen_add_del_lists(ownergroups,
|
||||
res_find.get('owner_group', []))
|
||||
|
||||
ownerservice_add, ownerservice_del = \
|
||||
gen_add_del_lists(ownerservices,
|
||||
res_find.get('owner_service', []))
|
||||
|
||||
# Add users and groups
|
||||
if len(user_add) > 0 or len(group_add) > 0:
|
||||
user_add_args = gen_member_args(args, user_add,
|
||||
group_add)
|
||||
commands.append([name, 'vault_add_member',
|
||||
user_add_args])
|
||||
user_add_args = gen_member_args(args, user_add,
|
||||
group_add, service_add)
|
||||
if user_add_args is not None:
|
||||
commands.append(
|
||||
[name, 'vault_add_member', user_add_args])
|
||||
|
||||
# Remove users and groups
|
||||
if len(user_del) > 0 or len(group_del) > 0:
|
||||
user_del_args = gen_member_args(args, user_del,
|
||||
group_del)
|
||||
commands.append([name, 'vault_remove_member',
|
||||
user_del_args])
|
||||
user_del_args = gen_member_args(args, user_del,
|
||||
group_del, service_del)
|
||||
if user_del_args is not None:
|
||||
commands.append(
|
||||
[name, 'vault_remove_member', user_del_args])
|
||||
|
||||
# Add owner users and groups
|
||||
if len(user_add) > 0 or len(group_add) > 0:
|
||||
owner_add_args = gen_member_args(args, owner_add,
|
||||
ownergroups_add)
|
||||
commands.append([name, 'vault_add_owner',
|
||||
owner_add_args])
|
||||
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])
|
||||
|
||||
# Remove owner users and groups
|
||||
if len(user_del) > 0 or len(group_del) > 0:
|
||||
owner_del_args = gen_member_args(args, owner_del,
|
||||
ownergroups_del)
|
||||
commands.append([name, 'vault_remove_owner',
|
||||
owner_del_args])
|
||||
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])
|
||||
|
||||
if vault_type == 'symmetric' \
|
||||
and 'ipavaultsalt' not in args:
|
||||
args['ipavaultsalt'] = os.urandom(32)
|
||||
|
||||
if vault_type == 'symmetric' \
|
||||
and 'ipavaultsalt' not in args:
|
||||
args['ipavaultsalt'] = os.urandom(32)
|
||||
|
||||
elif action in "member":
|
||||
# Add users and groups
|
||||
if users is not None or groups is not None:
|
||||
user_args = gen_member_args(args, users, groups)
|
||||
if any([users, groups, services]):
|
||||
user_args = gen_member_args(args, users, groups,
|
||||
services)
|
||||
commands.append([name, 'vault_add_member', user_args])
|
||||
if owners is not None or ownergroups is not None:
|
||||
owner_args = gen_member_args(args, owners, ownergroups)
|
||||
if any([owners, ownergroups, ownerservices]):
|
||||
owner_args = gen_member_args(args, owners, ownergroups,
|
||||
ownerservices)
|
||||
commands.append([name, 'vault_add_owner', owner_args])
|
||||
|
||||
if vault_data is not None:
|
||||
data_args = data_storage_args(
|
||||
args, args.get('data', ''), password)
|
||||
commands.append([name, 'vault_archive', data_args])
|
||||
pwdargs = data_storage_args(
|
||||
args, vault_data, password, password_file, private_key,
|
||||
private_key_file, datafile_in, datafile_out)
|
||||
if any([vault_data, datafile_in]):
|
||||
commands.append([name, "vault_archive", pwdargs])
|
||||
|
||||
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)
|
||||
|
||||
pwdargs = data_storage_args(
|
||||
args, vault_data, password, password_file, private_key,
|
||||
private_key_file, datafile_in, datafile_out)
|
||||
if 'data' in pwdargs:
|
||||
del pwdargs['data']
|
||||
|
||||
commands.append([name, "vault_retrieve", pwdargs])
|
||||
|
||||
elif state == "absent":
|
||||
if 'ipavaulttype' in args:
|
||||
@@ -579,32 +790,46 @@ def main():
|
||||
|
||||
elif action == "member":
|
||||
# remove users and groups
|
||||
if users is not None or groups is not None:
|
||||
user_args = gen_member_args(args, users, groups)
|
||||
commands.append([name, 'vault_remove_member',
|
||||
user_args])
|
||||
if any([users, groups, services]):
|
||||
user_args = gen_member_args(
|
||||
args, users, groups, services)
|
||||
commands.append(
|
||||
[name, 'vault_remove_member', user_args])
|
||||
|
||||
if owners is not None or ownergroups is not None:
|
||||
owner_args = gen_member_args(args, owners, ownergroups)
|
||||
commands.append([name, 'vault_remove_owner',
|
||||
owner_args])
|
||||
if any([owners, ownergroups, ownerservices]):
|
||||
owner_args = gen_member_args(
|
||||
args, owners, ownergroups, ownerservices)
|
||||
commands.append(
|
||||
[name, 'vault_remove_owner', owner_args])
|
||||
else:
|
||||
ansible_module.fail_json(
|
||||
msg="Invalid action '%s' for state '%s'" %
|
||||
(action, state))
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
ansible_module.fail_json(msg="Unknown state '%s'" % state)
|
||||
|
||||
# Execute commands
|
||||
|
||||
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':
|
||||
changed = 'Archived data into' in result['summary']
|
||||
elif command == 'vault_retrieve':
|
||||
if 'result' not in result:
|
||||
raise Exception("No result obtained.")
|
||||
if 'data' in result['result']:
|
||||
exit_args['data'] = result['result']['data']
|
||||
elif 'vault_data' in result['result']:
|
||||
exit_args['data'] = result['result']['vault_data']
|
||||
else:
|
||||
raise Exception("No data retrieved.")
|
||||
changed = False
|
||||
else:
|
||||
# ansible_module.warn("RESULT: %s" % (result))
|
||||
if "completed" in result:
|
||||
if result["completed"] > 0:
|
||||
changed = True
|
||||
|
||||
2
pytest.ini
Normal file
2
pytest.ini
Normal file
@@ -0,0 +1,2 @@
|
||||
[pytest]
|
||||
python_files = test_*.py
|
||||
@@ -33,9 +33,7 @@ from ansible.plugins.action import ActionBase
|
||||
|
||||
|
||||
def run_cmd(args, stdin=None):
|
||||
"""
|
||||
Execute an external command.
|
||||
"""
|
||||
"""Execute an external command."""
|
||||
p_in = None
|
||||
p_out = subprocess.PIPE
|
||||
p_err = subprocess.PIPE
|
||||
@@ -53,8 +51,10 @@ def run_cmd(args, stdin=None):
|
||||
|
||||
def kinit_password(principal, password, ccache_name, config):
|
||||
"""
|
||||
Perform kinit using principal/password, with the specified config file
|
||||
and store the TGT in ccache_name.
|
||||
Perform kinit using principal/password.
|
||||
|
||||
It uses the specified config file to kinit and stores the TGT
|
||||
in ccache_name.
|
||||
"""
|
||||
args = ["/usr/bin/kinit", principal, '-c', ccache_name]
|
||||
old_config = os.environ.get('KRB5_CONFIG')
|
||||
@@ -71,8 +71,10 @@ def kinit_password(principal, password, ccache_name, config):
|
||||
|
||||
def kinit_keytab(principal, keytab, ccache_name, config):
|
||||
"""
|
||||
Perform kinit using principal/keytab, with the specified config file
|
||||
and store the TGT in ccache_name.
|
||||
Perform kinit using principal/keytab.
|
||||
|
||||
It uses the specified config file to kinit and stores the TGT
|
||||
in ccache_name.
|
||||
"""
|
||||
if gssapi is None:
|
||||
raise ImportError("gssapi is not available")
|
||||
@@ -126,7 +128,7 @@ class ActionModule(ActionBase):
|
||||
|
||||
def run(self, tmp=None, task_vars=None):
|
||||
"""
|
||||
handler for credential cache transfer
|
||||
Handle credential cache transfer.
|
||||
|
||||
ipa* commands can either provide a password or a keytab file
|
||||
in order to authenticate on the managed node with Kerberos.
|
||||
@@ -142,7 +144,6 @@ class ActionModule(ActionBase):
|
||||
|
||||
Then the IPA commands can use this credential cache file.
|
||||
"""
|
||||
|
||||
if task_vars is None:
|
||||
task_vars = dict()
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Test ipaclient python3 binding
|
||||
from ipaclient.install.client import SECURE_PATH
|
||||
from ipaclient.install.client import SECURE_PATH # noqa: F401
|
||||
|
||||
# Check ipapython version to be >= 4.6
|
||||
from ipapython.version import NUM_VERSION, VERSION
|
||||
|
||||
@@ -100,7 +100,6 @@ def main():
|
||||
|
||||
realm = module.params.get('realm')
|
||||
hostname = module.params.get('hostname')
|
||||
servers = module.params.get('servers')
|
||||
debug = module.params.get('debug')
|
||||
|
||||
host_principal = 'host/%s@%s' % (hostname, realm)
|
||||
|
||||
@@ -13,7 +13,7 @@ from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
# pylint: disable=unused-import
|
||||
try:
|
||||
from ipalib import api
|
||||
from ipalib import api # noqa: F401
|
||||
except ImportError:
|
||||
HAS_IPALIB = False
|
||||
else:
|
||||
@@ -27,7 +27,7 @@ else:
|
||||
from ipapython import sysrestore
|
||||
|
||||
try:
|
||||
import ipaserver
|
||||
import ipaserver # noqa: F401
|
||||
except ImportError:
|
||||
HAS_IPASERVER = False
|
||||
else:
|
||||
@@ -41,7 +41,7 @@ VAR_LIB_PKI_TOMCAT = "/var/lib/pki/pki-tomcat"
|
||||
def is_ntpd_configured():
|
||||
# ntpd is configured when sysrestore.state contains the line
|
||||
# [ntpd]
|
||||
ntpd_conf_section = re.compile('^\s*\[ntpd\]\s*$')
|
||||
ntpd_conf_section = re.compile(r'^\s*\[ntpd\]\s*$')
|
||||
|
||||
try:
|
||||
with open(SERVER_SYSRESTORE_STATE) as f:
|
||||
@@ -56,7 +56,7 @@ def is_ntpd_configured():
|
||||
def is_dns_configured():
|
||||
# dns is configured when /etc/named.conf contains the line
|
||||
# dyndb "ipa" "/usr/lib64/bind/ldap.so" {
|
||||
bind_conf_section = re.compile('^\s*dyndb\s+"ipa"\s+"[^"]+"\s+{$')
|
||||
bind_conf_section = re.compile(r'^\s*dyndb\s+"ipa"\s+"[^"]+"\s+{$')
|
||||
|
||||
try:
|
||||
with open(NAMED_CONF) as f:
|
||||
|
||||
@@ -135,8 +135,7 @@ if six.PY3:
|
||||
|
||||
def get_host_diff(ipa_host, module_host):
|
||||
"""
|
||||
Compares two dictionaries containing host attributes and builds a dict
|
||||
of differences.
|
||||
Build a dict with the differences from two host dicts.
|
||||
|
||||
:param ipa_host: the host structure seen from IPA
|
||||
:param module_host: the target host structure seen from the module params
|
||||
@@ -164,7 +163,7 @@ def get_host_diff(ipa_host, module_host):
|
||||
|
||||
def get_module_host(module):
|
||||
"""
|
||||
Creates a structure representing the host information
|
||||
Create a structure representing the host information.
|
||||
|
||||
Reads the module parameters and builds the host structure as expected from
|
||||
the module
|
||||
@@ -189,7 +188,7 @@ def get_module_host(module):
|
||||
|
||||
def ensure_host_present(module, api, ipahost):
|
||||
"""
|
||||
Ensures that the host exists in IPA and has the same attributes.
|
||||
Ensure host exists in IPA and has the same attributes.
|
||||
|
||||
:param module: the ansible module
|
||||
:param api: IPA api handle
|
||||
@@ -246,7 +245,7 @@ def ensure_host_present(module, api, ipahost):
|
||||
|
||||
def ensure_host_absent(module, api, host):
|
||||
"""
|
||||
Ensures that the host does not exist in IPA
|
||||
Ensure host does not exist in IPA.
|
||||
|
||||
:param module: the ansible module
|
||||
:param api: the IPA API handle
|
||||
@@ -271,9 +270,7 @@ def ensure_host_absent(module, api, host):
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main routine for the ansible module.
|
||||
"""
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
principal=dict(default='admin'),
|
||||
@@ -288,7 +285,6 @@ def main():
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
principal = module.params.get('principal', 'admin')
|
||||
ccache = module.params.get('ccache')
|
||||
fqdn = unicode(module.params.get('fqdn'))
|
||||
state = module.params.get('state')
|
||||
|
||||
@@ -235,7 +235,6 @@ def is_client_configured():
|
||||
|
||||
:returns: boolean
|
||||
"""
|
||||
|
||||
return (os.path.isfile(paths.IPA_DEFAULT_CONF) and
|
||||
os.path.isfile(os.path.join(paths.IPA_CLIENT_SYSRESTORE,
|
||||
sysrestore.SYSRESTORE_STATEFILE)))
|
||||
@@ -243,11 +242,10 @@ def is_client_configured():
|
||||
|
||||
def get_ipa_conf():
|
||||
"""
|
||||
Return IPA configuration read from /etc/ipa/default.conf
|
||||
Return IPA configuration read from `/etc/ipa/default.conf`.
|
||||
|
||||
:returns: dict containing key,value
|
||||
"""
|
||||
|
||||
parser = RawConfigParser()
|
||||
parser.read(paths.IPA_DEFAULT_CONF)
|
||||
result = dict()
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
|
||||
# Test ipaerver python3 binding
|
||||
try:
|
||||
from ipaserver.install.server.replicainstall import install_check
|
||||
from ipaserver.install.server.replicainstall import ( # noqa: F401
|
||||
install_check,
|
||||
)
|
||||
except ImportError:
|
||||
from ipaserver.install.server.replicainstall import promote_check
|
||||
from ipaserver.install.server.replicainstall import ( # noqa: F401
|
||||
promote_check,
|
||||
)
|
||||
|
||||
# Check ipapython version to be >= 4.6
|
||||
from ipapython.version import NUM_VERSION, VERSION
|
||||
|
||||
@@ -123,8 +123,8 @@ def main():
|
||||
ccache=dict(required=True),
|
||||
_ca_enabled=dict(required=False, type='bool'),
|
||||
_ca_file=dict(required=False),
|
||||
_dirsrv_pkcs12_info=dict(required=False),
|
||||
_pkinit_pkcs12_info=dict(required=False),
|
||||
_dirsrv_pkcs12_info=dict(required=False, type='list'),
|
||||
_pkinit_pkcs12_info=dict(required=False, type='list'),
|
||||
_top_dir=dict(required=True),
|
||||
dirman_password=dict(required=True, no_log=True),
|
||||
ds_ca_subject=dict(required=True),
|
||||
|
||||
@@ -119,8 +119,8 @@ def main():
|
||||
ccache=dict(required=True),
|
||||
_ca_enabled=dict(required=False, type='bool'),
|
||||
_ca_file=dict(required=False),
|
||||
_dirsrv_pkcs12_info=dict(required=False),
|
||||
_pkinit_pkcs12_info=dict(required=False),
|
||||
_dirsrv_pkcs12_info=dict(required=False, type='list'),
|
||||
_pkinit_pkcs12_info=dict(required=False, type='list'),
|
||||
_top_dir=dict(required=True),
|
||||
dirman_password=dict(required=True, no_log=True),
|
||||
ds_ca_subject=dict(required=True),
|
||||
|
||||
@@ -106,7 +106,7 @@ def main():
|
||||
ccache=dict(required=True),
|
||||
_ca_enabled=dict(required=False, type='bool'),
|
||||
_ca_file=dict(required=False),
|
||||
_pkinit_pkcs12_info=dict(required=False),
|
||||
_pkinit_pkcs12_info=dict(required=False, type='list'),
|
||||
_top_dir=dict(required=True),
|
||||
dirman_password=dict(required=True, no_log=True),
|
||||
),
|
||||
|
||||
@@ -195,6 +195,7 @@ import os
|
||||
import tempfile
|
||||
import traceback
|
||||
import six
|
||||
from shutil import copyfile
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ansible_ipa_replica import (
|
||||
@@ -485,6 +486,21 @@ def main():
|
||||
"certificate are not signed by the same CA "
|
||||
"certificate")
|
||||
|
||||
# Copy pkcs12_files to make them persistent till deployment is done
|
||||
# and encode certificates for ansible compatibility
|
||||
if http_pkcs12_info is not None:
|
||||
copyfile(http_pkcs12_file.name, "/etc/ipa/.tmp_pkcs12_http")
|
||||
http_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_http", http_pin)
|
||||
http_ca_cert = ""
|
||||
if dirsrv_pkcs12_info is not None:
|
||||
copyfile(dirsrv_pkcs12_file.name, "/etc/ipa/.tmp_pkcs12_dirsrv")
|
||||
dirsrv_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_dirsrv", dirsrv_pin)
|
||||
dirsrv_ca_cert = ""
|
||||
if pkinit_pkcs12_info is not None:
|
||||
copyfile(pkinit_pkcs12_file.name, "/etc/ipa/.tmp_pkcs12_pkinit")
|
||||
pkinit_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_pkinit", pkinit_pin)
|
||||
pkinit_ca_cert = ""
|
||||
|
||||
ansible_log.debug("-- FQDN --")
|
||||
|
||||
installutils.verify_fqdn(config.host_name, options.no_host_dns)
|
||||
|
||||
@@ -138,8 +138,8 @@ def main():
|
||||
_ca_file=dict(required=False),
|
||||
_kra_enabled=dict(required=False, type='bool'),
|
||||
_kra_host_name=dict(required=False),
|
||||
_dirsrv_pkcs12_info=dict(required=False),
|
||||
_pkinit_pkcs12_info=dict(required=False),
|
||||
_dirsrv_pkcs12_info=dict(required=False, type='list'),
|
||||
_pkinit_pkcs12_info=dict(required=False, type='list'),
|
||||
_top_dir=dict(required=True),
|
||||
_ca_subject=dict(required=True),
|
||||
_subject_base=dict(required=True),
|
||||
|
||||
@@ -118,7 +118,7 @@ def main():
|
||||
_ca_file=dict(required=False),
|
||||
_kra_enabled=dict(required=False, type='bool'),
|
||||
_kra_host_name=dict(required=False),
|
||||
_pkinit_pkcs12_info=dict(required=False),
|
||||
_pkinit_pkcs12_info=dict(required=False, type='list'),
|
||||
_top_dir=dict(required=True),
|
||||
dirman_password=dict(required=True, no_log=True),
|
||||
),
|
||||
|
||||
@@ -190,7 +190,7 @@ def main():
|
||||
ccache=dict(required=True),
|
||||
installer_ccache=dict(required=True),
|
||||
_ca_enabled=dict(required=False, type='bool'),
|
||||
_dirsrv_pkcs12_info=dict(required=False),
|
||||
_dirsrv_pkcs12_info=dict(required=False, type='list'),
|
||||
_top_dir=dict(required=True),
|
||||
_add_to_ipaservers=dict(required=True, type='bool'),
|
||||
_ca_subject=dict(required=True),
|
||||
|
||||
@@ -115,7 +115,7 @@ def main():
|
||||
ccache=dict(required=True),
|
||||
_ca_enabled=dict(required=False, type='bool'),
|
||||
_ca_file=dict(required=False),
|
||||
_http_pkcs12_info=dict(required=False),
|
||||
_http_pkcs12_info=dict(required=False, type='list'),
|
||||
_top_dir=dict(required=True),
|
||||
dirman_password=dict(required=True, no_log=True),
|
||||
),
|
||||
|
||||
@@ -96,7 +96,7 @@ def main():
|
||||
# additional
|
||||
config_master_host_name=dict(required=True),
|
||||
ccache=dict(required=True),
|
||||
_pkinit_pkcs12_info=dict(required=False),
|
||||
_pkinit_pkcs12_info=dict(required=False, type='list'),
|
||||
_top_dir=dict(required=True),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
|
||||
@@ -407,6 +407,7 @@
|
||||
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 }}"
|
||||
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
|
||||
dirman_password: "{{ ipareplica_dirman_password }}"
|
||||
@@ -750,6 +751,16 @@
|
||||
state: absent
|
||||
when: result_ipareplica_enable_ipa.changed
|
||||
|
||||
always:
|
||||
- name: Cleanup temporary files
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
with_items:
|
||||
- "/etc/ipa/.tmp_pkcs12_dirsrv"
|
||||
- "/etc/ipa/.tmp_pkcs12_http"
|
||||
- "/etc/ipa/.tmp_pkcs12_pkinit"
|
||||
|
||||
when: not ansible_check_mode and
|
||||
not (result_ipareplica_test.client_already_configured is defined or
|
||||
result_ipareplica_test.server_already_configured is defined)
|
||||
|
||||
@@ -227,14 +227,17 @@ SSL certificate Variables
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaserver_dirsrv_cert_files` | Files containing the Directory Server SSL certificate and private keys. (list of strings) | no
|
||||
`ipaserver_http_cert_file` | File containing the Apache Server SSL certificate and private key. (string) | no
|
||||
`ipaserver_pkinit_cert_file` | File containing the Kerberos KDC SSL certificate and private key. (string) | no
|
||||
`ipaserver_http_cert_files` | File containing the Apache Server SSL certificate and private key. (list of string) | no
|
||||
`ipaserver_pkinit_cert_files` | File containing the Kerberos KDC SSL certificate and private key. (list of string) | no
|
||||
`ipaserver_dirsrv_pin` | The password to unlock the Directory Server private key. (string) | no
|
||||
`ipaserver_http_pin` | The password to unlock the Apache Server private key. (string) | no
|
||||
`ipaserver_pkinit_pin` | The password to unlock the Kerberos KDC private key. (string) | no
|
||||
`ipaserver_dirsrv_cert_name` | Name of the Directory Server SSL certificate to install. (string) | no
|
||||
`ipaserver_http_cert_name` | Name of the Apache Server SSL certificate to install. (string) | no
|
||||
`ipaserver_pkinit_cert_name` | Name of the Kerberos KDC SSL certificate to install. (string) | no
|
||||
`ipaserver_no_pkinit` | Disable pkinit setup steps (boolean) | no
|
||||
|
||||
NOTE: If one of the `ipaserver_http_cert_files` or `ipaserver_pkinit_cert_files` is specified, then both are required, so declaring only one of them will raise an error. Additionally, one of `ipaserver_pkinit_cert_files` or `ipaserver_no_pkinit` must be provided as well.
|
||||
|
||||
Client Variables
|
||||
----------------
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Test ipaerver python3 binding
|
||||
from ipaserver.install.server.install import install_check
|
||||
from ipaserver.install.server.install import install_check # noqa: F401
|
||||
|
||||
# Check ipapython version to be >= 4.6
|
||||
from ipapython.version import NUM_VERSION, VERSION
|
||||
|
||||
@@ -127,7 +127,7 @@ def main():
|
||||
no_hbac_allow=dict(required=False, type='bool', default=False),
|
||||
no_pkinit=dict(required=False, type='bool', default=False),
|
||||
dirsrv_config_file=dict(required=False),
|
||||
_dirsrv_pkcs12_info=dict(required=False),
|
||||
_dirsrv_pkcs12_info=dict(required=False, type='list'),
|
||||
# ssl certificate
|
||||
dirsrv_cert_files=dict(required=False, type='list', default=[]),
|
||||
subject_base=dict(required=False),
|
||||
|
||||
@@ -163,7 +163,7 @@ from ansible.module_utils.ansible_ipa_server import (
|
||||
AnsibleModuleLog, setup_logging, options, sysrestore, paths,
|
||||
ansible_module_get_parsed_ip_addresses,
|
||||
api_Backend_ldap2, redirect_stdout, ca, installutils, ds_init_info,
|
||||
custodiainstance, write_cache, x509
|
||||
custodiainstance, write_cache, x509, decode_certificate
|
||||
)
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ def main():
|
||||
no_pkinit=dict(required=False, type='bool', default=False),
|
||||
dirsrv_config_file=dict(required=False),
|
||||
dirsrv_cert_files=dict(required=False, type='list'),
|
||||
_dirsrv_pkcs12_info=dict(required=False),
|
||||
_dirsrv_pkcs12_info=dict(required=False, type='list'),
|
||||
# certificate system
|
||||
external_ca=dict(required=False, type='bool', default=False),
|
||||
external_ca_type=dict(required=False),
|
||||
@@ -265,8 +265,8 @@ def main():
|
||||
# additional
|
||||
options.domainlevel = ansible_module.params.get('domainlevel')
|
||||
options._http_ca_cert = ansible_module.params.get('_http_ca_cert')
|
||||
# tions._update_hosts_file = ansible_module.params.get(
|
||||
# 'update_hosts_file')
|
||||
if options._http_ca_cert:
|
||||
options._http_ca_cert = decode_certificate(options._http_ca_cert)
|
||||
|
||||
# init #################################################################
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ def main():
|
||||
dirsrv_config_file=dict(required=False),
|
||||
# ssl certificate
|
||||
dirsrv_cert_files=dict(required=False, type='list', default=[]),
|
||||
_dirsrv_pkcs12_info=dict(required=False),
|
||||
_dirsrv_pkcs12_info=dict(required=False, type='list'),
|
||||
# certificate system
|
||||
external_cert_files=dict(required=False, type='list', default=[]),
|
||||
subject_base=dict(required=False),
|
||||
|
||||
@@ -199,8 +199,8 @@ def main():
|
||||
|
||||
# _update_hosts_file=dict(required=False, type='bool',
|
||||
# default=False),
|
||||
_dirsrv_pkcs12_info=dict(required=False),
|
||||
_http_pkcs12_info=dict(required=False),
|
||||
_dirsrv_pkcs12_info=dict(required=False, type='list'),
|
||||
_http_pkcs12_info=dict(required=False, type='list'),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ def main():
|
||||
no_reverse=dict(required=False, type='bool', default=False),
|
||||
auto_forwarders=dict(required=False, type='bool', default=False),
|
||||
|
||||
_pkinit_pkcs12_info=dict(required=False),
|
||||
_pkinit_pkcs12_info=dict(required=False, type='list'),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -209,6 +209,7 @@ import sys
|
||||
import six
|
||||
import inspect
|
||||
import random
|
||||
from shutil import copyfile
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils._text import to_native
|
||||
@@ -219,7 +220,8 @@ from ansible.module_utils.ansible_ipa_server import (
|
||||
NUM_VERSION, is_ipa_configured, sysrestore, paths, bindinstance,
|
||||
read_cache, ca, tasks, check_ldap_conf, timeconf, httpinstance,
|
||||
check_dirsrv, ScriptError, get_fqdn, verify_fqdn, BadHostError,
|
||||
validate_domain_name, load_pkcs12, IPA_PYTHON_VERSION
|
||||
validate_domain_name, load_pkcs12, IPA_PYTHON_VERSION,
|
||||
encode_certificate
|
||||
)
|
||||
|
||||
if six.PY3:
|
||||
@@ -252,7 +254,7 @@ def main():
|
||||
dirsrv_config_file=dict(required=False),
|
||||
# ssl certificate
|
||||
dirsrv_cert_files=dict(required=False, type='list', default=None),
|
||||
http_cert_files=dict(required=False, type='list', defaullt=None),
|
||||
http_cert_files=dict(required=False, type='list', default=None),
|
||||
pkinit_cert_files=dict(required=False, type='list', default=None),
|
||||
dirsrv_pin=dict(required=False),
|
||||
http_pin=dict(required=False),
|
||||
@@ -1013,6 +1015,21 @@ def main():
|
||||
|
||||
# done ##################################################################
|
||||
|
||||
# Copy pkcs12_files to make them persistent till deployment is done
|
||||
# and encode certificates for ansible compatibility
|
||||
if http_pkcs12_info is not None:
|
||||
copyfile(http_pkcs12_file.name, "/etc/ipa/.tmp_pkcs12_http")
|
||||
http_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_http", http_pin)
|
||||
http_ca_cert = encode_certificate(http_ca_cert)
|
||||
if dirsrv_pkcs12_info is not None:
|
||||
copyfile(dirsrv_pkcs12_file.name, "/etc/ipa/.tmp_pkcs12_dirsrv")
|
||||
dirsrv_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_dirsrv", dirsrv_pin)
|
||||
dirsrv_ca_cert = encode_certificate(dirsrv_ca_cert)
|
||||
if pkinit_pkcs12_info is not None:
|
||||
copyfile(pkinit_pkcs12_file.name, "/etc/ipa/.tmp_pkcs12_pkinit")
|
||||
pkinit_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_pkinit", pkinit_pin)
|
||||
pkinit_ca_cert = encode_certificate(pkinit_ca_cert)
|
||||
|
||||
ansible_module.exit_json(changed=False,
|
||||
ipa_python_version=IPA_PYTHON_VERSION,
|
||||
# basic
|
||||
|
||||
@@ -37,11 +37,13 @@ __all__ = ["IPAChangeConf", "certmonger", "sysrestore", "root_logger",
|
||||
"validate_dm_password", "read_cache", "write_cache",
|
||||
"adtrustinstance", "IPAAPI_USER", "sync_time", "PKIIniLoader",
|
||||
"default_subject_base", "default_ca_subject_dn",
|
||||
"check_ldap_conf"]
|
||||
"check_ldap_conf", "encode_certificate", "decode_certificate"]
|
||||
|
||||
import sys
|
||||
import logging
|
||||
from contextlib import contextmanager as contextlib_contextmanager
|
||||
import six
|
||||
import base64
|
||||
|
||||
|
||||
from ipapython.version import NUM_VERSION, VERSION
|
||||
@@ -137,6 +139,17 @@ if NUM_VERSION >= 40500:
|
||||
except ImportError:
|
||||
check_ldap_conf = None
|
||||
|
||||
try:
|
||||
from ipalib.x509 import Encoding
|
||||
except ImportError:
|
||||
from cryptography.hazmat.primitives.serialization import Encoding
|
||||
|
||||
try:
|
||||
from ipalib.x509 import load_pem_x509_certificate
|
||||
except ImportError:
|
||||
from ipalib.x509 import load_certificate
|
||||
load_pem_x509_certificate = None
|
||||
|
||||
else:
|
||||
# IPA version < 4.5
|
||||
|
||||
@@ -322,3 +335,41 @@ def ansible_module_get_parsed_ip_addresses(ansible_module,
|
||||
ansible_module.fail_json(msg="Invalid IP Address %s: %s" % (ip, e))
|
||||
ip_addrs.append(ip_parsed)
|
||||
return ip_addrs
|
||||
|
||||
|
||||
def encode_certificate(cert):
|
||||
"""
|
||||
Encode a certificate using base64.
|
||||
|
||||
It also takes FreeIPA and Python versions into account.
|
||||
"""
|
||||
if isinstance(cert, (str, bytes)):
|
||||
encoded = base64.b64encode(cert)
|
||||
else:
|
||||
encoded = base64.b64encode(cert.public_bytes(Encoding.DER))
|
||||
if not six.PY2:
|
||||
encoded = encoded.decode('ascii')
|
||||
return encoded
|
||||
|
||||
|
||||
def decode_certificate(cert):
|
||||
"""
|
||||
Decode a certificate using base64.
|
||||
|
||||
It also takes FreeIPA versions into account and returns a IPACertificate
|
||||
for newer IPA versions.
|
||||
"""
|
||||
if hasattr(x509, "IPACertificate"):
|
||||
cert = cert.strip()
|
||||
if not cert.startswith("-----BEGIN CERTIFICATE-----"):
|
||||
cert = "-----BEGIN CERTIFICATE-----\n" + cert
|
||||
if not cert.endswith("-----END CERTIFICATE-----"):
|
||||
cert += "\n-----END CERTIFICATE-----"
|
||||
|
||||
if load_pem_x509_certificate is not None:
|
||||
cert = load_pem_x509_certificate(cert.encode('utf-8'))
|
||||
else:
|
||||
cert = load_certificate(cert.encode('utf-8'))
|
||||
else:
|
||||
cert = base64.b64decode(cert)
|
||||
return cert
|
||||
|
||||
@@ -286,6 +286,7 @@
|
||||
reverse_zones: "{{ result_ipaserver_prepare.reverse_zones }}"
|
||||
no_reverse: "{{ ipaserver_no_reverse }}"
|
||||
auto_forwarders: "{{ ipaserver_auto_forwarders }}"
|
||||
_http_ca_cert: "{{ result_ipaserver_test._http_ca_cert }}"
|
||||
register: result_ipaserver_setup_ca
|
||||
|
||||
- name: Copy /root/ipa.csr to "{{ inventory_hostname }}-ipa.csr"
|
||||
@@ -448,6 +449,16 @@
|
||||
|
||||
when: not result_ipaserver_setup_ca.csr_generated | bool
|
||||
|
||||
always:
|
||||
- name: Cleanup temporary files
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
with_items:
|
||||
- "/etc/ipa/.tmp_pkcs12_dirsrv"
|
||||
- "/etc/ipa/.tmp_pkcs12_http"
|
||||
- "/etc/ipa/.tmp_pkcs12_pkinit"
|
||||
|
||||
when: not ansible_check_mode and not
|
||||
(not result_ipaserver_test.changed and
|
||||
(result_ipaserver_test.client_already_configured is defined or
|
||||
|
||||
10
setup.cfg
10
setup.cfg
@@ -20,3 +20,13 @@ data_files =
|
||||
/usr/share/ansible/roles/ipaclient = roles/ipaclient/*
|
||||
/usr/share/ansible/roles/ipaserver = roles/ipaserver/*
|
||||
/usr/share/ansible/roles/ipareplica = roles/ipareplica/*
|
||||
|
||||
[flake8]
|
||||
extend-ignore = E203
|
||||
per-file-ignores =
|
||||
plugins/*:E402
|
||||
roles/*:E402
|
||||
|
||||
[pydocstyle]
|
||||
inherit = false
|
||||
ignore = D1,D212,D203
|
||||
|
||||
58
tests/README.md
Normal file
58
tests/README.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Running the tests
|
||||
|
||||
## Before starting
|
||||
|
||||
In order to run ansible-freeipa tests you will need to have `ansible` and `pytest` installed on your machine. We'll call this local machine `controller`.
|
||||
|
||||
You will also need to have a remote host with freeipa server installed and configured. We'll call this remote host `ipaserver`.
|
||||
|
||||
Some other requirements:
|
||||
|
||||
* The `controller` must be able to connect to `ipaserver` through ssh using keys.
|
||||
* `ipaserver` must be configured with DNS support. See [ipaserver role](../roles/ipaserver/README.md).
|
||||
* IPA admin password must be `SomeADMINpassword`.
|
||||
* Directory Server admin password must be `SomeDMpassword`.
|
||||
|
||||
|
||||
## Running the tests
|
||||
|
||||
To run the tests run:
|
||||
|
||||
```
|
||||
IPA_SERVER_HOST=<ipaserver_host_or_ip> pytest
|
||||
```
|
||||
|
||||
If you need to run using a different user you can use `ANSIBLE_REMOTE_USER`
|
||||
environment variable. For example:
|
||||
|
||||
```
|
||||
ANSIBLE_REMOTE_USER=root IPA_SERVER_HOST=<ipaserver_host_or_ip> pytest
|
||||
```
|
||||
|
||||
To select which tests to run use the option `-k`. For example:
|
||||
|
||||
```
|
||||
IPA_SERVER_HOST=<ipaserver_host_or_ip> pytest -k dnszone
|
||||
```
|
||||
|
||||
To see the ansible output use the option `--capture=sys`. For example:
|
||||
|
||||
```
|
||||
IPA_SERVER_HOST=<ipaserver_host_or_ip> pytest --capture=sys
|
||||
```
|
||||
|
||||
To see why tests were skipped use `-rs`. For example:
|
||||
|
||||
```
|
||||
IPA_SERVER_HOST=<ipaserver_host_or_ip> pytest -rs
|
||||
```
|
||||
|
||||
For a complete list of options check `pytest --help`.
|
||||
|
||||
|
||||
## Upcoming/desired improvements:
|
||||
|
||||
* A script to pre-config the complete test environment using virsh.
|
||||
* A test matrix to run tests against different distros in parallel (probably using tox).
|
||||
* Allow to connect to `ipaserver` using ssh and password.
|
||||
|
||||
5
tests/ansible.cfg
Normal file
5
tests/ansible.cfg
Normal file
@@ -0,0 +1,5 @@
|
||||
[defaults]
|
||||
roles_path = ../roles:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
|
||||
library = ../plugins/modules:~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
|
||||
module_utils = ../plugins/module_utils:~/.ansible/plugins/module_utils:/usr/share/ansible/plugins/module_utils
|
||||
host_key_checking = false
|
||||
20
tests/ca-less/certificates/pkinit/extensions.conf
Normal file
20
tests/ca-less/certificates/pkinit/extensions.conf
Normal file
@@ -0,0 +1,20 @@
|
||||
[kdc_cert]
|
||||
basicConstraints=CA:FALSE
|
||||
keyUsage=nonRepudiation,digitalSignature,keyEncipherment,keyAgreement
|
||||
extendedKeyUsage=1.3.6.1.5.2.3.5
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
issuerAltName=issuer:copy
|
||||
subjectAltName=otherName:1.3.6.1.5.2.2;SEQUENCE:kdc_princ_name
|
||||
|
||||
[kdc_princ_name]
|
||||
realm=EXP:0,GeneralString:${ENV::REALM}
|
||||
principal_name=EXP:1,SEQUENCE:kdc_principal_seq
|
||||
|
||||
[kdc_principal_seq]
|
||||
name_type=EXP:0,INTEGER:1
|
||||
name_string=EXP:1,SEQUENCE:kdc_principals
|
||||
|
||||
[kdc_principals]
|
||||
princ1=GeneralString:krbtgt
|
||||
princ2=GeneralString:${ENV::REALM}
|
||||
15
tests/ca-less/clean_up_certificates.yml
Normal file
15
tests/ca-less/clean_up_certificates.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Clean up certificates
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
- name: Run generate-certificates.sh
|
||||
command: >
|
||||
/bin/bash
|
||||
generate-certificates.sh delete "{{ item }}"
|
||||
args:
|
||||
chdir: "{{ playbook_dir }}"
|
||||
with_items:
|
||||
- "{{ groups.ipaserver[0] }}"
|
||||
- "{{ groups.ipareplicas[0] }}"
|
||||
153
tests/ca-less/generate-certificates.sh
Executable file
153
tests/ca-less/generate-certificates.sh
Executable file
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
ROOT_CA_DIR="certificates/root-ca"
|
||||
DIRSRV_CERTS_DIR="certificates/dirsrv"
|
||||
HTTPD_CERTS_DIR="certificates/httpd"
|
||||
PKINIT_CERTS_DIR="certificates/pkinit"
|
||||
PKCS12_PASSWORD="SomePKCS12password"
|
||||
|
||||
# generate_ipa_pkcs12_certificate \
|
||||
# $cert_name $ipa_fqdn $certs_dir $root_ca_cert $root_ca_private_key extensions_file extensions_name
|
||||
function generate_ipa_pkcs12_certificate {
|
||||
|
||||
cert_name=$1
|
||||
ipa_fqdn=$2
|
||||
certs_dir=$3
|
||||
root_ca_cert=$4
|
||||
root_ca_private_key=$5
|
||||
extensions_file=$6
|
||||
extensions_name=$7
|
||||
|
||||
# Generate CSR and private key
|
||||
openssl req -new -newkey rsa:4096 -nodes \
|
||||
-subj "/C=US/ST=Test/L=Testing/O=Default/CN=${ipa_fqdn}" \
|
||||
-keyout ${certs_dir}/private.key \
|
||||
-out ${certs_dir}/request.csr
|
||||
|
||||
# Sign CSR to generate PEM certificate
|
||||
if [ -z "${extensions_file}" ]; then
|
||||
openssl x509 -req -days 365 -sha256 \
|
||||
-CAcreateserial \
|
||||
-CA ${root_ca_cert} \
|
||||
-CAkey ${root_ca_private_key} \
|
||||
-in ${certs_dir}/request.csr \
|
||||
-out ${certs_dir}/cert.pem
|
||||
else
|
||||
openssl x509 -req -days 365 -sha256 \
|
||||
-CAcreateserial \
|
||||
-CA ${ROOT_CA_DIR}/cert.pem \
|
||||
-CAkey ${ROOT_CA_DIR}/private.key \
|
||||
-extfile ${extensions_file} \
|
||||
-extensions ${extensions_name} \
|
||||
-in ${certs_dir}/request.csr \
|
||||
-out ${certs_dir}/cert.pem
|
||||
fi
|
||||
|
||||
# Convert certificate to PKCS12 format
|
||||
openssl pkcs12 -export \
|
||||
-name ${cert_name} \
|
||||
-certfile ${root_ca_cert} \
|
||||
-in ${certs_dir}/cert.pem \
|
||||
-inkey ${certs_dir}/private.key \
|
||||
-passout "pass:${PKCS12_PASSWORD}" \
|
||||
-out ${certs_dir}/cert.p12
|
||||
}
|
||||
|
||||
# generate_ipa_pkcs12_certificates $ipa_fqdn $ipa_domain
|
||||
function generate_ipa_pkcs12_certificates {
|
||||
|
||||
host=$1
|
||||
if [ -z "$host" ]; then
|
||||
echo "ERROR: ipa-host-fqdn is not set"
|
||||
echo
|
||||
echo "usage: $0 create ipa-host-fqdn domain"
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
domain=$2
|
||||
if [ -z "$domain" ]; then
|
||||
echo "ERROR: domain is not set"
|
||||
echo
|
||||
echo "usage: $0 create ipa-host-fqdn domain"
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
# Generate certificates folder structure
|
||||
mkdir -p ${ROOT_CA_DIR}
|
||||
mkdir -p ${DIRSRV_CERTS_DIR}/$host
|
||||
mkdir -p ${HTTPD_CERTS_DIR}/$host
|
||||
mkdir -p ${PKINIT_CERTS_DIR}/$host
|
||||
|
||||
# Generate root CA
|
||||
if [ ! -f "${ROOT_CA_DIR}/private.key" ]; then
|
||||
openssl genrsa \
|
||||
-out ${ROOT_CA_DIR}/private.key 4096
|
||||
|
||||
openssl req -new -x509 -sha256 -nodes -days 3650 \
|
||||
-subj "/C=US/ST=Test/L=Testing/O=Default" \
|
||||
-key ${ROOT_CA_DIR}/private.key \
|
||||
-out ${ROOT_CA_DIR}/cert.pem
|
||||
fi
|
||||
|
||||
# Generate a certificate for the Directory Server
|
||||
if [ ! -f "${DIRSRV_CERTS_DIR}/$host/cert.pem" ]; then
|
||||
generate_ipa_pkcs12_certificate \
|
||||
"dirsrv-cert" \
|
||||
$host \
|
||||
"${DIRSRV_CERTS_DIR}/$host" \
|
||||
"${ROOT_CA_DIR}/cert.pem" \
|
||||
"${ROOT_CA_DIR}/private.key"
|
||||
fi
|
||||
|
||||
# Generate a certificate for the Apache server
|
||||
if [ ! -f "${HTTPD_CERTS_DIR}/$host/cert.pem" ]; then
|
||||
generate_ipa_pkcs12_certificate \
|
||||
"httpd-cert" \
|
||||
$host \
|
||||
"${HTTPD_CERTS_DIR}/$host" \
|
||||
"${ROOT_CA_DIR}/cert.pem" \
|
||||
"${ROOT_CA_DIR}/private.key"
|
||||
fi
|
||||
|
||||
# Generate a certificate for the KDC PKINIT
|
||||
if [ ! -f "${PKINIT_CERTS_DIR}/$host/cert.pem" ]; then
|
||||
export REALM=${domain^^}
|
||||
|
||||
generate_ipa_pkcs12_certificate \
|
||||
"pkinit-cert" \
|
||||
$host \
|
||||
"${PKINIT_CERTS_DIR}/$host" \
|
||||
"${ROOT_CA_DIR}/cert.pem" \
|
||||
"${ROOT_CA_DIR}/private.key" \
|
||||
"${PKINIT_CERTS_DIR}/extensions.conf" \
|
||||
"kdc_cert"
|
||||
fi
|
||||
}
|
||||
|
||||
# delete_ipa_pkcs12_certificates $ipa_fqdn
|
||||
function delete_ipa_pkcs12_certificates {
|
||||
|
||||
host=$1
|
||||
if [ -z "$host" ]; then
|
||||
echo "ERROR: ipa-host-fqdn is not set"
|
||||
echo
|
||||
echo "usage: $0 delete ipa-host-fqdn"
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
rm -f certificates/*/$host/*
|
||||
rm -f ${ROOT_CA_DIR}/*
|
||||
}
|
||||
|
||||
# Entrypoint
|
||||
case "$1" in
|
||||
create)
|
||||
generate_ipa_pkcs12_certificates $2 $3
|
||||
;;
|
||||
delete)
|
||||
delete_ipa_pkcs12_certificates $2
|
||||
;;
|
||||
*)
|
||||
echo $"Usage: $0 {create|delete}"
|
||||
;;
|
||||
esac
|
||||
82
tests/ca-less/install_replica_without_ca.yml
Normal file
82
tests/ca-less/install_replica_without_ca.yml
Normal file
@@ -0,0 +1,82 @@
|
||||
---
|
||||
- name: Generate certificates
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
- name: Run generate-certificates.sh
|
||||
command: >
|
||||
/bin/bash
|
||||
generate-certificates.sh create
|
||||
"{{ groups.ipareplicas[0] }}"
|
||||
"{{ ipareplica_domain | default(groups.ipareplicas[0].split('.')[1:] | join ('.')) }}"
|
||||
args:
|
||||
chdir: "{{ playbook_dir }}"
|
||||
|
||||
- name: Test ipareplicas installation without CA
|
||||
hosts: ipareplicas
|
||||
become: true
|
||||
|
||||
vars:
|
||||
# Root CA certificate
|
||||
ipareplica_ca_cert_files:
|
||||
- /root/ca-less-test/ca.crt
|
||||
# Directory server certificates
|
||||
ipareplica_dirsrv_cert_name: dirsrv-cert
|
||||
ipareplica_dirsrv_cert_files:
|
||||
- /root/ca-less-test/dirsrv.p12
|
||||
ipareplica_dirsrv_pin: SomePKCS12password
|
||||
# Apache certificates
|
||||
ipareplica_http_cert_name: httpd-cert
|
||||
ipareplica_http_cert_files:
|
||||
- /root/ca-less-test/httpd.p12
|
||||
ipareplica_http_pin: SomePKCS12password
|
||||
# PKINIT configuration
|
||||
ipareplica_no_pkinit: no
|
||||
ipareplica_pkinit_cert_name: pkinit-cert
|
||||
ipareplica_pkinit_cert_files:
|
||||
- /root/ca-less-test/pkinit.p12
|
||||
ipareplica_pkinit_pin: SomePKCS12password
|
||||
|
||||
pre_tasks:
|
||||
- name: Remove "/root/ca-less-test"
|
||||
file:
|
||||
path: "/root/ca-less-test"
|
||||
state: absent
|
||||
|
||||
- name: Generate "/root/ca-less-test"
|
||||
file:
|
||||
path: "/root/ca-less-test"
|
||||
state: directory
|
||||
|
||||
- name: Copy CA certificate
|
||||
copy:
|
||||
src: "{{ playbook_dir }}/certificates/root-ca/cert.pem"
|
||||
dest: "/root/ca-less-test/ca.crt"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Copy p12 certificates
|
||||
copy:
|
||||
src: "{{ playbook_dir }}/certificates/{{ item }}/{{ groups.ipareplicas[0] }}/cert.p12"
|
||||
dest: "/root/ca-less-test/{{ item }}.p12"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
with_items:
|
||||
- dirsrv
|
||||
- httpd
|
||||
- pkinit
|
||||
|
||||
roles:
|
||||
- role: ipareplica
|
||||
state: present
|
||||
|
||||
post_tasks:
|
||||
- name: Fix KDC certificate permissions
|
||||
file:
|
||||
path: /var/kerberos/krb5kdc/kdc.crt
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
74
tests/ca-less/install_server_without_ca.yml
Normal file
74
tests/ca-less/install_server_without_ca.yml
Normal file
@@ -0,0 +1,74 @@
|
||||
---
|
||||
- name: Generate certificates
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
- name: Run generate-certificates.sh
|
||||
command: >
|
||||
/bin/bash
|
||||
generate-certificates.sh create
|
||||
"{{ groups.ipaserver[0] }}"
|
||||
"{{ ipaserver_domain | default(groups.ipaserver[0].split('.')[1:] | join ('.')) }}"
|
||||
args:
|
||||
chdir: "{{ playbook_dir }}"
|
||||
|
||||
- name: Test ipaserver installation without CA
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
vars:
|
||||
# Root CA certificate
|
||||
ipaserver_ca_cert_files:
|
||||
- /root/ca-less-test/ca.crt
|
||||
# Directory server certificates
|
||||
ipaserver_dirsrv_cert_name: dirsrv-cert
|
||||
ipaserver_dirsrv_cert_files:
|
||||
- /root/ca-less-test/dirsrv.p12
|
||||
ipaserver_dirsrv_pin: SomePKCS12password
|
||||
# Apache certificates
|
||||
ipaserver_http_cert_name: httpd-cert
|
||||
ipaserver_http_cert_files:
|
||||
- /root/ca-less-test/httpd.p12
|
||||
ipaserver_http_pin: SomePKCS12password
|
||||
# PKINIT configuration
|
||||
ipaserver_no_pkinit: no
|
||||
ipaserver_pkinit_cert_name: pkinit-cert
|
||||
ipaserver_pkinit_cert_files:
|
||||
- /root/ca-less-test/pkinit.p12
|
||||
ipaserver_pkinit_pin: SomePKCS12password
|
||||
|
||||
pre_tasks:
|
||||
- name: Remove "/root/ca-less-test"
|
||||
file:
|
||||
path: "/root/ca-less-test"
|
||||
state: absent
|
||||
|
||||
- name: Generate "/root/ca-less-test"
|
||||
file:
|
||||
path: "/root/ca-less-test"
|
||||
state: directory
|
||||
|
||||
- name: Copy CA certificate
|
||||
copy:
|
||||
src: "{{ playbook_dir }}/certificates/root-ca/cert.pem"
|
||||
dest: "/root/ca-less-test/ca.crt"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Copy p12 certificates
|
||||
copy:
|
||||
src: "{{ playbook_dir }}/certificates/{{ item }}/{{ groups.ipaserver[0] }}/cert.p12"
|
||||
dest: "/root/ca-less-test/{{ item }}.p12"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
with_items:
|
||||
- dirsrv
|
||||
- httpd
|
||||
- pkinit
|
||||
|
||||
roles:
|
||||
- role: ipaserver
|
||||
state: present
|
||||
17
tests/ca-less/inventory
Normal file
17
tests/ca-less/inventory
Normal file
@@ -0,0 +1,17 @@
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
|
||||
[ipaserver:vars]
|
||||
ipaserver_domain=test.local
|
||||
ipaserver_realm=TEST.LOCAL
|
||||
ipaadmin_password=SomeADMINpassword
|
||||
ipadm_password=SomeDMpassword
|
||||
|
||||
[ipareplicas]
|
||||
ipareplica.test.local
|
||||
|
||||
[ipareplicas:vars]
|
||||
ipareplica_domain=test.local
|
||||
ipareplica_realm=TEST.LOCAL
|
||||
ipaadmin_password=SomeADMINpassword
|
||||
ipadm_password=SomeDMpassword
|
||||
388
tests/config/test_config.yml
Normal file
388
tests/config/test_config.yml
Normal file
@@ -0,0 +1,388 @@
|
||||
---
|
||||
- name: Playbook to handle server configuration
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Retrieve current configuration.
|
||||
- name: return current values of the global configuration options
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
register: previousconfig
|
||||
|
||||
- debug:
|
||||
msg: "{{previousconfig}}"
|
||||
|
||||
# setup environment.
|
||||
- name: create test group
|
||||
ipagroup:
|
||||
ipaadmin_password: 'SomeADMINpassword'
|
||||
name: somedefaultgroup
|
||||
|
||||
- name: Ensure the default e-mail domain is ipa.test.
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
emaildomain: ipa.test
|
||||
|
||||
- name: set default shell to '/bin/sh'
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
defaultshell: /bin/sh
|
||||
|
||||
- name: set default group
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
defaultgroup: ipausers
|
||||
|
||||
- name: set default home directory
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
homedirectory: /home
|
||||
|
||||
- name: clear pac-type
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
pac_type: ""
|
||||
|
||||
- name: set maxusername to 255
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
maxusername: 255
|
||||
|
||||
- name: set maxhostname to 255
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
maxhostname: 255
|
||||
|
||||
- name: set pwdexpnotify to 0
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
pwdexpnotify: 0
|
||||
|
||||
- name: set searchrecordslimit to 10
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
searchrecordslimit: 10
|
||||
|
||||
- name: set searchtimelimit to 1
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
searchtimelimit: 1
|
||||
|
||||
- name: clear configstring
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
configstring: ""
|
||||
|
||||
- name: set configstring to AllowNThash
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
configstring: 'KDC:Disable Lockout'
|
||||
|
||||
- name: set selinuxusermapdefault
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
selinuxusermapdefault: "staff_u:s0-s0:c0.c1023"
|
||||
|
||||
- name: set selinuxusermaporder
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
selinuxusermaporder: 'user_u:s0$staff_u:s0-s0:c0.c1023'
|
||||
|
||||
- name: set usersearch to `uid`
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
usersearch: uid
|
||||
|
||||
- name: set groupsearch to `cn`
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
groupsearch: cn
|
||||
|
||||
# tests
|
||||
- name: Ensure the default e-mail domain is somedomain.test.
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
emaildomain: somedomain.test
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure the default e-mail domain is somedomain.test, again.
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
emaildomain: somedomain.test
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: set default shell to '/bin/someshell'
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
defaultshell: /bin/someshell
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: set default shell to '/bin/someshell', again.
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
defaultshell: /bin/someshell
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: set default group
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
defaultgroup: somedefaultgroup
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: set default group
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
defaultgroup: somedefaultgroup
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: set default home directory
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
homedirectory: /Users
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: set default home directory
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
homedirectory: /Users
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: set pac-type
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
pac_type: "nfs:NONE"
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: set pac-type, again.
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
pac_type: "nfs:NONE"
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: set maxusername to 33
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
maxusername: 33
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: set maxusername to 33, again.
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
maxusername: 33
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: set maxhostname to 77
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
maxhostname: 77
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: set maxhostname to 77, again
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
maxhostname: 77
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: set pwdexpnotify to 17
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
pwdexpnotify: 17
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: set pwdexpnotify to 17, again
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
pwdexpnotify: 17
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: set searchrecordslimit to -1
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
searchrecordslimit: -1
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: set searchrecordslimit to -1, again.
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
searchrecordslimit: -1
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: set searchtimelimit to 12345
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
searchtimelimit: 12345
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: set searchtimelimit to 12345, again.
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
searchtimelimit: 12345
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: change enable_migration
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
enable_migration: '{{ not previousconfig.config.enable_migration }}'
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: change enable_migration, again
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
enable_migration: '{{ not previousconfig.config.enable_migration }}'
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: set configstring to AllowNThash
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
configstring: AllowNThash
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: set configstring to AllowNThash, again.
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
configstring: AllowNThash
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: set selinuxusermaporder
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
selinuxusermaporder: 'user_u:s0$staff_u:s0-s0:c0.c1023$sysadm_u:s0-s0:c0.c1023$unconfined_u:s0-s0:c0.c1023'
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: set selinuxusermaporder, again
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
selinuxusermaporder: 'user_u:s0$staff_u:s0-s0:c0.c1023$sysadm_u:s0-s0:c0.c1023$unconfined_u:s0-s0:c0.c1023'
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: set selinuxusermapdefault
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
selinuxusermapdefault: 'user_u:s0'
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: set selinuxusermapdefault, again
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
selinuxusermapdefault: 'user_u:s0'
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: set groupsearch to `description`
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
groupsearch: description
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: set groupsearch to `gidNumber`, again
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
groupsearch: description
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: set usersearch to `uidNumber`
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
usersearch: uidNumber
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: set usersearch to `uidNumber`, again
|
||||
ipaconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
usersearch: uidNumber
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: reset changed fields
|
||||
ipaconfig:
|
||||
ipaadmin_password: 'SomeADMINpassword'
|
||||
maxusername: '{{previousconfig.config.maxusername | default(omit)}}'
|
||||
maxhostname: '{{previousconfig.config.maxhostname | default(omit)}}'
|
||||
homedirectory: '{{previousconfig.config.homedirectory | default(omit)}}'
|
||||
defaultshell: '{{previousconfig.config.defaultshell | default(omit)}}'
|
||||
defaultgroup: '{{previousconfig.config.defaultgroup | default(omit)}}'
|
||||
emaildomain: '{{previousconfig.config.emaildomain | default(omit)}}'
|
||||
searchtimelimit: '{{previousconfig.config.searchtimelimit | default(omit)}}'
|
||||
searchrecordslimit: '{{previousconfig.config.searchrecordslimit | default(omit)}}'
|
||||
usersearch: '{{previousconfig.config.usersearch | default(omit)}}'
|
||||
groupsearch: '{{previousconfig.config.groupsearch | default(omit)}}'
|
||||
enable_migration: '{{previousconfig.config.enable_migration | default(omit)}}'
|
||||
groupobjectclasses: '{{previousconfig.config.groupobjectclasses | default(omit)}}'
|
||||
userobjectclasses: '{{previousconfig.config.userobjectclasses | default(omit)}}'
|
||||
pwdexpnotify: '{{previousconfig.config.pwdexpnotify | default(omit)}}'
|
||||
configstring: '{{previousconfig.config.configstring | default(omit)}}'
|
||||
selinuxusermapdefault: '{{previousconfig.config.selinuxusermapdefault | default(omit)}}'
|
||||
selinuxusermaporder: '{{previousconfig.config.selinuxusermaporder | default(omit)}}'
|
||||
pac_type: '{{previousconfig.config.pac_type | default(omit)}}'
|
||||
user_auth_type: '{{previousconfig.config.user_auth_type | default(omit)}}'
|
||||
domain_resolution_order: '{{previousconfig.config.domain_resolution_order | default(omit)}}'
|
||||
ca_renewal_master_server: '{{previousconfig.config.ca_renewal_master_server | default(omit)}}'
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: reset changed fields, again
|
||||
ipaconfig:
|
||||
ipaadmin_password: 'SomeADMINpassword'
|
||||
maxusername: '{{previousconfig.config.maxusername | default(omit)}}'
|
||||
maxhostname: '{{previousconfig.config.maxhostname | default(omit)}}'
|
||||
homedirectory: '{{previousconfig.config.homedirectory | default(omit)}}'
|
||||
defaultshell: '{{previousconfig.config.defaultshell | default(omit)}}'
|
||||
defaultgroup: '{{previousconfig.config.defaultgroup | default(omit)}}'
|
||||
emaildomain: '{{previousconfig.config.emaildomain | default(omit)}}'
|
||||
searchtimelimit: '{{previousconfig.config.searchtimelimit | default(omit)}}'
|
||||
searchrecordslimit: '{{previousconfig.config.searchrecordslimit | default(omit)}}'
|
||||
usersearch: '{{previousconfig.config.usersearch | default(omit)}}'
|
||||
groupsearch: '{{previousconfig.config.groupsearch | default(omit)}}'
|
||||
enable_migration: '{{previousconfig.config.enable_migration | default(omit)}}'
|
||||
groupobjectclasses: '{{previousconfig.config.groupobjectclasses | default(omit)}}'
|
||||
userobjectclasses: '{{previousconfig.config.userobjectclasses | default(omit)}}'
|
||||
pwdexpnotify: '{{previousconfig.config.pwdexpnotify | default(omit)}}'
|
||||
configstring: '{{previousconfig.config.configstring | default(omit)}}'
|
||||
selinuxusermapdefault: '{{previousconfig.config.selinuxusermapdefault | default(omit)}}'
|
||||
selinuxusermaporder: '{{previousconfig.config.selinuxusermaporder | default(omit)}}'
|
||||
pac_type: '{{previousconfig.config.pac_type | default(omit)}}'
|
||||
user_auth_type: '{{previousconfig.config.user_auth_type | default(omit)}}'
|
||||
domain_resolution_order: '{{previousconfig.config.domain_resolution_order | default(omit)}}'
|
||||
ca_renewal_master_server: '{{previousconfig.config.ca_renewal_master_server | default(omit)}}'
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
# cleanup
|
||||
|
||||
- name: cleanup test group
|
||||
ipagroup:
|
||||
ipaadmin_password: 'SomeADMINpassword'
|
||||
name: somedefaultgroup
|
||||
state: absent
|
||||
@@ -18,6 +18,29 @@
|
||||
state: absent
|
||||
|
||||
# Tests.
|
||||
- name: Set config to invalid IPv4.
|
||||
ipadnsconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
forwarders:
|
||||
- ip_address: 1.2.3.500
|
||||
register: result
|
||||
failed_when: not result.failed or "Invalid IP for DNS forwarder" not in result.msg
|
||||
|
||||
- name: Set config to invalid IP.
|
||||
ipadnsconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
forwarders:
|
||||
- ip_address: 1.in.va.lid
|
||||
register: result
|
||||
failed_when: not result.failed or "Invalid IP for DNS forwarder" not in result.msg
|
||||
|
||||
- name: Set config to invalid IPv6.
|
||||
ipadnsconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
forwarders:
|
||||
- ip_address: fd00::invalid
|
||||
register: result
|
||||
failed_when: not result.failed or "Invalid IP for DNS forwarder" not in result.msg
|
||||
|
||||
- name: Set dnsconfig.
|
||||
ipadnsconfig:
|
||||
|
||||
135
tests/dnsrecord/env_cleanup.yml
Normal file
135
tests/dnsrecord/env_cleanup.yml
Normal file
@@ -0,0 +1,135 @@
|
||||
---
|
||||
# Cleanup tasks.
|
||||
- name: Ensure that dns records are absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: "{{ testzone }}"
|
||||
del_all: yes
|
||||
name:
|
||||
- host01
|
||||
- host02
|
||||
- host03
|
||||
- host04
|
||||
- _ftp._tcp
|
||||
- _sip._udp
|
||||
state: absent
|
||||
|
||||
- name: Ensure that dns reverse ipv6 records are absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: ip6.arpa.
|
||||
del_all: yes
|
||||
name:
|
||||
- 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.f
|
||||
- 1.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.f
|
||||
- 1.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.f
|
||||
- 4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.f
|
||||
- 4.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.f
|
||||
- 4.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.f
|
||||
state: absent
|
||||
|
||||
- name: Ensure that dns reverse ipv6 records are absent (workaround)
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: "{{ zone_ipv6_reverse_workaround }}"
|
||||
del_all: yes
|
||||
name:
|
||||
- 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
|
||||
- 1.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
|
||||
- 1.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
|
||||
- 4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
|
||||
- 4.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
|
||||
- 4.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
|
||||
state: absent
|
||||
|
||||
- name: Ensure that dns reverse records are absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: "{{ zone_prefix_reverse_24 }}"
|
||||
name:
|
||||
- "101"
|
||||
- "102"
|
||||
- "103"
|
||||
- "104"
|
||||
- "111"
|
||||
- "112"
|
||||
- "113"
|
||||
- "114"
|
||||
- "121"
|
||||
- "122"
|
||||
- "123"
|
||||
- "124"
|
||||
del_all: yes
|
||||
state: absent
|
||||
|
||||
- name: Ensure that dns reverse records are absent (workaround 1)
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: "{{ zone_prefix_reverse_16 }}"
|
||||
name:
|
||||
- "101.122"
|
||||
- "102.122"
|
||||
- "103.122"
|
||||
- "104.122"
|
||||
- "111.122"
|
||||
- "112.122"
|
||||
- "113.122"
|
||||
- "114.122"
|
||||
- "121.122"
|
||||
- "122.122"
|
||||
- "123.122"
|
||||
- "124.122"
|
||||
del_all: yes
|
||||
state: absent
|
||||
|
||||
- name: Ensure that dns reverse records are absent (workaround 2)
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: "{{ zone_prefix_reverse_8 }}"
|
||||
name:
|
||||
- "168.101.122"
|
||||
- "168.102.122"
|
||||
- "168.103.122"
|
||||
- "168.104.122"
|
||||
- "168.111.122"
|
||||
- "168.112.122"
|
||||
- "168.113.122"
|
||||
- "168.114.122"
|
||||
- "168.121.122"
|
||||
- "168.122.122"
|
||||
- "168.123.122"
|
||||
- "168.124.122"
|
||||
del_all: yes
|
||||
state: absent
|
||||
|
||||
- name: Ensure that "{{ safezone }}" dns records are absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
zone_name: "{{ safezone }}"
|
||||
records:
|
||||
- name: iron01
|
||||
del_all: yes
|
||||
state: absent
|
||||
|
||||
- name: Ensure that NS record for "{{ safezone }}" is absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: iron01
|
||||
zone_name: "{{ safezone }}"
|
||||
ns_rec: iron01
|
||||
state: absent
|
||||
|
||||
- name: Ensure DNS testing zones are absent.
|
||||
ipadnszone:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
with_items:
|
||||
- "{{ zone_prefix_reverse }}"
|
||||
- "{{ zone_prefix_reverse_24 }}"
|
||||
- "{{ zone_prefix_reverse_16 }}"
|
||||
- "{{ zone_prefix_reverse_8 }}"
|
||||
- "{{ testzone }}"
|
||||
- ip6.arpa.
|
||||
- d.f.ip6.arpa.
|
||||
- "{{ safezone }}"
|
||||
31
tests/dnsrecord/env_setup.yml
Normal file
31
tests/dnsrecord/env_setup.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
- name: Setup variables and facts.
|
||||
include_tasks: env_vars.yml
|
||||
|
||||
# Cleanup before setup.
|
||||
- name: Cleanup test environment.
|
||||
include_tasks: env_cleanup.yml
|
||||
|
||||
# Common setup tasks.
|
||||
- name: Ensure DNS testing zones are present.
|
||||
ipadnszone:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "{{ item }}"
|
||||
skip_nameserver_check: yes
|
||||
skip_overlap_check: yes
|
||||
with_items:
|
||||
- "{{ zone_prefix_reverse }}"
|
||||
- "{{ zone_prefix_reverse_24 }}"
|
||||
- "{{ zone_prefix_reverse_16 }}"
|
||||
- "{{ zone_prefix_reverse_8 }}"
|
||||
- "{{ testzone }}"
|
||||
- ip6.arpa.
|
||||
|
||||
- name: Ensure DNSSEC zone '"{{ safezone }}"' is present.
|
||||
ipadnszone:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "{{ safezone }}"
|
||||
dnssec: yes
|
||||
skip_nameserver_check: yes
|
||||
skip_overlap_check: yes
|
||||
ignore_errors: yes
|
||||
17
tests/dnsrecord/env_vars.yml
Normal file
17
tests/dnsrecord/env_vars.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
# Set common vars and facts for test.
|
||||
- name: Set IPv4 address prefix.
|
||||
set_fact:
|
||||
ipv4_prefix: '192.168.122'
|
||||
ipv4_reverse_sufix: '122.168.192'
|
||||
|
||||
- name: Set zone prefixes.
|
||||
set_fact:
|
||||
testzone: 'testzone.test'
|
||||
safezone: 'safezone.test'
|
||||
zone_ipv6_reverse: "ip6.arpa."
|
||||
zone_ipv6_reverse_workaround: "d.f.ip6.arpa."
|
||||
zone_prefix_reverse: "in-addr.arpa"
|
||||
zone_prefix_reverse_24: "{{ ipv4_prefix.split('.')[::-1] | join ('.') }}.in-addr.arpa"
|
||||
zone_prefix_reverse_16: "{{ ipv4_prefix.split('.')[1::-1] | join ('.') }}.in-addr.arpa"
|
||||
zone_prefix_reverse_8: "{{ ipv4_prefix.split('.')[2::-1] | join ('.') }}.in-addr.arpa"
|
||||
234
tests/dnsrecord/test_compatibility_with_ansible_module.yml
Normal file
234
tests/dnsrecord/test_compatibility_with_ansible_module.yml
Normal file
@@ -0,0 +1,234 @@
|
||||
---
|
||||
- name: Test compatibility with Ansible ipa_dnsrecord module.
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
|
||||
# setup
|
||||
- name: Ensure DNS zones to be used are absent.
|
||||
ipadnszone:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
with_items:
|
||||
- testzone.local
|
||||
- 2.168.192.in-addr.arpa
|
||||
|
||||
- name: Ensure DNS zones to be used are present.
|
||||
ipadnszone:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "{{ item }}"
|
||||
with_items:
|
||||
- testzone.local
|
||||
- 2.168.192.in-addr.arpa
|
||||
|
||||
- name: Ensure that dns record 'host01' is absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: testzone.local
|
||||
record_type: 'AAAA'
|
||||
record_value: '::1'
|
||||
state: absent
|
||||
|
||||
- name: Ensure that dns record 'vm-001' is absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: vm-001
|
||||
zone_name: testzone.local
|
||||
record_type: 'AAAA'
|
||||
record_value: '::1'
|
||||
state: absent
|
||||
|
||||
- name: Ensure a PTR record is absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: 5
|
||||
record_type: 'PTR'
|
||||
record_value: 'internal.ipa.testzone.local'
|
||||
zone_name: 2.168.192.in-addr.arpa
|
||||
state: absent
|
||||
|
||||
- name: Ensure a TXT record is absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: _kerberos
|
||||
record_type: 'TXT'
|
||||
record_value: 'TESTZONE.LOCAL'
|
||||
zone_name: testzone.local
|
||||
state: absent
|
||||
|
||||
- name: Ensure a SRV record is absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: _kerberos._udp.testzone.local
|
||||
record_type: 'SRV'
|
||||
record_value: '10 50 88 ipa.testzone.local'
|
||||
zone_name: testzone.local
|
||||
state: absent
|
||||
|
||||
- name: Ensure an MX record is absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: '@'
|
||||
record_type: 'MX'
|
||||
record_value: '1 mailserver.testzone.local'
|
||||
zone_name: testzone.local
|
||||
state: absent
|
||||
|
||||
# tests
|
||||
- name: Ensure dns record is present
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: vm-001
|
||||
record_type: 'AAAA'
|
||||
record_value: '::1'
|
||||
zone_name: testzone.local
|
||||
state: present
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure that dns record exists with a TTL
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
record_type: 'AAAA'
|
||||
record_value: '::1'
|
||||
record_ttl: 300
|
||||
zone_name: testzone.local
|
||||
state: present
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure a PTR record is present
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: 5
|
||||
record_type: 'PTR'
|
||||
record_value: 'internal.ipa.testzone.local'
|
||||
zone_name: 2.168.192.in-addr.arpa
|
||||
state: present
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure a TXT record is present
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: _kerberos
|
||||
record_type: 'TXT'
|
||||
record_value: 'TESTZONE.LOCAL'
|
||||
zone_name: testzone.local
|
||||
state: present
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure a SRV record is present
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: _kerberos._udp.testzone.local
|
||||
record_type: 'SRV'
|
||||
record_value: '10 50 88 ipa.testzone.local'
|
||||
zone_name: testzone.local
|
||||
state: present
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure an MX record is present
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: '@'
|
||||
record_type: 'MX'
|
||||
record_value: '1 mailserver.testzone.local'
|
||||
zone_name: testzone.local
|
||||
state: present
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure that dns record is removed
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: testzone.local
|
||||
record_type: 'AAAA'
|
||||
record_value: '::1'
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
# cleanup
|
||||
- name: Ensure that dns record 'host01' is absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: testzone.local
|
||||
record_type: 'AAAA'
|
||||
record_value: '::1'
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: Ensure that dns record 'vm-001' is absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: vm-001
|
||||
zone_name: testzone.local
|
||||
record_type: 'AAAA'
|
||||
record_value: '::1'
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure a PTR record is absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: 5
|
||||
record_type: 'PTR'
|
||||
record_value: 'internal.ipa.testzone.local'
|
||||
zone_name: 2.168.192.in-addr.arpa
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure a TXT record is absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: _kerberos
|
||||
record_type: 'TXT'
|
||||
record_value: 'TESTZONE.LOCAL'
|
||||
zone_name: testzone.local
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure a SRV record is absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: _kerberos._udp.testzone.local
|
||||
record_type: 'SRV'
|
||||
record_value: '10 50 88 ipa.testzone.local'
|
||||
zone_name: testzone.local
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure an MX record is absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: '@'
|
||||
record_type: 'MX'
|
||||
record_value: '1 mailserver.testzone.local'
|
||||
zone_name: testzone.local
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure DNS zones to be used are absent.
|
||||
ipadnszone:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
with_items:
|
||||
- testzone.local
|
||||
- 2.168.192.in-addr.arpa
|
||||
1348
tests/dnsrecord/test_dnsrecord.yml
Normal file
1348
tests/dnsrecord/test_dnsrecord.yml
Normal file
File diff suppressed because it is too large
Load Diff
150
tests/dnsrecord/test_dnsrecord_full_records.yml
Normal file
150
tests/dnsrecord/test_dnsrecord_full_records.yml
Normal file
@@ -0,0 +1,150 @@
|
||||
---
|
||||
- name: Test dnsrecord with full records (*-rec variables).
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
gather_facts: yes
|
||||
|
||||
tasks:
|
||||
|
||||
- name: Setup test environment
|
||||
include_tasks: env_setup.yml
|
||||
|
||||
# tests
|
||||
|
||||
- name: Ensure that dns A record for 'host01' is present
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: "{{ testzone }}"
|
||||
a_rec: 192.168.122.101
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure that dns A record for 'host01' is present, again
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: "{{ testzone }}"
|
||||
a_rec: 192.168.122.101
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: Ensure that dns A records for 'host01' are present
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: "{{ testzone }}"
|
||||
a_rec:
|
||||
- 192.168.122.101
|
||||
- 192.168.122.102
|
||||
- 192.168.122.103
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure that dns A records for 'host01' are present, again
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: "{{ testzone }}"
|
||||
a_rec:
|
||||
- 192.168.122.101
|
||||
- 192.168.122.102
|
||||
- 192.168.122.103
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: Ensure that dns A records for 'host01' are absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: "{{ testzone }}"
|
||||
a_rec:
|
||||
- 192.168.122.101
|
||||
- 192.168.122.102
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure that dns A records for 'host01' are absent, again
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: "{{ testzone }}"
|
||||
a_rec:
|
||||
- 192.168.122.101
|
||||
- 192.168.122.102
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
####
|
||||
|
||||
- name: Ensure that dns AAAA record for 'host01' is present
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: "{{ testzone }}"
|
||||
aaaa_rec: fd00::0001
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure that dns AAAA record for 'host01' is present, again
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: "{{ testzone }}"
|
||||
aaaa_rec: fd00::0001
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: Ensure that dns AAAA records for 'host01' are present
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: "{{ testzone }}"
|
||||
aaaa_rec:
|
||||
- fd00::0001
|
||||
- fd00::0011
|
||||
- fd00::0021
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure that dns AAAAA records for 'host01' are present, again
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: "{{ testzone }}"
|
||||
aaaa_rec:
|
||||
- fd00::0001
|
||||
- fd00::0011
|
||||
- fd00::0021
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
- name: Ensure that dns AAAAA records for 'host01' are absent
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: "{{ testzone }}"
|
||||
aaaa_rec:
|
||||
- fd00::0001
|
||||
- fd00::0011
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: not result.changed
|
||||
|
||||
- name: Ensure that dns AAAAA records for 'host01' are absent, again
|
||||
ipadnsrecord:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01
|
||||
zone_name: "{{ testzone }}"
|
||||
aaaa_rec:
|
||||
- fd00::0001
|
||||
- fd00::0011
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: result.changed
|
||||
|
||||
# Cleanup
|
||||
- name: Cleanup test environment.
|
||||
include_tasks: env_cleanup.yml
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user