mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-03-27 13:53:06 +00:00
Compare commits
137 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62fd1551eb | ||
|
|
a24e90ad0c | ||
|
|
0b9718b3ec | ||
|
|
226b8c4d75 | ||
|
|
2f34e1ac6a | ||
|
|
e4ea7c8983 | ||
|
|
b3f024869c | ||
|
|
355438cea9 | ||
|
|
30b72422d9 | ||
|
|
10a84429e2 | ||
|
|
bf384ab1aa | ||
|
|
536b7cb5f3 | ||
|
|
17b100baec | ||
|
|
1488fb7b5e | ||
|
|
a733c031b0 | ||
|
|
ff1a026ef4 | ||
|
|
fa5d056e72 | ||
|
|
e0e3cb041e | ||
|
|
b54aaf127d | ||
|
|
4e16126b29 | ||
|
|
ed62c2f1bf | ||
|
|
dc9b0ce4e8 | ||
|
|
aa3bf1f015 | ||
|
|
f0aa531b28 | ||
|
|
6407fd8b2e | ||
|
|
2a1be13d3e | ||
|
|
2afca1fa5e | ||
|
|
2a40e42b0c | ||
|
|
8a33941188 | ||
|
|
0aef995bbe | ||
|
|
e75efb7a13 | ||
|
|
ed44344519 | ||
|
|
b186a1f28f | ||
|
|
d307635c38 | ||
|
|
74f3817531 | ||
|
|
97378c38cf | ||
|
|
6f15cd093a | ||
|
|
52f7f7848e | ||
|
|
fdd45cc475 | ||
|
|
a1cad32a46 | ||
|
|
7036fa3e1b | ||
|
|
95d935f185 | ||
|
|
dd3bc4fcdd | ||
|
|
c405229553 | ||
|
|
3fa3bf0822 | ||
|
|
7cef44c01d | ||
|
|
cd7d19bfeb | ||
|
|
0e748d372a | ||
|
|
e24340447d | ||
|
|
092ad81d03 | ||
|
|
4d22e917df | ||
|
|
a04a357b6a | ||
|
|
2081a1a8dd | ||
|
|
d1dfdc38c9 | ||
|
|
9fc1b043c1 | ||
|
|
bdf1efde80 | ||
|
|
513d5ee46b | ||
|
|
cd440a2049 | ||
|
|
e2317f304c | ||
|
|
7a23c668fc | ||
|
|
91d818b334 | ||
|
|
902d8b7238 | ||
|
|
d553f9a0b1 | ||
|
|
9cfe835b03 | ||
|
|
668830fc94 | ||
|
|
5ae39ec9de | ||
|
|
3f59332d99 | ||
|
|
30c405cb36 | ||
|
|
7275bbf6a3 | ||
|
|
6df89ad7db | ||
|
|
c5fa54f2cf | ||
|
|
8e3102270d | ||
|
|
89cfb5f4c4 | ||
|
|
5fb22581bb | ||
|
|
6976ef57eb | ||
|
|
0d9b164358 | ||
|
|
8b03e4d007 | ||
|
|
ef73a85320 | ||
|
|
5b3a4729f0 | ||
|
|
7245339934 | ||
|
|
638422e113 | ||
|
|
432376524c | ||
|
|
86701caf8b | ||
|
|
d1857c18ac | ||
|
|
edbdd3af79 | ||
|
|
2d3da2d72c | ||
|
|
329c16f742 | ||
|
|
66c0be06d0 | ||
|
|
f04c90f4db | ||
|
|
dfa4bcb68f | ||
|
|
b1328ba7d5 | ||
|
|
fe58f3a8ba | ||
|
|
4dc6192640 | ||
|
|
e9435410b2 | ||
|
|
de6a0429a0 | ||
|
|
40d85f83e4 | ||
|
|
678927f35c | ||
|
|
f0e6d0c89f | ||
|
|
c095c24950 | ||
|
|
34dc75802c | ||
|
|
feb33e4e3a | ||
|
|
3c50a8121f | ||
|
|
e8688d4cf5 | ||
|
|
d540be425a | ||
|
|
c1d7ed1df6 | ||
|
|
0fc8ddf450 | ||
|
|
012f0deb00 | ||
|
|
f27b0e3011 | ||
|
|
8b4b22dd00 | ||
|
|
91c4b83311 | ||
|
|
6925503a10 | ||
|
|
0da0b22ae7 | ||
|
|
f5f454915c | ||
|
|
8581b79eba | ||
|
|
a9602431ce | ||
|
|
9195494f37 | ||
|
|
81abf6889b | ||
|
|
81906edec6 | ||
|
|
5071653db3 | ||
|
|
df4ec30a51 | ||
|
|
73160a037b | ||
|
|
b7ed9ecfd5 | ||
|
|
a4f608854d | ||
|
|
8e6c5e566d | ||
|
|
431dc8667a | ||
|
|
bc16ccaef7 | ||
|
|
227c95e62e | ||
|
|
5abb515c92 | ||
|
|
1c4b50fa51 | ||
|
|
8fc2de1673 | ||
|
|
8d74fe34ef | ||
|
|
87ad46f7a4 | ||
|
|
4c3f4e6f7d | ||
|
|
489f4d5784 | ||
|
|
29fc03c625 | ||
|
|
2dc1deeb87 | ||
|
|
82a53b9ae4 |
18
.github/workflows/docs.yml
vendored
18
.github/workflows/docs.yml
vendored
@@ -5,7 +5,7 @@ on:
|
||||
- pull_request
|
||||
jobs:
|
||||
check_docs_oldest_supported:
|
||||
name: Check Ansible Documentation with ansible-core 2.13.
|
||||
name: Check Ansible Documentation with ansible-core 2.16.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
@@ -14,15 +14,15 @@ jobs:
|
||||
- uses: actions/setup-python@v5.1.0
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install Ansible 2.13
|
||||
- name: Install Ansible 2.16
|
||||
run: |
|
||||
python -m pip install "ansible-core >=2.13,<2.14"
|
||||
python -m pip install "ansible-core >=2.16,<2.17"
|
||||
- name: Run ansible-doc-test
|
||||
run: |
|
||||
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
|
||||
|
||||
check_docs_previous:
|
||||
name: Check Ansible Documentation with ansible-core 2.14.
|
||||
name: Check Ansible Documentation with ansible-core 2.18.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
@@ -31,15 +31,15 @@ jobs:
|
||||
- uses: actions/setup-python@v5.1.0
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install Ansible 2.14
|
||||
- name: Install Ansible 2.18
|
||||
run: |
|
||||
python -m pip install "ansible-core >=2.14,<2.15"
|
||||
python -m pip install "ansible-core >=2.18,<2.19"
|
||||
- name: Run ansible-doc-test
|
||||
run: |
|
||||
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
|
||||
|
||||
check_docs_current:
|
||||
name: Check Ansible Documentation with ansible-core 2.15.
|
||||
name: Check Ansible Documentation with ansible-core 2.19.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
@@ -48,9 +48,9 @@ jobs:
|
||||
- uses: actions/setup-python@v5.1.0
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install Ansible 2.15
|
||||
- name: Install Ansible 2.20
|
||||
run: |
|
||||
python -m pip install "ansible-core >=2.15,<2.16"
|
||||
python -m pip install "ansible-core <2.20"
|
||||
- name: Run ansible-doc-test
|
||||
run: |
|
||||
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
|
||||
|
||||
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
@@ -13,12 +13,12 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v5.1.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
python-version: "3.13"
|
||||
- name: Run ansible-lint
|
||||
run: |
|
||||
pip install "ansible-core>=2.16,<2.17" 'ansible-lint==6.22'
|
||||
utils/build-galaxy-release.sh -ki
|
||||
cd .galaxy-build
|
||||
utils/build-collection.sh -ki rpm
|
||||
cd .collection-build
|
||||
ansible-lint --profile production --exclude tests/integration/ --exclude tests/unit/ --parseable --nocolor
|
||||
|
||||
yamllint:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
repos:
|
||||
- repo: https://github.com/ansible/ansible-lint.git
|
||||
rev: v24.5.0
|
||||
rev: v25.9.2
|
||||
hooks:
|
||||
- id: ansible-lint
|
||||
always_run: false
|
||||
@@ -21,16 +21,16 @@ repos:
|
||||
--parseable
|
||||
--nocolor
|
||||
- repo: https://github.com/adrienverge/yamllint.git
|
||||
rev: v1.35.1
|
||||
rev: v1.37.1
|
||||
hooks:
|
||||
- id: yamllint
|
||||
files: \.(yaml|yml)$
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 7.0.0
|
||||
rev: 7.3.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
- repo: https://github.com/pycqa/pylint
|
||||
rev: v3.2.2
|
||||
rev: v4.0.2
|
||||
hooks:
|
||||
- id: pylint
|
||||
args:
|
||||
|
||||
@@ -20,4 +20,9 @@ rules:
|
||||
max: 160
|
||||
# Disabled rules
|
||||
indentation: disable
|
||||
comments: disable
|
||||
comments:
|
||||
min-spaces-from-content: 1
|
||||
comments-indentation: disable
|
||||
octal-values:
|
||||
forbid-implicit-octal: true
|
||||
forbid-explicit-octal: true
|
||||
|
||||
@@ -145,7 +145,7 @@ Variable | Description | Required
|
||||
`selinuxusermaporder` \| `ipaselinuxusermaporder`| Set ordered list in increasing priority of SELinux users | no
|
||||
`selinuxusermapdefault`\| `ipaselinuxusermapdefault` | Set default SELinux user when no match is found in SELinux map rule | no
|
||||
`pac_type` \| `ipakrbauthzdata` | set default types of PAC supported for services (choices: `MS-PAC`, `PAD`, `nfs:NONE`). Use `""` to clear this variable. | no
|
||||
`user_auth_type` \| `ipauserauthtype` | set default types of supported user authentication (choices: `password`, `radius`, `otp`, `pkinit`, `hardened`, `idp`, `disabled`, `""`). An additional check ensures that only types can be used that are supported by the IPA version. Use `""` to clear this variable. | no
|
||||
`user_auth_type` \| `ipauserauthtype` | set default types of supported user authentication (choices: `password`, `radius`, `otp`, `pkinit`, `hardened`, `idp`, `passkey`, `disabled`, `""`). An additional check ensures that only types can be used that are supported by the IPA version. Use `""` to clear this variable. | no
|
||||
`domain_resolution_order` \| `ipadomainresolutionorder` | Set list of domains used for short name qualification | no
|
||||
`ca_renewal_master_server` \| `ipacarenewalmasterserver`| Renewal master for IPA certificate authority. | no
|
||||
`enable_sid` | New users and groups automatically get a SID assigned. Cannot be deactivated once activated. Requires IPA 4.9.8+. (bool) | no
|
||||
|
||||
@@ -8,8 +8,12 @@ The group module allows to ensure presence and absence of groups and members of
|
||||
|
||||
The group module is as compatible as possible to the Ansible upstream `ipa_group` module, but additionally offers to add users to a group and also to remove users from a group.
|
||||
|
||||
## Note
|
||||
Ensuring presence (adding) of several groups with mixed types (`external`, `nonposix` and `posix`) requires a fix in FreeIPA. The module implements a workaround to automatically use `client` context if the fix is not present in the target node FreeIPA and if more than one group is provided to the task using the `groups` parameter. If `ipaapi_context` is forced to be `server`, the module will fail in this case.
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
* Ensuring presence (adding) of several groups with mixed types (`external`, `nonposix` and `posix`) requires a fix in FreeIPA. The module implements a workaround to automatically use `client` context if the fix is not present in the target node FreeIPA and if more than one group is provided to the task using the `groups` parameter. If `ipaapi_context` is forced to be `server`, the module will fail in this case.
|
||||
* Using `externalmember` or `idoverrideuser` is only supported with `ipaapi_context: server`. With 'client' context, module execution will fail.
|
||||
|
||||
|
||||
Features
|
||||
@@ -213,7 +217,7 @@ Example playbook to add members from a trusted realm to an external group:
|
||||
---
|
||||
- name: Playbook to handle groups.
|
||||
hosts: ipaserver
|
||||
|
||||
|
||||
tasks:
|
||||
- name: Create an external group and add members from a trust to it.
|
||||
ipagroup:
|
||||
@@ -276,6 +280,7 @@ Example playbook to ensure groups are absent:
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
=========
|
||||
|
||||
@@ -299,8 +304,8 @@ Variable | Description | Required
|
||||
`service` | List of service name strings assigned to this group. Only usable with IPA versions 4.7 and up. | no
|
||||
`membermanager_user` | List of member manager users assigned to this group. Only usable with IPA versions 4.8.4 and up. | no
|
||||
`membermanager_group` | List of member manager groups assigned to this group. Only usable with IPA versions 4.8.4 and up. | no
|
||||
`externalmember` \| `ipaexternalmember` \| `external_member`| List of members of a trusted domain in DOM\\name or name@domain form. | no
|
||||
`idoverrideuser` | List of user ID overrides to manage. Only usable with IPA versions 4.8.7 and up.| no
|
||||
`externalmember` \| `ipaexternalmember` \| `external_member`| List of members of a trusted domain in DOM\\name or name@domain form. Requires "server" context. | no
|
||||
`idoverrideuser` | List of user ID overrides to manage. Only usable with IPA versions 4.8.7 and up. Requires "server" context. | no
|
||||
`rename` \| `new_name` | Rename the user object to the new name string. Only usable with `state: renamed`. | no
|
||||
`action` | Work on group or member level. It can be on of `member` or `group` and defaults to `group`. | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent` or `renamed`, default: `present`. | yes
|
||||
|
||||
@@ -354,7 +354,7 @@ Variable | Description | Required
|
||||
`mac_address` \| `macaddress` | List of hardware MAC addresses. | no
|
||||
`sshpubkey` \| `ipasshpubkey` | List of SSH public keys | no
|
||||
`userclass` \| `class` | Host category (semantics placed on this attribute are for local interpretation) | no
|
||||
`auth_ind` \| `krbprincipalauthind` | Defines an allow list for Authentication Indicators. Use 'otp' to allow OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA authentications. Use empty string to reset auth_ind to the initial value. Other values may be used for custom configurations. An additional check ensures that only types can be used that are supported by the IPA version. Choices: ["radius", "otp", "pkinit", "hardened", "idp", ""] | no
|
||||
`auth_ind` \| `krbprincipalauthind` | Defines an allow list for Authentication Indicators. Use 'otp' to allow OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA authentications. Use empty string to reset auth_ind to the initial value. Other values may be used for custom configurations. An additional check ensures that only types can be used that are supported by the IPA version. Choices: ["radius", "otp", "pkinit", "hardened", "idp", "passkey", ""] | no
|
||||
`requires_pre_auth` \| `ipakrbrequirespreauth` | Pre-authentication is required for the service (bool) | no
|
||||
`ok_as_delegate` \| `ipakrbokasdelegate` | Client credentials may be delegated to the service (bool) | no
|
||||
`ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client (bool) | no
|
||||
|
||||
@@ -68,23 +68,6 @@ Example playbook to ensure a local domain idrange is present:
|
||||
name: local_domain_id_range
|
||||
base_id: 150000
|
||||
range_size: 200000
|
||||
```
|
||||
|
||||
Example playbook to ensure a local domain idrange is present, with RID and secondary RID base values:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA idrange.
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: Ensure local idrange is present
|
||||
ipaidrange:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: local_domain_id_range
|
||||
base_id: 150000000
|
||||
range_size: 200000
|
||||
rid_base: 1000000
|
||||
secondary_rid_base: 200000000
|
||||
```
|
||||
@@ -172,8 +155,8 @@ Variable | Description | Required
|
||||
`name` \| `cn` | The list of idrange name strings. | yes
|
||||
`base_id` \| `ipabaseid` | First Posix ID of the range. (int) | yes, if `state: present`
|
||||
`range_size` \| `ipaidrangesize` | Number of IDs in the range. (int) | yes, if `state: present`
|
||||
`rid_base` \| `ipabaserid` | First RID of the corresponding RID range. (int) | no
|
||||
`secondary_rid_base` \| `ipasecondarybaserid` | First RID of the secondary RID range. (int) | no
|
||||
`rid_base` \| `ipabaserid` | First RID of the corresponding RID range. (int) | yes, if `idrange_type: ipa-local` and `state: present` |
|
||||
`secondary_rid_base` \| `ipasecondarybaserid` | First RID of the secondary RID range. (int) | yes, if `idrange_type: ipa-local` and `state: present` |
|
||||
`dom_sid` \| `ipanttrusteddomainsid` | Domain SID of the trusted domain. | no
|
||||
`idrange_type` \| `iparangetype` | ID range type, one of `ipa-ad-trust`, `ipa-ad-trust-posix`, `ipa-local`. Only valid if idrange does not exist. | no
|
||||
`dom_name` \| `ipanttrusteddomainname` | Name of the trusted domain. Can only be used when `ipaapi_context: server`. | no
|
||||
|
||||
88
README-passkeyconfig.md
Normal file
88
README-passkeyconfig.md
Normal file
@@ -0,0 +1,88 @@
|
||||
Passkeyconfig module
|
||||
============
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The passkeyconfig module allows to manage FreeIPA passkey configuration settings.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Passkeyconfig management
|
||||
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipapasskeyconfig module.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.15+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
|
||||
By default, user verification for passkey authentication is turned on (`true`). Example playbook to ensure that the requirement for user verification for passkey authentication is turned off:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA passkeyconfig.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure require_user_verification is false
|
||||
ipapasskeyconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
require_user_verification: false
|
||||
```
|
||||
|
||||
|
||||
Example playbook to get current passkeyconfig:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to get IPA passkeyconfig.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Retrieve current passkey configuration
|
||||
ipapasskeyconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
---------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`ipaapi_context` | The context in which the module will execute. Executing in a server context is preferred. If not provided context will be determined by the execution environment. Valid values are `server` and `client`. | no
|
||||
`ipaapi_ldap_cache` | Use LDAP cache for IPA connection. The bool setting defaults to true. (bool) | no
|
||||
`require_user_verification` \| `iparequireuserverification` | Require user verification for passkey authentication. (bool) | no
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Rafael Guterres Jeffman
|
||||
@@ -230,6 +230,8 @@ Example playbook to ensure that different members are not associated with a role
|
||||
- User Administrators
|
||||
service:
|
||||
- service01
|
||||
sysaccount:
|
||||
- my-app
|
||||
action: member
|
||||
state: absent
|
||||
```
|
||||
@@ -253,7 +255,8 @@ Variable | Description | Required
|
||||
`host` | List of hosts to be assigned or not assigned to the role. | no
|
||||
`hostgroup` | List of hostgroups to be assigned or not assigned to the role. | no
|
||||
`service` | List of services to be assigned or not assigned to the role. | no
|
||||
`action` | Work on role or member level. It can be on of `member` or `role` and defaults to `role`. | no
|
||||
`sysaccount` | List of sysaccounts to be assigned or not assigned to the role. | no
|
||||
`action` | Work on role or member level. It can be one of `member` or `role` and defaults to `role`. | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. | no
|
||||
|
||||
|
||||
@@ -261,3 +264,4 @@ Authors
|
||||
=======
|
||||
|
||||
Rafael Jeffman
|
||||
Thomas Woerner
|
||||
|
||||
@@ -361,7 +361,7 @@ Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`certificate` \| `usercertificate` | Base-64 encoded service certificate. | no
|
||||
`pac_type` \| `ipakrbauthzdata` | Supported PAC type. It can be one of `MS-PAC`, `PAD`, or `NONE`. Use empty string to reset pac_type to the initial value. | no
|
||||
`auth_ind` \| `krbprincipalauthind` | Defines an allow list for Authentication Indicators. It can be any of `otp`, `radius`, `pkinit`, `hardened`, `idp` or `""`. An additional check ensures that only types can be used that are supported by the IPA version. Use empty string to reset auth_ind to the initial value. | no
|
||||
`auth_ind` \| `krbprincipalauthind` | Defines an allow list for Authentication Indicators. It can be any of `otp`, `radius`, `pkinit`, `hardened`, `idp`, `passkey` or `""`. An additional check ensures that only types can be used that are supported by the IPA version. Use empty string to reset auth_ind to the initial value. | no
|
||||
`requires_pre_auth` \| `ipakrbrequirespreauth` | Pre-authentication is required for the service. Default to true. (bool) | no
|
||||
`ok_as_delegate` \| `ipakrbokasdelegate` | Client credentials may be delegated to the service. Default to false. (bool) | no
|
||||
`ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client. Default to false. (bool) | no
|
||||
|
||||
196
README-sysaccount.md
Normal file
196
README-sysaccount.md
Normal file
@@ -0,0 +1,196 @@
|
||||
Sysaccount module
|
||||
============
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The sysaccount module allows to ensure presence and absence of system accounts.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Sysaccount management
|
||||
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipasysaccount module.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.15+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure sysaccount "my-app" is present with random password:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA sysaccount.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure sysaccount "my-app" is present with random password
|
||||
ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
random: true
|
||||
register: result
|
||||
|
||||
- name: Print generated random password
|
||||
debug:
|
||||
var: result.sysaccount.randompassword
|
||||
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure sysaccount "my-app" is present with given password:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA sysaccount.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure sysaccount "my-app" is present with given password
|
||||
ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
password: SomeAPPpassword
|
||||
```
|
||||
|
||||
|
||||
Example playbook to make sure sysaccount "my-app" is absent:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA sysaccount.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure sysaccount "my-app" is absent
|
||||
ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
state: absent
|
||||
```
|
||||
|
||||
Example playbook to ensure existing sysaccount my-app is privileged
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA sysaccount.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure existing sysaccount my-app is privileged
|
||||
ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
privileged: true
|
||||
```
|
||||
|
||||
Example playbook to ensure existing sysaccount my-app is not privileged
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA sysaccount.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure existing sysaccount my-app is not privileged
|
||||
ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
privileged: false
|
||||
```
|
||||
|
||||
Example playbook to ensure existing sysaccount my-app is disabled
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA sysaccount.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure existing sysaccount my-app is disabled
|
||||
ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
state: disabled
|
||||
```
|
||||
|
||||
Example playbook to ensure existing sysaccount my-app is enabled
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to manage IPA sysaccount.
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure existing sysaccount my-app is enabled
|
||||
ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
state: enabled
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
---------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`ipaapi_context` | The context in which the module will execute. Executing in a server context is preferred. If not provided context will be determined by the execution environment. Valid values are `server` and `client`. | no
|
||||
`ipaapi_ldap_cache` | Use LDAP cache for IPA connection. The bool setting defaults to true. (bool) | no
|
||||
`name` \| `login` | The list of sysaccount name strings - internally uid. (list of strings) | yes
|
||||
`description` | A description for the sysaccount. (string) | no
|
||||
`privileged` | Allow password updates without reset. This flag is not replicated. It is needed to set privileged on all servers, where it is needed. (bool) | no
|
||||
`random` | Generate a random user password. (bool) | no
|
||||
`password` \| `userpassword` | Set the password. (string) | no
|
||||
`update_password` | Set password for a sysaccount in present state only on creation or always. It can be one of `always` or `on_create` and defaults to `always`. | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent`, 'enabled', 'disabled', default: `present`. | no
|
||||
|
||||
|
||||
Return Values
|
||||
=============
|
||||
|
||||
There are only return values if a random passwords has been generated.
|
||||
|
||||
Variable | Description | Returned When
|
||||
-------- | ----------- | -------------
|
||||
`sysaccount` | Sysaccount dict (dict) <br>Options: | Always
|
||||
| `randompassword` - The generated random password | If random is yes and sysaccount did not exist or update_password is yes
|
||||
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Thomas Woerner
|
||||
@@ -452,7 +452,7 @@ Variable | Description | Required
|
||||
`manager` | List of manager user names. | no
|
||||
`carlicense` | List of car licenses. | no
|
||||
`sshpubkey` \| `ipasshpubkey` | List of SSH public keys. | no
|
||||
`userauthtype` \| `ipauserauthtype` | List of supported user authentication types. Choices: `password`, `radius`, `otp`, `pkinit`, `hardened`, `idp` and `""`. An additional check ensures that only types can be used that are supported by the IPA version. Use empty string to reset userauthtype to the initial value. | no
|
||||
`userauthtype` \| `ipauserauthtype` | List of supported user authentication types. Choices: `password`, `radius`, `otp`, `pkinit`, `hardened`, `idp`, `passkey` and `""`. An additional check ensures that only types can be used that are supported by the IPA version. Use empty string to reset userauthtype to the initial value. | no
|
||||
`userclass` | User category. (semantics placed on this attribute are for local interpretation). | no
|
||||
`radius` | RADIUS proxy configuration | no
|
||||
`radiususer` | RADIUS proxy username | no
|
||||
|
||||
@@ -38,6 +38,7 @@ Features
|
||||
* Modules for idview management
|
||||
* Modules for location management
|
||||
* Modules for netgroup management
|
||||
* Modules for passkeyconfig management
|
||||
* Modules for permission management
|
||||
* Modules for privilege management
|
||||
* Modules for pwpolicy management
|
||||
@@ -50,6 +51,7 @@ Features
|
||||
* Modules for sudocmd management
|
||||
* Modules for sudocmdgroup management
|
||||
* Modules for sudorule management
|
||||
* Modules for sysaccount management
|
||||
* Modules for topology management
|
||||
* Modules for trust management
|
||||
* Modules for user management
|
||||
@@ -66,7 +68,7 @@ Supported Distributions
|
||||
-----------------------
|
||||
|
||||
* RHEL/CentOS 7.4+
|
||||
* Fedora 26+
|
||||
* Fedora 40+
|
||||
* Ubuntu
|
||||
* Debian 10+ (ipaclient only, no server or replica!)
|
||||
|
||||
@@ -125,7 +127,7 @@ ansible-freeipa/plugins/module_utils to ~/.ansible/plugins/
|
||||
|
||||
**RPM package**
|
||||
|
||||
There are RPM packages available for Fedora 29+. These are installing the roles and modules into the global Ansible directories for `roles`, `plugins/modules` and `plugins/module_utils` in the `/usr/share/ansible` directory. Therefore is it possible to use the roles and modules without adapting the names like it is done in the example playbooks.
|
||||
There are RPM packages available for Fedora. These are installing the roles and modules into the global Ansible directories for `roles`, `plugins/modules` and `plugins/module_utils` in the `/usr/share/ansible` directory. Therefore is it possible to use the roles and modules without adapting the names like it is done in the example playbooks.
|
||||
|
||||
**Ansible Galaxy**
|
||||
|
||||
@@ -453,6 +455,7 @@ Modules in plugin/modules
|
||||
* [idview](README-idview.md)
|
||||
* [ipalocation](README-location.md)
|
||||
* [ipanetgroup](README-netgroup.md)
|
||||
* [ipapasskeyconfig](README-passkeyconfig.md)
|
||||
* [ipapermission](README-permission.md)
|
||||
* [ipaprivilege](README-privilege.md)
|
||||
* [ipapwpolicy](README-pwpolicy.md)
|
||||
@@ -465,6 +468,7 @@ Modules in plugin/modules
|
||||
* [ipasudocmd](README-sudocmd.md)
|
||||
* [ipasudocmdgroup](README-sudocmdgroup.md)
|
||||
* [ipasudorule](README-sudorule.md)
|
||||
* [ipasysaccount](README-sysaccount.md)
|
||||
* [ipatopologysegment](README-topology.md)
|
||||
* [ipatopologysuffix](README-topology.md)
|
||||
* [ipatrust](README-trust.md)
|
||||
|
||||
@@ -3,13 +3,13 @@ trigger:
|
||||
- master
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-20.04'
|
||||
vmImage: 'ubuntu-24.04'
|
||||
|
||||
variables:
|
||||
ansible_version: "-core >=2.16,<2.17"
|
||||
ansible_version: "-core >=2.18,<2.19"
|
||||
ansible_latest: "-core"
|
||||
ansible_minimum: "-core <2.16"
|
||||
distros: "fedora-latest,c9s,fedora-rawhide"
|
||||
distros: "fedora-latest,c9s,c10s,fedora-rawhide"
|
||||
|
||||
stages:
|
||||
|
||||
@@ -36,7 +36,7 @@ stages:
|
||||
# Supported distros
|
||||
|
||||
- ${{ each distro in split(variables.distros, ',') }}:
|
||||
- stage: ${{ replace(distro, '-', '_') }}_ansible_2_16
|
||||
- stage: ${{ replace(distro, '-', '_') }}_ansible_2_18
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/group_tests.yml
|
||||
@@ -49,7 +49,7 @@ stages:
|
||||
|
||||
# Galaxy on Fedora
|
||||
|
||||
- stage: galaxy_fedora_latest_ansible_2_16
|
||||
- stage: galaxy_fedora_latest_ansible_2_18
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/group_tests.yml
|
||||
|
||||
@@ -12,7 +12,7 @@ trigger: none
|
||||
pool:
|
||||
vmImage: 'ubuntu-24.04'
|
||||
|
||||
variables: { distros: "fedora-latest,fedora-rawhide,c9s" }
|
||||
variables: { distros: "fedora-latest,fedora-rawhide,c9s,c10s" }
|
||||
|
||||
stages:
|
||||
|
||||
|
||||
@@ -10,15 +10,26 @@ schedules:
|
||||
trigger: none
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-20.04'
|
||||
vmImage: 'ubuntu-24.04'
|
||||
|
||||
parameters:
|
||||
# Not really a parameter, but variables cannot be arrays or dicts
|
||||
# This maps the distro LATEST version to the avaiable ansible-core
|
||||
# version of the latest released compose.
|
||||
- name: "distro_ansible_map"
|
||||
type: object
|
||||
default:
|
||||
- { distro: "c8s", ansible_version: "<2.17", version_name: "2.16" }
|
||||
# c9s should use 2.14, but this version has an invalid certificate
|
||||
# and so is unsuable against ansible-galaxy.
|
||||
- { distro: "c9s", ansible_version: "<2.17", version_name: "2.16" }
|
||||
- { distro: "c10s", ansible_version: "<2.17", version_name: "2.16" }
|
||||
|
||||
variables:
|
||||
# We need to have two sets, as c8s is not supported by all ansible versions
|
||||
recent_distros: "fedora-latest,fedora-rawhide,c9s"
|
||||
distros: "fedora-latest,fedora-rawhide,c9s,c8s"
|
||||
distros: "fedora-latest,c10s,c9s,fedora-rawhide"
|
||||
ansible_version: "-core >=2.18,<2.19"
|
||||
ansible_latest: "-core"
|
||||
ansible_minimum: "-core <2.16"
|
||||
ansible_version: "-core >=2.16,<2.17"
|
||||
|
||||
stages:
|
||||
|
||||
@@ -38,7 +49,7 @@ stages:
|
||||
|
||||
# Latest ansible
|
||||
|
||||
- ${{ each distro in split(variables.recent_distros, ',') }}:
|
||||
- ${{ each distro in split(variables.distros, ',') }}:
|
||||
- stage: ${{ replace(distro, '-', '_') }}_ansible_latest
|
||||
dependsOn: []
|
||||
jobs:
|
||||
@@ -50,30 +61,44 @@ stages:
|
||||
skip_git_test: true
|
||||
test_galaxy: false
|
||||
|
||||
# Selected ansible-core version
|
||||
# Galaxy with Latest ansible
|
||||
|
||||
- ${{ each distro in split(variables.distros, ',') }}:
|
||||
- stage: ${{ replace(distro, '-', '_') }}_ansible_2_16
|
||||
- stage: galaxy_${{ replace(distro, '-', '_') }}_ansible_latest
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/group_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
distro: ${{ distro }}
|
||||
ansible_version: ${{ variables.ansible_version }}
|
||||
ansible_version: ${{ variables.ansible_latest }}
|
||||
skip_git_test: true
|
||||
test_galaxy: true
|
||||
|
||||
# Test with pinned ansible version for the distro
|
||||
|
||||
- ${{ each config in parameters.distro_ansible_map }}:
|
||||
- stage: ${{ config.distro }}_distro_ansible_${{ replace(config.version_name, '.', '_') }}
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/group_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
distro: ${{ config.distro }}
|
||||
ansible_version: -core${{ config.ansible_version }}
|
||||
skip_git_test: true
|
||||
test_galaxy: false
|
||||
|
||||
# Galaxy collection with selected ansible-core version
|
||||
# Test Galaxy collection with pinned ansible version for the distro
|
||||
|
||||
- ${{ each distro in split(variables.distros, ',') }}:
|
||||
- stage: galaxy_${{ replace(distro, '-', '_') }}_asible_2_16
|
||||
- ${{ each config in parameters.distro_ansible_map }}:
|
||||
- stage: galaxy_${{ config.distro }}_distro_ansible_${{ replace(config.version_name, '.', '_') }}
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/group_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
distro: ${{ distro }}
|
||||
ansible_version: ${{ variables.ansible_version }}
|
||||
distro: ${{ config.distro }}
|
||||
ansible_version: -core${{ config.ansible_version }}
|
||||
skip_git_test: true
|
||||
test_galaxy: true
|
||||
|
||||
@@ -3,18 +3,31 @@ trigger:
|
||||
- master
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-20.04'
|
||||
vmImage: 'ubuntu-24.04'
|
||||
|
||||
parameters:
|
||||
# Not really a parameter, but variables cannot be arrays or dicts
|
||||
# This maps the distro LATEST version to the avaiable ansible-core
|
||||
# version of the latest released compose.
|
||||
- name: "distro_ansible_map"
|
||||
type: object
|
||||
default:
|
||||
- { distro: "c8s", ansible_version: "<2.17", version_name: "2.16" }
|
||||
# c9s should use 2.14, but this version has an invalid certificate
|
||||
# and so is unsuable against ansible-galaxy.
|
||||
- { distro: "c9s", ansible_version: "<2.17", version_name: "2.16" }
|
||||
- { distro: "c10s", ansible_version: "<2.17", version_name: "2.16" }
|
||||
|
||||
variables:
|
||||
distros: "fedora-latest,c9s,c8s,fedora-rawhide"
|
||||
ansible_version: "-core >=2.15,<2.16"
|
||||
distros: "fedora-latest,c10s,c9s,fedora-rawhide"
|
||||
ansible_version: "-core >=2.18,<2.19"
|
||||
|
||||
stages:
|
||||
|
||||
# Test with repository in all distros
|
||||
# Test with repository in all "current" distros
|
||||
|
||||
- ${{ each distro in split(variables.distros, ',') }}:
|
||||
- stage: ${{ replace(distro, '-', '_') }}_ansible_2_16
|
||||
- stage: ${{ replace(distro, '-', '_') }}_ansible_2_18
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/run_tests.yml
|
||||
@@ -27,7 +40,7 @@ stages:
|
||||
|
||||
# Galaxy on Fedora
|
||||
|
||||
- stage: galaxy_fedora_latest_ansible_2_16
|
||||
- stage: galaxy_fedora_latest_ansible_2_18
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/run_tests.yml
|
||||
@@ -37,3 +50,18 @@ stages:
|
||||
ansible_version: ${{ variables.ansible_version }}
|
||||
skip_git_test: false
|
||||
test_galaxy: true
|
||||
|
||||
|
||||
# Test with pinned ansible version for the distro
|
||||
|
||||
- ${{ each config in parameters.distro_ansible_map }}:
|
||||
- stage: ${{ config.distro }}_distro_ansible_${{ replace(config.version_name, '.', '_') }}
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/run_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
distro: ${{ config.distro }}
|
||||
ansible_version: -core${{ config.ansible_version }}
|
||||
skip_git_test: false
|
||||
test_galaxy: false
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
- script: ansible-galaxy collection install containers.podman
|
||||
displayName: Install Ansible Galaxy collections
|
||||
|
||||
- script: infra/image/build.sh -p -s ${{ parameters.distro }}
|
||||
- script: infra/image/build.sh -s ${{ parameters.distro }}
|
||||
displayName: Build ${{ parameters.distro }} base image
|
||||
env:
|
||||
ANSIBLE_ROLES_PATH: "${PWD}/roles"
|
||||
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
|
||||
- script: |
|
||||
git fetch --unshallow
|
||||
utils/build-galaxy-release.sh -i
|
||||
utils/build-collection.sh -i rpm
|
||||
retryCountOnTaskFailure: 5
|
||||
displayName: Build Galaxy release
|
||||
condition: ${{ parameters.test_galaxy }}
|
||||
|
||||
21
infra/azure/templates/variables_c10s.yaml
Normal file
21
infra/azure/templates/variables_c10s.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
#
|
||||
# Variables must be defined as comma separated lists.
|
||||
# For easier management of items to enable/disable,
|
||||
# use one test/module on each line, followed by a comma.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# ipa_disabled_modules: >-
|
||||
# dnsconfig,
|
||||
# group,
|
||||
# hostgroup
|
||||
#
|
||||
# If no variables are set, set "empty: true" as at least
|
||||
# one item is needed in the set.
|
||||
---
|
||||
variables:
|
||||
empty: true
|
||||
# ipa_enabled_modules: >-
|
||||
# ipa_enabled_tests: >-
|
||||
# ipa_disabled_modules: >-
|
||||
# ipa_disabled_tests: >-
|
||||
@@ -15,7 +15,7 @@ valid_distro() {
|
||||
usage() {
|
||||
local prog="${0##*/}"
|
||||
cat << EOF
|
||||
usage: ${prog} [-h] [-p] [-n HOSTNAME] [-s] distro
|
||||
usage: ${prog} [-h] [-n HOSTNAME] [-s] distro
|
||||
${prog} build a container image to test ansible-freeipa.
|
||||
EOF
|
||||
}
|
||||
@@ -41,14 +41,14 @@ cpus="2"
|
||||
memory="3g"
|
||||
quayname="quay.io/ansible-freeipa/upstream-tests"
|
||||
deploy_server="N"
|
||||
privileged=""
|
||||
deploy_capabilities="SYS_ADMIN,SYSLOG"
|
||||
capabilities=""
|
||||
|
||||
while getopts ":hn:ps" option
|
||||
while getopts ":hn:s" option
|
||||
do
|
||||
case "${option}" in
|
||||
h) help && exit 0 ;;
|
||||
n) hostname="${OPTARG}" ;;
|
||||
p) privileged="privileged" ;;
|
||||
s) deploy_server="Y" ;;
|
||||
*) die -u "Invalid option: ${option}" ;;
|
||||
esac
|
||||
@@ -66,6 +66,8 @@ container_check
|
||||
|
||||
if [ "${deploy_server}" == "Y" ]
|
||||
then
|
||||
capabilities="${deploy_capabilities}"
|
||||
|
||||
[ -n "$(command -v "ansible-playbook")" ] || die "ansible-playbook is required to install FreeIPA."
|
||||
|
||||
deploy_playbook="${TOPDIR}/playbooks/install-server.yml"
|
||||
@@ -89,7 +91,7 @@ container_create "${name}" "${tag}" \
|
||||
"hostname=${hostname}" \
|
||||
"memory=${memory}" \
|
||||
"cpus=${cpus}" \
|
||||
"${privileged}"
|
||||
"${capabilities:+capabilities=$capabilities}"
|
||||
container_commit "${name}" "${quayname}:${tag}"
|
||||
|
||||
if [ "${deploy_server}" == "Y" ]
|
||||
@@ -117,13 +119,6 @@ then
|
||||
deployed=true
|
||||
fi
|
||||
echo
|
||||
|
||||
if $deployed; then
|
||||
log info "= Enabling services ="
|
||||
container_exec "${name}" systemctl enable fixnet
|
||||
container_exec "${name}" systemctl enable fixipaip
|
||||
echo
|
||||
fi
|
||||
|
||||
container_stop "${name}"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM quay.io/centos/centos:stream10-development
|
||||
FROM quay.io/centos/centos:stream10
|
||||
ENV container=podman
|
||||
|
||||
RUN rm -fv /var/cache/dnf/metadata_lock.pid; \
|
||||
@@ -9,7 +9,8 @@ dnf --assumeyes install \
|
||||
bash \
|
||||
systemd \
|
||||
procps-ng \
|
||||
iproute; \
|
||||
iproute \
|
||||
hostname; \
|
||||
rm -rf /var/cache/dnf/;
|
||||
|
||||
RUN (cd /lib/systemd/system/; \
|
||||
@@ -30,6 +31,8 @@ COPY system-service/fixipaip.sh /root/
|
||||
COPY system-service/fixnet.service /etc/systemd/system/
|
||||
COPY system-service/fixipaip.service /etc/systemd/system/
|
||||
RUN chmod +x /root/fixnet.sh /root/fixipaip.sh
|
||||
RUN systemctl enable fixnet.service
|
||||
RUN systemctl enable fixipaip.service
|
||||
|
||||
STOPSIGNAL RTMIN+3
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ dnf --assumeyes install \
|
||||
bash \
|
||||
systemd \
|
||||
procps-ng \
|
||||
hostname \
|
||||
iproute; \
|
||||
dnf clean all; \
|
||||
rm -rf /var/cache/dnf/;
|
||||
@@ -34,6 +35,8 @@ COPY system-service/fixipaip.sh /root/
|
||||
COPY system-service/fixnet.service /etc/systemd/system/
|
||||
COPY system-service/fixipaip.service /etc/systemd/system/
|
||||
RUN chmod +x /root/fixnet.sh /root/fixipaip.sh
|
||||
RUN systemctl enable fixnet.service
|
||||
RUN systemctl enable fixipaip.service
|
||||
|
||||
STOPSIGNAL RTMIN+3
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ dnf --assumeyes install \
|
||||
bash \
|
||||
systemd \
|
||||
procps-ng \
|
||||
hostname \
|
||||
iproute; \
|
||||
rm -rf /var/cache/dnf/;
|
||||
|
||||
@@ -30,6 +31,8 @@ COPY system-service/fixipaip.sh /root/
|
||||
COPY system-service/fixnet.service /etc/systemd/system/
|
||||
COPY system-service/fixipaip.service /etc/systemd/system/
|
||||
RUN chmod +x /root/fixnet.sh /root/fixipaip.sh
|
||||
RUN systemctl enable fixnet.service
|
||||
RUN systemctl enable fixipaip.service
|
||||
|
||||
STOPSIGNAL RTMIN+3
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ dnf --assumeyes install \
|
||||
bash \
|
||||
systemd \
|
||||
procps-ng \
|
||||
hostname \
|
||||
iproute; \
|
||||
dnf clean all; \
|
||||
rm -rf /var/cache/dnf/;
|
||||
@@ -33,6 +34,8 @@ COPY system-service/fixipaip.sh /root/
|
||||
COPY system-service/fixnet.service /etc/systemd/system/
|
||||
COPY system-service/fixipaip.service /etc/systemd/system/
|
||||
RUN chmod +x /root/fixnet.sh /root/fixipaip.sh
|
||||
RUN systemctl enable fixnet.service
|
||||
RUN systemctl enable fixipaip.service
|
||||
|
||||
STOPSIGNAL RTMIN+3
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ dnf --assumeyes install \
|
||||
bash \
|
||||
systemd \
|
||||
procps-ng \
|
||||
hostname \
|
||||
iproute; \
|
||||
dnf clean all; \
|
||||
rm -rf /var/cache/dnf/;
|
||||
@@ -33,6 +34,8 @@ COPY system-service/fixipaip.sh /root/
|
||||
COPY system-service/fixnet.service /etc/systemd/system/
|
||||
COPY system-service/fixipaip.service /etc/systemd/system/
|
||||
RUN chmod +x /root/fixnet.sh /root/fixipaip.sh
|
||||
RUN systemctl enable fixnet.service
|
||||
RUN systemctl enable fixipaip.service
|
||||
|
||||
STOPSIGNAL RTMIN+3
|
||||
|
||||
|
||||
@@ -4,13 +4,20 @@
|
||||
SCRIPTDIR="$(dirname -- "$(readlink -f "${BASH_SOURCE[0]}")")"
|
||||
TOPDIR="$(readlink -f "${SCRIPTDIR}/../..")"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "${SCRIPTDIR}/shdefaults"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "${TOPDIR}/utils/shfun"
|
||||
|
||||
container_create() {
|
||||
local name=${1}
|
||||
local image=${2}
|
||||
shift 2
|
||||
declare -a extra_opts=()
|
||||
declare -a extra_opts
|
||||
readarray -t extra_opts < \
|
||||
<(sed -e "s/-/--cap-drop=/g" -e "s/+/--cap-add=/g" \
|
||||
<<< "$(printf '%s\n' "${CAP_DEFAULTS[@]}")")
|
||||
for opt in "$@"
|
||||
do
|
||||
[ -z "${opt}" ] && continue
|
||||
@@ -18,7 +25,8 @@ container_create() {
|
||||
hostname=*) extra_opts+=("--${opt}") ;;
|
||||
cpus=*) extra_opts+=("--${opt}") ;;
|
||||
memory=*) extra_opts+=("--${opt}") ;;
|
||||
privileged) extra_opts+=("--${opt}") ;;
|
||||
capabilities=*) extra_opts+=("--cap-add=${opt##*=}") ;;
|
||||
volume=*) extra_opts+=("--volume=${opt##*=}") ;;
|
||||
*) log error "container_create: Invalid option: ${opt}" ;;
|
||||
esac
|
||||
done
|
||||
@@ -47,6 +55,19 @@ container_start() {
|
||||
|
||||
log info "= Starting ${name} ="
|
||||
podman start "${name}"
|
||||
# Add host entry to /etc/hosts
|
||||
ip=$(podman inspect "${name}" --format "{{.NetworkSettings.IPAddress}}")
|
||||
hostname=$(podman inspect "${name}" --format "{{.Config.Hostname}}")
|
||||
if [ -n "${ip}" ] && [ -n "${hostname}" ]; then
|
||||
cmd=$(cat <<EOF
|
||||
sed -i -E "/\s+${hostname}(\s|$)/d" /etc/hosts
|
||||
echo -e "$ip\t${hostname} ${hostname%%.*}" >> /etc/hosts
|
||||
EOF
|
||||
)
|
||||
podman exec "${name}" bash -c "$cmd"
|
||||
fi
|
||||
# Ensure /etc/shadow is readable
|
||||
podman exec "${name}" bash -c "chmod u+r /etc/shadow"
|
||||
echo
|
||||
}
|
||||
|
||||
@@ -175,3 +196,54 @@ container_image_list() {
|
||||
container_check() {
|
||||
[ -n "$(command -v "podman")" ] || die "podman is required."
|
||||
}
|
||||
|
||||
container_copy() {
|
||||
local name="${1}"
|
||||
local source="${2}"
|
||||
local destination="${3}"
|
||||
|
||||
log info "= Copying ${source} to ${name}:${destination} ="
|
||||
podman cp "${source}" "${name}:${destination}"
|
||||
echo
|
||||
}
|
||||
|
||||
container_fetch() {
|
||||
local name="${1}"
|
||||
local source="${2}"
|
||||
local destination="${3}"
|
||||
|
||||
log info "= Copying ${name}:${source} to ${destination} ="
|
||||
podman cp "${name}:${source}" "${destination}"
|
||||
echo
|
||||
}
|
||||
|
||||
container_tee() {
|
||||
local name=${1}
|
||||
local destination=${2}
|
||||
tmpfile=$(mktemp /tmp/container-temp.XXXXXX)
|
||||
|
||||
log info "= Creating ${name}:${destination} from stdin ="
|
||||
cat - > "${tmpfile}"
|
||||
podman cp "${tmpfile}" "${name}:${destination}"
|
||||
rm "${tmpfile}"
|
||||
echo
|
||||
}
|
||||
|
||||
container_save() {
|
||||
local name=${1}
|
||||
|
||||
archive="${name}.tar"
|
||||
log info "= Saving ${name} to ${archive} ="
|
||||
# podman is not able to overwrite the archive
|
||||
[ -f "${archive}" ] && rm "${archive}"
|
||||
podman save -o "${archive}" "${name}"
|
||||
echo
|
||||
}
|
||||
|
||||
container_load() {
|
||||
local name=${1}
|
||||
|
||||
image_name=$(podman load -q -i "${name}" | sed -e "s/^Loaded image: //")
|
||||
image=$(podman image list -q "${image_name}")
|
||||
echo "$image"
|
||||
}
|
||||
|
||||
11
infra/image/shdefaults
Normal file
11
infra/image/shdefaults
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash -eu
|
||||
# This file is meant to be source'd by other scripts
|
||||
|
||||
# Set default capabilities options for freeipa containers.
|
||||
# Use +CAP to add the capability and -CAP to drop the capability.
|
||||
CAP_DEFAULTS=(
|
||||
"+DAC_READ_SEARCH" # Required for SSSD
|
||||
"+SYS_PTRACE" # Required for debugging
|
||||
"+SYS_ADMIN" # Required to make dbus-brokder for systemd 258 work
|
||||
# Should be "+AUDIT_WRITE", "+SETUID", "+SETGID"
|
||||
)
|
||||
@@ -1,6 +1,7 @@
|
||||
[Unit]
|
||||
Description=Fix IPA server IP in IPA Server
|
||||
After=ipa.service
|
||||
PartOf=ipa.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
@@ -9,4 +10,4 @@ StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
WantedBy=ipa.service
|
||||
|
||||
@@ -50,9 +50,9 @@ if [ -z "${FORWARDER}" ] || [ "${FORWARDER}" == "127.0.0.1" ]; then
|
||||
fi
|
||||
|
||||
echo "Fix IPA:"
|
||||
echo " HOSTNAME: '${HOSTNAME}'"
|
||||
echo " IP: '${IP}'"
|
||||
echo " PTR: '${PTR}'"
|
||||
echo " HOSTNAME: '${HOSTNAME}'"
|
||||
echo " IP: '${IP}'"
|
||||
echo " PTR: '${PTR}'"
|
||||
echo " FORWARDER: '${FORWARDER}'"
|
||||
|
||||
ZONES=$(ipa -e in_server=true dnszone-find --name-from-ip="${HOSTNAME}." \
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
[Unit]
|
||||
Description=Fix server IP in IPA Server
|
||||
Wants=network.target
|
||||
After=network.target
|
||||
Before=ipa.service
|
||||
Description=Fix /etc/hosts and with local DNS also /etc/resolv.conf
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
@@ -11,4 +8,4 @@ StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=ipa.service
|
||||
WantedBy=container-ipa.target
|
||||
|
||||
@@ -39,26 +39,35 @@ if [ -z "${IP}" ] || ! valid_ipv4 "${IP}" ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DOMAIN=${HOSTNAME#*.}
|
||||
|
||||
echo "Fix NET:"
|
||||
echo " HOSTNAME: '${HOSTNAME}'"
|
||||
echo " IP: '${IP}'"
|
||||
echo " DOMAIN: '${DOMAIN}'"
|
||||
echo " IP: '${IP}'"
|
||||
echo
|
||||
|
||||
if grep -qE "^[^(#\s*)][0-9\.]+\s$HOSTNAME(\s|$)" /etc/hosts
|
||||
then
|
||||
sed -i.bak -e "s/.*${HOSTNAME}/${IP}\t${HOSTNAME}/" /etc/hosts
|
||||
else
|
||||
echo -e "$IP\t${HOSTNAME} ${HOSTNAME%%.*}" >> /etc/hosts
|
||||
fi
|
||||
# /etc/hosts
|
||||
|
||||
cp -a /etc/resolv.conf /etc/resolv.conf.fixnet
|
||||
cat > /etc/resolv.conf <<EOF
|
||||
search ${HOSTNAME#*.}
|
||||
nameserver 127.0.0.1
|
||||
EOF
|
||||
sed -i -E "/\s+${HOSTNAME}(\s|$)/d" /etc/hosts
|
||||
echo -e "$IP\t${HOSTNAME} ${HOSTNAME%%.*}" >> /etc/hosts
|
||||
|
||||
echo "/etc/hosts:"
|
||||
cat "/etc/hosts"
|
||||
|
||||
# /etc/resolv.conf
|
||||
|
||||
# If bind is not installed, exit
|
||||
[ -f "/etc/named.conf" ] || exit 0
|
||||
# If dyndb is not enabled for bind, exit
|
||||
grep -q '^dyndb "ipa"' "/etc/named.conf" || exit 0
|
||||
|
||||
cp -a /etc/resolv.conf /etc/resolv.conf.fixnet
|
||||
cat > /etc/resolv.conf <<EOF
|
||||
search ${DOMAIN}
|
||||
nameserver 127.0.0.1
|
||||
EOF
|
||||
|
||||
echo
|
||||
echo "/etc/resolv.conf:"
|
||||
cat "/etc/resolv.conf"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
- ip_address: 8.8.8.8
|
||||
- ip_address: 8.8.4.4
|
||||
port: 52
|
||||
#serial: 1234
|
||||
# serial: 1234
|
||||
refresh: 3600
|
||||
retry: 900
|
||||
expire: 1209600
|
||||
|
||||
10
playbooks/passkeyconfig/passkeyconfig-present.yml
Normal file
10
playbooks/passkeyconfig/passkeyconfig-present.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: Passkeyconfig example
|
||||
hosts: ipaserver
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: Set passkeyconfig require_user_verification to false
|
||||
ipapasskeyconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
require_user_verification: false
|
||||
14
playbooks/passkeyconfig/passkeyconfig-retrieve.yml
Normal file
14
playbooks/passkeyconfig/passkeyconfig-retrieve.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
- name: Passkeyconfig get current configuration example
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Get current passkey configuration
|
||||
ipapasskeyconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
register: result
|
||||
|
||||
- name: Display current passkey configuration
|
||||
ansible.builtin.debug:
|
||||
var: result.passkeyconfig
|
||||
11
playbooks/sysaccount/sysaccount-absent.yml
Normal file
11
playbooks/sysaccount/sysaccount-absent.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Sysaccount example
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure sysaccount my-app is absent
|
||||
ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
state: absent
|
||||
11
playbooks/sysaccount/sysaccount-disabled.yml
Normal file
11
playbooks/sysaccount/sysaccount-disabled.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Sysaccount example
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure sysaccount my-app is disabled
|
||||
ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
state: disabled
|
||||
11
playbooks/sysaccount/sysaccount-enabled.yml
Normal file
11
playbooks/sysaccount/sysaccount-enabled.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Sysaccount example
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure sysaccount my-app is enabled
|
||||
ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
state: enabled
|
||||
11
playbooks/sysaccount/sysaccount-present.yml
Normal file
11
playbooks/sysaccount/sysaccount-present.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Sysaccount example
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure sysaccount my-app is present with random password
|
||||
ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
random: true
|
||||
11
playbooks/sysaccount/sysaccount-privileged.yml
Normal file
11
playbooks/sysaccount/sysaccount-privileged.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Sysaccount example
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure sysaccount my-app is privileged
|
||||
ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
privileged: true
|
||||
11
playbooks/sysaccount/sysaccount-unprivileged.yml
Normal file
11
playbooks/sysaccount/sysaccount-unprivileged.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Sysaccount example
|
||||
hosts: ipaserver
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Ensure sysaccount my-app is not privileged
|
||||
ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
privileged: false
|
||||
@@ -11,7 +11,7 @@
|
||||
dest: "{{ ansible_facts['env'].HOME }}/password.txt"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: 0600
|
||||
mode: "0600"
|
||||
- name: Ensure symmetric vault exists with password from file.
|
||||
ipavault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
dest: "{{ ansible_facts['env'].HOME }}/public.pem"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: 0600
|
||||
mode: "0600"
|
||||
- name: Ensure asymmetric vault exists with public key from file.
|
||||
ipavault:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
|
||||
@@ -107,7 +107,7 @@ from ansible.plugins.inventory import BaseInventoryPlugin
|
||||
from ansible.module_utils.six.moves.urllib.parse import quote
|
||||
|
||||
|
||||
class InventoryModule(BaseInventoryPlugin):
|
||||
class InventoryModule(BaseInventoryPlugin): # pylint: disable=R0901
|
||||
|
||||
NAME = 'freeipa'
|
||||
|
||||
|
||||
@@ -589,6 +589,20 @@ def ensure_fqdn(name, domain):
|
||||
return name
|
||||
|
||||
|
||||
def convert_to_sid(items):
|
||||
"""Convert all items to SID, if possible."""
|
||||
def get_sid(data):
|
||||
try:
|
||||
return get_trusted_domain_object_sid(data)
|
||||
except ipalib_errors.NotFound:
|
||||
return data
|
||||
if items is None:
|
||||
return None
|
||||
if not isinstance(items, (list, tuple)):
|
||||
items = [items]
|
||||
return [get_sid(item) for item in items]
|
||||
|
||||
|
||||
def api_get_realm():
|
||||
return api.env.realm
|
||||
|
||||
@@ -903,6 +917,13 @@ def get_trusted_domain_sid_from_name(dom_name):
|
||||
return unicode(sid) if sid is not None else None
|
||||
|
||||
|
||||
def get_trusted_domain_object_sid(object_name):
|
||||
"""Given an object name, returns de object SID."""
|
||||
domain_validator = __get_domain_validator()
|
||||
sid = domain_validator.get_trusted_domain_object_sid(object_name)
|
||||
return unicode(sid) if sid is not None else None
|
||||
|
||||
|
||||
class IPAParamMapping(Mapping):
|
||||
"""
|
||||
Provides IPA API mapping to playbook parameters or computed values.
|
||||
|
||||
@@ -106,7 +106,7 @@ RETURN = '''
|
||||
'''
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import (
|
||||
IPAAnsibleModule, compare_args_ipa
|
||||
IPAAnsibleModule, compare_args_ipa, ipalib_errors
|
||||
)
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ class AutomountMap(IPAAnsibleModule):
|
||||
location,
|
||||
{"automountmapname": name, "all": True}
|
||||
)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
return None
|
||||
return response["result"]
|
||||
|
||||
@@ -132,7 +132,7 @@ class AutomountMap(IPAAnsibleModule):
|
||||
"""Check if 'name' is an indirect map for 'parentmap'."""
|
||||
try:
|
||||
maps = self.ipa_command("automountmap_find", location, {})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
return []
|
||||
|
||||
result = []
|
||||
|
||||
@@ -487,6 +487,8 @@ def main():
|
||||
|
||||
# revoked
|
||||
reason = ansible_module.params_get("revocation_reason")
|
||||
if reason is not None:
|
||||
reason = get_revocation_reason(ansible_module, reason)
|
||||
|
||||
# general
|
||||
serial_number = ansible_module.params.get("serial_number")
|
||||
@@ -521,6 +523,9 @@ def main():
|
||||
invalid.append("revocation_reason")
|
||||
if state == "revoked":
|
||||
invalid.extend(["certificate_out", "chain"])
|
||||
# Reason 8 (revomeFromCRL) is the same as release hold
|
||||
if reason == 8:
|
||||
state = "released"
|
||||
elif state == "held":
|
||||
reason = 6 # certificateHold
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ options:
|
||||
type: list
|
||||
elements: str
|
||||
choices: ["password", "radius", "otp", "pkinit", "hardened", "idp",
|
||||
"disabled", ""]
|
||||
"passkey", "disabled", ""]
|
||||
aliases: ["ipauserauthtype"]
|
||||
ca_renewal_master_server:
|
||||
description: Renewal master for IPA certificate authority.
|
||||
@@ -356,7 +356,7 @@ def config_show(module):
|
||||
def get_netbios_name(module):
|
||||
try:
|
||||
_result = module.ipa_command_no_name("trustconfig_show", {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
return None
|
||||
return _result["result"]["ipantflatname"][0]
|
||||
|
||||
@@ -426,7 +426,7 @@ def main():
|
||||
user_auth_type=dict(type="list", elements="str", required=False,
|
||||
choices=["password", "radius", "otp",
|
||||
"pkinit", "hardened", "idp",
|
||||
"disabled", ""],
|
||||
"passkey", "disabled", ""],
|
||||
aliases=["ipauserauthtype"]),
|
||||
ca_renewal_master_server=dict(type="str", required=False),
|
||||
domain_resolution_order=dict(type="list", elements="str",
|
||||
|
||||
@@ -124,14 +124,14 @@ RETURN = """
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa
|
||||
IPAAnsibleModule, compare_args_ipa, ipalib_errors
|
||||
|
||||
|
||||
def find_delegation(module, name):
|
||||
"""Find if a delegation with the given name already exist."""
|
||||
try:
|
||||
_result = module.ipa_command("delegation_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if delegation name is not found.
|
||||
return None
|
||||
return _result["result"]
|
||||
|
||||
@@ -1454,11 +1454,13 @@ def define_commands_for_present_state(module, zone_name, entry, res_find):
|
||||
# Create reverse records for existing records
|
||||
for ipv in ['a', 'aaaa']:
|
||||
record = '%srecord' % ipv
|
||||
if record in args and ('%s_extra_create_reverse' % ipv) in args:
|
||||
if (
|
||||
record in args
|
||||
and args.pop('%s_extra_create_reverse' % ipv, False)
|
||||
):
|
||||
cmds = create_reverse_ip_record(
|
||||
module, zone_name, name, args[record])
|
||||
_commands.extend(cmds)
|
||||
del args['%s_extra_create_reverse' % ipv]
|
||||
for record, fields in _RECORD_PARTS.items():
|
||||
part_fields = [f for f in fields if f in args]
|
||||
if part_fields:
|
||||
@@ -1620,7 +1622,6 @@ def main():
|
||||
commands.extend(cmds)
|
||||
|
||||
# Execute commands
|
||||
|
||||
changed = ansible_module.execute_ipa_commands(
|
||||
commands, exception_handler=exception_handler)
|
||||
|
||||
|
||||
@@ -113,13 +113,14 @@ options:
|
||||
externalmember:
|
||||
description:
|
||||
- List of members of a trusted domain in DOM\\name or name@domain form.
|
||||
Requires "server" context.
|
||||
required: false
|
||||
type: list
|
||||
elements: str
|
||||
aliases: ["ipaexternalmember", "external_member"]
|
||||
idoverrideuser:
|
||||
description:
|
||||
- User ID overrides to add
|
||||
- User ID overrides to add. Requires "server" context.
|
||||
required: false
|
||||
type: list
|
||||
elements: str
|
||||
@@ -188,13 +189,14 @@ options:
|
||||
externalmember:
|
||||
description:
|
||||
- List of members of a trusted domain in DOM\\name or name@domain form.
|
||||
Requires "server" context.
|
||||
required: false
|
||||
type: list
|
||||
elements: str
|
||||
aliases: ["ipaexternalmember", "external_member"]
|
||||
idoverrideuser:
|
||||
description:
|
||||
- User ID overrides to add
|
||||
- User ID overrides to add. Requires "server" context.
|
||||
required: false
|
||||
type: list
|
||||
elements: str
|
||||
@@ -297,6 +299,7 @@ EXAMPLES = """
|
||||
posix: yes
|
||||
|
||||
# Create an external group and add members from a trust to it.
|
||||
# Module will fail if running under 'client' context.
|
||||
- ipagroup:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: extgroup
|
||||
@@ -327,7 +330,8 @@ RETURN = """
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \
|
||||
gen_add_list, gen_intersection_list, api_check_param
|
||||
gen_add_list, gen_intersection_list, api_check_param, \
|
||||
convert_to_sid
|
||||
from ansible.module_utils import six
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
@@ -562,21 +566,29 @@ def main():
|
||||
# The simple solution is to switch to client context for ensuring
|
||||
# several groups simply if the user was not explicitly asking for
|
||||
# the server context no matter if mixed types are used.
|
||||
context = None
|
||||
context = ansible_module.params_get("ipaapi_context")
|
||||
if state == "present" and groups is not None and len(groups) > 1 \
|
||||
and not FIX_6741_DEEPCOPY_OBJECTCLASSES:
|
||||
_context = ansible_module.params_get("ipaapi_context")
|
||||
if _context is None:
|
||||
if context is None:
|
||||
context = "client"
|
||||
ansible_module.debug(
|
||||
"Switching to client context due to an unfixed issue in "
|
||||
"your IPA version: https://pagure.io/freeipa/issue/9349")
|
||||
elif _context == "server":
|
||||
elif context == "server":
|
||||
ansible_module.fail_json(
|
||||
msg="Ensuring several groups with server context is not "
|
||||
"supported by your IPA version: "
|
||||
"https://pagure.io/freeipa/issue/9349")
|
||||
|
||||
if (
|
||||
(externalmember is not None
|
||||
or idoverrideuser is not None)
|
||||
and context == "client"
|
||||
):
|
||||
ansible_module.fail_json(
|
||||
msg="Cannot use externalmember in client context."
|
||||
)
|
||||
|
||||
# Use groups if names is None
|
||||
if groups is not None:
|
||||
names = groups
|
||||
@@ -676,6 +688,23 @@ def main():
|
||||
# Make sure group exists
|
||||
res_find = find_group(ansible_module, name)
|
||||
|
||||
# external members must de handled as SID
|
||||
externalmember = convert_to_sid(externalmember)
|
||||
|
||||
# idoverrides need to be compared through SID
|
||||
idoverrideuser_sid = convert_to_sid(idoverrideuser)
|
||||
res_idoverrideuser_sid = convert_to_sid(
|
||||
(res_find or {}).get("member_idoverrideuser", []))
|
||||
idoverride_set = dict(
|
||||
list(zip(idoverrideuser_sid or [], idoverrideuser or [])) +
|
||||
list(
|
||||
zip(
|
||||
res_idoverrideuser_sid or [],
|
||||
(res_find or {}).get("member_idoverrideuser", [])
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
user_add, user_del = [], []
|
||||
group_add, group_del = [], []
|
||||
service_add, service_del = [], []
|
||||
@@ -723,11 +752,12 @@ def main():
|
||||
res_find = {}
|
||||
|
||||
# if we just created/modified the group, update res_find
|
||||
res_find.setdefault("objectclass", [])
|
||||
classes = list(res_find.setdefault("objectclass", []))
|
||||
if external and not is_external_group(res_find):
|
||||
res_find["objectclass"].append("ipaexternalgroup")
|
||||
classes.append("ipaexternalgroup")
|
||||
if posix and not is_posix_group(res_find):
|
||||
res_find["objectclass"].append("posixgroup")
|
||||
classes.append("posixgroup")
|
||||
res_find["objectclass"] = classes
|
||||
|
||||
member_args = gen_member_args(
|
||||
user, group, service, externalmember, idoverrideuser
|
||||
@@ -746,13 +776,25 @@ def main():
|
||||
|
||||
(externalmember_add,
|
||||
externalmember_del) = gen_add_del_lists(
|
||||
externalmember, res_find.get("member_external"))
|
||||
externalmember, (
|
||||
list(res_find.get("member_external", []))
|
||||
+ list(res_find.get("ipaexternalmember", []))
|
||||
)
|
||||
)
|
||||
|
||||
# There are multiple ways to name an AD User, and any
|
||||
# can be used in idoverrides, so we create the add/del
|
||||
# lists based on SID, and then use the given user name
|
||||
# to the idoverride.
|
||||
(idoverrides_add,
|
||||
idoverrides_del) = gen_add_del_lists(
|
||||
idoverrideuser,
|
||||
res_find.get("member_idoverrideuser")
|
||||
)
|
||||
idoverrideuser_sid, res_idoverrideuser_sid)
|
||||
idoverrides_add = [
|
||||
idoverride_set[sid] for sid in set(idoverrides_add)
|
||||
]
|
||||
idoverrides_del = [
|
||||
idoverride_set[sid] for sid in set(idoverrides_del)
|
||||
]
|
||||
|
||||
membermanager_user_add, membermanager_user_del = \
|
||||
gen_add_del_lists(
|
||||
@@ -780,9 +822,16 @@ def main():
|
||||
service_add = gen_add_list(
|
||||
service, res_find.get("member_service"))
|
||||
externalmember_add = gen_add_list(
|
||||
externalmember, res_find.get("member_external"))
|
||||
externalmember, (
|
||||
list(res_find.get("member_external", []))
|
||||
+ list(res_find.get("ipaexternalmember", []))
|
||||
)
|
||||
)
|
||||
idoverrides_add = gen_add_list(
|
||||
idoverrideuser, res_find.get("member_idoverrideuser"))
|
||||
idoverrideuser_sid, res_idoverrideuser_sid)
|
||||
idoverrides_add = [
|
||||
idoverride_set[sid] for sid in set(idoverrides_add)
|
||||
]
|
||||
|
||||
membermanager_user_add = gen_add_list(
|
||||
membermanager_user,
|
||||
@@ -815,9 +864,16 @@ def main():
|
||||
service_del = gen_intersection_list(
|
||||
service, res_find.get("member_service"))
|
||||
externalmember_del = gen_intersection_list(
|
||||
externalmember, res_find.get("member_external"))
|
||||
externalmember, (
|
||||
list(res_find.get("member_external", []))
|
||||
+ list(res_find.get("ipaexternalmember", []))
|
||||
)
|
||||
)
|
||||
idoverrides_del = gen_intersection_list(
|
||||
idoverrideuser, res_find.get("member_idoverrideuser"))
|
||||
idoverrideuser_sid, res_idoverrideuser_sid)
|
||||
idoverrides_del = [
|
||||
idoverride_set[sid] for sid in set(idoverrides_del)
|
||||
]
|
||||
|
||||
membermanager_user_del = gen_intersection_list(
|
||||
membermanager_user, res_find.get("membermanager_user"))
|
||||
@@ -860,7 +916,7 @@ def main():
|
||||
if len(externalmember_del) > 0:
|
||||
del_member_args["ipaexternalmember"] = \
|
||||
externalmember_del
|
||||
elif externalmember or external:
|
||||
elif externalmember:
|
||||
ansible_module.fail_json(
|
||||
msg="Cannot add external members to a "
|
||||
"non-external group."
|
||||
|
||||
@@ -184,7 +184,7 @@ options:
|
||||
type: list
|
||||
elements: str
|
||||
aliases: ["krbprincipalauthind"]
|
||||
choices: ["radius", "otp", "pkinit", "hardened", "idp", ""]
|
||||
choices: ["radius", "otp", "pkinit", "hardened", "idp", "passkey", ""]
|
||||
required: false
|
||||
requires_pre_auth:
|
||||
description: Pre-authentication is required for the service
|
||||
@@ -356,7 +356,7 @@ options:
|
||||
type: list
|
||||
elements: str
|
||||
aliases: ["krbprincipalauthind"]
|
||||
choices: ["radius", "otp", "pkinit", "hardened", "idp", ""]
|
||||
choices: ["radius", "otp", "pkinit", "hardened", "idp", "passkey", ""]
|
||||
required: false
|
||||
requires_pre_auth:
|
||||
description: Pre-authentication is required for the service
|
||||
@@ -758,7 +758,7 @@ def main():
|
||||
auth_ind=dict(type='list', elements="str",
|
||||
aliases=["krbprincipalauthind"], default=None,
|
||||
choices=["radius", "otp", "pkinit", "hardened", "idp",
|
||||
""]),
|
||||
"passkey", ""]),
|
||||
requires_pre_auth=dict(type="bool", aliases=["ipakrbrequirespreauth"],
|
||||
default=None),
|
||||
ok_as_delegate=dict(type="bool", aliases=["ipakrbokasdelegate"],
|
||||
|
||||
@@ -155,7 +155,7 @@ RETURN = """
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa
|
||||
IPAAnsibleModule, compare_args_ipa, ipalib_errors
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
@@ -168,7 +168,7 @@ def find_idoverridegroup(module, idview, anchor):
|
||||
_result = module.ipa_command("idoverridegroup_show", idview,
|
||||
{"ipaanchoruuid": anchor,
|
||||
"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if idoverridegroup anchor is not found.
|
||||
return None
|
||||
return _result["result"]
|
||||
|
||||
@@ -315,7 +315,8 @@ RETURN = """
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
|
||||
gen_intersection_list, encode_certificate, convert_input_certificates
|
||||
gen_intersection_list, encode_certificate, convert_input_certificates, \
|
||||
ipalib_errors
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
@@ -328,7 +329,7 @@ def find_idoverrideuser(module, idview, anchor):
|
||||
_result = module.ipa_command("idoverrideuser_show", idview,
|
||||
{"ipaanchoruuid": anchor,
|
||||
"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if idoverrideuser anchor is not found.
|
||||
return None
|
||||
|
||||
|
||||
@@ -184,7 +184,8 @@ RETURN = """
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, template_str, urlparse
|
||||
IPAAnsibleModule, compare_args_ipa, template_str, urlparse, \
|
||||
ipalib_errors
|
||||
from ansible.module_utils import six
|
||||
from copy import deepcopy
|
||||
import string
|
||||
@@ -269,7 +270,7 @@ def find_idp(module, name):
|
||||
"""Find if a idp with the given name already exist."""
|
||||
try:
|
||||
_result = module.ipa_command("idp_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if idp name is not found.
|
||||
return None
|
||||
|
||||
|
||||
@@ -143,7 +143,8 @@ RETURN = """
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, get_trusted_domain_sid_from_name
|
||||
IPAAnsibleModule, compare_args_ipa, get_trusted_domain_sid_from_name, \
|
||||
ipalib_errors
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
@@ -154,7 +155,7 @@ def find_idrange(module, name):
|
||||
"""Find if a idrange with the given name already exist."""
|
||||
try:
|
||||
_result = module.ipa_command("idrange_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if idrange name is not found.
|
||||
return None
|
||||
return _result["result"]
|
||||
@@ -280,6 +281,14 @@ def main():
|
||||
|
||||
# Connect to IPA API
|
||||
with ansible_module.ipa_connect():
|
||||
# set required fields
|
||||
required = ["base_id", "range_size"]
|
||||
requires_baserid = (
|
||||
ansible_module.ipa_command_param_exists("config_mod", "enable_sid")
|
||||
and idrange_type in [None, "ipa-local"]
|
||||
)
|
||||
if requires_baserid:
|
||||
required.extend(["rid_base", "secondary_rid_base"])
|
||||
|
||||
commands = []
|
||||
for name in names:
|
||||
@@ -320,6 +329,18 @@ def main():
|
||||
del args["iparangetype"]
|
||||
commands.append([name, "idrange_mod", args])
|
||||
else:
|
||||
# Check if required parameters were given
|
||||
missing_params = [
|
||||
pname for pname in required
|
||||
if ansible_module.params_get(pname) is None
|
||||
]
|
||||
if missing_params:
|
||||
ansible_module.fail_json(
|
||||
msg=(
|
||||
"Missing required parameters: %s"
|
||||
% (", ".join(missing_params))
|
||||
)
|
||||
)
|
||||
commands.append([name, "idrange_add", args])
|
||||
|
||||
elif state == "absent":
|
||||
|
||||
@@ -138,7 +138,7 @@ def find_idview(module, name):
|
||||
"""Find if a idview with the given name already exist."""
|
||||
try:
|
||||
_result = module.ipa_command("idview_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if idview name is not found.
|
||||
return None
|
||||
return _result["result"]
|
||||
|
||||
@@ -76,14 +76,14 @@ RETURN = """
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa
|
||||
IPAAnsibleModule, compare_args_ipa, ipalib_errors
|
||||
|
||||
|
||||
def find_location(module, name):
|
||||
"""Find if a location with the given name already exist."""
|
||||
try:
|
||||
_result = module.ipa_command("location_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if location name is not found.
|
||||
return None
|
||||
return _result["result"]
|
||||
|
||||
174
plugins/modules/ipapasskeyconfig.py
Normal file
174
plugins/modules/ipapasskeyconfig.py
Normal file
@@ -0,0 +1,174 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
# Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2025 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/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
"status": ["preview"],
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: ipapasskeyconfig
|
||||
short_description: Manage FreeIPA passkeyconfig
|
||||
description: Manage FreeIPA passkeyconfig
|
||||
extends_documentation_fragment:
|
||||
- ipamodule_base_docs
|
||||
options:
|
||||
require_user_verification:
|
||||
description: Require user verification for passkey authentication
|
||||
required: false
|
||||
type: bool
|
||||
default: true
|
||||
aliases: ["iparequireuserverification"]
|
||||
author:
|
||||
- Rafael Guterres Jeffman (@rjeffman)
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Set passkeyconfig
|
||||
- ipapasskeyconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
require_user_verification: false
|
||||
|
||||
# Get current passkeyconfig
|
||||
- ipapasskeyconfig:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
passkeyconfig:
|
||||
description: Dict of passkeyconfig settings
|
||||
returned: always
|
||||
type: dict
|
||||
contains:
|
||||
require_user_verification:
|
||||
description: Require user verification for passkey authentication
|
||||
type: bool
|
||||
returned: always
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, ipalib_errors
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
def find_passkeyconfig(module):
|
||||
"""Find the current passkeyconfig settings."""
|
||||
try:
|
||||
_result = module.ipa_command_no_name(
|
||||
"passkeyconfig_show", {"all": True})
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if passkeyconfig is not found.
|
||||
return None
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_args(require_user_verification):
|
||||
_args = {}
|
||||
if require_user_verification is not None:
|
||||
_args["iparequireuserverification"] = require_user_verification
|
||||
return _args
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = IPAAnsibleModule(
|
||||
argument_spec=dict(
|
||||
# passkeyconfig
|
||||
require_user_verification=dict(
|
||||
required=False, type='bool',
|
||||
aliases=["iparequireuserverification"],
|
||||
default=None
|
||||
),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
ansible_module._ansible_debug = True
|
||||
|
||||
# Get parameters
|
||||
require_user_verification = (
|
||||
ansible_module.params_get("require_user_verification")
|
||||
)
|
||||
|
||||
# Init
|
||||
changed = False
|
||||
exit_args = {}
|
||||
|
||||
# Connect to IPA API
|
||||
with ansible_module.ipa_connect():
|
||||
|
||||
if not ansible_module.ipa_command_exists("passkeyconfig_show"):
|
||||
msg = "Managing passkeyconfig is not supported by your IPA version"
|
||||
ansible_module.fail_json(msg=msg)
|
||||
|
||||
result = find_passkeyconfig(ansible_module)
|
||||
|
||||
if result is None:
|
||||
ansible_module.fail_json(msg="Could not retrieve passkeyconfig")
|
||||
|
||||
if require_user_verification is not None:
|
||||
# Generate args
|
||||
args = gen_args(require_user_verification)
|
||||
|
||||
# Check if there are different settings in the find result.
|
||||
# If yes: modify
|
||||
if not compare_args_ipa(ansible_module, args, result):
|
||||
changed = True
|
||||
if not ansible_module.check_mode:
|
||||
try:
|
||||
ansible_module.ipa_command_no_name(
|
||||
"passkeyconfig_mod", args)
|
||||
except ipalib_errors.EmptyModlist:
|
||||
changed = False
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(
|
||||
msg="passkeyconfig_mod failed: %s" % str(e))
|
||||
else:
|
||||
# No parameters provided, just return current config
|
||||
pass
|
||||
|
||||
# Get updated config if changes were made
|
||||
if changed:
|
||||
result = find_passkeyconfig(ansible_module)
|
||||
|
||||
# Prepare exit args
|
||||
exit_args["passkeyconfig"] = {}
|
||||
if result:
|
||||
# Map IPA API field to module parameter
|
||||
if "iparequireuserverification" in result:
|
||||
exit_args["passkeyconfig"]["require_user_verification"] = \
|
||||
result["iparequireuserverification"][0]
|
||||
|
||||
# Done
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -154,14 +154,14 @@ RETURN = """
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, to_text
|
||||
IPAAnsibleModule, compare_args_ipa, to_text, ipalib_errors
|
||||
|
||||
|
||||
def find_permission(module, name):
|
||||
"""Find if a permission with the given name already exist."""
|
||||
try:
|
||||
_result = module.ipa_command("permission_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if permission name is not found.
|
||||
return None
|
||||
_res = _result["result"]
|
||||
|
||||
@@ -124,7 +124,7 @@ RETURN = """
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
|
||||
gen_intersection_list
|
||||
gen_intersection_list, ipalib_errors
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
@@ -135,7 +135,7 @@ def find_privilege(module, name):
|
||||
"""Find if a privilege with the given name already exist."""
|
||||
try:
|
||||
_result = module.ipa_command("privilege_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if privilege name is not found.
|
||||
return None
|
||||
return _result["result"]
|
||||
|
||||
@@ -85,6 +85,11 @@ options:
|
||||
type: list
|
||||
elements: str
|
||||
required: false
|
||||
sysaccount:
|
||||
description: List of sysaccounts.
|
||||
type: list
|
||||
elements: str
|
||||
required: false
|
||||
action:
|
||||
description: Work on role or member level.
|
||||
type: str
|
||||
@@ -129,7 +134,7 @@ EXAMPLES = """
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, gen_add_del_lists, compare_args_ipa, \
|
||||
gen_intersection_list, ensure_fqdn
|
||||
gen_intersection_list, ensure_fqdn, ipalib_errors
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
@@ -140,7 +145,7 @@ def find_role(module, name):
|
||||
"""Find if a role with the given name already exist."""
|
||||
try:
|
||||
_result = module.ipa_command("role_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if role name is not found.
|
||||
return None
|
||||
return _result["result"]
|
||||
@@ -177,7 +182,7 @@ def check_parameters(module):
|
||||
"description",
|
||||
"user", "group",
|
||||
"host", "hostgroup",
|
||||
"service",
|
||||
"service", "sysaccount",
|
||||
"privilege",
|
||||
]
|
||||
|
||||
@@ -225,7 +230,7 @@ def ensure_absent_state(module, name, action, res_find):
|
||||
{"privilege": del_list}])
|
||||
|
||||
member_args = {}
|
||||
for key in ['user', 'group', 'hostgroup']:
|
||||
for key in ['user', 'group', 'hostgroup', 'sysaccount']:
|
||||
_members = module.params_get_lowercase(key)
|
||||
if _members:
|
||||
del_list = gen_intersection_list(
|
||||
@@ -335,7 +340,7 @@ def ensure_role_with_members_is_present(module, name, res_find, action):
|
||||
add_members = {}
|
||||
del_members = {}
|
||||
|
||||
for key in ["user", "group", "hostgroup"]:
|
||||
for key in ["user", "group", "hostgroup", "sysaccount"]:
|
||||
_members = module.params_get_lowercase(key)
|
||||
if _members is not None:
|
||||
add_list, del_list = gen_add_del_lists(
|
||||
@@ -437,6 +442,8 @@ def create_module():
|
||||
default=None),
|
||||
service=dict(required=False, type='list', elements="str",
|
||||
default=None),
|
||||
sysaccount=dict(required=False, type='list', elements="str",
|
||||
default=None),
|
||||
|
||||
# state
|
||||
action=dict(type="str", default="role",
|
||||
@@ -467,8 +474,15 @@ def main():
|
||||
state = ansible_module.params_get("state")
|
||||
action = ansible_module.params_get("action")
|
||||
names = ansible_module.params_get("name")
|
||||
sysaccount = ansible_module.params_get("sysaccount")
|
||||
commands = []
|
||||
|
||||
has_sysaccount_member = ansible_module.ipa_command_param_exists(
|
||||
"role_add_member", "sysaccount")
|
||||
if not has_sysaccount_member and sysaccount is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="sysaccount members are not supported by your IPA version")
|
||||
|
||||
for name in names:
|
||||
cmds = role_commands_for_name(ansible_module, state, action, name)
|
||||
commands.extend(cmds)
|
||||
|
||||
@@ -113,14 +113,14 @@ RETURN = """
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa
|
||||
IPAAnsibleModule, compare_args_ipa, ipalib_errors
|
||||
|
||||
|
||||
def find_selfservice(module, name):
|
||||
"""Find if a selfservice with the given name already exist."""
|
||||
try:
|
||||
_result = module.ipa_command("selfservice_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if selfservice name is not found.
|
||||
return None
|
||||
return _result["result"]
|
||||
|
||||
@@ -192,14 +192,14 @@ RETURN = """
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, DNSName
|
||||
IPAAnsibleModule, compare_args_ipa, DNSName, ipalib_errors
|
||||
|
||||
|
||||
def find_server(module, name):
|
||||
"""Find if a server with the given name already exist."""
|
||||
try:
|
||||
_result = module.ipa_command("server_show", name, {"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if server name is not found.
|
||||
return None
|
||||
return _result["result"]
|
||||
@@ -214,7 +214,7 @@ def server_role_status(module, name):
|
||||
"include_master": True,
|
||||
"raw": True,
|
||||
"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if server name is not found.
|
||||
return None
|
||||
return _result["result"][0]
|
||||
|
||||
@@ -74,7 +74,7 @@ options:
|
||||
type: list
|
||||
elements: str
|
||||
required: false
|
||||
choices: ["otp", "radius", "pkinit", "hardened", "idp", ""]
|
||||
choices: ["otp", "radius", "pkinit", "hardened", "idp", "passkey", ""]
|
||||
aliases: ["krbprincipalauthind"]
|
||||
skip_host_check:
|
||||
description: Skip checking if host object exists.
|
||||
@@ -192,7 +192,7 @@ options:
|
||||
type: list
|
||||
elements: str
|
||||
required: false
|
||||
choices: ["otp", "radius", "pkinit", "hardened", "idp", ""]
|
||||
choices: ["otp", "radius", "pkinit", "hardened", "idp", "passkey", ""]
|
||||
aliases: ["krbprincipalauthind"]
|
||||
skip_host_check:
|
||||
description: Skip checking if host object exists.
|
||||
@@ -560,7 +560,7 @@ def init_ansible_module():
|
||||
auth_ind=dict(type="list", elements="str",
|
||||
aliases=["krbprincipalauthind"],
|
||||
choices=["otp", "radius", "pkinit", "hardened", "idp",
|
||||
""]),
|
||||
"passkey", ""]),
|
||||
skip_host_check=dict(type="bool"),
|
||||
force=dict(type="bool"),
|
||||
requires_pre_auth=dict(
|
||||
|
||||
@@ -142,7 +142,7 @@ def find_servicedelegationrule(module, name):
|
||||
try:
|
||||
_result = module.ipa_command("servicedelegationrule_show", name,
|
||||
{"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if servicedelegationrule name is not found.
|
||||
return None
|
||||
return _result["result"]
|
||||
|
||||
@@ -106,7 +106,7 @@ RETURN = """
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, gen_add_del_lists, gen_add_list, gen_intersection_list, \
|
||||
servicedelegation_normalize_principals
|
||||
servicedelegation_normalize_principals, ipalib_errors
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
@@ -118,7 +118,7 @@ def find_servicedelegationtarget(module, name):
|
||||
try:
|
||||
_result = module.ipa_command("servicedelegationtarget_show", name,
|
||||
{"all": True})
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if servicedelegationtarget name is not found.
|
||||
return None
|
||||
return _result["result"]
|
||||
|
||||
@@ -710,7 +710,11 @@ def main():
|
||||
|
||||
# Generate addition and removal lists
|
||||
host_add, host_del = gen_add_del_lists(
|
||||
entry.host, res_find.get('memberhost_host', []))
|
||||
entry.host, (
|
||||
list(res_find.get('memberhost_host', []))
|
||||
+ list(res_find.get('externalhost', []))
|
||||
)
|
||||
)
|
||||
|
||||
hostgroup_add, hostgroup_del = gen_add_del_lists(
|
||||
entry.hostgroup,
|
||||
@@ -721,7 +725,11 @@ def main():
|
||||
entry.hostmask, res_find.get('hostmask', []))
|
||||
|
||||
user_add, user_del = gen_add_del_lists(
|
||||
entry.user, res_find.get('memberuser_user', []))
|
||||
entry.user, (
|
||||
list(res_find.get('memberuser_user', []))
|
||||
+ list(res_find.get('externaluser', []))
|
||||
)
|
||||
)
|
||||
|
||||
group_add, group_del = gen_add_del_lists(
|
||||
entry.group, res_find.get('memberuser_group', []))
|
||||
@@ -751,8 +759,7 @@ def main():
|
||||
# the provided list against both users and external
|
||||
# users list.
|
||||
runasuser_add, runasuser_del = gen_add_del_lists(
|
||||
entry.runasuser,
|
||||
(
|
||||
entry.runasuser, (
|
||||
list(res_find.get('ipasudorunas_user', []))
|
||||
+ list(res_find.get('ipasudorunasextuser', []))
|
||||
)
|
||||
@@ -785,7 +792,11 @@ def main():
|
||||
# the sudorule already
|
||||
if entry.host is not None:
|
||||
host_add = gen_add_list(
|
||||
entry.host, res_find.get("memberhost_host"))
|
||||
entry.host, (
|
||||
list(res_find.get("memberhost_host", []))
|
||||
+ list(res_find.get("externalhost", []))
|
||||
)
|
||||
)
|
||||
if entry.hostgroup is not None:
|
||||
hostgroup_add = gen_add_list(
|
||||
entry.hostgroup,
|
||||
@@ -796,7 +807,11 @@ def main():
|
||||
entry.hostmask, res_find.get("hostmask"))
|
||||
if entry.user is not None:
|
||||
user_add = gen_add_list(
|
||||
entry.user, res_find.get("memberuser_user"))
|
||||
entry.user, (
|
||||
list(res_find.get('memberuser_user', []))
|
||||
+ list(res_find.get('externaluser', []))
|
||||
)
|
||||
)
|
||||
if entry.group is not None:
|
||||
group_add = gen_add_list(
|
||||
entry.group, res_find.get("memberuser_group"))
|
||||
@@ -862,7 +877,11 @@ def main():
|
||||
# in sudorule
|
||||
if entry.host is not None:
|
||||
host_del = gen_intersection_list(
|
||||
entry.host, res_find.get("memberhost_host"))
|
||||
entry.host, (
|
||||
list(res_find.get("memberhost_host", []))
|
||||
+ list(res_find.get("externalhost", []))
|
||||
)
|
||||
)
|
||||
|
||||
if entry.hostgroup is not None:
|
||||
hostgroup_del = gen_intersection_list(
|
||||
@@ -876,7 +895,11 @@ def main():
|
||||
|
||||
if entry.user is not None:
|
||||
user_del = gen_intersection_list(
|
||||
entry.user, res_find.get("memberuser_user"))
|
||||
entry.user, (
|
||||
list(res_find.get('memberuser_user', []))
|
||||
+ list(res_find.get('externaluser', []))
|
||||
)
|
||||
)
|
||||
|
||||
if entry.group is not None:
|
||||
group_del = gen_intersection_list(
|
||||
@@ -911,8 +934,7 @@ def main():
|
||||
# users list.
|
||||
if entry.runasuser is not None:
|
||||
runasuser_del = gen_intersection_list(
|
||||
entry.runasuser,
|
||||
(
|
||||
entry.runasuser, (
|
||||
list(res_find.get('ipasudorunas_user', []))
|
||||
+ list(res_find.get('ipasudorunasextuser', []))
|
||||
)
|
||||
|
||||
309
plugins/modules/ipasysaccount.py
Normal file
309
plugins/modules/ipasysaccount.py
Normal file
@@ -0,0 +1,309 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
# Thomas Woerner <twoerner@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2025 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/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
"status": ["preview"],
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: ipasysaccount
|
||||
short_description: Manage FreeIPA system account
|
||||
description: Manage FreeIPA system account
|
||||
extends_documentation_fragment:
|
||||
- ipamodule_base_docs
|
||||
- ipamodule_base_docs.delete_continue
|
||||
options:
|
||||
name:
|
||||
description: The list of sysaccount name strings (internally uid).
|
||||
required: true
|
||||
type: list
|
||||
elements: str
|
||||
aliases: ["login"]
|
||||
description:
|
||||
description: A description for the sysaccount.
|
||||
type: str
|
||||
required: false
|
||||
privileged:
|
||||
description: Allow password updates without reset.
|
||||
type: bool
|
||||
required: false
|
||||
random:
|
||||
description: Generate a random user password.
|
||||
required: false
|
||||
type: bool
|
||||
password:
|
||||
description: Set the user password.
|
||||
required: false
|
||||
type: str
|
||||
aliases: ["userpassword"]
|
||||
update_password:
|
||||
description:
|
||||
Set password for a sysaccount in present state only on creation or always
|
||||
type: str
|
||||
choices: ["always", "on_create"]
|
||||
required: false
|
||||
state:
|
||||
description: The state to ensure.
|
||||
choices: ["present", "absent", "enabled", "disabled"]
|
||||
default: present
|
||||
type: str
|
||||
author:
|
||||
- Thomas Woerner (@t-woerner)
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Ensure sysaccount my-app is present
|
||||
- ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
random: true
|
||||
|
||||
# Ensure sysaccount my-app is absent
|
||||
- ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
state: absent
|
||||
|
||||
# Ensure existing sysaccount my-app is privileged
|
||||
- ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
privileged: true
|
||||
|
||||
# Ensure existing sysaccount my-app is not privileged
|
||||
- ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
privileged: false
|
||||
|
||||
# Ensure existing sysaccount my-app is disabled
|
||||
- ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
state: disabled
|
||||
|
||||
# Ensure existing sysaccount my-app is enabled
|
||||
- ipasysaccount:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: my-app
|
||||
state: enabled
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
sysaccount:
|
||||
description: Sysaccount dict with random password
|
||||
returned: |
|
||||
If random is yes and user sysaccount not exist or update_password is yes
|
||||
type: dict
|
||||
contains:
|
||||
randompassword:
|
||||
description: The generated random password
|
||||
type: str
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa, ipalib_errors
|
||||
from ansible.module_utils import six
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
def find_sysaccount(module, name):
|
||||
"""Find if a sysaccount with the given name already exist."""
|
||||
try:
|
||||
_result = module.ipa_command("sysaccount_show", name, {"all": True})
|
||||
except ipalib_errors.NotFound:
|
||||
# An exception is raised if sysaccount name is not found.
|
||||
return None
|
||||
return _result["result"]
|
||||
|
||||
|
||||
def gen_args(description, random, privileged, password):
|
||||
_args = {}
|
||||
if description is not None:
|
||||
_args["description"] = description
|
||||
if random is not None:
|
||||
_args["random"] = random
|
||||
if privileged is not None:
|
||||
_args["privileged"] = privileged
|
||||
if password is not None:
|
||||
_args["userpassword"] = password
|
||||
return _args
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def result_handler(module, result, command, name, args, exit_args, errors):
|
||||
if "random" in args and command in ["sysaccount_add", "sysaccount_mod"] \
|
||||
and "randompassword" in result["result"]:
|
||||
exit_args["randompassword"] = \
|
||||
result["result"]["randompassword"]
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = IPAAnsibleModule(
|
||||
argument_spec=dict(
|
||||
# general
|
||||
name=dict(type="list", elements="str", required=True,
|
||||
aliases=["login"]),
|
||||
# present
|
||||
description=dict(required=False, type='str', default=None),
|
||||
random=dict(required=False, type='bool', default=None),
|
||||
privileged=dict(required=False, type='bool', default=None),
|
||||
password=dict(required=False, type='str',
|
||||
aliases=["userpassword"], default=None),
|
||||
|
||||
# mod
|
||||
update_password=dict(type='str', default=None, no_log=False,
|
||||
choices=['always', 'on_create']),
|
||||
|
||||
# state
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent", "enabled", "disabled"]),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
ipa_module_options=["delete_continue"],
|
||||
mutually_exclusive=[["random", "password"]]
|
||||
)
|
||||
|
||||
ansible_module._ansible_debug = True
|
||||
|
||||
# Get parameters
|
||||
|
||||
# general
|
||||
names = ansible_module.params_get("name")
|
||||
|
||||
# present
|
||||
description = ansible_module.params_get("description")
|
||||
random = ansible_module.params_get("random")
|
||||
privileged = ansible_module.params_get("privileged")
|
||||
password = ansible_module.params_get("password")
|
||||
|
||||
# mod
|
||||
update_password = ansible_module.params_get("update_password")
|
||||
|
||||
# absent
|
||||
delete_continue = ansible_module.params_get("delete_continue")
|
||||
|
||||
# state
|
||||
state = ansible_module.params_get("state")
|
||||
|
||||
# Check parameters
|
||||
|
||||
invalid = []
|
||||
|
||||
if state == "present" and len(names) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Only one sysaccount can be added at a time.")
|
||||
|
||||
if state in ["absent", "enabled", "disabled"]:
|
||||
if len(names) < 1:
|
||||
ansible_module.fail_json(msg="No name given.")
|
||||
invalid = ["description", "random", "privileged", "password"]
|
||||
|
||||
ansible_module.params_fail_used_invalid(invalid, state)
|
||||
|
||||
# Init
|
||||
|
||||
changed = False
|
||||
exit_args = {}
|
||||
|
||||
# Connect to IPA API
|
||||
with ansible_module.ipa_connect():
|
||||
|
||||
if not ansible_module.ipa_command_exists("sysaccount_add"):
|
||||
ansible_module.fail_json(
|
||||
msg=("Managing sysaccounts is not supported by your "
|
||||
"IPA version")
|
||||
)
|
||||
|
||||
commands = []
|
||||
for name in names:
|
||||
# Make sure sysaccount exists
|
||||
res_find = find_sysaccount(ansible_module, name)
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
|
||||
# Generate args
|
||||
args = gen_args(description, random, privileged, password)
|
||||
|
||||
# Found the sysaccount
|
||||
if res_find is not None:
|
||||
# Ignore password and random with
|
||||
# update_password == on_create
|
||||
if update_password == "on_create":
|
||||
if "userpassword" in args:
|
||||
del args["userpassword"]
|
||||
if "random" in args:
|
||||
del args["random"]
|
||||
# if using "random:false" password should not be
|
||||
# generated.
|
||||
if not args.get("random", True):
|
||||
del args["random"]
|
||||
|
||||
# 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, "sysaccount_mod", args])
|
||||
else:
|
||||
commands.append([name, "sysaccount_add", args])
|
||||
|
||||
elif state == "absent":
|
||||
if res_find is not None:
|
||||
commands.append(
|
||||
[name, "sysaccount_del", {"continue": delete_continue}]
|
||||
)
|
||||
|
||||
elif state == "enabled":
|
||||
if res_find is not None and res_find["nsaccountlock"]:
|
||||
commands.append([name, "sysaccount_enable", {}])
|
||||
|
||||
elif state == "disabled":
|
||||
if res_find is not None and not res_find["nsaccountlock"]:
|
||||
commands.append([name, "sysaccount_disable", {}])
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Execute commands
|
||||
|
||||
changed = ansible_module.execute_ipa_commands(
|
||||
commands, result_handler, keeponly=["randompassword"],
|
||||
exit_args=exit_args)
|
||||
|
||||
# Done
|
||||
|
||||
ansible_module.exit_json(changed=changed, sysaccount=exit_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -208,7 +208,8 @@ options:
|
||||
Use empty string to reset userauthtype to the initial value.
|
||||
type: list
|
||||
elements: str
|
||||
choices: ["password", "radius", "otp", "pkinit", "hardened", "idp", ""]
|
||||
choices: ["password", "radius", "otp", "pkinit", "hardened", "idp",
|
||||
"passkey", ""]
|
||||
required: false
|
||||
aliases: ["ipauserauthtype"]
|
||||
userclass:
|
||||
@@ -480,7 +481,8 @@ options:
|
||||
Use empty string to reset userauthtype to the initial value.
|
||||
type: list
|
||||
elements: str
|
||||
choices: ["password", "radius", "otp", "pkinit", "hardened", "idp", ""]
|
||||
choices: ["password", "radius", "otp", "pkinit", "hardened", "idp",
|
||||
"passkey", ""]
|
||||
required: false
|
||||
aliases: ["ipauserauthtype"]
|
||||
userclass:
|
||||
@@ -1070,7 +1072,7 @@ def main():
|
||||
userauthtype=dict(type='list', elements="str",
|
||||
aliases=["ipauserauthtype"], default=None,
|
||||
choices=["password", "radius", "otp", "pkinit",
|
||||
"hardened", "idp", ""]),
|
||||
"hardened", "idp", "passkey", ""]),
|
||||
userclass=dict(type="list", elements="str", aliases=["class"],
|
||||
default=None),
|
||||
radius=dict(type="str", aliases=["ipatokenradiusconfiglink"],
|
||||
|
||||
@@ -4,3 +4,4 @@ junit_family = xunit1
|
||||
markers=
|
||||
source_order: mark test as order bound
|
||||
playbook: playbook tests
|
||||
pythonpath = tests
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
-r requirements-tests.txt
|
||||
ipdb==0.13.4
|
||||
pre-commit==2.20.0
|
||||
flake8==7.0.0
|
||||
flake8
|
||||
flake8-bugbear
|
||||
pylint>=3.2
|
||||
wrapt==1.14.1
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
-r requirements.txt
|
||||
pytest==7.1.3
|
||||
pytest-sourceorder==0.6.0
|
||||
pytest
|
||||
pytest-sourceorder
|
||||
pytest-split>=0.8.0
|
||||
pytest-custom_exit_code>=0.3.0
|
||||
pytest-testinfra==6.8.0
|
||||
pytest-randomly==3.12.0
|
||||
pytest-testinfra
|
||||
pytest-randomly
|
||||
pyyaml>=3
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
setuptools
|
||||
|
||||
@@ -34,7 +34,7 @@ Supported Distributions
|
||||
|
||||
* RHEL/CentOS 7.6+
|
||||
* CentOS Stream 8+
|
||||
* Fedora 26+
|
||||
* Fedora 40+
|
||||
* Ubuntu 16.04 and 18.04
|
||||
|
||||
|
||||
|
||||
@@ -91,20 +91,21 @@
|
||||
enabled: yes
|
||||
state: started
|
||||
|
||||
- name: Firewalld - Verify runtime zone "{{ ipabackup_firewalld_zone }}"
|
||||
ansible.builtin.shell: >
|
||||
firewall-cmd
|
||||
--info-zone="{{ ipabackup_firewalld_zone }}"
|
||||
>/dev/null
|
||||
- name: Firewalld - Verify zones
|
||||
when: ipabackup_firewalld_zone is defined
|
||||
block:
|
||||
- name: Firewalld - Verify runtime zone from ipabackup_firewalld_zone
|
||||
ansible.builtin.shell: >
|
||||
firewall-cmd
|
||||
--info-zone="{{ ipabackup_firewalld_zone }}"
|
||||
>/dev/null
|
||||
|
||||
- name: Firewalld - Verify permanent zone "{{ ipabackup_firewalld_zone }}"
|
||||
ansible.builtin.shell: >
|
||||
firewall-cmd
|
||||
--permanent
|
||||
--info-zone="{{ ipabackup_firewalld_zone }}"
|
||||
>/dev/null
|
||||
when: ipabackup_firewalld_zone is defined
|
||||
- name: Firewalld - Verify permanent zone from ipabackup_firewalld_zone
|
||||
ansible.builtin.shell: >
|
||||
firewall-cmd
|
||||
--permanent
|
||||
--info-zone="{{ ipabackup_firewalld_zone }}"
|
||||
>/dev/null
|
||||
|
||||
### RESTORE
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Supported Distributions
|
||||
|
||||
* RHEL/CentOS 7.4+
|
||||
* CentOS Stream 8+
|
||||
* Fedora 26+
|
||||
* Fedora 40+
|
||||
* Ubuntu
|
||||
* Debian
|
||||
|
||||
@@ -202,6 +202,8 @@ Variable | Description | Required
|
||||
`ipaclient_request_cert` | The bool value defines if the certificate for the machine wil be requested. The certificate will be stored in /etc/ipa/nssdb under the nickname "Local IPA host". . `ipaclient_request_cert` defaults to `no`. The option is deprecated and will be removed in a future release. | no
|
||||
`ipaclient_keytab` | The string value contains the path on the node of a backup host keytab from a previous enrollment. | no
|
||||
`ipaclient_automount_location` | Automount location | no
|
||||
`ipaclient_dns_over_tls` | Configure DNS over TLS. Requires FreeIPA version 4.12.5 or later. (bool, default: false) | no
|
||||
`ipaclient_no_dnssec_validation` | Disable DNSSEC validation for DNS over TLS. This turns off DNSSEC validation for unbound. Ignored if `ipaserver_dns_over_tls` is not enabled. (bool, default: false) | no
|
||||
|
||||
|
||||
Server Variables
|
||||
|
||||
@@ -26,6 +26,8 @@ ipasssd_enable_dns_updates: no
|
||||
ipasssd_no_krb5_offline_passwords: no
|
||||
ipasssd_preserve_sssd: no
|
||||
ipaclient_request_cert: no
|
||||
ipaclient_dns_over_tls: no
|
||||
ipaclient_no_dnssec_validation: no
|
||||
|
||||
### packages ###
|
||||
ipaclient_install_packages: yes
|
||||
|
||||
@@ -86,6 +86,16 @@ options:
|
||||
type: bool
|
||||
required: no
|
||||
default: no
|
||||
dns_over_tls:
|
||||
description: Configure DNS over TLS
|
||||
type: bool
|
||||
default: no
|
||||
required: no
|
||||
no_dnssec_validation:
|
||||
description: Disable DNSSEC validation for DNS over TLS
|
||||
type: bool
|
||||
default: no
|
||||
required: no
|
||||
enable_dns_updates:
|
||||
description: |
|
||||
Configures the machine to attempt dns updates when the ip address
|
||||
@@ -212,7 +222,9 @@ def main():
|
||||
mkhomedir=dict(required=False, type='bool'),
|
||||
on_master=dict(required=False, type='bool'),
|
||||
dnsok=dict(required=False, type='bool', default=False),
|
||||
|
||||
dns_over_tls=dict(required=False, type='bool', default=False),
|
||||
no_dnssec_validation=dict(required=False, type='bool',
|
||||
default=False),
|
||||
enable_dns_updates=dict(required=False, type='bool'),
|
||||
all_ip_addresses=dict(required=False, type='bool', default=False),
|
||||
ip_addresses=dict(required=False, type='list', elements='str',
|
||||
@@ -249,6 +261,8 @@ def main():
|
||||
options.mkhomedir = module.params.get('mkhomedir')
|
||||
options.on_master = module.params.get('on_master')
|
||||
dnsok = module.params.get('dnsok')
|
||||
options.dns_over_tls = module.params.get('dns_over_tls')
|
||||
options.no_dnssec_validation = module.params.get('no_dnssec_validation')
|
||||
|
||||
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
|
||||
statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE)
|
||||
@@ -256,6 +270,7 @@ def main():
|
||||
os.environ['KRB5CCNAME'] = paths.IPA_DNS_CCACHE
|
||||
|
||||
options.dns_updates = module.params.get('enable_dns_updates')
|
||||
options.dns_over_tls = module.params.get('dns_over_tls')
|
||||
options.all_ip_addresses = module.params.get('all_ip_addresses')
|
||||
options.ip_addresses = ansible_module_get_parsed_ip_addresses(module)
|
||||
options.request_cert = module.params.get('request_cert')
|
||||
@@ -279,6 +294,7 @@ def main():
|
||||
options.no_sssd = False
|
||||
options.sssd = not options.no_sssd
|
||||
options.no_ac = False
|
||||
options.dns_over_tls = module.params.get('dns_over_tls')
|
||||
nosssd_files = module.params.get('nosssd_files')
|
||||
selinux_works = module.params.get('selinux_works')
|
||||
krb_name = module.params.get('krb_name')
|
||||
@@ -339,17 +355,19 @@ def main():
|
||||
ca_subject)
|
||||
ca_certs_trust = [(c, n,
|
||||
certstore.key_policy_to_trust_flags(t, True, u))
|
||||
for (c, n, t, u) in ca_certs]
|
||||
for (c, n, t, u) in [x[0:4] for x in ca_certs]]
|
||||
|
||||
if hasattr(paths, "KDC_CA_BUNDLE_PEM"):
|
||||
x509.write_certificate_list(
|
||||
[c for c, n, t, u in ca_certs if t is not False],
|
||||
[c for c, n, t, u in [x[0:4] for x in ca_certs]
|
||||
if t is not False],
|
||||
paths.KDC_CA_BUNDLE_PEM,
|
||||
# mode=0o644
|
||||
)
|
||||
if hasattr(paths, "CA_BUNDLE_PEM"):
|
||||
x509.write_certificate_list(
|
||||
[c for c, n, t, u in ca_certs if t is not False],
|
||||
[c for c, n, t, u in [x[0:4] for x in ca_certs]
|
||||
if t is not False],
|
||||
paths.CA_BUNDLE_PEM,
|
||||
# mode=0o644
|
||||
)
|
||||
@@ -370,13 +388,22 @@ def main():
|
||||
tasks.insert_ca_certs_into_systemwide_ca_store(ca_certs)
|
||||
|
||||
if not options.on_master:
|
||||
client_dns(cli_server[0], hostname, options)
|
||||
argspec_client_dns = getargspec(client_dns)
|
||||
if "statestore" in argspec_client_dns.args:
|
||||
client_dns(cli_server[0], hostname, options, statestore)
|
||||
else:
|
||||
client_dns(cli_server[0], hostname, options)
|
||||
|
||||
if hasattr(paths, "SSH_CONFIG_DIR"):
|
||||
ssh_config_dir = paths.SSH_CONFIG_DIR
|
||||
else:
|
||||
ssh_config_dir = services.knownservices.sshd.get_config_dir()
|
||||
update_ssh_keys(hostname, ssh_config_dir, options.create_sshfp)
|
||||
argspec_update_ssh_keys = getargspec(update_ssh_keys)
|
||||
# Hotfix for https://github.com/freeipa/freeipa/pull/7343
|
||||
if "options" in argspec_update_ssh_keys.args:
|
||||
update_ssh_keys(hostname, ssh_config_dir, options, cli_server[0])
|
||||
else:
|
||||
update_ssh_keys(hostname, ssh_config_dir, options.create_sshfp)
|
||||
|
||||
try:
|
||||
os.remove(CCACHE_FILE)
|
||||
|
||||
@@ -91,6 +91,11 @@ options:
|
||||
changes
|
||||
type: bool
|
||||
required: no
|
||||
dns_over_tls:
|
||||
description: Configure DNS over TLS
|
||||
type: bool
|
||||
default: no
|
||||
required: no
|
||||
preserve_sssd:
|
||||
description: Preserve old SSSD configuration if possible
|
||||
type: bool
|
||||
@@ -140,6 +145,7 @@ def main():
|
||||
fixed_primary=dict(required=False, type='bool'),
|
||||
permit=dict(required=False, type='bool'),
|
||||
enable_dns_updates=dict(required=False, type='bool'),
|
||||
dns_over_tls=dict(required=False, type='bool', default=False),
|
||||
preserve_sssd=dict(required=False, type='bool'),
|
||||
no_krb5_offline_passwords=dict(required=False, type='bool'),
|
||||
),
|
||||
@@ -169,11 +175,13 @@ def main():
|
||||
options.primary = module.params.get('fixed_primary')
|
||||
options.permit = module.params.get('permit')
|
||||
options.dns_updates = module.params.get('enable_dns_updates')
|
||||
options.dns_over_tls = module.params.get('dns_over_tls')
|
||||
options.preserve_sssd = module.params.get('preserve_sssd')
|
||||
|
||||
options.no_krb5_offline_passwords = module.params.get(
|
||||
'no_krb5_offline_passwords')
|
||||
options.krb5_offline_passwords = not options.no_krb5_offline_passwords
|
||||
options.dns_over_tls = False
|
||||
|
||||
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
|
||||
client_domain = hostname[hostname.find(".") + 1:]
|
||||
|
||||
@@ -124,6 +124,16 @@ options:
|
||||
type: bool
|
||||
required: no
|
||||
default: no
|
||||
dns_over_tls:
|
||||
description: Configure DNS over TLS
|
||||
type: bool
|
||||
default: no
|
||||
required: no
|
||||
no_dnssec_validation:
|
||||
description: Disable DNSSEC validation for DNS over TLS
|
||||
type: bool
|
||||
default: no
|
||||
required: no
|
||||
enable_dns_updates:
|
||||
description:
|
||||
Configures the machine to attempt dns updates when the ip address
|
||||
@@ -248,7 +258,8 @@ from ansible.module_utils.ansible_ipa_client import (
|
||||
CLIENT_INSTALL_ERROR, tasks, check_ldap_conf, timeconf, constants,
|
||||
validate_hostname, nssldap_exists, gssapi, remove_file,
|
||||
check_ip_addresses, ipadiscovery, print_port_conf_info,
|
||||
IPA_PYTHON_VERSION, getargspec
|
||||
IPA_PYTHON_VERSION, getargspec, services,
|
||||
CLIENT_SUPPORTS_NO_DNSSEC_VALIDATION
|
||||
)
|
||||
|
||||
|
||||
@@ -328,6 +339,9 @@ def main():
|
||||
default=None),
|
||||
all_ip_addresses=dict(required=False, type='bool', default=False),
|
||||
on_master=dict(required=False, type='bool', default=False),
|
||||
dns_over_tls=dict(required=False, type='bool', default=False),
|
||||
no_dnssec_validation=dict(required=False, type='bool',
|
||||
default=False),
|
||||
# sssd
|
||||
enable_dns_updates=dict(required=False, type='bool',
|
||||
default=False),
|
||||
@@ -356,6 +370,8 @@ def main():
|
||||
options.ip_addresses = module.params.get('ip_addresses')
|
||||
options.all_ip_addresses = module.params.get('all_ip_addresses')
|
||||
options.on_master = module.params.get('on_master')
|
||||
options.dns_over_tls = module.params.get('dns_over_tls')
|
||||
options.no_dnssec_validation = module.params.get('no_dnssec_validation')
|
||||
options.enable_dns_updates = module.params.get('enable_dns_updates')
|
||||
|
||||
# Get domain from first server if domain is not set, but if there are
|
||||
@@ -365,6 +381,16 @@ def main():
|
||||
options.domain_name = options.servers[0][
|
||||
options.servers[0].find(".") + 1:]
|
||||
|
||||
if options.dns_over_tls \
|
||||
and not services.knownservices["unbound"].is_installed():
|
||||
module.fail_json(
|
||||
msg="To enable DNS over TLS, package ipa-client-encrypted-dns "
|
||||
"must be installed.")
|
||||
if options.dns_over_tls and not CLIENT_SUPPORTS_NO_DNSSEC_VALIDATION:
|
||||
module.fail_json(
|
||||
msg="Important patches for DNS over TLS are missing in your IPA "
|
||||
"version.")
|
||||
|
||||
try:
|
||||
self = options
|
||||
|
||||
|
||||
@@ -231,8 +231,6 @@ try:
|
||||
cli_realm, cli_domain, cli_server, cli_kdc, dnsok,
|
||||
filename, client_domain, client_hostname, force=False,
|
||||
configure_sssd=True):
|
||||
# pylint: disable=global-variable-not-assigned
|
||||
global options
|
||||
options.force = force
|
||||
options.sssd = configure_sssd
|
||||
return ipa_client_install.configure_krb5_conf(
|
||||
@@ -312,6 +310,15 @@ try:
|
||||
except ImportError:
|
||||
configure_selinux_for_client = None
|
||||
|
||||
try:
|
||||
CLIENT_SUPPORTS_NO_DNSSEC_VALIDATION = False
|
||||
from ipaclient.install.client import ClientInstallInterface
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
if hasattr(ClientInstallInterface, "no_dnssec_validation"):
|
||||
CLIENT_SUPPORTS_NO_DNSSEC_VALIDATION = True
|
||||
|
||||
logger = logging.getLogger("ipa-client-install")
|
||||
root_logger = logger
|
||||
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
---
|
||||
# tasks file for ipaclient
|
||||
|
||||
- name: Install - Ensure that IPA client packages are installed
|
||||
ansible.builtin.package:
|
||||
name: "{{ ipaclient_packages }}"
|
||||
state: present
|
||||
- name: Install - Package installation
|
||||
when: ipaclient_install_packages | bool
|
||||
block:
|
||||
|
||||
- name: Install - Set packages for installation
|
||||
ansible.builtin.set_fact:
|
||||
_ipapackages: "{{ ipaclient_packages }}"
|
||||
|
||||
- name: Install - Set packages for installlation, add DOT
|
||||
ansible.builtin.set_fact:
|
||||
_ipapackages: "{{ _ipapackages + ipaclient_packages_dot }}"
|
||||
when: ipaclient_dns_over_tls | bool
|
||||
|
||||
- name: Install - Ensure that packages are installed
|
||||
ansible.builtin.package:
|
||||
name: "{{ _ipapackages }}"
|
||||
state: present
|
||||
|
||||
- name: Install - Set ipaclient_servers
|
||||
ansible.builtin.set_fact:
|
||||
@@ -38,7 +50,7 @@
|
||||
msg: "ipaclient_domain or ipaserver_domain is required for ipaclient_configure_dns_resolver"
|
||||
when: ipaserver_domain is not defined and ipaclient_domain is not defined
|
||||
|
||||
- name: Install - Fail on missing ipaclient_servers
|
||||
- name: Install - Fail on missing ipaclient_dns_servers
|
||||
ansible.builtin.fail:
|
||||
msg: "ipaclient_dns_servers is required for ipaclient_configure_dns_resolver"
|
||||
when: ipaclient_dns_servers is not defined
|
||||
@@ -69,9 +81,10 @@
|
||||
ip_addresses: "{{ ipaclient_ip_addresses | default(omit) }}"
|
||||
all_ip_addresses: "{{ ipaclient_all_ip_addresses }}"
|
||||
on_master: "{{ ipaclient_on_master }}"
|
||||
dns_over_tls: "{{ ipaclient_dns_over_tls }}"
|
||||
no_dnssec_validation: "{{ ipaclient_no_dnssec_validation }}"
|
||||
### sssd ###
|
||||
enable_dns_updates: "{{ ipassd_enable_dns_updates
|
||||
| default(ipasssd_enable_dns_updates) }}"
|
||||
enable_dns_updates: "{{ ipasssd_enable_dns_updates }}"
|
||||
register: result_ipaclient_test
|
||||
|
||||
- name: Install - Client deployment
|
||||
@@ -152,7 +165,7 @@
|
||||
ansible.builtin.copy:
|
||||
src: "{{ ipaadmin_keytab }}"
|
||||
dest: "{{ keytab_temp.path }}"
|
||||
mode: 0600
|
||||
mode: "0600"
|
||||
delegate_to: "{{ result_ipaclient_test.servers[0] }}"
|
||||
when: ipaadmin_keytab is defined
|
||||
|
||||
@@ -168,10 +181,10 @@
|
||||
|
||||
- name: Install - Store the previously obtained OTP
|
||||
no_log: yes
|
||||
when: result_ipaclient_get_otp.host is defined
|
||||
ansible.builtin.set_fact:
|
||||
ipaadmin_orig_password: "{{ ipaadmin_password | default(omit) }}"
|
||||
ipaadmin_password: "{{ result_ipaclient_get_otp.host.randompassword
|
||||
if result_ipaclient_get_otp.host is defined }}"
|
||||
ipaadmin_password: "{{ result_ipaclient_get_otp.host.randompassword | default(omit) }}"
|
||||
rescue:
|
||||
- name: Install - Report error for OTP generation
|
||||
ansible.builtin.debug:
|
||||
@@ -321,16 +334,12 @@
|
||||
no_sshd: "{{ ipaclient_no_sshd }}"
|
||||
no_sudo: "{{ ipaclient_no_sudo }}"
|
||||
all_ip_addresses: "{{ ipaclient_all_ip_addresses }}"
|
||||
fixed_primary: "{{ ipassd_fixed_primary
|
||||
| default(ipasssd_fixed_primary) }}"
|
||||
permit: "{{ ipassd_permit | default(ipasssd_permit) }}"
|
||||
enable_dns_updates: "{{ ipassd_enable_dns_updates
|
||||
| default(ipasssd_enable_dns_updates) }}"
|
||||
preserve_sssd: "{{ ipassd_preserve_sssd
|
||||
| default(ipasssd_preserve_sssd) }}"
|
||||
no_krb5_offline_passwords:
|
||||
"{{ ipassd_no_krb5_offline_passwords
|
||||
| default(ipasssd_no_krb5_offline_passwords) }}"
|
||||
fixed_primary: "{{ ipasssd_fixed_primary }}"
|
||||
permit: "{{ ipasssd_permit }}"
|
||||
enable_dns_updates: "{{ ipasssd_enable_dns_updates }}"
|
||||
dns_over_tls: "{{ ipaclient_dns_over_tls }}"
|
||||
preserve_sssd: "{{ ipasssd_preserve_sssd }}"
|
||||
no_krb5_offline_passwords: "{{ ipasssd_no_krb5_offline_passwords }}"
|
||||
|
||||
- name: Install - IPA API calls for remaining enrollment parts
|
||||
ipaclient_api:
|
||||
@@ -365,23 +374,20 @@
|
||||
ca_enabled: "{{ result_ipaclient_api.ca_enabled }}"
|
||||
on_master: "{{ ipaclient_on_master }}"
|
||||
dnsok: "{{ result_ipaclient_test.dnsok }}"
|
||||
enable_dns_updates: "{{ ipassd_enable_dns_updates
|
||||
| default(ipasssd_enable_dns_updates) }}"
|
||||
enable_dns_updates: "{{ ipasssd_enable_dns_updates }}"
|
||||
dns_over_tls: "{{ ipaclient_dns_over_tls }}"
|
||||
no_dnssec_validation: "{{ ipaclient_no_dnssec_validation }}"
|
||||
all_ip_addresses: "{{ ipaclient_all_ip_addresses }}"
|
||||
ip_addresses: "{{ ipaclient_ip_addresses | default(omit) }}"
|
||||
request_cert: "{{ ipaclient_request_cert }}"
|
||||
preserve_sssd: "{{ ipassd_preserve_sssd
|
||||
| default(ipasssd_preserve_sssd) }}"
|
||||
preserve_sssd: "{{ ipasssd_preserve_sssd }}"
|
||||
no_ssh: "{{ ipaclient_no_ssh }}"
|
||||
no_sshd: "{{ ipaclient_no_sshd }}"
|
||||
no_sudo: "{{ ipaclient_no_sudo }}"
|
||||
subid: "{{ ipaclient_subid }}"
|
||||
fixed_primary: "{{ ipassd_fixed_primary
|
||||
| default(ipasssd_fixed_primary) }}"
|
||||
permit: "{{ ipassd_permit | default(ipasssd_permit) }}"
|
||||
no_krb5_offline_passwords:
|
||||
"{{ ipassd_no_krb5_offline_passwords
|
||||
| default(ipasssd_no_krb5_offline_passwords) }}"
|
||||
fixed_primary: "{{ ipasssd_fixed_primary }}"
|
||||
permit: "{{ ipasssd_permit }}"
|
||||
no_krb5_offline_passwords: "{{ ipasssd_no_krb5_offline_passwords }}"
|
||||
no_dns_sshfp: "{{ ipaclient_no_dns_sshfp }}"
|
||||
nosssd_files: "{{ result_ipaclient_test.nosssd_files }}"
|
||||
selinux_works: "{{ result_ipaclient_test.selinux_works }}"
|
||||
|
||||
@@ -15,8 +15,3 @@
|
||||
ipaclient_configure_dns_resolver:
|
||||
state: absent
|
||||
when: ipaclient_cleanup_dns_resolver | bool
|
||||
|
||||
#- name: Remove IPA client package
|
||||
# ansible.builtin.package:
|
||||
# name: "{{ ipaclient_packages }}"
|
||||
# state: absent
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
# vars/Debian.yml
|
||||
ipaclient_packages: [ "freeipa-client" ]
|
||||
ipaclient_packages_dot: [ ]
|
||||
# Debian Buster must use python2 as Python interpreter due
|
||||
# to the way freeipa-client package is defined.
|
||||
# You must install package python2.7 before executing this role.
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
# vars/Debian.yml
|
||||
---
|
||||
ipaclient_packages: [ "freeipa-client" ]
|
||||
ipaclient_packages_dot: [ ]
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# defaults file for ipaclient
|
||||
# vars/Fedora-25.yml
|
||||
---
|
||||
ipaclient_packages: [ "ipa-client", "libselinux-python" ]
|
||||
#ansible_python_interpreter: '/usr/bin/python2'
|
||||
@@ -1,5 +0,0 @@
|
||||
# defaults file for ipaclient
|
||||
# vars/Fedora-26.yml
|
||||
---
|
||||
ipaclient_packages: [ "ipa-client", "libselinux-python" ]
|
||||
#ansible_python_interpreter: '/usr/bin/python2'
|
||||
@@ -1,5 +0,0 @@
|
||||
# defaults file for ipaclient
|
||||
# vars/RedHat-7.3.yml
|
||||
---
|
||||
ipaclient_packages: [ "ipa-client", "ipa-admintools", "libselinux-python" ]
|
||||
#ansible_python_interpreter: '/usr/bin/python2'
|
||||
@@ -2,4 +2,4 @@
|
||||
# vars/RedHat-7
|
||||
---
|
||||
ipaclient_packages: [ "ipa-client", "libselinux-python" ]
|
||||
#ansible_python_interpreter: '/usr/bin/python2'
|
||||
ipaclient_packages_dot: [ ]
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
# vars/RedHat-8.yml
|
||||
---
|
||||
ipaclient_packages: [ "@idm:DL1/client" ]
|
||||
ipaclient_packages_dot: [ ]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# vars/Ubuntu-18.04.yml
|
||||
---
|
||||
ipaclient_packages: [ "freeipa-client" ]
|
||||
ipaclient_packages_dot: [ ]
|
||||
# Ubuntu Bionic Beaver must use python2 as Python interpreter due
|
||||
# to the way python-ipalib package is defined.
|
||||
# Package python2.7 must be installed before executing this role.
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
# vars/default.yml
|
||||
---
|
||||
ipaclient_packages: [ "ipa-client", "python3-libselinux" ]
|
||||
#ansible_python_interpreter: '/usr/bin/python3'
|
||||
ipaclient_packages_dot: [ "ipa-client-encrypted-dns" ]
|
||||
|
||||
@@ -28,7 +28,7 @@ Supported Distributions
|
||||
|
||||
* RHEL/CentOS 7.6+
|
||||
* CentOS Stream 8+
|
||||
* Fedora 26+
|
||||
* Fedora 40+
|
||||
* Ubuntu 16.04 and 18.04
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ Variable | Description | Required
|
||||
`ipaservers` | This group with the IPA master full qualified hostnames. (list of strings) | mostly
|
||||
`ipareplicas` | Group of IPA replica hostnames. (list of strings) | yes
|
||||
`ipaadmin_password` | The password for the IPA admin user (string) | mostly
|
||||
`ipareplica_ip_addresses` | The list of master server IP addresses. (list of strings) | no
|
||||
`ipareplica_ip_addresses` | The list of IPA replica IP addresses. (list of strings) | no
|
||||
`ipareplica_domain` | The primary DNS domain of an existing IPA deployment. (string) | no
|
||||
`ipaserver_realm` | The Kerberos realm of an existing IPA deployment. (string) | no
|
||||
`ipaserver_hostname` | Fully qualified name of the server. (string) | no
|
||||
@@ -270,6 +270,11 @@ Variable | Description | Required
|
||||
`ipareplica_auto_forwarders` | Add DNS forwarders configured in /etc/resolv.conf to the list of forwarders used by IPA DNS. (bool, default: false) | no
|
||||
`ipareplica_forward_policy` | DNS forwarding policy for global forwarders specified using other options. (choice: first,only) | no
|
||||
`ipareplica_no_dnssec_validation` | Disable DNSSEC validation on this server. (bool, default: false) | no
|
||||
`ipareplica_dot_forwarders` | List of DNS over TLS forwarders. Required if `ipareplica_dns_over_tls` is enabled. (list of strings) | no
|
||||
`ipareplica_dns_over_tls` \| `ipaclient_dns_over_tls` | Configure DNS over TLS. Requires FreeIPA version 4.12.5 or later. (bool, default: false) | no
|
||||
`ipareplica_dns_over_tls_cert` | Certificate to use for DNS over TLS. If empty, a new certificate will be requested from IPA CA. (string) | no
|
||||
`ipareplica_dns_over_tls_key` | Key for certificate specified in `ipareplica_dns_over_tls_cert`. (string) | no
|
||||
`ipareplica_dns_policy` | Encrypted DNS policy. Only usable if `ipareplica_dns_over_tls` is enabled. (choice: relaxed, enforced, default: relaxed) | no
|
||||
|
||||
AD trust Variables
|
||||
------------------
|
||||
|
||||
@@ -17,10 +17,10 @@ ipareplica_no_ui_redirect: no
|
||||
ipaclient_mkhomedir: no
|
||||
ipaclient_force_join: no
|
||||
ipaclient_no_ntp: no
|
||||
#ipaclient_ssh_trust_dns: no
|
||||
#ipaclient_no_ssh: no
|
||||
#ipaclient_no_sshd: no
|
||||
#ipaclient_no_dns_sshfp: no
|
||||
# ipaclient_ssh_trust_dns: no
|
||||
# ipaclient_no_ssh: no
|
||||
# ipaclient_no_sshd: no
|
||||
# ipaclient_no_dns_sshfp: no
|
||||
ipaclient_ssh_trust_dns: no
|
||||
### certificate system ###
|
||||
ipareplica_skip_schema_check: no
|
||||
|
||||
@@ -47,7 +47,7 @@ options:
|
||||
type: str
|
||||
required: no
|
||||
ip_addresses:
|
||||
description: List of Master Server IP Addresses
|
||||
description: List of IPA replica IP addresses
|
||||
type: list
|
||||
elements: str
|
||||
required: no
|
||||
|
||||
@@ -47,7 +47,7 @@ options:
|
||||
type: str
|
||||
required: no
|
||||
ip_addresses:
|
||||
description: List of Master Server IP Addresses
|
||||
description: List of IPA replica IP addresses
|
||||
type: list
|
||||
elements: str
|
||||
required: no
|
||||
|
||||
@@ -49,7 +49,7 @@ options:
|
||||
type: str
|
||||
required: no
|
||||
ip_addresses:
|
||||
description: List of Master Server IP Addresses
|
||||
description: List of IPA replica IP addresses
|
||||
type: list
|
||||
elements: str
|
||||
required: no
|
||||
@@ -224,6 +224,32 @@ options:
|
||||
type: bool
|
||||
default: no
|
||||
required: no
|
||||
dot_forwarders:
|
||||
description: List of DNS over TLS forwarders
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
required: no
|
||||
dns_over_tls:
|
||||
description: Configure DNS over TLS
|
||||
type: bool
|
||||
default: no
|
||||
required: no
|
||||
dns_over_tls_cert:
|
||||
description:
|
||||
Certificate to use for DNS over TLS. If empty, a new
|
||||
certificate will be requested from IPA CA
|
||||
type: str
|
||||
required: no
|
||||
dns_over_tls_key:
|
||||
description: Key for certificate specified in dns_over_tls_cert
|
||||
type: str
|
||||
required: no
|
||||
dns_policy:
|
||||
description: Encrypted DNS policy
|
||||
type: str
|
||||
choices: ['relaxed', 'enforced']
|
||||
default: 'relaxed'
|
||||
enable_compat:
|
||||
description: Enable support for trusted domains for old clients
|
||||
type: bool
|
||||
@@ -354,6 +380,15 @@ def main():
|
||||
choices=['first', 'only'], default=None),
|
||||
no_dnssec_validation=dict(required=False, type='bool',
|
||||
default=False),
|
||||
dot_forwarders=dict(required=False, type='list', elements='str',
|
||||
default=[]),
|
||||
dns_over_tls=dict(required=False, type='bool',
|
||||
default=False),
|
||||
dns_over_tls_cert=dict(required=False, type='str'),
|
||||
dns_over_tls_key=dict(required=False, type='str'),
|
||||
dns_policy=dict(required=False, type='str',
|
||||
choices=['relaxed', 'enforced'],
|
||||
default='relaxed'),
|
||||
# ad trust
|
||||
enable_compat=dict(required=False, type='bool', default=False),
|
||||
netbios_name=dict(required=False, type='str'),
|
||||
@@ -430,6 +465,11 @@ def main():
|
||||
options.forward_policy = ansible_module.params.get('forward_policy')
|
||||
options.no_dnssec_validation = ansible_module.params.get(
|
||||
'no_dnssec_validation')
|
||||
options.dot_forwarders = ansible_module.params.get('dot_forwarders')
|
||||
options.dns_over_tls = ansible_module.params.get('dns_over_tls')
|
||||
options.dns_over_tls_cert = ansible_module.params.get('dns_over_tls_cert')
|
||||
options.dns_over_tls_key = ansible_module.params.get('dns_over_tls_key')
|
||||
options.dns_policy = ansible_module.params.get('dns_policy')
|
||||
# ad trust
|
||||
options.enable_compat = ansible_module.params.get('enable_compat')
|
||||
options.netbios_name = ansible_module.params.get('netbios_name')
|
||||
|
||||
@@ -72,6 +72,32 @@ options:
|
||||
type: bool
|
||||
default: no
|
||||
required: no
|
||||
dot_forwarders:
|
||||
description: List of DNS over TLS forwarders
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
required: no
|
||||
dns_over_tls:
|
||||
description: Configure DNS over TLS
|
||||
type: bool
|
||||
default: no
|
||||
required: no
|
||||
dns_over_tls_cert:
|
||||
description:
|
||||
Certificate to use for DNS over TLS. If empty, a new
|
||||
certificate will be requested from IPA CA
|
||||
type: str
|
||||
required: no
|
||||
dns_over_tls_key:
|
||||
description: Key for certificate specified in dns_over_tls_cert
|
||||
type: str
|
||||
required: no
|
||||
dns_policy:
|
||||
description: Encrypted DNS policy
|
||||
type: str
|
||||
choices: ['relaxed', 'enforced']
|
||||
default: 'relaxed'
|
||||
dns_ip_addresses:
|
||||
description: The dns ip_addresses setting
|
||||
type: list
|
||||
@@ -117,6 +143,9 @@ from ansible.module_utils.ansible_ipa_replica import (
|
||||
gen_ReplicaConfig, gen_remote_api, api, redirect_stdout, dns,
|
||||
ansible_module_get_parsed_ip_addresses
|
||||
)
|
||||
# pylint: disable=unused-import
|
||||
from ansible.module_utils.ansible_ipa_replica import bindinstance # noqa: F401
|
||||
# pylint: enable=unused-import
|
||||
|
||||
|
||||
def main():
|
||||
@@ -135,6 +164,14 @@ def main():
|
||||
choices=['first', 'only'], default=None),
|
||||
no_dnssec_validation=dict(required=False, type='bool',
|
||||
default=False),
|
||||
dot_forwarders=dict(required=False, type='list', elements='str',
|
||||
default=[]),
|
||||
dns_over_tls=dict(required=False, type='bool', default=False),
|
||||
dns_over_tls_cert=dict(required=False, type='str'),
|
||||
dns_over_tls_key=dict(required=False, type='str'),
|
||||
dns_policy=dict(required=False, type='str',
|
||||
choices=['relaxed', 'enforced'],
|
||||
default='relaxed'),
|
||||
# additional
|
||||
dns_ip_addresses=dict(required=True, type='list', elements='str'),
|
||||
dns_reverse_zones=dict(required=True, type='list', elements='str'),
|
||||
@@ -167,6 +204,11 @@ def main():
|
||||
options.forward_policy = ansible_module.params.get('forward_policy')
|
||||
options.no_dnssec_validation = ansible_module.params.get(
|
||||
'no_dnssec_validation')
|
||||
options.dot_forwarders = ansible_module.params.get('dot_forwarders')
|
||||
options.dns_over_tls = ansible_module.params.get('dns_over_tls')
|
||||
options.dns_over_tls_cert = ansible_module.params.get('dns_over_tls_cert')
|
||||
options.dns_over_tls_key = ansible_module.params.get('dns_over_tls_key')
|
||||
options.dns_policy = ansible_module.params.get('dns_policy')
|
||||
# additional
|
||||
dns.ip_addresses = ansible_module_get_parsed_ip_addresses(
|
||||
ansible_module, 'dns_ip_addresses')
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user