mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-04-10 10:50:55 +00:00
Compare commits
126 Commits
fix-role-r
...
v0.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c7f433135 | ||
|
|
e90ce386fb | ||
|
|
af37ad97aa | ||
|
|
dccf2ed12d | ||
|
|
cf4c590303 | ||
|
|
a7e532a4dc | ||
|
|
8e664157dd | ||
|
|
8f549f57c6 | ||
|
|
1f66660995 | ||
|
|
e44de3c5cb | ||
|
|
97a03ee47b | ||
|
|
19a94ac476 | ||
|
|
29576c1aab | ||
|
|
7e53d70d64 | ||
|
|
56fd5419cb | ||
|
|
3ff782f871 | ||
|
|
b2fd94e702 | ||
|
|
75d815e745 | ||
|
|
ce5ee80f20 | ||
|
|
06f06c487c | ||
|
|
b7900f1c64 | ||
|
|
121f59bba7 | ||
|
|
bbf6d51f70 | ||
|
|
f2b4bac386 | ||
|
|
c05a7233ec | ||
|
|
b8398c4737 | ||
|
|
287d12d455 | ||
|
|
786c902a3c | ||
|
|
a85f7ce9be | ||
|
|
cdf411dfd3 | ||
|
|
a3510de0d6 | ||
|
|
f7acb7b2a8 | ||
|
|
af7060d3a9 | ||
|
|
880e7ccf08 | ||
|
|
48db01a5fa | ||
|
|
6a0db7712c | ||
|
|
d5179b523e | ||
|
|
3ef69390ed | ||
|
|
a250665a1e | ||
|
|
d24bdbcefd | ||
|
|
4a62879232 | ||
|
|
9883514cb6 | ||
|
|
aab6caf3e4 | ||
|
|
8c0b1fb5a1 | ||
|
|
095d726c5b | ||
|
|
7811afee82 | ||
|
|
f3270ca0fd | ||
|
|
b678fa73a6 | ||
|
|
b9f0f95509 | ||
|
|
214a31eb81 | ||
|
|
316f5eded0 | ||
|
|
6458deb344 | ||
|
|
58de022edb | ||
|
|
609901eda6 | ||
|
|
39d5558bd2 | ||
|
|
8b06e31e26 | ||
|
|
366e023db7 | ||
|
|
c74cd084f2 | ||
|
|
c2f68a3401 | ||
|
|
32f6ef18f2 | ||
|
|
3b32f27508 | ||
|
|
5927e1c47d | ||
|
|
b7e1a99b6e | ||
|
|
dc7bf52585 | ||
|
|
d6afa976f5 | ||
|
|
a7c52db406 | ||
|
|
a8e9b2ae00 | ||
|
|
5fa81a437b | ||
|
|
0395f4136f | ||
|
|
b4fbfadeec | ||
|
|
9a97303cca | ||
|
|
246593d77f | ||
|
|
d69eb94d90 | ||
|
|
9cb75cdea7 | ||
|
|
675125ed0b | ||
|
|
46bbc7bbd7 | ||
|
|
41e8226d0c | ||
|
|
531e544b30 | ||
|
|
abbd15e6f5 | ||
|
|
fbb2819df8 | ||
|
|
0af8f35e83 | ||
|
|
9a3f08b6c9 | ||
|
|
f013f98a0f | ||
|
|
b7722a476f | ||
|
|
3c2700f68b | ||
|
|
8ca282e276 | ||
|
|
4ef4e706b7 | ||
|
|
daee6a6c74 | ||
|
|
d52364bac9 | ||
|
|
3e5c54d4fd | ||
|
|
33db65374b | ||
|
|
15aacc2c57 | ||
|
|
f7b175d5c8 | ||
|
|
6aa1187c86 | ||
|
|
048f955011 | ||
|
|
fc3b8dba5b | ||
|
|
3a57244075 | ||
|
|
7e2b00799c | ||
|
|
5ef6e61f77 | ||
|
|
603f0c1374 | ||
|
|
64adb6c175 | ||
|
|
ee0cb2a2d0 | ||
|
|
3ab575bcac | ||
|
|
b5e93c705f | ||
|
|
7dd0b547c4 | ||
|
|
343617502d | ||
|
|
16f67ce92d | ||
|
|
75d16c2da4 | ||
|
|
542e241440 | ||
|
|
ee370ad3f8 | ||
|
|
cee8b3a39b | ||
|
|
e96ef4e98e | ||
|
|
563a03d94b | ||
|
|
da5dc0c472 | ||
|
|
70e3e1a544 | ||
|
|
8852fa6ece | ||
|
|
19058f1320 | ||
|
|
46427d10ef | ||
|
|
3633ba5a3d | ||
|
|
627c644166 | ||
|
|
5a5fbfb25b | ||
|
|
8e08868e1a | ||
|
|
b8f96c6201 | ||
|
|
fd28559edf | ||
|
|
5d6adee15e | ||
|
|
7a6036f7cf |
23
.ansible-lint
Normal file
23
.ansible-lint
Normal file
@@ -0,0 +1,23 @@
|
||||
exclude_paths:
|
||||
- roles
|
||||
- .tox
|
||||
- .venv
|
||||
|
||||
parseable: true
|
||||
|
||||
quiet: false
|
||||
|
||||
skip_list:
|
||||
- '201' # Trailing whitespace
|
||||
- '204' # Lines should be no longer than 160 chars
|
||||
- '206' # Variables should have spaces before and after: {{ var_name }}'
|
||||
- '208' # File permissions not mentioned
|
||||
- '301' # Commands should not change things if nothing needs doing'
|
||||
- '305' # Use shell only when shell functionality is required'
|
||||
- '306' # Shells that use pipes should set the pipefail option'
|
||||
- '502' # All tasks should be named
|
||||
- '505' # Referenced missing file
|
||||
|
||||
use_default_rules: true
|
||||
|
||||
verbosity: 1
|
||||
9
.copr/Makefile
Normal file
9
.copr/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
srpm:
|
||||
# Setup development environment
|
||||
echo "Installing base development environment"
|
||||
dnf install -y dnf-plugins-core git-all
|
||||
echo "Call SRPM build Script"
|
||||
./utils/build-srpm.sh
|
||||
if [[ "${outdir}" != "" ]]; then \
|
||||
mv /builddir/build/SRPMS/* ${outdir}; \
|
||||
fi
|
||||
33
.github/workflows/lint.yml
vendored
Normal file
33
.github/workflows/lint.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: Run Linters
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
jobs:
|
||||
linters:
|
||||
name: Run Linters
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: "3.6"
|
||||
|
||||
- name: Run ansible-lint
|
||||
uses: ansible/ansible-lint-action@master
|
||||
with:
|
||||
targets: |
|
||||
tests/*.yml
|
||||
tests/*/*.yml
|
||||
tests/*/*/*.yml
|
||||
playbooks/*.yml
|
||||
playbooks/*/*.yml
|
||||
env:
|
||||
ANSIBLE_MODULE_UTILS: plugins/module_utils
|
||||
ANSIBLE_LIBRARY: plugins/modules
|
||||
|
||||
- name: Run yaml-lint
|
||||
uses: ibiqlik/action-yamllint@v1
|
||||
|
||||
- name: Run Python linters
|
||||
uses: rjeffman/python-lint-action@master
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,2 +1,8 @@
|
||||
*.pyc
|
||||
*.retry
|
||||
|
||||
# ignore virtual environments
|
||||
/.tox/
|
||||
/.venv/
|
||||
|
||||
tests/logs/
|
||||
|
||||
28
.yamllint
Normal file
28
.yamllint
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
ignore: |
|
||||
/.tox/
|
||||
/.venv/
|
||||
/.github/
|
||||
|
||||
extends: default
|
||||
|
||||
rules:
|
||||
braces:
|
||||
max-spaces-inside: 1
|
||||
level: error
|
||||
brackets:
|
||||
max-spaces-inside: 1
|
||||
level: error
|
||||
truthy:
|
||||
allowed-values: ["yes", "no", "true", "false", "True", "False"]
|
||||
level: error
|
||||
# Disabled rules
|
||||
document-start: disable
|
||||
indentation: disable
|
||||
line-length: disable
|
||||
colons: disable
|
||||
empty-lines: disable
|
||||
comments: disable
|
||||
comments-indentation: disable
|
||||
trailing-spaces: disable
|
||||
new-line-at-end-of-file: disable
|
||||
@@ -19,6 +19,7 @@ Supported FreeIPA Versions
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipaconfig module.
|
||||
|
||||
Some variables are only supported on newer versions of FreeIPA. Check `Variables` section for details.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
@@ -91,7 +92,7 @@ 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
|
||||
`maxhostname` \| `ipamaxhostnamelength` | Set the maximum hostname length between 64-255. Only usable with IPA versions 4.8.0 and up. | 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
|
||||
|
||||
157
README-delegation.md
Normal file
157
README-delegation.md
Normal file
@@ -0,0 +1,157 @@
|
||||
Delegation module
|
||||
=================
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The delegation module allows to ensure presence, absence of delegations and delegation attributes.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Delegation management
|
||||
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipadelegation module.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure delegation "basic manager attributes" is present:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA delegation.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
permission: read
|
||||
attribute:
|
||||
- businesscategory
|
||||
- employeetype
|
||||
group: managers
|
||||
membergroup: employees
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure delegation "basic manager attributes" is absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA delegation.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure "basic manager attributes" member attributes employeetype and employeenumber are present:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA delegation.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
attribute:
|
||||
- employeenumber
|
||||
- employeetype
|
||||
action: member
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure "basic manager attributes" member attributes employeetype and employeenumber are absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA delegation.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
attribute:
|
||||
- employeenumber
|
||||
- employeetype
|
||||
action: member
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure delegation "basic manager attributes" is absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA delegation.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
---------
|
||||
|
||||
ipadelegation
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`name` \| `aciname` | The list of delegation name strings. | yes
|
||||
`permission` \| `permissions` | The permission to grant `read`, `read,write`, `write`]. Default is `write`. | no
|
||||
`attribute` \| `attrs` | The attribute list to which the delegation applies. | no
|
||||
`membergroup` \| `memberof` | The user group to apply delegation to. | no
|
||||
`group` | User group ACI grants access to. | no
|
||||
`action` | Work on delegation or member level. It can be on of `member` or `delegation` and defaults to `delegation`. | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. | no
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Thomas Woerner
|
||||
@@ -152,6 +152,46 @@ Example playbook to remove a zone:
|
||||
|
||||
```
|
||||
|
||||
Example playbook to create a zone for reverse DNS lookup, from an IP address:
|
||||
|
||||
```yaml
|
||||
|
||||
---
|
||||
- name: dnszone present
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure zone for reverse DNS lookup is present.
|
||||
ipadnszone:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name_from_ip: 192.168.1.2
|
||||
state: present
|
||||
```
|
||||
|
||||
Note that, on the previous example the zone created with `name_from_ip` might be "1.168.192.in-addr.arpa.", "168.192.in-addr.arpa.", or "192.in-addr.arpa.", depending on the DNS response the system get while querying for zones, and for this reason, when creating a zone using `name_from_ip`, the inferred zone name is returned to the controller, in the attribute `dnszone.name`. Since the zone inferred might not be what a user expects, `name_from_ip` can only be used with `state: present`. To have more control over the zone name, the prefix length for the IP address can be provided.
|
||||
|
||||
Example playbook to create a zone for reverse DNS lookup, from an IP address, given the prefix length and displaying the resulting zone name:
|
||||
|
||||
```yaml
|
||||
|
||||
---
|
||||
- name: dnszone present
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure zone for reverse DNS lookup is present.
|
||||
ipadnszone:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name_from_ip: 192.168.1.2/24
|
||||
state: present
|
||||
register: result
|
||||
- name: Display inferred zone name.
|
||||
debug:
|
||||
msg: "Zone name: {{ result.dnszone.name }}"
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
=========
|
||||
@@ -163,7 +203,8 @@ Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`name` \| `zone_name` | The zone name string. | yes
|
||||
`name` \| `zone_name` | The zone name string or list of strings. | no
|
||||
`name_from_ip` | Derive zone name from reverse of IP (PTR). Can only be used with `state: present`. | no
|
||||
`forwarders` | The list of forwarders dicts. Each `forwarders` dict entry has:| no
|
||||
| `ip_address` - The IPv4 or IPv6 address of the DNS server. | yes
|
||||
| `port` - The custom port that should be used on this server. | no
|
||||
@@ -189,6 +230,17 @@ Variable | Description | Required
|
||||
`skip_nameserver_check` | Force DNS zone creation even if nameserver is not resolvable | no
|
||||
|
||||
|
||||
Return Values
|
||||
=============
|
||||
|
||||
ipadnszone
|
||||
----------
|
||||
|
||||
Variable | Description | Returned When
|
||||
-------- | ----------- | -------------
|
||||
`dnszone` | DNS Zone dict with zone name infered from `name_from_ip`. <br>Options: | If `state` is `present`, `name_from_ip` is used, and a zone was created.
|
||||
| `name` - The name of the zone created, inferred from `name_from_ip`. | Always
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ Supported FreeIPA Versions
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipagroup module.
|
||||
|
||||
Some variables are only supported on newer versions of FreeIPA. Check `Variables` section for details.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
@@ -19,6 +19,8 @@ Supported FreeIPA Versions
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipahostgroup module.
|
||||
|
||||
Some variables are only supported on newer versions of FreeIPA. Check `Variables` section for details.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
@@ -105,6 +107,23 @@ Example playbook to make sure hosts and hostgroups are absent in databases hostg
|
||||
state: absent
|
||||
```
|
||||
|
||||
Example playbook to rename an existing playbook:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to handle hostgroups
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
# Ensure host-group databases is absent
|
||||
- ipahostgroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: databases
|
||||
rename: datalake
|
||||
state: renamed
|
||||
```
|
||||
|
||||
Example playbook to make sure host-group databases is absent:
|
||||
|
||||
```yaml
|
||||
@@ -121,7 +140,6 @@ Example playbook to make sure host-group databases is absent:
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
=========
|
||||
|
||||
@@ -139,8 +157,9 @@ Variable | Description | Required
|
||||
`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
|
||||
`rename` \| `new_name` | Rename hostgroup to the provided name. Only usable with IPA versions 4.8.7 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
|
||||
`state` | The state to ensure. It can be one of `present`, `absent` or `renamed`, default: `present`. | no
|
||||
|
||||
|
||||
Authors
|
||||
|
||||
92
README-location.md
Normal file
92
README-location.md
Normal file
@@ -0,0 +1,92 @@
|
||||
Location module
|
||||
===============
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The location module allows to ensure presence and absence of locations.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Location management
|
||||
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipalocation module.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure location "my_location1" is present:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA location.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipalocation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my_location1
|
||||
description: My Location 1
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure location "my_location1" is absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA location.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipalocation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my_location1
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
---------
|
||||
|
||||
ipalocation
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`name` \| `idnsname` | The list of location name strings. | yes
|
||||
`description` | The IPA location string | false
|
||||
`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. | no
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Thomas Woerner
|
||||
147
README-privilege.md
Normal file
147
README-privilege.md
Normal file
@@ -0,0 +1,147 @@
|
||||
Privilege module
|
||||
================
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The privilege module allows to ensure presence and absence of privileges and privilege members.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Privilege management
|
||||
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipaprivilege module.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure privilege "Broad Privilege" is present:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA privilege.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipaprivilege:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: Broad Privilege
|
||||
description: Broad Privilege
|
||||
```
|
||||
|
||||
Example playbook to make sure privilege "Broad Privilege" member permission has multiple values:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA privilege permission member.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipaprivilege:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: Broad Privilege
|
||||
permission:
|
||||
- "Write IPA Configuration"
|
||||
- "System: Write DNS Configuration"
|
||||
- "System: Update DNS Entries"
|
||||
action: member
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure privilege "Broad Privilege" member permission 'Write IPA Configuration' is absent:
|
||||
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA privilege permission member.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipaprivilege:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: Broad Privilege
|
||||
permission:
|
||||
- "Write IPA Configuration"
|
||||
action: member
|
||||
state: absent
|
||||
```
|
||||
|
||||
Example playbook to rename privilege "Broad Privilege" to "DNS Special Privilege":
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA privilege.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipaprivilege:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: Broad Privilege
|
||||
rename: DNS Special Privilege
|
||||
state: renamed
|
||||
```
|
||||
|
||||
Example playbook to make sure privilege "DNS Special Privilege" is absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA privilege.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
- name: Ensure privilege Broad Privilege is absent
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: DNS Special Privilege
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
---------
|
||||
|
||||
ipaprivilege
|
||||
------------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin`. | no
|
||||
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node. | no
|
||||
`name` \| `cn` | The list of privilege name strings. | yes
|
||||
`description` | Privilege description. | no
|
||||
`rename` \| `new_name` | Rename the privilege object. | no
|
||||
`permission` | Permissions to be added to the privilege. | no
|
||||
`action` | Work on privilege or member level. It can be one of `member` or `privilege` and defaults to `privilege`. | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent` or `renamed`, default: `present`. | no
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Rafael Guterres Jeffman
|
||||
@@ -1,5 +1,5 @@
|
||||
Service module
|
||||
==============
|
||||
Role module
|
||||
===========
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
151
README-selfservice.md
Normal file
151
README-selfservice.md
Normal file
@@ -0,0 +1,151 @@
|
||||
Selfservice module
|
||||
=================
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The selfservice module allows to ensure presence, absence of selfservices and selfservice attributes.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Selfservice management
|
||||
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipaselfservice module.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure selfservice "Users can manage their own name details" is present:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA selfservice.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipaselfservice:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "Users can manage their own name details"
|
||||
permission: read
|
||||
attribute:
|
||||
- title
|
||||
- initials
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure selfservice "Users can manage their own name details" is absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA selfservice.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipaselfservice:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "Users can manage their own name details"
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure "Users can manage their own name details" member attribute initials is present:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA selfservice.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipaselfservice:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "Users can manage their own name details"
|
||||
attribute:
|
||||
- initials
|
||||
action: member
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure "Users can manage their own name details" member attribute initials is absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA selfservice.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipaselfservice:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "Users can manage their own name details"
|
||||
attribute:
|
||||
- initials
|
||||
action: member
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure selfservice "Users can manage their own name details" is absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA selfservice.
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- ipaselfservice:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "Users can manage their own name details"
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
---------
|
||||
|
||||
ipaselfservice
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`name` \| `aciname` | The list of selfservice name strings. | yes
|
||||
`permission` \| `permissions` | The permission to grant `read`, `read,write`, `write`]. Default is `write`. | no
|
||||
`attribute` \| `attrs` | The attribute list to which the selfservice applies. | no
|
||||
`action` | Work on selfservice or member level. It can be on of `member` or `selfservice` and defaults to `selfservice`. | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. | no
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Thomas Woerner
|
||||
@@ -18,7 +18,7 @@ Supported FreeIPA Versions
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipaservice module.
|
||||
|
||||
Option `skip_host_check` requires FreeIPA version 4.7.0 or later.
|
||||
Some variables are only supported on newer versions of FreeIPA. Check `Variables` section for details.
|
||||
|
||||
|
||||
Requirements
|
||||
@@ -56,7 +56,7 @@ Example playbook to make sure service is present:
|
||||
- ipaservice:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
certificate:
|
||||
certificate: |
|
||||
- MIIC/zCCAeegAwIBAgIUMNHIbn+hhrOVew/2WbkteisV29QwDQYJKoZIhvcNAQELBQAw
|
||||
DzENMAsGA1UEAwwEdGVzdDAeFw0yMDAyMDQxNDQxMDhaFw0zMDAyMDExNDQxMDhaMA8xDT
|
||||
ALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+XVVGFYpH
|
||||
@@ -77,7 +77,7 @@ Example playbook to make sure service is present:
|
||||
requires_pre_auth: false
|
||||
ok_as_delegate: false
|
||||
ok_to_auth_as_delegate: false
|
||||
skip-host-check: true
|
||||
skip_host_check: true
|
||||
force: true
|
||||
```
|
||||
|
||||
@@ -167,7 +167,7 @@ Example playbook to ensure service has a certificate:
|
||||
- ipaservice:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
certificate:
|
||||
certificate: |
|
||||
- MIIC/zCCAeegAwIBAgIUMNHIbn+hhrOVew/2WbkteisV29QwDQYJKoZIhvcNAQELBQAw
|
||||
DzENMAsGA1UEAwwEdGVzdDAeFw0yMDAyMDQxNDQxMDhaFw0zMDAyMDExNDQxMDhaMA8xDT
|
||||
ALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+XVVGFYpH
|
||||
@@ -298,7 +298,7 @@ Variable | Description | Required
|
||||
`requires_pre_auth` \| `ipakrbrequirespreauth` | Pre-authentication is required for the service. Default to true. (bool) | no
|
||||
`ok_as_delegate` \| `ipakrbokasdelegate` | Client credentials may be delegated to the service. Default to false. (bool) | no
|
||||
`ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client. Default to false. (bool) | no
|
||||
`skip_host_check` | Force service to be created even when host object does not exist to manage it. Default to false. (bool)| no
|
||||
`skip_host_check` | Force service to be created even when host object does not exist to manage it. Only usable with IPA versions 4.7.0 and up. Default to false. (bool)| no
|
||||
`force` | Force principal name even if host not in DNS. Default to false. (bool) | no
|
||||
`host` \| `managedby_host`| Hosts that can manage the service. | no
|
||||
`principal` \| `krbprincipalname` | List of principal aliases for the service. | no
|
||||
|
||||
119
README-trust.md
Normal file
119
README-trust.md
Normal file
@@ -0,0 +1,119 @@
|
||||
Trust module
|
||||
============
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The trust module allows to ensure presence and absence of a domain trust.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Trust management
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipatrust module.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
|
||||
* Ansible version: 2.8+
|
||||
|
||||
**Node**
|
||||
|
||||
* Supported FreeIPA version (see above)
|
||||
* samba-4
|
||||
* ipa-server-trust-ad
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
Example playbook to ensure a one-way trust is present:
|
||||
Omitting the two_way option implies the default of one-way
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to ensure a one-way trust is present
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: ensure the one-way trust present
|
||||
ipatrust:
|
||||
realm: ad.example.test
|
||||
admin: Administrator
|
||||
password: secret_password
|
||||
state: present
|
||||
```
|
||||
|
||||
Example playbook to ensure a two-way trust is present using a shared-secret:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to ensure a two-way trust is present
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: ensure the two-way trust is present
|
||||
ipatrust:
|
||||
realm: ad.example.test
|
||||
trust_secret: my_share_Secret
|
||||
two_way: True
|
||||
state: present
|
||||
```
|
||||
|
||||
Example playbook to ensure a trust is absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to ensure a trust is absent
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: ensure the trust is absent
|
||||
ipatrust:
|
||||
realm: ad.example.test
|
||||
state: absent
|
||||
```
|
||||
|
||||
This will only delete the ipa-side of the trust and it does NOT delete the id-range that matches the trust,
|
||||
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipatrust
|
||||
-------
|
||||
|
||||
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
|
||||
`realm` | The realm name string. | yes
|
||||
`admin` | Active Directory domain administrator string. | no
|
||||
`password` | Active Directory domain administrator's password string. | no
|
||||
`server` | Domain controller for the Active Directory domain string. | no
|
||||
`trust_secret` | Shared secret for the trust string. | no
|
||||
`base_id` | First posix id for the trusted domain integer. | no
|
||||
`range_size` | Size of the ID range reserved for the trusted domain integer. | no
|
||||
`range_type` | Type of trusted domain ID range, It can be one of `ipa-ad-trust` or `ipa-ad-trust-posix`and defaults to `ipa-ad-trust`. | no
|
||||
`two_way` | Establish bi-directional trust. By default trust is inbound one-way only. (bool) | no
|
||||
`external` | Establish external trust to a domain in another forest. The trust is not transitive beyond the domain. (bool) | no
|
||||
`state` | The state to ensure. It can be one of `present` or `absent`, default: `present`. | yes
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Rob Verduijn
|
||||
@@ -437,7 +437,7 @@ There are only return values if one or more random passwords have been generated
|
||||
|
||||
Variable | Description | Returned When
|
||||
-------- | ----------- | -------------
|
||||
`host` | Host dict with random password. (dict) <br>Options: | If random is yes and user did not exist or update_password is yes
|
||||
`user` | User dict with random password. (dict) <br>Options: | If random is yes and user did not exist or update_password is yes
|
||||
| `randompassword` - The generated random password | If only one user is handled by the module
|
||||
| `name` - The user name of the user that got a new random password. (dict) <br> Options: <br> `randompassword` - The generated random password | If several users are handled by the module
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ Example playbook to make sure vault is absent:
|
||||
state: absent
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.data }}"
|
||||
msg: "{{ result.vault.data }}"
|
||||
```
|
||||
|
||||
Variables
|
||||
@@ -246,7 +246,8 @@ 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`.
|
||||
`vault` | Vault dict with archived data. (dict) <br>Options: | If `state` is `retrieved` and `out` is not defined.
|
||||
| `data` - The vault data. | Always
|
||||
|
||||
|
||||
Notes
|
||||
|
||||
@@ -27,6 +27,7 @@ Features
|
||||
* Modules for sudocmdgroup management
|
||||
* Modules for sudorule management
|
||||
* Modules for topology management
|
||||
* Modules fot trust management
|
||||
* Modules for user management
|
||||
* Modules for vault management
|
||||
|
||||
@@ -429,6 +430,7 @@ Modules in plugin/modules
|
||||
* [ipasudorule](README-sudorule.md)
|
||||
* [ipatopologysegment](README-topology.md)
|
||||
* [ipatopologysuffix](README-topology.md)
|
||||
* [ipatrust](README-trust.md)
|
||||
* [ipauser](README-user.md)
|
||||
* [ipavault](README-vault.md)
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
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
|
||||
18
molecule/centos-7-build/molecule.yml
Normal file
18
molecule/centos-7-build/molecule.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: centos-7-build
|
||||
image: centos/systemd
|
||||
pre_build_image: true
|
||||
hostname: ipaserver.test.local
|
||||
dns_servers:
|
||||
- 8.8.8.8
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
command: /usr/sbin/init
|
||||
privileged: true
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../resources/playbooks/prepare-build.yml
|
||||
18
molecule/centos-7/molecule.yml
Normal file
18
molecule/centos-7/molecule.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: centos-7
|
||||
image: quay.io/ansible-freeipa/upstream-tests:centos-7
|
||||
pre_build_image: true
|
||||
hostname: ipaserver.test.local
|
||||
dns_servers:
|
||||
- 127.0.0.1
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
command: /usr/sbin/init
|
||||
privileged: true
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../resources/playbooks/prepare.yml
|
||||
18
molecule/centos-8-build/molecule.yml
Normal file
18
molecule/centos-8-build/molecule.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: centos-8-build
|
||||
image: centos:8
|
||||
pre_build_image: true
|
||||
hostname: ipaserver.test.local
|
||||
dns_servers:
|
||||
- 8.8.8.8
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
command: /usr/sbin/init
|
||||
privileged: true
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../resources/playbooks/prepare-build.yml
|
||||
18
molecule/centos-8/molecule.yml
Normal file
18
molecule/centos-8/molecule.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: centos-8
|
||||
image: quay.io/ansible-freeipa/upstream-tests:centos-8
|
||||
pre_build_image: true
|
||||
hostname: ipaserver.test.local
|
||||
dns_servers:
|
||||
- 127.0.0.1
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
command: /usr/sbin/init
|
||||
privileged: true
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../resources/playbooks/prepare.yml
|
||||
1
molecule/default
Symbolic link
1
molecule/default
Symbolic link
@@ -0,0 +1 @@
|
||||
centos-8
|
||||
30
molecule/fedora-latest-build/Dockerfile
Normal file
30
molecule/fedora-latest-build/Dockerfile
Normal file
@@ -0,0 +1,30 @@
|
||||
FROM fedora:latest
|
||||
ENV container=docker
|
||||
|
||||
RUN rm -fv /var/cache/dnf/metadata_lock.pid; \
|
||||
dnf makecache; \
|
||||
dnf --assumeyes install \
|
||||
/usr/bin/python3 \
|
||||
/usr/bin/python3-config \
|
||||
/usr/bin/dnf-3 \
|
||||
sudo \
|
||||
bash \
|
||||
systemd \
|
||||
procps-ng \
|
||||
iproute && \
|
||||
dnf clean all; \
|
||||
(cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
|
||||
rm -f /lib/systemd/system/multi-user.target.wants/*;\
|
||||
rm -f /etc/systemd/system/*.wants/*;\
|
||||
rm -f /lib/systemd/system/local-fs.target.wants/*; \
|
||||
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
|
||||
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
|
||||
rm -f /lib/systemd/system/basic.target.wants/*;\
|
||||
rm -f /lib/systemd/system/anaconda.target.wants/*; \
|
||||
rm -rf /var/cache/dnf/;
|
||||
|
||||
STOPSIGNAL RTMIN+3
|
||||
|
||||
VOLUME ["/sys/fs/cgroup"]
|
||||
|
||||
CMD ["/usr/sbin/init"]
|
||||
18
molecule/fedora-latest-build/molecule.yml
Normal file
18
molecule/fedora-latest-build/molecule.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: fedora-latest-build
|
||||
image: fedora-latest
|
||||
dockerfile: Dockerfile
|
||||
hostname: ipaserver.test.local
|
||||
dns_servers:
|
||||
- 8.8.8.8
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
command: /usr/sbin/init
|
||||
privileged: true
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../resources/playbooks/prepare-build.yml
|
||||
18
molecule/fedora-latest/molecule.yml
Normal file
18
molecule/fedora-latest/molecule.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: fedora-latest
|
||||
image: quay.io/ansible-freeipa/upstream-tests:fedora-latest
|
||||
pre_build_image: true
|
||||
hostname: ipaserver.test.local
|
||||
dns_servers:
|
||||
- 127.0.0.1
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
command: /usr/sbin/init
|
||||
privileged: true
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../resources/playbooks/prepare.yml
|
||||
1
molecule/resources/playbooks/library
Symbolic link
1
molecule/resources/playbooks/library
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../plugins/modules/
|
||||
1
molecule/resources/playbooks/module_utils
Symbolic link
1
molecule/resources/playbooks/module_utils
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../plugins/module_utils/
|
||||
27
molecule/resources/playbooks/prepare-build.yml
Normal file
27
molecule/resources/playbooks/prepare-build.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
tasks:
|
||||
- include_tasks: prepare-common.yml
|
||||
|
||||
- name: Ensure sudo package is installed
|
||||
package:
|
||||
name: sudo
|
||||
|
||||
- name: Ensure nss package is updated
|
||||
package:
|
||||
name: nss
|
||||
state: latest # noqa 403
|
||||
|
||||
- include_role:
|
||||
name: ipaserver
|
||||
vars:
|
||||
ipaserver_setup_dns: yes
|
||||
ipaserver_setup_kra: yes
|
||||
ipaserver_auto_forwarders: yes
|
||||
ipaserver_no_dnssec_validation: yes
|
||||
ipaserver_auto_reverse: yes
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipadm_password: SomeDMpassword
|
||||
ipaserver_domain: test.local
|
||||
ipaserver_realm: TEST.LOCAL
|
||||
33
molecule/resources/playbooks/prepare-common.yml
Normal file
33
molecule/resources/playbooks/prepare-common.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
# IPA depends on IPv6 and without it dirsrv service won't start.
|
||||
- name: Ensure IPv6 is ENABLED
|
||||
sysctl:
|
||||
name: "{{ item.name }}"
|
||||
value: "{{ item.value }}"
|
||||
sysctl_set: yes
|
||||
state: present
|
||||
reload: yes
|
||||
with_items :
|
||||
- name: net.ipv6.conf.all.disable_ipv6
|
||||
value: 0
|
||||
- name: net.ipv6.conf.lo.disable_ipv6
|
||||
value: 0
|
||||
- name: net.ipv6.conf.eth0.disable_ipv6
|
||||
value: 1
|
||||
|
||||
# Set fs.protected_regular to 0
|
||||
# This is needed in some IPA versions in order to get KRA enabled.
|
||||
# See https://pagure.io/freeipa/issue/7906 for more information.
|
||||
- name: stat protected_regular
|
||||
stat:
|
||||
path: /proc/sys/fs/protected_regular
|
||||
register: result
|
||||
|
||||
- name: Ensure fs.protected_regular is disabled
|
||||
sysctl:
|
||||
name: fs.protected_regular
|
||||
value: 0
|
||||
sysctl_set: yes
|
||||
state: present
|
||||
reload: yes
|
||||
when: result.stat.exists
|
||||
|
||||
26
molecule/resources/playbooks/prepare.yml
Normal file
26
molecule/resources/playbooks/prepare.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
tasks:
|
||||
- include_tasks: prepare-common.yml
|
||||
|
||||
# In some distros DS won't start up after reboot
|
||||
# This is due to a problem in 389-ds. See tickets:
|
||||
# * https://pagure.io/389-ds-base/issue/47429
|
||||
# * https://pagure.io/389-ds-base/issue/51039
|
||||
#
|
||||
# To avoid this problem we create the directories before starting IPA.
|
||||
- name: Ensure lock dirs for DS exists
|
||||
file:
|
||||
state: directory
|
||||
owner: dirsrv
|
||||
group: dirsrv
|
||||
path: "{{ item }}"
|
||||
loop:
|
||||
- /var/lock/dirsrv/
|
||||
- /var/lock/dirsrv/slapd-TEST-LOCAL/
|
||||
|
||||
- name: Ensure IPA server is up an running
|
||||
service:
|
||||
name: ipa
|
||||
state: started
|
||||
1
molecule/resources/playbooks/roles
Symbolic link
1
molecule/resources/playbooks/roles
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../roles/
|
||||
11
playbooks/delegation/delegation-absent.yml
Normal file
11
playbooks/delegation/delegation-absent.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Delegation absent
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure delegation "basic manager attributes" is absent
|
||||
ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
state: absent
|
||||
15
playbooks/delegation/delegation-member-absent.yml
Normal file
15
playbooks/delegation/delegation-member-absent.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Delegation member absent
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure delegation "basic manager attributes" member attributes employeenumber and employeetype are absent
|
||||
ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
attribute:
|
||||
- employeenumber
|
||||
- employeetype
|
||||
action: member
|
||||
state: absent
|
||||
13
playbooks/delegation/delegation-member-present.yml
Normal file
13
playbooks/delegation/delegation-member-present.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: Delegation member present
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure delegation "basic manager attributes" member attribute departmentnumber is present
|
||||
ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
attribute:
|
||||
- departmentnumber
|
||||
action: member
|
||||
15
playbooks/delegation/delegation-present.yml
Normal file
15
playbooks/delegation/delegation-present.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Delegation present
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure delegation "basic manager attributes" is present
|
||||
ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
permission: read
|
||||
attribute:
|
||||
- businesscategory
|
||||
group: managers
|
||||
membergroup: employees
|
||||
11
playbooks/dnsforwardzone/ensure-dnsforwardzone-is-absent.yml
Normal file
11
playbooks/dnsforwardzone/ensure-dnsforwardzone-is-absent.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Playbook to manage DNS forward zone
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure DNS zone is present
|
||||
- ipadnsforwardzone:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
state: absent
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
- name: Playbook to manage DNS forward zone
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure DNS zone is present
|
||||
- ipadnsforwardzone:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: example.com
|
||||
forwarders:
|
||||
- ip_address: 8.8.8.8
|
||||
forwardpolicy: first
|
||||
skip_overlap_check: true
|
||||
permission: yes
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
- name: Playbook to manage DNS forward zone
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Ensure DNS zone is present
|
||||
- ipadnsforwardzone:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: example.com
|
||||
forwarders:
|
||||
- ip_address: 192.168.100.123
|
||||
port: 8063
|
||||
15
playbooks/dnszone/dnszone-reverse-from-ip.yml
Normal file
15
playbooks/dnszone/dnszone-reverse-from-ip.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Playbook to ensure DNS zone exist
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure zone exist, finding zone name from IP address.
|
||||
ipadnszone:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name_from_ip: 10.1.2.3/24
|
||||
register: result
|
||||
|
||||
- name: Zone name inferred from `name_from_ip`
|
||||
debug:
|
||||
msg: "Zone created: {{ result.dnszone.name }}"
|
||||
@@ -4,7 +4,7 @@
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
ipahost:
|
||||
- ipahost:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01.exmaple.com
|
||||
managedby_host: server.exmaple.com
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
ipahost:
|
||||
- ipahost:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01.exmaple.com
|
||||
managedby_host: server.exmaple.com
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
ipahost:
|
||||
- ipahost:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: host01.exmaple.com
|
||||
managedby_host: server.exmaple.com
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure hosts manadegby_host is absent.
|
||||
ipahost:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
hosts:
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure hosts manadegby_host is absent.
|
||||
ipahost:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
hosts:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
ipahost:
|
||||
- ipahost:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
hosts:
|
||||
- name: host01.exmaple.com
|
||||
|
||||
@@ -23,4 +23,3 @@
|
||||
- name: Print generated random password for host02.example.com
|
||||
debug:
|
||||
var: ipahost.host["host02.example.com"].randompassword
|
||||
|
||||
|
||||
12
playbooks/hostgroup/rename-hostgroup.yml
Normal file
12
playbooks/hostgroup/rename-hostgroup.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Playbook to handle hostgroups
|
||||
hosts: ipaserver
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- name : Rename host-group from `databases` to `datalake`
|
||||
ipahostgroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: databases
|
||||
rename: datalake
|
||||
state: renamed
|
||||
11
playbooks/location/location-absent.yml
Normal file
11
playbooks/location/location-absent.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Location absent test
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure location my_location1 is absent
|
||||
ipalocation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my_location1
|
||||
state: absent
|
||||
10
playbooks/location/location-present.yml
Normal file
10
playbooks/location/location-present.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Location present test
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure location my_location1 is present
|
||||
ipalocation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my_location1
|
||||
10
playbooks/privilege/privilege-absent.yml
Normal file
10
playbooks/privilege/privilege-absent.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Privilege absent example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure privilege "Broad Privilege" is absent
|
||||
ipaprivilege:
|
||||
name: Broad Privilege
|
||||
state: absent
|
||||
14
playbooks/privilege/privilege-member-absent.yml
Normal file
14
playbooks/privilege/privilege-member-absent.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
- name: Privilege absent example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure privilege "Broad Privilege" permission is absent
|
||||
ipaprivilege:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: Broad Privilege
|
||||
permission:
|
||||
- "System: Write IPA Configuration"
|
||||
action: member
|
||||
state: absent
|
||||
15
playbooks/privilege/privilege-member-present.yml
Normal file
15
playbooks/privilege/privilege-member-present.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Privilege member present example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure privilege "Broad Privilege" permissions are present
|
||||
ipaprivilege:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: Broad Privilege
|
||||
permission:
|
||||
- "System: Write IPA Configuration"
|
||||
- "System: Write DNS Configuration"
|
||||
- "System: Update DNS Entries"
|
||||
action: member
|
||||
11
playbooks/privilege/privilege-present.yml
Normal file
11
playbooks/privilege/privilege-present.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Privilege present example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure privilege Broad Privilege is present
|
||||
ipaprivilege:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: Broad Privilege
|
||||
description: Broad Privilege
|
||||
11
playbooks/selfservice/selfservice-absent.yml
Normal file
11
playbooks/selfservice/selfservice-absent.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Delegation absent
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure delegation "basic manager attributes" is absent
|
||||
ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
state: absent
|
||||
15
playbooks/selfservice/selfservice-member-absent.yml
Normal file
15
playbooks/selfservice/selfservice-member-absent.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Delegation member absent
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure delegation "basic manager attributes" member attributes employeenumber and employeetype are absent
|
||||
ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
attribute:
|
||||
- employeenumber
|
||||
- employeetype
|
||||
action: member
|
||||
state: absent
|
||||
13
playbooks/selfservice/selfservice-member-present.yml
Normal file
13
playbooks/selfservice/selfservice-member-present.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: Delegation member present
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure delegation "basic manager attributes" member attribute departmentnumber is present
|
||||
ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
attribute:
|
||||
- departmentnumber
|
||||
action: member
|
||||
13
playbooks/selfservice/selfservice-present.yml
Normal file
13
playbooks/selfservice/selfservice-present.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: Delegation present
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure delegation "basic manager attributes" is present
|
||||
ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
permission: read
|
||||
attribute:
|
||||
- businesscategory
|
||||
@@ -7,7 +7,7 @@
|
||||
tasks:
|
||||
# Ensure management host is absent.
|
||||
- ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
host: "{{ groups.ipaserver[0] }}"
|
||||
action: member
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
tasks:
|
||||
# Ensure management host is present.
|
||||
- ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
host: "{{ groups.ipaserver[0] }}"
|
||||
action: member
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
tasks:
|
||||
# Ensure service is absent
|
||||
- ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
state: absent
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
tasks:
|
||||
# Ensure service is disabled
|
||||
- ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
state: disabled
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
tasks:
|
||||
# Ensure service is present
|
||||
- ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
certificate:
|
||||
- MIICBjCCAW8CFHnm32VcXaUDGfEGdDL/erPSijUAMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQwHhcNMjAwMTIzMDA1NjQ2WhcNMjEwMTIyMDA1NjQ2WjBCMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYrdVmsr7iT3f67DM5bb1osSEe5/c91UUMEIcFq5wrgBhzVfs8iIMDVC1yiUGTsDLJNJc4nb1tUxeR9K5fh25E6n/eWDBP75NStotjAXRU4Ahi3FNRhWFOKesds5xNqgDk5/dY8UekJv2yUblQuZzeF8b2XFrmHuCaYuFctzPfWwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACF+5RS8Ce0HRixGPu4Xd51i+Kzblg++lx8fDJ8GW5G16/Z1AsB72Hc7etJL2PksHlue/xCq6SA9fIfHc4TBNCiWjPSP1NhHJeYyoPiSkcYsqXuxWyoyRLbnAhBVvhoiqZbUt3u3tGB0uMMA0yJvj07mP7Nea2KdBYVH8X1pM0V+
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
tasks:
|
||||
# Ensure service is present
|
||||
- ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/ihavenodns.info
|
||||
force: yes
|
||||
# state: absent
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
tasks:
|
||||
# Ensure service is present
|
||||
- ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.ansible.com
|
||||
skip_host_check: yes
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
tasks:
|
||||
# Ensure service is present
|
||||
- ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
tasks:
|
||||
- name: Service HTTP/www.example.com members allow_create_keytab absent for users, groups, hosts and hostgroups
|
||||
ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
allow_create_keytab_user:
|
||||
- user01
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
tasks:
|
||||
- name: Service HTTP/www.example.com members allow_create_keytab present for users, groups, hosts and hostgroups
|
||||
ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
allow_create_keytab_user:
|
||||
- user01
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
tasks:
|
||||
- name: Service HTTP/www.example.com members allow_retrieve_keytab absent for users, groups, hosts and hostgroups
|
||||
ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
allow_retrieve_keytab_user:
|
||||
- user01
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
tasks:
|
||||
- name: Service HTTP/www.example.com members allow_retrieve_keytab present for users, groups, hosts and hostgroups
|
||||
ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
allow_retrieve_keytab_user:
|
||||
- user01
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
tasks:
|
||||
# Ensure service certificate is absent
|
||||
- ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
|
||||
certificate:
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
tasks:
|
||||
# Ensure service certificate is present
|
||||
- ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
certificate:
|
||||
- MIICBjCCAW8CFHnm32VcXaUDGfEGdDL/erPSijUAMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQwHhcNMjAwMTIzMDA1NjQ2WhcNMjEwMTIyMDA1NjQ2WjBCMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYrdVmsr7iT3f67DM5bb1osSEe5/c91UUMEIcFq5wrgBhzVfs8iIMDVC1yiUGTsDLJNJc4nb1tUxeR9K5fh25E6n/eWDBP75NStotjAXRU4Ahi3FNRhWFOKesds5xNqgDk5/dY8UekJv2yUblQuZzeF8b2XFrmHuCaYuFctzPfWwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACF+5RS8Ce0HRixGPu4Xd51i+Kzblg++lx8fDJ8GW5G16/Z1AsB72Hc7etJL2PksHlue/xCq6SA9fIfHc4TBNCiWjPSP1NhHJeYyoPiSkcYsqXuxWyoyRLbnAhBVvhoiqZbUt3u3tGB0uMMA0yJvj07mP7Nea2KdBYVH8X1pM0V+
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
tasks:
|
||||
- name: Service HTTP/www.exmaple.com member principals host/test.exmaple.com absent
|
||||
ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
principal:
|
||||
- host/test.exmaple.com
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
tasks:
|
||||
- name: Service HTTP/www.exmaple.com member principals host/test.exmaple.com present
|
||||
ipaservice:
|
||||
ipaadmin_password: MyPassword123
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: HTTP/www.example.com
|
||||
principal:
|
||||
- host/test.exmaple.com
|
||||
|
||||
12
playbooks/trust/add-trust.yml
Normal file
12
playbooks/trust/add-trust.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Playbook to create a trust
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: ensure the trust is present
|
||||
ipatrust:
|
||||
realm: windows.local
|
||||
admin: Administrator
|
||||
password: secret_password
|
||||
state: present
|
||||
10
playbooks/trust/del-trust.yml
Normal file
10
playbooks/trust/del-trust.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Playbook to delete trust
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: ensure the trust is absent
|
||||
ipatrust:
|
||||
realm: windows.local
|
||||
state: absent
|
||||
@@ -14,4 +14,4 @@
|
||||
state: retrieved
|
||||
register: result
|
||||
- debug:
|
||||
msg: "Data: {{ result.data }}"
|
||||
msg: "Data: {{ result.vault.data }}"
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
state: retrieved
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.data | b64decode }}"
|
||||
msg: "{{ result.vault.data }}"
|
||||
|
||||
@@ -506,7 +506,7 @@ class FreeIPABaseModule(AnsibleModule):
|
||||
# when needed.
|
||||
self.ipa_params = AnsibleFreeIPAParams(self)
|
||||
|
||||
def get_ipa_command_args(self):
|
||||
def get_ipa_command_args(self, **kwargs):
|
||||
"""
|
||||
Return a dict to be passed to an IPA command.
|
||||
|
||||
@@ -538,7 +538,7 @@ class FreeIPABaseModule(AnsibleModule):
|
||||
elif hasattr(self, param_name):
|
||||
method = getattr(self, param_name)
|
||||
if callable(method):
|
||||
value = method()
|
||||
value = method(**kwargs)
|
||||
|
||||
# We don't have a way to guess the value so fail.
|
||||
else:
|
||||
@@ -610,13 +610,16 @@ class FreeIPABaseModule(AnsibleModule):
|
||||
exit the module with proper arguments.
|
||||
|
||||
"""
|
||||
if exc_val:
|
||||
self.fail_json(msg=str(exc_val))
|
||||
|
||||
# TODO: shouldn't we also disconnect from api backend?
|
||||
temp_kdestroy(self.ccache_dir, self.ccache_name)
|
||||
|
||||
self.exit_json(changed=self.changed, user=self.exit_args)
|
||||
if exc_type == SystemExit:
|
||||
raise
|
||||
|
||||
if exc_val:
|
||||
self.fail_json(msg=str(exc_val))
|
||||
|
||||
self.exit_json(changed=self.changed, **self.exit_args)
|
||||
|
||||
def get_command_errors(self, command, result):
|
||||
"""Look for erros into command results."""
|
||||
@@ -655,14 +658,22 @@ class FreeIPABaseModule(AnsibleModule):
|
||||
except Exception as excpt:
|
||||
self.fail_json(msg="%s: %s: %s" % (command, name, str(excpt)))
|
||||
else:
|
||||
if "completed" in result:
|
||||
if result["completed"] > 0:
|
||||
self.changed = True
|
||||
else:
|
||||
self.changed = True
|
||||
|
||||
self.process_command_result(name, command, args, result)
|
||||
self.get_command_errors(command, result)
|
||||
|
||||
def process_command_result(self, name, command, args, result):
|
||||
"""
|
||||
Process an API command result.
|
||||
|
||||
This method can be overriden in subclasses, and change self.exit_values
|
||||
to return data in the result for the controller.
|
||||
"""
|
||||
if "completed" in result:
|
||||
if result["completed"] > 0:
|
||||
self.changed = True
|
||||
else:
|
||||
self.changed = True
|
||||
|
||||
def require_ipa_attrs_change(self, command_args, ipa_attrs):
|
||||
"""
|
||||
Compare given args with current object attributes.
|
||||
|
||||
340
plugins/modules/ipadelegation.py
Normal file
340
plugins/modules/ipadelegation.py
Normal file
@@ -0,0 +1,340 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
# Thomas Woerner <twoerner@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: ipadelegation
|
||||
short description: Manage FreeIPA delegations
|
||||
description: Manage FreeIPA delegations and delegation attributes
|
||||
options:
|
||||
ipaadmin_principal:
|
||||
description: The admin principal.
|
||||
default: admin
|
||||
ipaadmin_password:
|
||||
description: The admin password.
|
||||
required: false
|
||||
name:
|
||||
description: The list of delegation name strings.
|
||||
required: true
|
||||
aliases: ["aciname"]
|
||||
permission:
|
||||
description: Permissions to grant (read, write). Default is write.
|
||||
required: false
|
||||
aliases: ["permissions"]
|
||||
attribute:
|
||||
description: Attribute list to which the delegation applies
|
||||
required: false
|
||||
aliases: ["attrs"]
|
||||
membergroup
|
||||
description: User group to apply delegation to
|
||||
required: false
|
||||
aliases: ["memberof"]
|
||||
group:
|
||||
description: User group ACI grants access to
|
||||
required: false
|
||||
action:
|
||||
description: Work on delegation or member level.
|
||||
choices: ["delegation", "member"]
|
||||
default: delegation
|
||||
required: false
|
||||
state:
|
||||
description: The state to ensure.
|
||||
choices: ["present", "absent"]
|
||||
default: present
|
||||
required: true
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Ensure delegation "basic manager attributes" is present
|
||||
- ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
permission: read
|
||||
attribute:
|
||||
- businesscategory
|
||||
- employeetype
|
||||
group: managers
|
||||
membergroup: employees
|
||||
|
||||
# Ensure delegation "basic manager attributes" member attribute
|
||||
# departmentnumber is present
|
||||
- ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
attribute:
|
||||
- departmentnumber
|
||||
action: member
|
||||
|
||||
# Ensure delegation "basic manager attributes" member attributes
|
||||
# employeetype and employeenumber are present
|
||||
- ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
attribute:
|
||||
- employeenumber
|
||||
- employeetype
|
||||
action: member
|
||||
state: absent
|
||||
|
||||
# Ensure delegation "basic manager attributes" is absent
|
||||
- ipadelegation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "basic manager attributes"
|
||||
state: absent
|
||||
"""
|
||||
|
||||
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
|
||||
import six
|
||||
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
def find_delegation(module, name):
|
||||
"""Find if a delegation with the given name already exist."""
|
||||
try:
|
||||
_result = api_command(module, "delegation_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if delegation name is not found.
|
||||
return None
|
||||
else:
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_args(permission, attribute, membergroup, group):
|
||||
_args = {}
|
||||
if permission is not None:
|
||||
_args["permissions"] = permission
|
||||
if attribute is not None:
|
||||
_args["attrs"] = attribute
|
||||
if membergroup is not None:
|
||||
_args["memberof"] = membergroup
|
||||
if group is not None:
|
||||
_args["group"] = group
|
||||
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),
|
||||
|
||||
name=dict(type="list", aliases=["aciname"], default=None,
|
||||
required=True),
|
||||
# present
|
||||
permission=dict(required=False, type='list',
|
||||
aliases=["permissions"], default=None),
|
||||
attribute=dict(required=False, type='list', aliases=["attrs"],
|
||||
default=None),
|
||||
membergroup=dict(type="str", aliases=["memberof"], default=None),
|
||||
group=dict(type="str", default=None),
|
||||
action=dict(type="str", default="delegation",
|
||||
choices=["member", "delegation"]),
|
||||
# state
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent"]),
|
||||
),
|
||||
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")
|
||||
names = module_params_get(ansible_module, "name")
|
||||
|
||||
# present
|
||||
permission = module_params_get(ansible_module, "permission")
|
||||
attribute = module_params_get(ansible_module, "attribute")
|
||||
membergroup = module_params_get(ansible_module, "membergroup")
|
||||
group = module_params_get(ansible_module, "group")
|
||||
action = module_params_get(ansible_module, "action")
|
||||
# state
|
||||
state = module_params_get(ansible_module, "state")
|
||||
|
||||
# Check parameters
|
||||
|
||||
if state == "present":
|
||||
if len(names) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Only one delegation be added at a time.")
|
||||
if action == "member":
|
||||
invalid = ["permission", "membergroup", "group"]
|
||||
for x in invalid:
|
||||
if vars()[x] is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="Argument '%s' can not be used with action "
|
||||
"'%s' and state '%s'" % (x, action, state))
|
||||
|
||||
if state == "absent":
|
||||
if len(names) < 1:
|
||||
ansible_module.fail_json(msg="No name given.")
|
||||
invalid = ["permission", "membergroup", "group"]
|
||||
if action == "delegation":
|
||||
invalid.append("attribute")
|
||||
for x in invalid:
|
||||
if vars()[x] is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="Argument '%s' can not be used with action "
|
||||
"'%s' and state '%s'" % (x, action, state))
|
||||
|
||||
if permission is not None:
|
||||
perm = [p for p in permission if p not in ("read", "write")]
|
||||
if perm:
|
||||
ansible_module.fail_json(msg="Invalid permission '%s'" % perm)
|
||||
if len(set(permission)) != len(permission):
|
||||
ansible_module.fail_json(
|
||||
msg="Invalid permission '%s', items are not unique" %
|
||||
repr(permission))
|
||||
|
||||
if attribute is not None:
|
||||
if len(set(attribute)) != len(attribute):
|
||||
ansible_module.fail_json(
|
||||
msg="Invalid attribute '%s', items are not unique" %
|
||||
repr(attribute))
|
||||
|
||||
# Init
|
||||
|
||||
changed = False
|
||||
exit_args = {}
|
||||
ccache_dir = None
|
||||
ccache_name = None
|
||||
try:
|
||||
if not valid_creds(ansible_module, ipaadmin_principal):
|
||||
ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
|
||||
ipaadmin_password)
|
||||
api_connect()
|
||||
|
||||
commands = []
|
||||
for name in names:
|
||||
# Make sure delegation exists
|
||||
res_find = find_delegation(ansible_module, name)
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
|
||||
# Generate args
|
||||
args = gen_args(permission, attribute, membergroup, group)
|
||||
|
||||
if action == "delegation":
|
||||
# Found the delegation
|
||||
if res_find is not None:
|
||||
# For all settings is args, check if there are
|
||||
# different settings in the find result.
|
||||
# If yes: modify
|
||||
if not compare_args_ipa(ansible_module, args,
|
||||
res_find):
|
||||
commands.append([name, "delegation_mod", args])
|
||||
else:
|
||||
commands.append([name, "delegation_add", args])
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(
|
||||
msg="No delegation '%s'" % name)
|
||||
|
||||
if attribute is None:
|
||||
ansible_module.fail_json(msg="No attributes given")
|
||||
|
||||
# New attribute list (add given ones to find result)
|
||||
# Make list with unique entries
|
||||
attrs = list(set(list(res_find["attrs"]) + attribute))
|
||||
if len(attrs) > len(res_find["attrs"]):
|
||||
commands.append([name, "delegation_mod",
|
||||
{"attrs": attrs}])
|
||||
|
||||
elif state == "absent":
|
||||
if action == "delegation":
|
||||
if res_find is not None:
|
||||
commands.append([name, "delegation_del", {}])
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(
|
||||
msg="No delegation '%s'" % name)
|
||||
|
||||
if attribute is None:
|
||||
ansible_module.fail_json(msg="No attributes given")
|
||||
|
||||
# New attribute list (remove given ones from find result)
|
||||
# Make list with unique entries
|
||||
attrs = list(set(res_find["attrs"]) - set(attribute))
|
||||
if len(attrs) < 1:
|
||||
ansible_module.fail_json(
|
||||
msg="At minimum one attribute is needed.")
|
||||
|
||||
# Entries New number of attributes is smaller
|
||||
if len(attrs) < len(res_find["attrs"]):
|
||||
commands.append([name, "delegation_mod",
|
||||
{"attrs": attrs}])
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Execute commands
|
||||
|
||||
for name, command, args in commands:
|
||||
try:
|
||||
result = api_command(ansible_module, command, name,
|
||||
args)
|
||||
if "completed" in result:
|
||||
if result["completed"] > 0:
|
||||
changed = True
|
||||
else:
|
||||
changed = True
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(msg="%s: %s: %s" % (command, name,
|
||||
str(e)))
|
||||
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(msg=str(e))
|
||||
|
||||
finally:
|
||||
temp_kdestroy(ccache_dir, ccache_name)
|
||||
|
||||
# Done
|
||||
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -106,6 +106,7 @@ 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, \
|
||||
module_params_get
|
||||
@@ -150,7 +151,7 @@ def forwarder_list(forwarders):
|
||||
formatter = "{ip_address} port {port}"
|
||||
else:
|
||||
formatter = "{ip_address}"
|
||||
fwd_list.append(formatter.format(**forwarder))
|
||||
fwd_list.append(to_text(formatter.format(**forwarder)))
|
||||
return fwd_list
|
||||
|
||||
|
||||
|
||||
@@ -41,8 +41,14 @@ options:
|
||||
name:
|
||||
description: The zone name string.
|
||||
required: true
|
||||
type: str
|
||||
type: list
|
||||
alises: ["zone_name"]
|
||||
name_from_ip:
|
||||
description: |
|
||||
Derive zone name from reverse of IP (PTR).
|
||||
Can only be used with `state: present`.
|
||||
required: false
|
||||
type: str
|
||||
forwarders:
|
||||
description: The list of global DNS forwarders.
|
||||
required: false
|
||||
@@ -188,6 +194,14 @@ EXAMPLES = """
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
dnszone:
|
||||
description: DNS Zone dict with zone name infered from `name_from_ip`.
|
||||
returned:
|
||||
If `state` is `present`, `name_from_ip` is used, and a zone was created.
|
||||
options:
|
||||
name:
|
||||
description: The name of the zone created, inferred from `name_from_ip`.
|
||||
returned: always
|
||||
"""
|
||||
|
||||
from ipapython.dnsutil import DNSName # noqa: E402
|
||||
@@ -197,6 +211,12 @@ from ansible.module_utils.ansible_freeipa_module import (
|
||||
is_ipv6_addr,
|
||||
is_valid_port,
|
||||
) # noqa: E402
|
||||
import netaddr
|
||||
import six
|
||||
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
class DNSZoneModule(FreeIPABaseModule):
|
||||
@@ -268,7 +288,7 @@ class DNSZoneModule(FreeIPABaseModule):
|
||||
|
||||
return True
|
||||
|
||||
def get_ipa_nsec3paramrecord(self):
|
||||
def get_ipa_nsec3paramrecord(self, **kwargs):
|
||||
nsec3param_rec = self.ipa_params.nsec3param_rec
|
||||
if nsec3param_rec is not None:
|
||||
error_msg = (
|
||||
@@ -280,7 +300,7 @@ class DNSZoneModule(FreeIPABaseModule):
|
||||
self.fail_json(msg=error_msg)
|
||||
return nsec3param_rec
|
||||
|
||||
def get_ipa_idnsforwarders(self):
|
||||
def get_ipa_idnsforwarders(self, **kwargs):
|
||||
if self.ipa_params.forwarders is not None:
|
||||
forwarders = []
|
||||
for forwarder in self.ipa_params.forwarders:
|
||||
@@ -304,14 +324,14 @@ class DNSZoneModule(FreeIPABaseModule):
|
||||
|
||||
return forwarders
|
||||
|
||||
def get_ipa_idnsallowtransfer(self):
|
||||
def get_ipa_idnsallowtransfer(self, **kwargs):
|
||||
if self.ipa_params.allow_transfer is not None:
|
||||
error_msg = "Invalid ip_address for DNS allow_transfer: %s"
|
||||
self.validate_ips(self.ipa_params.allow_transfer, error_msg)
|
||||
|
||||
return (";".join(self.ipa_params.allow_transfer) or "none") + ";"
|
||||
|
||||
def get_ipa_idnsallowquery(self):
|
||||
def get_ipa_idnsallowquery(self, **kwargs):
|
||||
if self.ipa_params.allow_query is not None:
|
||||
error_msg = "Invalid ip_address for DNS allow_query: %s"
|
||||
self.validate_ips(self.ipa_params.allow_query, error_msg)
|
||||
@@ -334,81 +354,141 @@ class DNSZoneModule(FreeIPABaseModule):
|
||||
|
||||
return ".".join((name, domain))
|
||||
|
||||
def get_ipa_idnssoarname(self):
|
||||
def get_ipa_idnssoarname(self, **kwargs):
|
||||
if self.ipa_params.admin_email is not None:
|
||||
return DNSName(
|
||||
self._replace_at_symbol_in_rname(self.ipa_params.admin_email)
|
||||
)
|
||||
|
||||
def get_ipa_idnssoamname(self):
|
||||
def get_ipa_idnssoamname(self, **kwargs):
|
||||
if self.ipa_params.name_server is not None:
|
||||
return DNSName(self.ipa_params.name_server)
|
||||
|
||||
def get_ipa_skip_overlap_check(self):
|
||||
if not self.zone and self.ipa_params.skip_overlap_check is not None:
|
||||
def get_ipa_skip_overlap_check(self, **kwargs):
|
||||
zone = kwargs.get('zone')
|
||||
if not zone and self.ipa_params.skip_overlap_check is not None:
|
||||
return self.ipa_params.skip_overlap_check
|
||||
|
||||
def get_ipa_skip_nameserver_check(self):
|
||||
if not self.zone and self.ipa_params.skip_nameserver_check is not None:
|
||||
def get_ipa_skip_nameserver_check(self, **kwargs):
|
||||
zone = kwargs.get('zone')
|
||||
if not zone and self.ipa_params.skip_nameserver_check is not None:
|
||||
return self.ipa_params.skip_nameserver_check
|
||||
|
||||
def __reverse_zone_name(self, ipaddress):
|
||||
"""
|
||||
Infer reverse zone name from an ip address.
|
||||
|
||||
This function uses the same heuristics as FreeIPA to infer the zone
|
||||
name from ip.
|
||||
"""
|
||||
try:
|
||||
ip = netaddr.IPAddress(str(ipaddress))
|
||||
except (netaddr.AddrFormatError, ValueError):
|
||||
net = netaddr.IPNetwork(ipaddress)
|
||||
items = net.ip.reverse_dns.split('.')
|
||||
prefixlen = net.prefixlen
|
||||
ip_version = net.version
|
||||
else:
|
||||
items = ip.reverse_dns.split('.')
|
||||
prefixlen = 24 if ip.version == 4 else 64
|
||||
ip_version = ip.version
|
||||
if ip_version == 4:
|
||||
return u'.'.join(items[4 - prefixlen // 8:])
|
||||
elif ip_version == 6:
|
||||
return u'.'.join(items[32 - prefixlen // 4:])
|
||||
else:
|
||||
self.fail_json(msg="Invalid IP version for reverse zone.")
|
||||
|
||||
def get_zone(self, zone_name):
|
||||
get_zone_args = {"idnsname": zone_name, "all": True}
|
||||
response = self.api_command("dnszone_find", args=get_zone_args)
|
||||
|
||||
zone = None
|
||||
is_zone_active = False
|
||||
|
||||
if response["count"] == 1:
|
||||
self.zone = response["result"][0]
|
||||
self.is_zone_active = self.zone.get("idnszoneactive") == ["TRUE"]
|
||||
return self.zone
|
||||
zone = response["result"][0]
|
||||
is_zone_active = zone.get("idnszoneactive") == ["TRUE"]
|
||||
|
||||
# Zone doesn't exist yet
|
||||
self.zone = None
|
||||
self.is_zone_active = False
|
||||
return zone, is_zone_active
|
||||
|
||||
@property
|
||||
def zone_name(self):
|
||||
def get_zone_names(self):
|
||||
zone_names = self.__get_zone_names_from_params()
|
||||
if len(zone_names) > 1 and self.ipa_params.state != "absent":
|
||||
self.fail_json(
|
||||
msg=("Please provide a single name. Multiple values for 'name'"
|
||||
"can only be supplied for state 'absent'.")
|
||||
)
|
||||
|
||||
return zone_names
|
||||
|
||||
def __get_zone_names_from_params(self):
|
||||
if not self.ipa_params.name:
|
||||
return [self.__reverse_zone_name(self.ipa_params.name_from_ip)]
|
||||
return self.ipa_params.name
|
||||
|
||||
def check_ipa_params(self):
|
||||
if not self.ipa_params.name and not self.ipa_params.name_from_ip:
|
||||
self.fail_json(
|
||||
msg="Either `name` or `name_from_ip` must be provided."
|
||||
)
|
||||
if self.ipa_params.state != "present" and self.ipa_params.name_from_ip:
|
||||
self.fail_json(
|
||||
msg=(
|
||||
"Cannot use argument `name_from_ip` with state `%s`."
|
||||
% self.ipa_params.state
|
||||
)
|
||||
)
|
||||
|
||||
def define_ipa_commands(self):
|
||||
# Look for existing zone in IPA
|
||||
self.get_zone(self.zone_name)
|
||||
args = self.get_ipa_command_args()
|
||||
just_added = False
|
||||
for zone_name in self.get_zone_names():
|
||||
# Look for existing zone in IPA
|
||||
zone, is_zone_active = self.get_zone(zone_name)
|
||||
args = self.get_ipa_command_args(zone=zone)
|
||||
just_added = False
|
||||
|
||||
if self.ipa_params.state in ["present", "enabled", "disabled"]:
|
||||
if not self.zone:
|
||||
# Since the zone doesn't exist we just create it
|
||||
# with given args
|
||||
self.add_ipa_command("dnszone_add", self.zone_name, args)
|
||||
self.is_zone_active = True
|
||||
just_added = True
|
||||
if self.ipa_params.state in ["present", "enabled", "disabled"]:
|
||||
if not zone:
|
||||
# Since the zone doesn't exist we just create it
|
||||
# with given args
|
||||
self.add_ipa_command("dnszone_add", zone_name, args)
|
||||
is_zone_active = True
|
||||
just_added = True
|
||||
|
||||
else:
|
||||
# Zone already exist so we need to verify if given args
|
||||
# matches the current config. If not we updated it.
|
||||
if self.require_ipa_attrs_change(args, self.zone):
|
||||
self.add_ipa_command("dnszone_mod", self.zone_name, args)
|
||||
else:
|
||||
# Zone already exist so we need to verify if given args
|
||||
# matches the current config. If not we updated it.
|
||||
if self.require_ipa_attrs_change(args, zone):
|
||||
self.add_ipa_command("dnszone_mod", zone_name, args)
|
||||
|
||||
if self.ipa_params.state == "enabled" and not self.is_zone_active:
|
||||
self.add_ipa_command("dnszone_enable", self.zone_name)
|
||||
if self.ipa_params.state == "enabled" and not is_zone_active:
|
||||
self.add_ipa_command("dnszone_enable", zone_name)
|
||||
|
||||
if self.ipa_params.state == "disabled" and self.is_zone_active:
|
||||
self.add_ipa_command("dnszone_disable", self.zone_name)
|
||||
if self.ipa_params.state == "disabled" and is_zone_active:
|
||||
self.add_ipa_command("dnszone_disable", zone_name)
|
||||
|
||||
if self.ipa_params.state == "absent":
|
||||
if self.zone:
|
||||
self.add_ipa_command("dnszone_del", self.zone_name)
|
||||
if self.ipa_params.state == "absent":
|
||||
if zone:
|
||||
self.add_ipa_command("dnszone_del", zone_name)
|
||||
|
||||
# Due to a bug in FreeIPA dnszone-add won't set
|
||||
# SOA Serial. The good news is that dnszone-mod does the job.
|
||||
# See: https://pagure.io/freeipa/issue/8227
|
||||
# Because of that, if the zone was just added with a given serial
|
||||
# we run mod just after to workaround the bug
|
||||
if just_added and self.ipa_params.serial is not None:
|
||||
args = {
|
||||
"idnssoaserial": self.ipa_params.serial,
|
||||
}
|
||||
self.add_ipa_command("dnszone_mod", self.zone_name, args)
|
||||
# Due to a bug in FreeIPA dnszone-add won't set
|
||||
# SOA Serial. The good news is that dnszone-mod does the job.
|
||||
# See: https://pagure.io/freeipa/issue/8227
|
||||
# Because of that, if the zone was just added with a given serial
|
||||
# we run mod just after to workaround the bug
|
||||
if just_added and self.ipa_params.serial is not None:
|
||||
args = {
|
||||
"idnssoaserial": self.ipa_params.serial,
|
||||
}
|
||||
self.add_ipa_command("dnszone_mod", zone_name, args)
|
||||
|
||||
def process_command_result(self, name, command, args, result):
|
||||
super(DNSZoneModule, self).process_command_result(
|
||||
name, command, args, result
|
||||
)
|
||||
if command == "dnszone_add" and self.ipa_params.name_from_ip:
|
||||
dnszone_exit_args = self.exit_args.setdefault('dnszone', {})
|
||||
dnszone_exit_args['name'] = name
|
||||
|
||||
|
||||
def get_argument_spec():
|
||||
@@ -426,8 +506,9 @@ def get_argument_spec():
|
||||
ipaadmin_principal=dict(type="str", default="admin"),
|
||||
ipaadmin_password=dict(type="str", required=False, no_log=True),
|
||||
name=dict(
|
||||
type="str", default=None, required=True, aliases=["zone_name"]
|
||||
type="list", default=None, required=False, aliases=["zone_name"]
|
||||
),
|
||||
name_from_ip=dict(type="str", default=None, required=False),
|
||||
forwarders=dict(
|
||||
type="list",
|
||||
default=None,
|
||||
@@ -467,7 +548,11 @@ def get_argument_spec():
|
||||
|
||||
|
||||
def main():
|
||||
DNSZoneModule(argument_spec=get_argument_spec()).ipa_run()
|
||||
DNSZoneModule(
|
||||
argument_spec=get_argument_spec(),
|
||||
mutually_exclusive=[["name", "name_from_ip"]],
|
||||
required_one_of=[["name", "name_from_ip"]],
|
||||
).ipa_run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -70,6 +70,12 @@ options:
|
||||
- Only usable with IPA versions 4.8.4 and up.
|
||||
required: false
|
||||
type: list
|
||||
rename:
|
||||
description:
|
||||
- Rename hostgroup to the given name.
|
||||
- Only usable with IPA versions 4.8.7 and up.
|
||||
required: false
|
||||
aliases: ["new_name"]
|
||||
action:
|
||||
description: Work on hostgroup or member level
|
||||
default: hostgroup
|
||||
@@ -77,7 +83,7 @@ options:
|
||||
state:
|
||||
description: State to ensure
|
||||
default: present
|
||||
choices: ["present", "absent"]
|
||||
choices: ["present", "absent", "renamed"]
|
||||
author:
|
||||
- Thomas Woerner
|
||||
"""
|
||||
@@ -116,6 +122,12 @@ EXAMPLES = """
|
||||
action: member
|
||||
state: absent
|
||||
|
||||
# Rename hostgroup
|
||||
- ipahostgroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: databases
|
||||
rename: datalake
|
||||
|
||||
# Ensure host-group databases is absent
|
||||
- ipahostgroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
@@ -129,7 +141,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, gen_add_del_lists, api_check_command
|
||||
module_params_get, gen_add_del_lists, api_check_command, api_check_param
|
||||
|
||||
|
||||
def find_hostgroup(module, name):
|
||||
@@ -149,12 +161,14 @@ def find_hostgroup(module, name):
|
||||
return None
|
||||
|
||||
|
||||
def gen_args(description, nomembers):
|
||||
def gen_args(description, nomembers, rename):
|
||||
_args = {}
|
||||
if description is not None:
|
||||
_args["description"] = description
|
||||
if nomembers is not None:
|
||||
_args["nomembers"] = nomembers
|
||||
if rename is not None:
|
||||
_args["rename"] = rename
|
||||
|
||||
return _args
|
||||
|
||||
@@ -186,11 +200,13 @@ def main():
|
||||
membermanager_user=dict(required=False, type='list', default=None),
|
||||
membermanager_group=dict(required=False, type='list',
|
||||
default=None),
|
||||
rename=dict(required=False, type='str', default=None,
|
||||
aliases=["new_name"]),
|
||||
action=dict(type="str", default="hostgroup",
|
||||
choices=["member", "hostgroup"]),
|
||||
# state
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent"]),
|
||||
choices=["present", "absent", "renamed"]),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
@@ -215,6 +231,7 @@ def main():
|
||||
"membermanager_user")
|
||||
membermanager_group = module_params_get(ansible_module,
|
||||
"membermanager_group")
|
||||
rename = module_params_get(ansible_module, "rename")
|
||||
action = module_params_get(ansible_module, "action")
|
||||
# state
|
||||
state = module_params_get(ansible_module, "state")
|
||||
@@ -225,19 +242,38 @@ def main():
|
||||
if len(names) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Only one hostgroup can be added at a time.")
|
||||
invalid = ["rename"]
|
||||
if action == "member":
|
||||
invalid = ["description", "nomembers"]
|
||||
for x in invalid:
|
||||
if vars()[x] is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="Argument '%s' can not be used with action "
|
||||
"'%s'" % (x, action))
|
||||
invalid.extend(["description", "nomembers"])
|
||||
for x in invalid:
|
||||
if vars()[x] is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="Argument '%s' can not be used with action "
|
||||
"'%s'" % (x, action))
|
||||
|
||||
if state == "renamed":
|
||||
if len(names) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Only one hostgroup can be added at a time.")
|
||||
if action == "member":
|
||||
ansible_module.fail_json(
|
||||
msg="Action '%s' can not be used with state '%s'" %
|
||||
(action, state))
|
||||
invalid = [
|
||||
"description", "nomembers", "host", "hostgroup",
|
||||
"membermanager_user", "membermanager_group"
|
||||
]
|
||||
for x in invalid:
|
||||
if vars()[x] is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="Argument '%s' can not be used with state '%s'" %
|
||||
(x, state))
|
||||
|
||||
if state == "absent":
|
||||
if len(names) < 1:
|
||||
ansible_module.fail_json(
|
||||
msg="No name given.")
|
||||
invalid = ["description", "nomembers"]
|
||||
invalid = ["description", "nomembers", "rename"]
|
||||
if action == "hostgroup":
|
||||
invalid.extend(["host", "hostgroup"])
|
||||
for x in invalid:
|
||||
@@ -266,6 +302,10 @@ def main():
|
||||
msg="Managing a membermanager user or group is not supported "
|
||||
"by your IPA version"
|
||||
)
|
||||
has_mod_rename = api_check_param("hostgroup_mod", "rename")
|
||||
if not has_mod_rename and rename is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="Renaming hostgroups is not supported by your IPA version")
|
||||
|
||||
commands = []
|
||||
|
||||
@@ -276,7 +316,7 @@ def main():
|
||||
# Create command
|
||||
if state == "present":
|
||||
# Generate args
|
||||
args = gen_args(description, nomembers)
|
||||
args = gen_args(description, nomembers, rename)
|
||||
|
||||
if action == "hostgroup":
|
||||
# Found the hostgroup
|
||||
@@ -375,6 +415,22 @@ def main():
|
||||
}]
|
||||
)
|
||||
|
||||
elif state == "renamed":
|
||||
if res_find is not None:
|
||||
if rename != name:
|
||||
commands.append(
|
||||
[name, "hostgroup_mod", {"rename": rename}]
|
||||
)
|
||||
else:
|
||||
# If a hostgroup with the desired name exists, do nothing.
|
||||
new_find = find_hostgroup(ansible_module, rename)
|
||||
if new_find is None:
|
||||
# Fail only if the either hostsgroups do not exist.
|
||||
ansible_module.fail_json(
|
||||
msg="Attribute `rename` can not be used, unless "
|
||||
"hostgroup exists."
|
||||
)
|
||||
|
||||
elif state == "absent":
|
||||
if action == "hostgroup":
|
||||
if res_find is not None:
|
||||
|
||||
220
plugins/modules/ipalocation.py
Normal file
220
plugins/modules/ipalocation.py
Normal file
@@ -0,0 +1,220 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
# Thomas Woerner <twoerner@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: ipalocation
|
||||
short description: Manage FreeIPA location
|
||||
description: Manage FreeIPA location
|
||||
options:
|
||||
ipaadmin_principal:
|
||||
description: The admin principal.
|
||||
default: admin
|
||||
ipaadmin_password:
|
||||
description: The admin password.
|
||||
required: false
|
||||
name:
|
||||
description: The list of location name strings.
|
||||
required: true
|
||||
aliases: ["idnsname"]
|
||||
description:
|
||||
description: The IPA location string
|
||||
required: false
|
||||
state:
|
||||
description: The state to ensure.
|
||||
choices: ["present", "absent"]
|
||||
default: present
|
||||
required: true
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Ensure location my_location1 is present
|
||||
- ipalocation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my_location1
|
||||
description: My location 1
|
||||
|
||||
# Ensure location my_location1 is absent
|
||||
- ipalocation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my_location1
|
||||
state: absent
|
||||
"""
|
||||
|
||||
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
|
||||
import six
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
def find_location(module, name):
|
||||
"""Find if a location with the given name already exist."""
|
||||
try:
|
||||
_result = api_command(module, "location_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if location name is not found.
|
||||
return None
|
||||
else:
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_args(description):
|
||||
_args = {}
|
||||
if description is not None:
|
||||
_args["description"] = description
|
||||
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),
|
||||
|
||||
name=dict(type="list", aliases=["idnsname"],
|
||||
default=None, required=True),
|
||||
# present
|
||||
description=dict(required=False, type='str', default=None),
|
||||
# state
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent"]),
|
||||
),
|
||||
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")
|
||||
names = module_params_get(ansible_module, "name")
|
||||
|
||||
# present
|
||||
description = module_params_get(ansible_module, "description")
|
||||
|
||||
# state
|
||||
state = module_params_get(ansible_module, "state")
|
||||
|
||||
# Check parameters
|
||||
|
||||
if state == "present":
|
||||
if len(names) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Only one location be added at a time.")
|
||||
|
||||
if state == "absent":
|
||||
if len(names) < 1:
|
||||
ansible_module.fail_json(msg="No name given.")
|
||||
invalid = ["description"]
|
||||
for x in invalid:
|
||||
if vars()[x] is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="Argument '%s' can not be used with state '%s'" %
|
||||
(x, state))
|
||||
|
||||
# Init
|
||||
|
||||
changed = False
|
||||
exit_args = {}
|
||||
ccache_dir = None
|
||||
ccache_name = None
|
||||
try:
|
||||
if not valid_creds(ansible_module, ipaadmin_principal):
|
||||
ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
|
||||
ipaadmin_password)
|
||||
api_connect()
|
||||
|
||||
commands = []
|
||||
for name in names:
|
||||
# Make sure location exists
|
||||
res_find = find_location(ansible_module, name)
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
|
||||
# Generate args
|
||||
args = gen_args(description)
|
||||
|
||||
# Found the location
|
||||
if res_find is not None:
|
||||
# For all settings is args, check if there are
|
||||
# different settings in the find result.
|
||||
# If yes: modify
|
||||
if not compare_args_ipa(ansible_module, args,
|
||||
res_find):
|
||||
commands.append([name, "location_mod", args])
|
||||
else:
|
||||
commands.append([name, "location_add", args])
|
||||
|
||||
elif state == "absent":
|
||||
if res_find is not None:
|
||||
commands.append([name, "location_del", {}])
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Execute commands
|
||||
|
||||
for name, command, args in commands:
|
||||
try:
|
||||
result = api_command(ansible_module, command, name,
|
||||
args)
|
||||
if "completed" in result:
|
||||
if result["completed"] > 0:
|
||||
changed = True
|
||||
else:
|
||||
changed = True
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(msg="%s: %s: %s" % (command, name,
|
||||
str(e)))
|
||||
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(msg=str(e))
|
||||
|
||||
finally:
|
||||
temp_kdestroy(ccache_dir, ccache_name)
|
||||
|
||||
# Done
|
||||
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
357
plugins/modules/ipaprivilege.py
Normal file
357
plugins/modules/ipaprivilege.py
Normal file
@@ -0,0 +1,357 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
# Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2020 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""ansible-freeipa module to manage FreeIPA privileges."""
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
"status": ["preview"],
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: ipaprivilege
|
||||
short description: Manage FreeIPA privilege
|
||||
description: Manage FreeIPA privilege and privilege members
|
||||
options:
|
||||
ipaadmin_principal:
|
||||
description: The admin principal.
|
||||
default: admin
|
||||
ipaadmin_password:
|
||||
description: The admin password.
|
||||
required: false
|
||||
name:
|
||||
description: The list of privilege name strings.
|
||||
required: true
|
||||
aliases: ["cn"]
|
||||
description:
|
||||
description: Privilege description
|
||||
required: false
|
||||
rename:
|
||||
description: Rename the privilege object.
|
||||
required: false
|
||||
aliases: ["new_name"]
|
||||
permission:
|
||||
description: Permissions to be added to the privilege.
|
||||
required: false
|
||||
action:
|
||||
description: Work on privilege or member level.
|
||||
choices: ["privilege", "member"]
|
||||
default: privilege
|
||||
required: false
|
||||
state:
|
||||
description: The state to ensure.
|
||||
choices: ["present", "absent", "renamed"]
|
||||
default: present
|
||||
required: true
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Ensure privilege "Broad Privilege" is present
|
||||
- ipaprivilege:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: Broad Privilege
|
||||
description: Broad Privilege
|
||||
|
||||
# Ensure privilege "Broad Privilege" has permissions set
|
||||
- ipaprivilege:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: Broad Privilege
|
||||
permission:
|
||||
- "Write IPA Configuration"
|
||||
- "System: Write DNS Configuration"
|
||||
- "System: Update DNS Entries"
|
||||
action: member
|
||||
|
||||
# Ensure privilege member permission 'Write IPA Configuration' is absent
|
||||
- ipaprivilege:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: Broad Privilege
|
||||
permission:
|
||||
- "Write IPA Configuration"
|
||||
action: member
|
||||
state: absent
|
||||
|
||||
# Rename privilege "Broad Privilege" to "DNS Special Privilege"
|
||||
- ipaprivilege:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: Broad Privilege
|
||||
rename: DNS Special Privilege
|
||||
state: renamed
|
||||
|
||||
# Ensure privilege "DNS Special Privilege" is absent
|
||||
- ipaprivilege:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: DNS Special Privilege
|
||||
state: absent
|
||||
"""
|
||||
|
||||
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, gen_add_del_lists
|
||||
import six
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
def find_privilege(module, name):
|
||||
"""Find if a privilege with the given name already exist."""
|
||||
try:
|
||||
_result = api_command(module, "privilege_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if privilege name is not found.
|
||||
return None
|
||||
else:
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
# general
|
||||
ipaadmin_principal=dict(type="str", default="admin"),
|
||||
ipaadmin_password=dict(type="str", required=False, no_log=True),
|
||||
|
||||
name=dict(type="list", aliases=["cn"],
|
||||
default=None, required=True),
|
||||
# present
|
||||
description=dict(required=False, type='str', default=None),
|
||||
rename=dict(required=False, type='str', default=None,
|
||||
aliases=["new_name"], ),
|
||||
permission=dict(required=False, type='list', default=None),
|
||||
action=dict(type="str", default="privilege",
|
||||
choices=["member", "privilege"]),
|
||||
# state
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent", "renamed"]),
|
||||
),
|
||||
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")
|
||||
names = module_params_get(ansible_module, "name")
|
||||
|
||||
# present
|
||||
description = module_params_get(ansible_module, "description")
|
||||
permission = module_params_get(ansible_module, "permission")
|
||||
rename = module_params_get(ansible_module, "rename")
|
||||
action = module_params_get(ansible_module, "action")
|
||||
|
||||
# state
|
||||
state = module_params_get(ansible_module, "state")
|
||||
|
||||
# Check parameters
|
||||
invalid = []
|
||||
|
||||
if state == "present":
|
||||
if len(names) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Only one privilege be added at a time.")
|
||||
if action == "member":
|
||||
invalid = ["description"]
|
||||
|
||||
if state == "absent":
|
||||
if len(names) < 1:
|
||||
ansible_module.fail_json(msg="No name given.")
|
||||
invalid = ["description", "rename"]
|
||||
if action == "privilege":
|
||||
invalid.append("permission")
|
||||
|
||||
if state == "renamed":
|
||||
if len(names) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Only one privilege be added at a time.")
|
||||
invalid = ["description", "permission"]
|
||||
if action != "privilege":
|
||||
ansible_module.fail_json(
|
||||
msg="Action '%s' can not be used with state '%s'"
|
||||
% (action, state))
|
||||
|
||||
for x in invalid:
|
||||
if vars()[x] is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="Argument '%s' can not be used with action "
|
||||
"'%s' and state '%s'" % (x, action, state))
|
||||
|
||||
# Init
|
||||
|
||||
changed = False
|
||||
exit_args = {}
|
||||
ccache_dir = None
|
||||
ccache_name = None
|
||||
try:
|
||||
if not valid_creds(ansible_module, ipaadmin_principal):
|
||||
ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
|
||||
ipaadmin_password)
|
||||
api_connect()
|
||||
|
||||
commands = []
|
||||
for name in names:
|
||||
# Make sure privilege exists
|
||||
res_find = find_privilege(ansible_module, name)
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
|
||||
args = {}
|
||||
if description:
|
||||
args['description'] = description
|
||||
|
||||
if action == "privilege":
|
||||
# Found the privilege
|
||||
if res_find is not None:
|
||||
# For all settings is args, check if there are
|
||||
# different settings in the find result.
|
||||
# If yes: modify
|
||||
if not compare_args_ipa(ansible_module, args,
|
||||
res_find):
|
||||
commands.append([name, "privilege_mod", args])
|
||||
else:
|
||||
commands.append([name, "privilege_add", args])
|
||||
|
||||
member_args = {}
|
||||
if permission:
|
||||
member_args['permission'] = permission
|
||||
|
||||
if not compare_args_ipa(ansible_module, member_args,
|
||||
res_find):
|
||||
|
||||
# Generate addition and removal lists
|
||||
permission_add, permission_del = gen_add_del_lists(
|
||||
permission, res_find.get("member_permission"))
|
||||
|
||||
# Add members
|
||||
if len(permission_add) > 0:
|
||||
commands.append([name, "privilege_add_permission",
|
||||
{
|
||||
"permission": permission_add,
|
||||
}])
|
||||
# Remove members
|
||||
if len(permission_del) > 0:
|
||||
commands.append([
|
||||
name,
|
||||
"privilege_remove_permission",
|
||||
{"permission": permission_del}
|
||||
])
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(
|
||||
msg="No privilege '%s'" % name)
|
||||
|
||||
if permission is None:
|
||||
ansible_module.fail_json(msg="No permission given")
|
||||
|
||||
commands.append([name, "privilege_add_permission",
|
||||
{"permission": permission}])
|
||||
|
||||
elif state == "absent":
|
||||
if action == "privilege":
|
||||
if res_find is not None:
|
||||
commands.append([name, "privilege_del", {}])
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(
|
||||
msg="No privilege '%s'" % name)
|
||||
|
||||
if permission is None:
|
||||
ansible_module.fail_json(msg="No permission given")
|
||||
|
||||
commands.append([name, "privilege_remove_permission",
|
||||
{
|
||||
"permission": permission,
|
||||
}])
|
||||
|
||||
elif state == "renamed":
|
||||
if not rename:
|
||||
ansible_module.fail_json(msg="No rename value given.")
|
||||
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(
|
||||
msg="No privilege found to be renamed: '%s'" % (name))
|
||||
|
||||
if name != rename:
|
||||
commands.append(
|
||||
[name, "privilege_mod", {"rename": rename}])
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Execute commands
|
||||
|
||||
for name, command, args in commands:
|
||||
try:
|
||||
result = api_command(ansible_module, command, name,
|
||||
args)
|
||||
if "completed" in result:
|
||||
if result["completed"] > 0:
|
||||
changed = True
|
||||
else:
|
||||
changed = True
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(
|
||||
msg="%s: %s: %s" % (command, name, str(e)))
|
||||
# Get all errors
|
||||
# All "already a member" and "not a member" failures in the
|
||||
# result are ignored. All others are reported.
|
||||
errors = []
|
||||
for failed_item in result.get("failed", []):
|
||||
failed = result["failed"][failed_item]
|
||||
for member_type in failed:
|
||||
for member, failure in failed[member_type]:
|
||||
if "already a member" in failure \
|
||||
or "not a member" in failure:
|
||||
continue
|
||||
errors.append("%s: %s %s: %s" % (
|
||||
command, member_type, member, failure))
|
||||
if len(errors) > 0:
|
||||
ansible_module.fail_json(msg=", ".join(errors))
|
||||
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(msg=str(e))
|
||||
|
||||
finally:
|
||||
temp_kdestroy(ccache_dir, ccache_name)
|
||||
|
||||
# Done
|
||||
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -30,9 +30,9 @@ ANSIBLE_METADATA = {
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: ipaservice
|
||||
short description: Manage FreeIPA service
|
||||
description: Manage FreeIPA service
|
||||
module: iparole
|
||||
short description: Manage FreeIPA role
|
||||
description: Manage FreeIPA role
|
||||
options:
|
||||
ipaadmin_principal:
|
||||
description: The admin principal.
|
||||
@@ -66,7 +66,7 @@ options:
|
||||
description: List of services.
|
||||
required: false
|
||||
action:
|
||||
description: Work on service or member level.
|
||||
description: Work on role or member level.
|
||||
choices: ["role", "member"]
|
||||
default: role
|
||||
required: false
|
||||
|
||||
323
plugins/modules/ipaselfservice.py
Normal file
323
plugins/modules/ipaselfservice.py
Normal file
@@ -0,0 +1,323 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
# Thomas Woerner <twoerner@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: ipaselfservice
|
||||
short description: Manage FreeIPA selfservices
|
||||
description: Manage FreeIPA selfservices and selfservice attributes
|
||||
options:
|
||||
ipaadmin_principal:
|
||||
description: The admin principal.
|
||||
default: admin
|
||||
ipaadmin_password:
|
||||
description: The admin password.
|
||||
required: false
|
||||
name:
|
||||
description: The list of selfservice name strings.
|
||||
required: true
|
||||
aliases: ["aciname"]
|
||||
permission:
|
||||
description: Permissions to grant (read, write). Default is write.
|
||||
required: false
|
||||
aliases: ["permissions"]
|
||||
attribute:
|
||||
description: Attribute list to which the selfservice applies
|
||||
required: false
|
||||
aliases: ["attrs"]
|
||||
action:
|
||||
description: Work on selfservice or member level.
|
||||
choices: ["selfservice", "member"]
|
||||
default: selfservice
|
||||
required: false
|
||||
state:
|
||||
description: The state to ensure.
|
||||
choices: ["present", "absent"]
|
||||
default: present
|
||||
required: true
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Ensure selfservice "Users can manage their own name details" is present
|
||||
- ipaselfservice:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "Users can manage their own name details"
|
||||
permission: read
|
||||
attribute:
|
||||
- title
|
||||
- initials
|
||||
|
||||
# Ensure selfservice "Users can manage their own name details" member
|
||||
# attribute departmentnumber is present
|
||||
- ipaselfservice:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "Users can manage their own name details"
|
||||
attribute:
|
||||
- initials
|
||||
action: member
|
||||
|
||||
# Ensure selfservice "Users can manage their own name details" member
|
||||
# attributes employeetype and employeenumber are present
|
||||
- ipaselfservice:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "Users can manage their own name details"
|
||||
attribute:
|
||||
- title
|
||||
- initials
|
||||
action: member
|
||||
state: absent
|
||||
|
||||
# Ensure selfservice "Users can manage their own name details" is absent
|
||||
- ipaselfservice:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: "Users can manage their own name details"
|
||||
state: absent
|
||||
"""
|
||||
|
||||
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
|
||||
import six
|
||||
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
def find_selfservice(module, name):
|
||||
"""Find if a selfservice with the given name already exist."""
|
||||
try:
|
||||
_result = api_command(module, "selfservice_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# An exception is raised if selfservice name is not found.
|
||||
return None
|
||||
else:
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_args(permission, attribute):
|
||||
_args = {}
|
||||
if permission is not None:
|
||||
_args["permissions"] = permission
|
||||
if attribute is not None:
|
||||
_args["attrs"] = attribute
|
||||
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),
|
||||
|
||||
name=dict(type="list", aliases=["aciname"], default=None,
|
||||
required=True),
|
||||
# present
|
||||
permission=dict(required=False, type='list',
|
||||
aliases=["permissions"], default=None),
|
||||
attribute=dict(required=False, type='list', aliases=["attrs"],
|
||||
default=None),
|
||||
action=dict(type="str", default="selfservice",
|
||||
choices=["member", "selfservice"]),
|
||||
# state
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent"]),
|
||||
),
|
||||
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")
|
||||
names = module_params_get(ansible_module, "name")
|
||||
|
||||
# present
|
||||
permission = module_params_get(ansible_module, "permission")
|
||||
attribute = module_params_get(ansible_module, "attribute")
|
||||
action = module_params_get(ansible_module, "action")
|
||||
# state
|
||||
state = module_params_get(ansible_module, "state")
|
||||
|
||||
# Check parameters
|
||||
|
||||
if state == "present":
|
||||
if len(names) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Only one selfservice be added at a time.")
|
||||
if action == "member":
|
||||
invalid = ["permission"]
|
||||
for x in invalid:
|
||||
if vars()[x] is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="Argument '%s' can not be used with action "
|
||||
"'%s' and state '%s'" % (x, action, state))
|
||||
|
||||
if state == "absent":
|
||||
if len(names) < 1:
|
||||
ansible_module.fail_json(msg="No name given.")
|
||||
invalid = ["permission"]
|
||||
if action == "selfservice":
|
||||
invalid.append("attribute")
|
||||
for x in invalid:
|
||||
if vars()[x] is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="Argument '%s' can not be used with action "
|
||||
"'%s' and state '%s'" % (x, action, state))
|
||||
|
||||
if permission is not None:
|
||||
perm = [p for p in permission if p not in ("read", "write")]
|
||||
if perm:
|
||||
ansible_module.fail_json(msg="Invalid permission '%s'" % perm)
|
||||
if len(set(permission)) != len(permission):
|
||||
ansible_module.fail_json(
|
||||
msg="Invalid permission '%s', items are not unique" %
|
||||
repr(permission))
|
||||
|
||||
if attribute is not None:
|
||||
if len(set(attribute)) != len(attribute):
|
||||
ansible_module.fail_json(
|
||||
msg="Invalid attribute '%s', items are not unique" %
|
||||
repr(attribute))
|
||||
|
||||
# Init
|
||||
|
||||
changed = False
|
||||
exit_args = {}
|
||||
ccache_dir = None
|
||||
ccache_name = None
|
||||
try:
|
||||
if not valid_creds(ansible_module, ipaadmin_principal):
|
||||
ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
|
||||
ipaadmin_password)
|
||||
api_connect()
|
||||
|
||||
commands = []
|
||||
for name in names:
|
||||
# Make sure selfservice exists
|
||||
res_find = find_selfservice(ansible_module, name)
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
|
||||
# Generate args
|
||||
args = gen_args(permission, attribute)
|
||||
|
||||
if action == "selfservice":
|
||||
# Found the selfservice
|
||||
if res_find is not None:
|
||||
# For all settings is args, check if there are
|
||||
# different settings in the find result.
|
||||
# If yes: modify
|
||||
if not compare_args_ipa(ansible_module, args,
|
||||
res_find):
|
||||
commands.append([name, "selfservice_mod", args])
|
||||
else:
|
||||
commands.append([name, "selfservice_add", args])
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(
|
||||
msg="No selfservice '%s'" % name)
|
||||
|
||||
if attribute is None:
|
||||
ansible_module.fail_json(msg="No attributes given")
|
||||
|
||||
# New attribute list (add given ones to find result)
|
||||
# Make list with unique entries
|
||||
attrs = list(set(list(res_find["attrs"]) + attribute))
|
||||
if len(attrs) > len(res_find["attrs"]):
|
||||
commands.append([name, "selfservice_mod",
|
||||
{"attrs": attrs}])
|
||||
|
||||
elif state == "absent":
|
||||
if action == "selfservice":
|
||||
if res_find is not None:
|
||||
commands.append([name, "selfservice_del", {}])
|
||||
|
||||
elif action == "member":
|
||||
if res_find is None:
|
||||
ansible_module.fail_json(
|
||||
msg="No selfservice '%s'" % name)
|
||||
|
||||
if attribute is None:
|
||||
ansible_module.fail_json(msg="No attributes given")
|
||||
|
||||
# New attribute list (remove given ones from find result)
|
||||
# Make list with unique entries
|
||||
attrs = list(set(res_find["attrs"]) - set(attribute))
|
||||
if len(attrs) < 1:
|
||||
ansible_module.fail_json(
|
||||
msg="At minimum one attribute is needed.")
|
||||
|
||||
# Entries New number of attributes is smaller
|
||||
if len(attrs) < len(res_find["attrs"]):
|
||||
commands.append([name, "selfservice_mod",
|
||||
{"attrs": attrs}])
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Execute commands
|
||||
|
||||
for name, command, args in commands:
|
||||
try:
|
||||
result = api_command(ansible_module, command, name,
|
||||
args)
|
||||
if "completed" in result:
|
||||
if result["completed"] > 0:
|
||||
changed = True
|
||||
else:
|
||||
changed = True
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(msg="%s: %s: %s" % (command, name,
|
||||
str(e)))
|
||||
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(msg=str(e))
|
||||
|
||||
finally:
|
||||
temp_kdestroy(ccache_dir, ccache_name)
|
||||
|
||||
# Done
|
||||
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -460,7 +460,7 @@ def main():
|
||||
allow_retrieve_keytab_group = module_params_get(
|
||||
ansible_module, "allow_retrieve_keytab_group")
|
||||
allow_retrieve_keytab_host = module_params_get(
|
||||
ansible_module, "allow_create_keytab_host")
|
||||
ansible_module, "allow_retrieve_keytab_host")
|
||||
allow_retrieve_keytab_hostgroup = module_params_get(
|
||||
ansible_module, "allow_retrieve_keytab_hostgroup")
|
||||
delete_continue = module_params_get(ansible_module, "delete_continue")
|
||||
@@ -727,7 +727,7 @@ def main():
|
||||
# Allow retrieve keytab
|
||||
if len(allow_retrieve_keytab_user_add) > 0 or \
|
||||
len(allow_retrieve_keytab_group_add) > 0 or \
|
||||
len(allow_retrieve_keytab_hostgroup_add) > 0 or \
|
||||
len(allow_retrieve_keytab_host_add) > 0 or \
|
||||
len(allow_retrieve_keytab_hostgroup_add) > 0:
|
||||
commands.append(
|
||||
[name, "service_allow_retrieve_keytab",
|
||||
|
||||
274
plugins/modules/ipatrust.py
Normal file
274
plugins/modules/ipatrust.py
Normal file
@@ -0,0 +1,274 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
# Rob Verduijn <rob.verduijn@gmail.com>
|
||||
#
|
||||
# Copyright (C) 2019 By Rob Verduijn
|
||||
# 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/>.
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
|
||||
temp_kdestroy, valid_creds, api_connect, api_command, module_params_get
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'supported_by': 'community',
|
||||
'status': ['preview'],
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: ipatrust
|
||||
short_description: Manage FreeIPA Domain Trusts.
|
||||
description: Manage FreeIPA Domain Trusts.
|
||||
options:
|
||||
realm:
|
||||
description:
|
||||
- Realm name
|
||||
required: true
|
||||
trust_type:
|
||||
description:
|
||||
- Trust type (ad for Active Directory, default)
|
||||
default: ad
|
||||
required: true
|
||||
admin:
|
||||
description:
|
||||
- Active Directory domain administrator
|
||||
required: false
|
||||
password:
|
||||
description:
|
||||
- Active Directory domain administrator's password
|
||||
required: false
|
||||
server:
|
||||
description:
|
||||
- Domain controller for the Active Directory domain (optional)
|
||||
required: false
|
||||
trust_secret:
|
||||
description:
|
||||
- Shared secret for the trust
|
||||
required: false
|
||||
base_id:
|
||||
description:
|
||||
- First Posix ID of the range reserved for the trusted domain
|
||||
required: false
|
||||
range_size:
|
||||
description:
|
||||
- Size of the ID range reserved for the trusted domain
|
||||
range_type:
|
||||
description:
|
||||
- Type of trusted domain ID range, one of ipa-ad-trust, ipa-ad-trust-posix
|
||||
default: ipa-ad-trust
|
||||
required: false
|
||||
two_way:
|
||||
description:
|
||||
- Establish bi-directional trust. By default trust is inbound one-way only.
|
||||
default: false
|
||||
required: false
|
||||
choices: ["true", "false"]
|
||||
external:
|
||||
description:
|
||||
- Establish external trust to a domain in another forest.
|
||||
- The trust is not transitive beyond the domain.
|
||||
default: false
|
||||
required: false
|
||||
choices: ["true", "false"]
|
||||
state:
|
||||
description: State to ensure
|
||||
default: present
|
||||
required: true
|
||||
choices: ["present", "absent"]
|
||||
|
||||
author:
|
||||
- Rob Verduijn
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# add ad-trust
|
||||
- ipatrust:
|
||||
realm: ad.example.test
|
||||
trust_type: ad
|
||||
admin: Administrator
|
||||
password: Welcome2020!
|
||||
state: present
|
||||
|
||||
# delete ad-trust
|
||||
- ipatrust:
|
||||
realm: ad.example.test
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
"""
|
||||
|
||||
|
||||
def find_trust(module, realm):
|
||||
_args = {
|
||||
"all": True,
|
||||
"cn": realm,
|
||||
}
|
||||
|
||||
_result = api_command(module, "trust_find", realm, _args)
|
||||
|
||||
if len(_result["result"]) > 1:
|
||||
module.fail_json(msg="There is more than one realm '%s'" % (realm))
|
||||
elif len(_result["result"]) == 1:
|
||||
return _result["result"][0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def del_trust(module, realm):
|
||||
_args = {}
|
||||
|
||||
_result = api_command(module, "trust_del", realm, _args)
|
||||
if len(_result["result"]["failed"]) > 0:
|
||||
module.fail_json(
|
||||
msg="Trust deletion has failed for '%s'" % (realm))
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def add_trust(module, realm, args):
|
||||
_args = args
|
||||
|
||||
_result = api_command(module, "trust_add", realm, _args)
|
||||
|
||||
if "cn" not in _result["result"]:
|
||||
module.fail_json(
|
||||
msg="Trust add has failed for '%s'" % (realm))
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def gen_args(trust_type, admin, password, server, trust_secret, base_id,
|
||||
range_size, range_type, two_way, external):
|
||||
_args = {}
|
||||
if trust_type is not None:
|
||||
_args["trust_type"] = trust_type
|
||||
if admin is not None:
|
||||
_args["realm_admin"] = admin
|
||||
if password is not None:
|
||||
_args["realm_passwd"] = password
|
||||
if server is not None:
|
||||
_args["realm_server"] = server
|
||||
if trust_secret is not None:
|
||||
_args["trust_secret"] = trust_secret
|
||||
if base_id is not None:
|
||||
_args["base_id"] = base_id
|
||||
if range_size is not None:
|
||||
_args["range_size"] = range_size
|
||||
if two_way is not None:
|
||||
_args["bidirectional"] = two_way
|
||||
if external is not None:
|
||||
_args["external"] = external
|
||||
|
||||
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),
|
||||
realm=dict(type="str", default=None, required=True),
|
||||
# state
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent"]),
|
||||
# present
|
||||
trust_type=dict(type="str", default="ad", required=False),
|
||||
admin=dict(type="str", default=None, required=False),
|
||||
password=dict(type="str", default=None,
|
||||
required=False, no_log=True),
|
||||
server=dict(type="str", default=None, required=False),
|
||||
trust_secret=dict(type="str", default=None,
|
||||
required=False, no_log=True),
|
||||
base_id=dict(type="int", default=None, required=False),
|
||||
range_size=dict(type="int", default=200000, required=False),
|
||||
range_type=dict(type="str", default="ipa-ad-trust",
|
||||
required=False, choices=["ipa-ad-trust-posix",
|
||||
"ipa-ad-trust"]),
|
||||
two_way=dict(type="bool", default=False, required=False),
|
||||
external=dict(type="bool", default=False, required=False),
|
||||
),
|
||||
mutually_exclusive=[["trust_secret", "admin"]],
|
||||
required_together=[["admin", "password"]],
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
ansible_module._ansible_debug = True
|
||||
|
||||
# general
|
||||
ipaadmin_principal = module_params_get(
|
||||
ansible_module, "ipaadmin_principal")
|
||||
ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
|
||||
realm = module_params_get(ansible_module, "realm")
|
||||
|
||||
# state
|
||||
state = module_params_get(ansible_module, "state")
|
||||
|
||||
# trust
|
||||
trust_type = module_params_get(ansible_module, "trust_type")
|
||||
admin = module_params_get(ansible_module, "admin")
|
||||
password = module_params_get(ansible_module, "password")
|
||||
server = module_params_get(ansible_module, "server")
|
||||
trust_secret = module_params_get(ansible_module, "trust_secret")
|
||||
base_id = module_params_get(ansible_module, "base_id")
|
||||
range_size = module_params_get(ansible_module, "range_size")
|
||||
range_type = module_params_get(ansible_module, "range_type")
|
||||
two_way = module_params_get(ansible_module, "two_way")
|
||||
external = module_params_get(ansible_module, "external")
|
||||
|
||||
changed = False
|
||||
exit_args = {}
|
||||
ccache_dir = None
|
||||
ccache_name = None
|
||||
try:
|
||||
if not valid_creds(ansible_module, ipaadmin_principal):
|
||||
ccache_dir, ccache_name = temp_kinit(
|
||||
ipaadmin_principal, ipaadmin_password)
|
||||
api_connect()
|
||||
res_find = find_trust(ansible_module, realm)
|
||||
|
||||
if state == "absent":
|
||||
if res_find is not None:
|
||||
del_trust(ansible_module, realm)
|
||||
changed = True
|
||||
elif res_find is None:
|
||||
if admin is None and trust_secret is None:
|
||||
ansible_module.fail_json(
|
||||
msg="one of admin or trust_secret is required when state "
|
||||
"is present")
|
||||
else:
|
||||
args = gen_args(trust_type, admin, password, server,
|
||||
trust_secret, base_id, range_size, range_type,
|
||||
two_way, external)
|
||||
|
||||
add_trust(ansible_module, realm, args)
|
||||
changed = True
|
||||
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(msg=str(e))
|
||||
|
||||
finally:
|
||||
temp_kdestroy(ccache_dir, ccache_name)
|
||||
|
||||
# Done
|
||||
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -243,7 +243,7 @@ EXAMPLES = """
|
||||
state: retrieved
|
||||
register: result
|
||||
- debug:
|
||||
msg: "{{ result.data }}"
|
||||
msg: "{{ result.vault.data }}"
|
||||
|
||||
# Change password of a symmetric vault
|
||||
- ipavault:
|
||||
@@ -267,7 +267,7 @@ EXAMPLES = """
|
||||
username: user01
|
||||
description: An asymmetric vault
|
||||
vault_type: asymmetric
|
||||
public_key:
|
||||
public_key: |
|
||||
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTR
|
||||
HTkFEQ0JpUUtCZ1FDdGFudjRkK3ptSTZ0T3ova1RXdGowY3AxRAowUENoYy8vR0pJMTUzTi
|
||||
9CN3UrN0h3SXlRVlZoNUlXZG1UcCtkWXYzd09yeVpPbzYvbHN5eFJaZ2pZRDRwQ3VGCjlxM
|
||||
@@ -303,10 +303,15 @@ EXAMPLES = """
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
user:
|
||||
description: The vault data.
|
||||
returned: If state is retrieved.
|
||||
type: string
|
||||
vault:
|
||||
description: Vault dict with archived data.
|
||||
returned: If state is `retrieved`.
|
||||
type: dict
|
||||
options:
|
||||
data:
|
||||
description: The vault data.
|
||||
returned: always
|
||||
type: string
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -489,8 +494,10 @@ def check_encryption_params(module, state, action, vault_type, salt,
|
||||
new_password, new_password_file, res_find):
|
||||
vault_type_invalid = []
|
||||
|
||||
if res_find is not None:
|
||||
if vault_type is None and res_find is not None:
|
||||
vault_type = res_find['ipavaulttype']
|
||||
if isinstance(vault_type, (tuple, list)):
|
||||
vault_type = vault_type[0]
|
||||
|
||||
if vault_type == "standard":
|
||||
vault_type_invalid = ['public_key', 'public_key_file', 'password',
|
||||
@@ -510,6 +517,16 @@ def check_encryption_params(module, state, action, vault_type, salt,
|
||||
module.fail_json(
|
||||
msg="Cannot modify password of inexistent vault.")
|
||||
|
||||
if (
|
||||
salt is not None
|
||||
and not(
|
||||
any([password, password_file])
|
||||
and any([new_password, new_password_file])
|
||||
)
|
||||
):
|
||||
module.fail_json(
|
||||
msg="Vault `salt` can only change when changing the password.")
|
||||
|
||||
if vault_type == "asymmetric":
|
||||
vault_type_invalid = [
|
||||
'password', 'password_file', 'new_password', 'new_password_file'
|
||||
@@ -739,7 +756,7 @@ def main():
|
||||
res_vault_type = res_find.get('ipavaulttype')[0]
|
||||
args['ipavaulttype'] = vault_type = res_vault_type
|
||||
else:
|
||||
args['ipavaulttype'] = vault_type = "symmetric"
|
||||
args['ipavaulttype'] = vault_type = u"symmetric"
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
@@ -761,7 +778,12 @@ def main():
|
||||
commands.append([name, "vault_mod_internal", args])
|
||||
|
||||
else:
|
||||
if vault_type == 'symmetric' \
|
||||
and 'ipavaultsalt' not in args:
|
||||
args['ipavaultsalt'] = os.urandom(32)
|
||||
|
||||
commands.append([name, "vault_add_internal", args])
|
||||
|
||||
if vault_type != 'standard' and vault_data is None:
|
||||
vault_data = ''
|
||||
|
||||
@@ -819,14 +841,6 @@ def main():
|
||||
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 any([users, groups, services]):
|
||||
@@ -909,12 +923,12 @@ def main():
|
||||
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']
|
||||
if "data" in result["result"]:
|
||||
data_return = exit_args.setdefault("vault", {})
|
||||
data_return["data"] = result["result"]["data"]
|
||||
else:
|
||||
raise Exception("No data retrieved.")
|
||||
if not datafile_out:
|
||||
raise Exception("No data retrieved.")
|
||||
changed = False
|
||||
else:
|
||||
if "completed" in result:
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
[pytest]
|
||||
python_files = test_*.py
|
||||
junit_family = xunit1
|
||||
markers=
|
||||
source_order: mark test as order bound
|
||||
playbook: playbook tests
|
||||
|
||||
2
requirements-dev.txt
Normal file
2
requirements-dev.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
-r requirements-tests.txt
|
||||
ipdb
|
||||
7
requirements-tests.txt
Normal file
7
requirements-tests.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
-r requirements.txt
|
||||
pytest>=2.7
|
||||
pytest-sourceorder>=0.5
|
||||
pytest-split-tests>=1.0.3
|
||||
testinfra>=5.0
|
||||
jmespath>=0.9 # needed for the `json_query` filter
|
||||
pyyaml>=3
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Test ipaclient python3 binding
|
||||
from ipaclient.install.client import SECURE_PATH # noqa: F401
|
||||
|
||||
# Check ipapython version to be >= 4.6
|
||||
from ipapython.version import NUM_VERSION, VERSION
|
||||
if NUM_VERSION < 40600:
|
||||
raise Exception("ipa %s not usable with python3" % VERSION)
|
||||
@@ -37,12 +37,12 @@ description:
|
||||
options:
|
||||
domain:
|
||||
description: Primary DNS domain of the IPA deployment
|
||||
required: no
|
||||
required: yes
|
||||
firefox_dir:
|
||||
description:
|
||||
Specify directory where Firefox is installed (for example
|
||||
'/usr/lib/firefox')
|
||||
required: yes
|
||||
required: no
|
||||
author:
|
||||
- Thomas Woerner
|
||||
'''
|
||||
|
||||
@@ -107,9 +107,12 @@ if NUM_VERSION >= 40400:
|
||||
from ipalib import api, errors, x509
|
||||
from ipalib import constants
|
||||
try:
|
||||
from ipalib.install import sysrestore
|
||||
from ipalib import sysrestore
|
||||
except ImportError:
|
||||
from ipapython import sysrestore
|
||||
try:
|
||||
from ipalib.install import sysrestore
|
||||
except ImportError:
|
||||
from ipapython import sysrestore
|
||||
try:
|
||||
from ipalib.install import certmonger
|
||||
except ImportError:
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
state: present
|
||||
when: ipaclient_install_packages | bool
|
||||
|
||||
#- name: Install - Include Python2/3 import test
|
||||
# import_tasks: "{{ role_path }}/tasks/python_2_3_test.yml"
|
||||
|
||||
- name: Install - Set ipaclient_servers
|
||||
set_fact:
|
||||
ipaclient_servers: "{{ groups['ipaservers'] | list }}"
|
||||
@@ -113,10 +110,6 @@
|
||||
fail: msg="Keytab or password is required for getting otp"
|
||||
when: ipaadmin_keytab is undefined and ipaadmin_password is undefined
|
||||
|
||||
#- name: Install - Include Python2/3 import test
|
||||
# import_tasks: "{{ role_path }}/tasks/python_2_3_test.yml"
|
||||
# delegate_to: "{{ result_ipaclient_test.servers[0] }}"
|
||||
|
||||
- name: Install - Get One-Time Password for client enrollment
|
||||
no_log: yes
|
||||
ipaclient_get_otp:
|
||||
@@ -350,6 +343,7 @@
|
||||
- name: Install - Configure firefox
|
||||
ipaclient_setup_firefox:
|
||||
firefox_dir: "{{ ipaclient_firefox_dir | default(omit) }}"
|
||||
domain: "{{ result_ipaclient_test.domain }}"
|
||||
when: ipaclient_configure_firefox | bool
|
||||
|
||||
- name: Install - Configure NIS
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
- block:
|
||||
- name: Verify Python3 import
|
||||
script: py3test.py
|
||||
register: result_py3test
|
||||
failed_when: False
|
||||
changed_when: False
|
||||
check_mode: no
|
||||
|
||||
- name: Set python interpreter to 3
|
||||
set_fact:
|
||||
ansible_python_interpreter: "/usr/bin/python3"
|
||||
when: result_py3test.rc == 0
|
||||
|
||||
- name: Set python interpreter to 2
|
||||
set_fact:
|
||||
ansible_python_interpreter: "/usr/bin/python2"
|
||||
when: result_py3test.failed or result_py3test.rc != 0
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user