mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-03-29 14:53:06 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50611a042f | ||
|
|
cf01262b27 | ||
|
|
0c3d35a577 | ||
|
|
771b0ba029 | ||
|
|
364267f1ab | ||
|
|
2afb8c6a2f | ||
|
|
a36e8e0876 | ||
|
|
1cb0ac67a2 | ||
|
|
d2968b2611 | ||
|
|
03d904b7ea | ||
|
|
7a5fadfc8d | ||
|
|
45b2648af2 | ||
|
|
27fb3e1bb7 | ||
|
|
115f96d0be | ||
|
|
da2631d923 | ||
|
|
c708ef781e | ||
|
|
e7de098790 | ||
|
|
45d8008033 | ||
|
|
5f580b5152 | ||
|
|
7e42102aa5 | ||
|
|
3a3b4cb397 | ||
|
|
5afd889023 | ||
|
|
5d881a9bf3 | ||
|
|
2092220634 | ||
|
|
ca4518a623 |
153
README-group.md
Normal file
153
README-group.md
Normal file
@@ -0,0 +1,153 @@
|
||||
Group module
|
||||
============
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The group module allows to add, remove, enable, disable, unlock und undelete groups.
|
||||
|
||||
The group module is as compatible as possible to the Ansible upstream `ipa_group` module, but addtionally offers to add users to a group and also to remove users from a group.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
* Group management
|
||||
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipagroup module.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
|
||||
Example playbook to add groups:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to handle groups
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
# Create group ops with gid 1234
|
||||
- ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: ops
|
||||
gidnumber: 1234
|
||||
|
||||
# Create group sysops
|
||||
- ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: sysops
|
||||
user:
|
||||
- pinky
|
||||
|
||||
# Create group appops
|
||||
- ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: appops
|
||||
```
|
||||
|
||||
Example playbook to add users to a group:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to handle groups
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
# Add user member brain to group sysops
|
||||
- ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: sysops
|
||||
action: member
|
||||
user:
|
||||
- brain
|
||||
```
|
||||
`action` controls if a the group or member will be handled. To add or remove members, set `action` to `member`.
|
||||
|
||||
|
||||
Example playbook to add group members to a group:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to handle groups
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
# Add group members sysops and appops to group sysops
|
||||
- ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: ops
|
||||
group:
|
||||
- sysops
|
||||
- appops
|
||||
```
|
||||
|
||||
Example playbook to remove groups:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to handle groups
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
# Remove goups sysops, appops and ops
|
||||
- ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: sysops,appops,ops
|
||||
state: absent
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipagroup
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`name` \| `cn` | The list of group name strings. | no
|
||||
`description` | The group description string. | no
|
||||
`gid` \| `gidnumber` | The GID integer. | no
|
||||
`nonposix` | Create as a non-POSIX group. (bool) | no
|
||||
`external` | Allow adding external non-IPA members from trusted domains. (flag) | no
|
||||
`nomembers` | Suppress processing of membership attributes. (bool) | no
|
||||
`user` | List of user name strings assigned to this group. | no
|
||||
`group` | List of group name strings assigned to this group. | no
|
||||
`service` | List of service name strings assigned to this group | no
|
||||
`action` | Work on group or member level. It can be on of `member` or `group` and defaults to `group`. | no
|
||||
`state` | The state to ensure. It can be one of `present` or `absent`, defauilt: `present`. | yes
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Thomas Woerner
|
||||
@@ -50,7 +50,7 @@ Example playbook to add a topology segment wiht default name (cn):
|
||||
tasks:
|
||||
- name: Add topology segment
|
||||
ipatopologysegment:
|
||||
password: MyPassword123
|
||||
ipaadmin_password: MyPassword123
|
||||
suffix: domain
|
||||
left: ipareplica1.test.local
|
||||
right: ipareplica2.test.local
|
||||
@@ -70,7 +70,7 @@ Example playbook to delete a topology segment:
|
||||
tasks:
|
||||
- name: Delete topology segment
|
||||
ipatopologysegment:
|
||||
password: MyPassword123
|
||||
ipaadmin_password: MyPassword123
|
||||
suffix: domain
|
||||
left: ipareplica1.test.local
|
||||
right: ipareplica2.test.local
|
||||
@@ -90,7 +90,7 @@ Example playbook to reinitialize a topology segment:
|
||||
tasks:
|
||||
- name: Reinitialize topology segment
|
||||
ipatopologysegment:
|
||||
password: MyPassword123
|
||||
ipaadmin_password: MyPassword123
|
||||
suffix: domain
|
||||
left: ipareplica1.test.local
|
||||
right: ipareplica2.test.local
|
||||
@@ -111,7 +111,7 @@ Example playbook to verify a topology suffix:
|
||||
tasks:
|
||||
- name: Verify topology suffix
|
||||
ipatopologysuffix:
|
||||
password: MyPassword123
|
||||
ipaadmin_password: MyPassword123
|
||||
suffix: domain
|
||||
state: verified
|
||||
```
|
||||
@@ -136,7 +136,7 @@ Example playbook to add a list of topology segments:
|
||||
tasks:
|
||||
- name: Add topology segment
|
||||
ipatopologysegment:
|
||||
password: "{{ ipaadmin_password }}"
|
||||
ipaadmin_password: "{{ ipaadmin_password }}"
|
||||
suffix: "{{ item.suffix }}"
|
||||
name: "{{ item.name | default(omit) }}"
|
||||
left: "{{ item.left }}"
|
||||
@@ -157,8 +157,8 @@ ipatopologysegment
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`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
|
||||
`suffix` | The topology suffix to be used, this can either be `domain`, `ca` or `domain+ca` | yes
|
||||
`name` \| `cn` | The topology segment name (cn) is the unique identifier for a segment. | no
|
||||
`left` \| `leftnode` | The left replication node string - an IPA server | no
|
||||
@@ -174,8 +174,8 @@ Verify FreeIPA topology suffix
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`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
|
||||
`suffix` | The topology suffix to be used, this can either be `domain` or `ca` | yes
|
||||
`state` | The state to ensure. It can only be `verified` | yes
|
||||
|
||||
|
||||
197
README-user.md
Normal file
197
README-user.md
Normal file
@@ -0,0 +1,197 @@
|
||||
User module
|
||||
===========
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The user module allows to add, remove, enable, disable, unlock und undelete users.
|
||||
|
||||
The user module is as compatible as possible to the Ansible upstream `ipa_user` module, but addtionally offers to preserve delete, enable, disable, unlock and undelete users.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
* User management
|
||||
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
|
||||
FreeIPA versions 4.4.0 and up are supported by the ipauser module.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
|
||||
**Node**
|
||||
* Supported FreeIPA version (see above)
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Example inventory file
|
||||
|
||||
```ini
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
```
|
||||
|
||||
|
||||
Example playbook to add users:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to handle users
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
# Create user pinky
|
||||
- ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky
|
||||
first: pinky
|
||||
last: Acme
|
||||
uid: 10001
|
||||
gid: 100
|
||||
phone: "+555123457"
|
||||
email: pinky@acme.com
|
||||
passwordexpiration: "2023-01-19 23:59:59"
|
||||
password: "no-brain"
|
||||
update_password: on_create
|
||||
|
||||
# Create user brain
|
||||
- ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: brain
|
||||
first: brain
|
||||
last: Acme
|
||||
```
|
||||
`update_password` controls if a password for a user will be set in present state only on creation or every time (always).
|
||||
|
||||
|
||||
Example playbook to delete a user, but preserve it:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to handle users
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
# Remove user pinky and brain
|
||||
- ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky
|
||||
preserve: yes
|
||||
state: disabled
|
||||
```
|
||||
|
||||
|
||||
Example playbook to undelete a user.
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to handle users
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
# Remove user pinky and brain
|
||||
- ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky
|
||||
state: undeleted
|
||||
```
|
||||
|
||||
|
||||
Example playbook to disable a user:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to handle users
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
# Remove user pinky and brain
|
||||
- ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky
|
||||
state: disabled
|
||||
```
|
||||
|
||||
|
||||
Example playbook to enable a users:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to handle users
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
# Remove user pinky and brain
|
||||
- ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky,brain
|
||||
state: disabled
|
||||
```
|
||||
|
||||
|
||||
Example playbook to delete users:
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to handle users
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
# Remove user pinky and brain
|
||||
- ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky,brain
|
||||
state: disabled
|
||||
```
|
||||
|
||||
|
||||
Variables
|
||||
=========
|
||||
|
||||
ipauser
|
||||
-------
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
|
||||
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
|
||||
`name` | The list of user name strings. | no
|
||||
`first` \| `givenname` | The first name string. | no
|
||||
`last` | The last name | no
|
||||
`fullname` \| `cn` | The full name string. | no
|
||||
`displayname` | The display name string. | no
|
||||
`homedir` | The home directory string. | no
|
||||
`shell` \| `loginshell` | The login shell string. | no
|
||||
`email` | List of email address strings. | no
|
||||
`principalname` \| `krbprincipalname` | The kerberos principal sptring. | no
|
||||
`passwordexpiration` \| `krbpasswordexpiration` | The kerberos password expiration date. Possible formats: `YYYYMMddHHmmssZ`, `YYYY-MM-ddTHH:mm:ssZ`, `YYYY-MM-ddTHH:mmZ`, `YYYY-MM-ddZ`, `YYYY-MM-dd HH:mm:ssZ` or `YYYY-MM-dd HH:mmZ`. The trailing 'Z' can be skipped. | no
|
||||
`password` | The user password string. | no
|
||||
`uid` \| `uidnumber` | The UID integer. | no
|
||||
`gid` \| `gidnumber` | The GID integer. | no
|
||||
`phone` \| `telephonenumber` | List of telephone number strings, | no
|
||||
`title` | The job title string. | no
|
||||
~~`sshpubkey` \| `ipasshpubkey`~~ | ~~List of SSH public keys.~~ | ~~no~~
|
||||
`update_password` | Set password for a user in present state only on creation or always. It can be one of `always` or `on_create` and defaults to `always`. | no
|
||||
`preserve` | Delete a user, keeping the entry available for future use. (bool) | no
|
||||
`state` | The state to ensure. It can be one of `present`, `absent`, `enabled`, `disabled`, `unlocked` or `undeleted`, default: `present`. | yes
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
Thomas Woerner
|
||||
15
README.md
15
README.md
@@ -1,7 +1,7 @@
|
||||
FreeIPA Ansible roles
|
||||
=====================
|
||||
|
||||
This repository contains [Ansible](https://www.ansible.com/) roles and playbooks to install and uninstall [FreeIPA](https://www.freeipa.org/) `servers`, `replicas` and `clients`. Also modules for topology management.
|
||||
This repository contains [Ansible](https://www.ansible.com/) roles and playbooks to install and uninstall [FreeIPA](https://www.freeipa.org/) `servers`, `replicas` and `clients`. Also modules for group, topology and user management.
|
||||
|
||||
**Note**: The ansible playbooks and roles require a configured ansible environment where the ansible nodes are reachable and are properly set up to have an IP address and a working package manager.
|
||||
|
||||
@@ -11,7 +11,9 @@ Features
|
||||
* Cluster deployments: Server, replicas and clients in one playbook
|
||||
* One-time-password (OTP) support for client installation
|
||||
* Repair mode for clients
|
||||
* Modules for group management
|
||||
* Modules for topology management
|
||||
* Modules for user management
|
||||
|
||||
Supported FreeIPA Versions
|
||||
--------------------------
|
||||
@@ -32,6 +34,7 @@ Requirements
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+ (ansible-freeipa is an Ansible Collection)
|
||||
* /usr/bin/kinit is required on the controller if a one time password (OTP) is used
|
||||
* python3-gssapi is required on the controller if a one time password (OTP) is used with keytab to install the client.
|
||||
|
||||
**Node**
|
||||
@@ -41,11 +44,11 @@ Requirements
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
**External CA**
|
||||
**External signed CA**
|
||||
|
||||
External CA support is not supported or working. The currently needed two step process is an issue for the processing in the role. The configuration of the server is partly done already and needs to be continued after the CSR has been handled. This is for example breaking the deployment of a server with replicas or clients in one playbook.
|
||||
External signed CA is now supported. But the currently needed two step process is an issue for the processing in a simple playbook.
|
||||
|
||||
Work is planned to have a new method to handle CSR for external CAs in a separate step before starting the server installation.
|
||||
Work is planned to have a new method to handle CSR for external signed CAs in a separate step before starting the server installation.
|
||||
|
||||
|
||||
Usage
|
||||
@@ -66,7 +69,7 @@ The roles provided by ansible-freeipa are not available in ansible galaxy so far
|
||||
Ansible inventory file
|
||||
----------------------
|
||||
|
||||
The most important parts of the inventory file is the definition of the nodes, settings and the topology. Please remember to use [Ansible vault](https://docs.ansible.com/ansible/latest/user_guide/vault.html) for passwords. The examples here are not using vault for better readability.
|
||||
The most important parts of the inventory file is the definition of the nodes, settings and the management modules. Please remember to use [Ansible vault](https://docs.ansible.com/ansible/latest/user_guide/vault.html) for passwords. The examples here are not using vault for better readability.
|
||||
|
||||
**Master server**
|
||||
|
||||
@@ -348,5 +351,7 @@ Roles
|
||||
Modules in plugin/modules
|
||||
=========================
|
||||
|
||||
* [ipagroup](README-group.md)
|
||||
* [ipatopologysegment](README-topology.md)
|
||||
* [ipatopologysuffix](README-topology.md)
|
||||
* [ipauser](README-user.md)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace: "freeipa"
|
||||
name: "ansible_freeipa"
|
||||
version: "0.1.1"
|
||||
version: "0.1.5-1"
|
||||
description: ""
|
||||
|
||||
authors:
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
tasks:
|
||||
- name: Add topology segment
|
||||
ipatopologysegment:
|
||||
password: MyPassword123
|
||||
ipaadmin_password: MyPassword123
|
||||
suffix: domain
|
||||
left: ipareplica1.test.local
|
||||
right: ipareplica2.test.local
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
tasks:
|
||||
- name: Add topology segment
|
||||
ipatopologysegment:
|
||||
password: "{{ ipaadmin_password }}"
|
||||
ipaadmin_password: "{{ ipaadmin_password }}"
|
||||
suffix: "{{ item.suffix }}"
|
||||
name: "{{ item.name | default(omit) }}"
|
||||
left: "{{ item.left }}"
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
tasks:
|
||||
- name: Add topology segment
|
||||
ipatopologysegment:
|
||||
password: "{{ ipaadmin_password }}"
|
||||
ipaadmin_password: "{{ ipaadmin_password }}"
|
||||
suffix: "{{ item.suffix }}"
|
||||
name: "{{ item.name | default(omit) }}"
|
||||
left: "{{ item.left }}"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
tasks:
|
||||
- name: Delete topology segment
|
||||
ipatopologysegment:
|
||||
password: MyPassword123
|
||||
ipaadmin_password: MyPassword123
|
||||
suffix: domain
|
||||
left: ipareplica1.test.local
|
||||
right: ipareplica2.test.local
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
tasks:
|
||||
- name: Add topology segment
|
||||
ipatopologysegment:
|
||||
password: "{{ ipaadmin_password }}"
|
||||
ipaadmin_password: "{{ ipaadmin_password }}"
|
||||
suffix: "{{ item.suffix }}"
|
||||
name: "{{ item.name | default(omit) }}"
|
||||
left: "{{ item.left }}"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
tasks:
|
||||
- name: Reinitialize topology segment
|
||||
ipatopologysegment:
|
||||
password: MyPassword123
|
||||
ipaadmin_password: MyPassword123
|
||||
suffix: domain
|
||||
left: ipareplica1.test.local
|
||||
right: ipareplica2.test.local
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
tasks:
|
||||
- name: Verify topology suffix
|
||||
ipatopologysuffix:
|
||||
password: MyPassword123
|
||||
ipaadmin_password: MyPassword123
|
||||
suffix: domain
|
||||
state: verified
|
||||
|
||||
24
playbooks/user/add-group.yml
Normal file
24
playbooks/user/add-group.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
- name: Playbook to handle groups
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Create group ops with gid 1234
|
||||
ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: ops
|
||||
gidnumber: 1234
|
||||
|
||||
- name: Create group sysops
|
||||
ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: sysops
|
||||
user:
|
||||
- pinky
|
||||
|
||||
- name: Create group appops
|
||||
ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: appops
|
||||
|
||||
13
playbooks/user/add-groups-to-group.yml
Normal file
13
playbooks/user/add-groups-to-group.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: Playbook to handle groups
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Add group members sysops and appops to group sysops
|
||||
ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: ops
|
||||
group:
|
||||
- sysops
|
||||
- appops
|
||||
13
playbooks/user/add-user-to-group.yml
Normal file
13
playbooks/user/add-user-to-group.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: Playbook to handle groups
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Add user member brain to group sysops
|
||||
ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: sysops
|
||||
action: member
|
||||
user:
|
||||
- brain
|
||||
20
playbooks/user/add-user.yml
Normal file
20
playbooks/user/add-user.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
- name: Playbook to handle users
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Create user pinky
|
||||
ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky
|
||||
first: pinky
|
||||
last: Acme
|
||||
uid: 10001
|
||||
gid: 100
|
||||
phone: "+555123457"
|
||||
email: pinky@acme.com
|
||||
passwordexpiration: "2023-01-19 23:59:59"
|
||||
password: "no-brain"
|
||||
update_password: on_create
|
||||
|
||||
11
playbooks/user/delete-group.yml
Normal file
11
playbooks/user/delete-group.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Playbook to handle groups
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Remove goups sysops, appops and ops
|
||||
ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: sysops,appops,ops
|
||||
state: absent
|
||||
12
playbooks/user/delete-preserve-user.yml
Normal file
12
playbooks/user/delete-preserve-user.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- name: Playbook to handle users
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Delete and preserve user pinky
|
||||
ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky
|
||||
preserve: yes
|
||||
state: disabled
|
||||
11
playbooks/user/delete-user.yml
Normal file
11
playbooks/user/delete-user.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Playbook to handle users
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Remove user pinky and brain
|
||||
ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky
|
||||
state: disabled
|
||||
11
playbooks/user/disable-user.yml
Normal file
11
playbooks/user/disable-user.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Playbook to handle users
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Disable user pinky
|
||||
ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky
|
||||
state: disabled
|
||||
11
playbooks/user/enable-user.yml
Normal file
11
playbooks/user/enable-user.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Playbook to handle users
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Enable user pinky
|
||||
ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky
|
||||
state: disabled
|
||||
11
playbooks/user/undelete-user.yml
Normal file
11
playbooks/user/undelete-user.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Playbook to handle users
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Undelete preserved user pinky
|
||||
ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky
|
||||
state: undeleted
|
||||
@@ -25,9 +25,10 @@ import os
|
||||
import sys
|
||||
import tempfile
|
||||
import shutil
|
||||
from datetime import datetime
|
||||
from ipalib import api, errors
|
||||
from ipalib.config import Env
|
||||
from ipalib.constants import DEFAULT_CONFIG
|
||||
from ipalib.constants import DEFAULT_CONFIG, LDAP_GENERALIZED_TIME_FORMAT
|
||||
try:
|
||||
from ipalib.install.kinit import kinit_password
|
||||
except ImportError:
|
||||
@@ -36,7 +37,6 @@ from ipapython.ipautil import run
|
||||
from ipaplatform.paths import paths
|
||||
from ipalib.krb_utils import get_credentials_if_valid
|
||||
|
||||
|
||||
def valid_creds(principal):
|
||||
"""
|
||||
Get valid credintials matching the princial
|
||||
@@ -120,3 +120,40 @@ def execute_api_command(module, principal, password, command, name, args):
|
||||
|
||||
finally:
|
||||
temp_kdestroy(ccache_dir, ccache_name)
|
||||
|
||||
|
||||
def date_format(value):
|
||||
accepted_date_formats = [
|
||||
LDAP_GENERALIZED_TIME_FORMAT, # generalized time
|
||||
'%Y-%m-%dT%H:%M:%SZ', # ISO 8601, second precision
|
||||
'%Y-%m-%dT%H:%MZ', # ISO 8601, minute precision
|
||||
'%Y-%m-%dZ', # ISO 8601, date only
|
||||
'%Y-%m-%d %H:%M:%SZ', # non-ISO 8601, second precision
|
||||
'%Y-%m-%d %H:%MZ', # non-ISO 8601, minute precision
|
||||
]
|
||||
|
||||
for date_format in accepted_date_formats:
|
||||
try:
|
||||
return datetime.strptime(value, date_format)
|
||||
except ValueError:
|
||||
pass
|
||||
raise ValueError("Invalid date '%s'" % value)
|
||||
|
||||
|
||||
def compare_args_ipa(module, args, ipa):
|
||||
for key in args.keys():
|
||||
if key not in ipa:
|
||||
return False
|
||||
else:
|
||||
arg = args[key]
|
||||
ipa_arg = ipa[key]
|
||||
# If ipa_arg is a list and arg is not, replace arg
|
||||
# with list containing arg. Most args in a find result
|
||||
# are lists, but not all.
|
||||
if isinstance(ipa_arg, list) and not isinstance(arg, list):
|
||||
arg = [arg]
|
||||
#module.warn("%s <=> %s" % (arg, ipa_arg))
|
||||
if arg != ipa_arg:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
422
plugins/modules/ipagroup.py
Normal file
422
plugins/modules/ipagroup.py
Normal file
@@ -0,0 +1,422 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
# Thomas Woerner <twoerner@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2019 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
"status": ["preview"],
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: ipagroup
|
||||
short description: Manage FreeIPA groups
|
||||
description: Manage FreeIPA groups
|
||||
options:
|
||||
ipaadmin_principal:
|
||||
description: The admin principal
|
||||
default: admin
|
||||
ipaadmin_password:
|
||||
description: The admin password
|
||||
required: false
|
||||
name:
|
||||
description: The group name
|
||||
required: false
|
||||
aliases: ["cn"]
|
||||
description:
|
||||
description: The group description
|
||||
required: false
|
||||
gid:
|
||||
description: The GID
|
||||
required: false
|
||||
aliases: ["gidnumber"]
|
||||
nonposix:
|
||||
description: Create as a non-POSIX group
|
||||
required: false
|
||||
type: bool
|
||||
external:
|
||||
description: Allow adding external non-IPA members from trusted domains
|
||||
required: false
|
||||
type: bool
|
||||
nomembers:
|
||||
description: Suppress processing of membership attributes
|
||||
required: false
|
||||
type: bool
|
||||
user:
|
||||
description: List of user names assigned to this group.
|
||||
required: false
|
||||
type: list
|
||||
group:
|
||||
description: List of group names assigned to this group.
|
||||
required: false
|
||||
type: list
|
||||
service:
|
||||
description: List of service names assigned to this group.
|
||||
required: false
|
||||
type: list
|
||||
action:
|
||||
description: Work on group or member level
|
||||
default: group
|
||||
choices: ["member", "group"]
|
||||
state:
|
||||
description: State to ensure
|
||||
default: present
|
||||
choices: ["present", "absent"]
|
||||
author:
|
||||
- Thomas Woerner
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Create group ops with gid 1234
|
||||
- ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: ops
|
||||
gidnumber: 1234
|
||||
|
||||
# Create group sysops
|
||||
- ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: sysops
|
||||
|
||||
# Create group appops
|
||||
- ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: appops
|
||||
|
||||
# Add user member pinky to group sysops
|
||||
- ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: sysops
|
||||
action: member
|
||||
user:
|
||||
- pinky
|
||||
|
||||
# Add user member brain to group sysops
|
||||
- ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: sysops
|
||||
action: member
|
||||
user:
|
||||
- brain
|
||||
|
||||
# Add group members sysops and appops to group sysops
|
||||
- ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: ops
|
||||
group:
|
||||
- sysops
|
||||
- appops
|
||||
|
||||
# Remove goups sysops, appops and ops
|
||||
- ipagroup:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: sysops,appops,ops
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils._text import to_bytes, to_native, to_text
|
||||
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
|
||||
temp_kdestroy, valid_creds, api_connect, api_command, date_format, \
|
||||
compare_args_ipa
|
||||
|
||||
|
||||
def find_group(module, name):
|
||||
#module.warn("find_group(.., %s)" % to_text(name))
|
||||
_args = {
|
||||
"all": True,
|
||||
"cn": to_text(name),
|
||||
}
|
||||
|
||||
_result = api_command(module, "group_find", to_text(name), _args)
|
||||
|
||||
if len(_result["result"]) > 1:
|
||||
module.fail_json(
|
||||
msg="There is more than one group '%s'" % (name))
|
||||
elif len(_result["result"]) == 1:
|
||||
return _result["result"][0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def gen_args(description, gid, nonposix, external, nomembers):
|
||||
_args = {}
|
||||
if description is not None:
|
||||
_args["description"] = description
|
||||
if gid is not None:
|
||||
_args["gidnumber"] = str(gid)
|
||||
if nonposix is not None:
|
||||
_args["nonposix"] = nonposix
|
||||
if external is not None:
|
||||
_args["external"] = external
|
||||
if nomembers is not None:
|
||||
_args["nomembers"] = nomembers
|
||||
|
||||
return _args
|
||||
|
||||
|
||||
def gen_member_args(user, group, service):
|
||||
_args = {}
|
||||
if user is not None:
|
||||
_args["member_user"] = user
|
||||
if group is not None:
|
||||
_args["member_group"] = group
|
||||
if service is not None:
|
||||
_args["member_service"] = service
|
||||
|
||||
return _args
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
# general
|
||||
ipaadmin_principal=dict(type="str", default="admin"),
|
||||
ipaadmin_password=dict(type="str", required=False, no_log=True),
|
||||
|
||||
name=dict(type="list", aliases=["cn"], default=None,
|
||||
required=True),
|
||||
# present
|
||||
description=dict(type="str", default=None),
|
||||
gid=dict(type="int", aliases=["gidnumber"], default=None),
|
||||
nonposix=dict(required=False, type='bool', default=None),
|
||||
external=dict(required=False, type='bool', default=None),
|
||||
nomembers=dict(required=False, type='bool', default=None),
|
||||
user=dict(required=False, type='list', default=None),
|
||||
group=dict(required=False, type='list', default=None),
|
||||
service=dict(required=False, type='list', default=None),
|
||||
action=dict(type="str", default="group",
|
||||
choices=["member", "group"]),
|
||||
# state
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent",
|
||||
"member_present", "member_absent"]),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
ansible_module._ansible_debug = True
|
||||
|
||||
# Get parameters
|
||||
|
||||
# general
|
||||
ipaadmin_principal = ansible_module.params.get("ipaadmin_principal")
|
||||
ipaadmin_password = ansible_module.params.get("ipaadmin_password")
|
||||
names = ansible_module.params.get("name")
|
||||
|
||||
# present
|
||||
description = ansible_module.params.get("description")
|
||||
gid = ansible_module.params.get("gid")
|
||||
nonposix = ansible_module.params.get("nonposix")
|
||||
external = ansible_module.params.get("external")
|
||||
nomembers = ansible_module.params.get("nomembers")
|
||||
user = ansible_module.params.get("user")
|
||||
group = ansible_module.params.get("group")
|
||||
service = ansible_module.params.get("service")
|
||||
action = ansible_module.params.get("action")
|
||||
# state
|
||||
state = ansible_module.params.get("state")
|
||||
|
||||
# Check parameters
|
||||
|
||||
if state == "present":
|
||||
if len(names) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Onle one group can be added at a time.")
|
||||
if action == "member":
|
||||
invalid = [ "description", "gid", "nonposix", "external",
|
||||
"nomembers" ]
|
||||
for x in invalid:
|
||||
if vars()[x] is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="Argument '%s' can not be used with action "
|
||||
"'%s'" % (x, action))
|
||||
|
||||
if state == "absent":
|
||||
if len(names) < 1:
|
||||
ansible_module.fail_json(
|
||||
msg="No name given.")
|
||||
invalid = [ "description", "gid", "nonposix", "external", "nomembers" ]
|
||||
if action == "group":
|
||||
invalid.extend(["user", "group", "service"])
|
||||
for x in invalid:
|
||||
if vars()[x] is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="Argument '%s' can not be used with state '%s'" % \
|
||||
(x, state))
|
||||
|
||||
# Init
|
||||
|
||||
changed = False
|
||||
exit_args = { }
|
||||
ccache_dir = None
|
||||
ccache_name = None
|
||||
try:
|
||||
if not valid_creds(ipaadmin_principal):
|
||||
ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
|
||||
ipaadmin_password)
|
||||
api_connect()
|
||||
|
||||
commands = []
|
||||
|
||||
for name in names:
|
||||
# Make sure group exists
|
||||
res_find = find_group(ansible_module, name)
|
||||
#ansible_module.warn("res_find: %s" % repr(res_find))
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
# Generate args
|
||||
args = gen_args(description, gid, nonposix, external,
|
||||
nomembers)
|
||||
|
||||
if action == "group":
|
||||
# Found the group
|
||||
if res_find is not None:
|
||||
# For all settings is args, check if there are
|
||||
# different settings in the find result.
|
||||
# If yes: modify
|
||||
if not compare_args_ipa(ansible_module, args,
|
||||
res_find):
|
||||
commands.append([name, "group_mod", args])
|
||||
else:
|
||||
commands.append([name, "group_add", args])
|
||||
# Set res_find to empty dict for next step
|
||||
res_find = {}
|
||||
|
||||
member_args = gen_member_args(user, group, service)
|
||||
if not compare_args_ipa(ansible_module, member_args,
|
||||
res_find):
|
||||
# Generate addition and removal lists
|
||||
user_add = list(
|
||||
set(user or []) -
|
||||
set(res_find.get("member_user", [])))
|
||||
user_del = list(
|
||||
set(res_find.get("member_user", [])) -
|
||||
set(user or []))
|
||||
group_add = list(
|
||||
set(group or []) -
|
||||
set(res_find.get("member_group", [])))
|
||||
group_del = list(
|
||||
set(res_find.get("member_group", [])) -
|
||||
set(group or []))
|
||||
service_add = list(
|
||||
set(service or []) -
|
||||
set(res_find.get("member_service", [])))
|
||||
service_del = list(
|
||||
set(res_find.get("member_service", [])) -
|
||||
set(service or []))
|
||||
|
||||
# Add members
|
||||
if len(user_add) > 0 or len(group_add) > 0 or \
|
||||
len(service_add) > 0:
|
||||
commands.append([name, "group_add_member",
|
||||
{
|
||||
"user": user_add,
|
||||
"group": group_add,
|
||||
"service": service_add,
|
||||
}])
|
||||
# Remove members
|
||||
if len(user_del) > 0 or len(group_del) > 0 or \
|
||||
len(service_del) > 0:
|
||||
commands.append([name, "group_remove_member",
|
||||
{
|
||||
"user": user_del,
|
||||
"group": group_del,
|
||||
"service": service_del,
|
||||
}])
|
||||
elif action == "member":
|
||||
user_add = list(
|
||||
set(user or []) -
|
||||
set(res_find.get("member_user", [])))
|
||||
group_add = list(
|
||||
set(group or []) -
|
||||
set(res_find.get("member_group", [])))
|
||||
service_add = list(
|
||||
set(service or []) -
|
||||
set(res_find.get("member_service", [])))
|
||||
|
||||
# Add members
|
||||
if len(user_add) > 0 or len(group_add) > 0 or \
|
||||
len(service_add) > 0:
|
||||
commands.append([name, "group_add_member",
|
||||
{
|
||||
"user": user,
|
||||
"group": group,
|
||||
"service": service,
|
||||
}])
|
||||
|
||||
elif state == "absent":
|
||||
if action == "group":
|
||||
if res_find is not None:
|
||||
commands.append([name, "group_del", {}])
|
||||
|
||||
elif action == "member":
|
||||
# Remove intersection member
|
||||
user_del = list(
|
||||
set(user or []) &
|
||||
set(res_find.get("member_user", [])))
|
||||
group_del = list(
|
||||
set(group or []) &
|
||||
set(res_find.get("member_group", [])))
|
||||
service_del = list(
|
||||
set(service or []) &
|
||||
set(res_find.get("member_service", [])))
|
||||
|
||||
# Remove members
|
||||
if len(user_del) > 0 or len(group_del) > 0 or \
|
||||
len(service_del) > 0:
|
||||
commands.append([name, "group_remove_member",
|
||||
{
|
||||
"user": user,
|
||||
"group": group,
|
||||
"service": service,
|
||||
}])
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Execute commands
|
||||
|
||||
for name, command, args in commands:
|
||||
try:
|
||||
result = api_command(ansible_module, command,
|
||||
to_text(name), args)
|
||||
changed = True
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(msg="%s: %s: %s" % (command, name,
|
||||
str(e)))
|
||||
|
||||
#except Exception as e:
|
||||
# ansible_module.fail_json(msg=str(e))
|
||||
|
||||
finally:
|
||||
temp_kdestroy(ccache_dir, ccache_name)
|
||||
|
||||
# Done
|
||||
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -32,10 +32,10 @@ module: ipatopologysegment
|
||||
short description: Manage FreeIPA topology segments
|
||||
description: Manage FreeIPA topology segments
|
||||
options:
|
||||
principal:
|
||||
ipaadmin_principal:
|
||||
description: The admin principal
|
||||
default: admin
|
||||
password:
|
||||
ipaadmin_password:
|
||||
description: The admin password
|
||||
required: false
|
||||
suffix:
|
||||
@@ -173,8 +173,8 @@ def find_left_right_cn(module, suffix, left, right, name):
|
||||
def main():
|
||||
ansible_module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
principal=dict(type="str", default="admin"),
|
||||
password=dict(type="str", required=False, no_log=True),
|
||||
ipaadmin_principal=dict(type="str", default="admin"),
|
||||
ipaadmin_password=dict(type="str", required=False, no_log=True),
|
||||
suffix=dict(choices=["domain", "ca", "domain+ca"], required=True),
|
||||
name=dict(type="str", aliases=["cn"], default=None),
|
||||
left=dict(type="str", aliases=["leftnode"], default=None),
|
||||
@@ -192,8 +192,8 @@ def main():
|
||||
|
||||
# Get parameters
|
||||
|
||||
principal = ansible_module.params.get("principal")
|
||||
password = ansible_module.params.get("password")
|
||||
ipaadmin_principal = ansible_module.params.get("ipaadmin_principal")
|
||||
ipaadmin_password = ansible_module.params.get("ipaadmin_password")
|
||||
suffixes = ansible_module.params.get("suffix")
|
||||
name = ansible_module.params.get("name")
|
||||
left = ansible_module.params.get("left")
|
||||
@@ -214,8 +214,9 @@ def main():
|
||||
ccache_dir = None
|
||||
ccache_name = None
|
||||
try:
|
||||
if not valid_creds(principal):
|
||||
ccache_dir, ccache_name = temp_kinit(principal, password)
|
||||
if not valid_creds(ipaadmin_principal):
|
||||
ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
|
||||
ipaadmin_password)
|
||||
api_connect()
|
||||
|
||||
commands = []
|
||||
|
||||
@@ -32,10 +32,10 @@ module: ipatopologysuffix
|
||||
short description: Verify FreeIPA topology suffix
|
||||
description: Verify FreeIPA topology suffix
|
||||
options:
|
||||
principal:
|
||||
ipaadmin_principal:
|
||||
description: The admin principal
|
||||
default: admin
|
||||
password:
|
||||
ipaadmin_password:
|
||||
description: The admin password
|
||||
required: false
|
||||
suffix:
|
||||
@@ -66,8 +66,8 @@ from ansible.module_utils.ansible_freeipa_module import execute_api_command
|
||||
def main():
|
||||
ansible_module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
principal=dict(type="str", default="admin"),
|
||||
password=dict(type="str", required=False, no_log=True),
|
||||
ipaadmin_principal=dict(type="str", default="admin"),
|
||||
ipaadmin_password=dict(type="str", required=False, no_log=True),
|
||||
suffix=dict(choices=["domain", "ca"], required=True),
|
||||
state=dict(type="str", default="verified",
|
||||
choices=["verified"]),
|
||||
@@ -79,8 +79,8 @@ def main():
|
||||
|
||||
# Get parameters
|
||||
|
||||
principal = ansible_module.params.get("principal")
|
||||
password = ansible_module.params.get("password")
|
||||
ipaadmin_principal = ansible_module.params.get("ipaadmin_principal")
|
||||
ipaadmin_password = ansible_module.params.get("ipaadmin_password")
|
||||
suffix = ansible_module.params.get("suffix")
|
||||
state = ansible_module.params.get("state")
|
||||
|
||||
@@ -98,7 +98,7 @@ def main():
|
||||
|
||||
# Execute command
|
||||
|
||||
execute_api_command(ansible_module, principal, password,
|
||||
execute_api_command(ansible_module, ipaadmin_principal, ipaadmin_password,
|
||||
command, to_text(suffix), args)
|
||||
|
||||
# Done
|
||||
|
||||
457
plugins/modules/ipauser.py
Normal file
457
plugins/modules/ipauser.py
Normal file
@@ -0,0 +1,457 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Authors:
|
||||
# Thomas Woerner <twoerner@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2019 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
"metadata_version": "1.0",
|
||||
"supported_by": "community",
|
||||
"status": ["preview"],
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: ipauser
|
||||
short description: Manage FreeIPA users
|
||||
description: Manage FreeIPA users
|
||||
options:
|
||||
ipaadmin_principal:
|
||||
description: The admin principal
|
||||
default: admin
|
||||
ipaadmin_password:
|
||||
description: The admin password
|
||||
required: false
|
||||
name:
|
||||
description: The list of users (internally uid).
|
||||
required: false
|
||||
first:
|
||||
description: The first name
|
||||
required: false
|
||||
aliases: ["givenname"]
|
||||
last:
|
||||
description: The last name
|
||||
required: false
|
||||
fullname:
|
||||
description: The full name
|
||||
required: false
|
||||
aliases: ["cn"]
|
||||
displayname:
|
||||
description: The display name
|
||||
required: false
|
||||
homedir:
|
||||
description: The home directory
|
||||
required: false
|
||||
shell:
|
||||
description: The login shell
|
||||
required: false
|
||||
aliases: ["loginshell"]
|
||||
email:
|
||||
description: List of email addresses
|
||||
required: false
|
||||
principalname:
|
||||
description: The kerberos principal
|
||||
required: false
|
||||
aliases: ["krbprincipalname"]
|
||||
passwordexpiration:
|
||||
description:
|
||||
- The kerberos password expiration date
|
||||
- (possible formats: YYYYMMddHHmmssZ, YYYY-MM-ddTHH:mm:ssZ,
|
||||
- YYYY-MM-ddTHH:mmZ, YYYY-MM-ddZ, YYYY-MM-dd HH:mm:ssZ,
|
||||
- YYYY-MM-dd HH:mmZ) The trailing 'Z' can be skipped.
|
||||
required: false
|
||||
aliases: ["krbpasswordexpiration"]
|
||||
password:
|
||||
description: The user password
|
||||
required: false
|
||||
uid:
|
||||
description: The UID
|
||||
required: false
|
||||
aliases: ["uidnumber"]
|
||||
gid:
|
||||
description: The GID
|
||||
required: false
|
||||
aliases: ["gidnumber"]
|
||||
phone:
|
||||
description: List of telephone numbers
|
||||
required: false
|
||||
aliases: ["telephonenumber"]
|
||||
title:
|
||||
description: The job title
|
||||
required: false
|
||||
#sshpubkey:
|
||||
# description: List of SSH public keys
|
||||
# required: false
|
||||
# aliases: ["ipasshpubkey"]
|
||||
# ..
|
||||
update_password:
|
||||
description: Set password for a user in present state only on creation or always
|
||||
default: 'always'
|
||||
choices: ["always", "on_create"]
|
||||
preserve:
|
||||
description: Delete a user, keeping the entry available for future use
|
||||
required: false
|
||||
state:
|
||||
description: State to ensure
|
||||
default: present
|
||||
choices: ["present", "absent",
|
||||
"enabled", "disabled",
|
||||
"unlocked", "undeleted"]
|
||||
author:
|
||||
- Thomas Woerner
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Create user pinky
|
||||
- ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky
|
||||
first: pinky
|
||||
last: Acme
|
||||
uid: 10001
|
||||
gid: 100
|
||||
phone: "+555123457"
|
||||
email: pinky@acme.com
|
||||
passwordexpiration: "2023-01-19 23:59:59"
|
||||
password: "no-brain"
|
||||
update_password: on_create
|
||||
|
||||
# Create user brain
|
||||
- ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: brain
|
||||
first: brain
|
||||
last: Acme
|
||||
|
||||
# Delete user pinky, but preserved
|
||||
- ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky
|
||||
preserve: yes
|
||||
state: absent
|
||||
|
||||
# Undelete user pinky
|
||||
- ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky
|
||||
state: undeleted
|
||||
|
||||
# Disable user pinky
|
||||
- ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky,brain
|
||||
state: disabled
|
||||
|
||||
# Enable user pinky and brain
|
||||
- ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky,brain
|
||||
state: enabled
|
||||
|
||||
# Remove user pinky and brain
|
||||
- ipauser:
|
||||
ipaadmin_password: MyPassword123
|
||||
name: pinky,brain
|
||||
state: disabled
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils._text import to_bytes, to_native, to_text
|
||||
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
|
||||
temp_kdestroy, valid_creds, api_connect, api_command, date_format, \
|
||||
compare_args_ipa
|
||||
|
||||
|
||||
def find_user(module, name, preserved=False):
|
||||
#module.warn("find_user(.., %s)" % to_text(name))
|
||||
_args = {
|
||||
"all": True,
|
||||
"uid": to_text(name),
|
||||
}
|
||||
if preserved:
|
||||
_args["preserved"] = preserved
|
||||
|
||||
_result = api_command(module, "user_find", to_text(name), _args)
|
||||
|
||||
if len(_result["result"]) > 1:
|
||||
module.fail_json(
|
||||
msg="There is more than one user '%s'" % (name))
|
||||
elif len(_result["result"]) == 1:
|
||||
return _result["result"][0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def gen_args(first, last, fullname, displayname, homedir, shell, emails,
|
||||
principalname, passwordexpiration, password, uid, gid,
|
||||
phones, title, sshpubkey):
|
||||
_args = {}
|
||||
if first is not None:
|
||||
_args["givenname"] = first
|
||||
if last is not None:
|
||||
_args["sn"] = last
|
||||
if fullname is not None:
|
||||
_args["cn"] = fullname
|
||||
if displayname is not None:
|
||||
_args["displayname"] = displayname
|
||||
if homedir is not None:
|
||||
_args["homedirectory"] = homedir
|
||||
if shell is not None:
|
||||
_args["loginshell"] = shell
|
||||
if emails is not None and len(emails) > 0:
|
||||
_args["mail"] = emails
|
||||
if principalname is not None:
|
||||
_args["krbprincipalname"] = principalname
|
||||
if passwordexpiration is not None:
|
||||
_args["krbpasswordexpiration"] = passwordexpiration
|
||||
if password is not None:
|
||||
_args["userpassword"] = password
|
||||
if uid is not None:
|
||||
_args["uidnumber"] = str(uid)
|
||||
if gid is not None:
|
||||
_args["gidnumber"] = str(gid)
|
||||
if phones is not None and len(phones) > 0:
|
||||
_args["telephonenumber"] = phones
|
||||
if title is not None:
|
||||
_args["title"] = title
|
||||
if sshpubkey is not None:
|
||||
_args["ipasshpubkey"] = sshpubkey
|
||||
|
||||
return _args
|
||||
|
||||
|
||||
def main():
|
||||
ansible_module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
# general
|
||||
ipaadmin_principal=dict(type="str", default="admin"),
|
||||
ipaadmin_password=dict(type="str", required=False, no_log=True),
|
||||
|
||||
name=dict(type="list", aliases=["login"], default=None,
|
||||
required=True),
|
||||
# present
|
||||
first=dict(type="str", aliases=["givenname"], default=None),
|
||||
last=dict(type="str", default=None),
|
||||
fullname=dict(type="str", aliases=["cn"], default=None),
|
||||
displayname=dict(type="str", default=None),
|
||||
homedir=dict(type="str", default=None),
|
||||
shell=dict(type="str", aliases=["loginshell"], default=None),
|
||||
email=dict(type="list", default=None),
|
||||
principalname=dict(type="str", aliases=["krbprincipalname"],
|
||||
default=None),
|
||||
passwordexpiration=dict(type="str",
|
||||
aliases=["krbpasswordexpiration"],
|
||||
default=None),
|
||||
password=dict(type="str", default=None, no_log=True),
|
||||
uid=dict(type="int", aliases=["uidnumber"], default=None),
|
||||
gid=dict(type="int", aliases=["gidnumber"], default=None),
|
||||
phone=dict(type="list", aliases=["telephonenumber"], default=None),
|
||||
title=dict(type="str", default=None),
|
||||
#sshpubkey=dict(type="list", aliases=["ipasshpubkey"],
|
||||
# default=None),
|
||||
update_password=dict(type='str', default=None,
|
||||
choices=['always', 'on_create']),
|
||||
# deleted
|
||||
preserve=dict(required=False, type='bool', default=None),
|
||||
# state
|
||||
state=dict(type="str", default="present",
|
||||
choices=["present", "absent", "enabled", "disabled",
|
||||
"unlocked", "undeleted"]),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
ansible_module._ansible_debug = True
|
||||
|
||||
# Get parameters
|
||||
|
||||
# general
|
||||
ipaadmin_principal = ansible_module.params.get("ipaadmin_principal")
|
||||
ipaadmin_password = ansible_module.params.get("ipaadmin_password")
|
||||
names = ansible_module.params.get("name")
|
||||
|
||||
# present
|
||||
first = ansible_module.params.get("first")
|
||||
last = ansible_module.params.get("last")
|
||||
fullname = ansible_module.params.get("fullname")
|
||||
displayname = ansible_module.params.get("displayname")
|
||||
homedir = ansible_module.params.get("homedir")
|
||||
shell = ansible_module.params.get("shell")
|
||||
emails = ansible_module.params.get("email")
|
||||
principalname = ansible_module.params.get("principalname")
|
||||
passwordexpiration = ansible_module.params.get("passwordexpiration")
|
||||
if passwordexpiration is not None:
|
||||
if passwordexpiration[:-1] != "Z":
|
||||
passwordexpiration = "%sZ" % passwordexpiration
|
||||
passwordexpiration = date_format(passwordexpiration)
|
||||
password = ansible_module.params.get("password")
|
||||
uid = ansible_module.params.get("uid")
|
||||
gid = ansible_module.params.get("gid")
|
||||
phones = ansible_module.params.get("phone")
|
||||
title = ansible_module.params.get("title")
|
||||
sshpubkey = ansible_module.params.get("sshpubkey")
|
||||
update_password = ansible_module.params.get("update_password")
|
||||
# deleted
|
||||
preserve = ansible_module.params.get("preserve")
|
||||
# state
|
||||
state = ansible_module.params.get("state")
|
||||
|
||||
# Check parameters
|
||||
|
||||
if state == "present":
|
||||
if len(names) != 1:
|
||||
ansible_module.fail_json(
|
||||
msg="Onle one user can be added at a time.")
|
||||
if first is None:
|
||||
ansible_module.fail_json(msg="First name is needed")
|
||||
if last is None:
|
||||
ansible_module.fail_json(msg="Last name is needed")
|
||||
|
||||
if state == "absent":
|
||||
if len(names) < 1:
|
||||
ansible_module.fail_json(
|
||||
msg="No name given.")
|
||||
for x in [ "first", "last", "fullname", "displayname", "homedir",
|
||||
"shell", "emails", "principalname", "passwordexpiration",
|
||||
"password", "uid", "gid", "phones", "title", "sshpubkey",
|
||||
"update_password" ]:
|
||||
if vars()[x] is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="Argument '%s' can not be used with state '%s'" % \
|
||||
(x, state))
|
||||
else:
|
||||
if preserve is not None:
|
||||
ansible_module.fail_json(
|
||||
msg="Preserve is only possible for state=absent")
|
||||
|
||||
if update_password is None:
|
||||
update_password = "always"
|
||||
|
||||
# Init
|
||||
|
||||
changed = False
|
||||
exit_args = { }
|
||||
ccache_dir = None
|
||||
ccache_name = None
|
||||
try:
|
||||
if not valid_creds(ipaadmin_principal):
|
||||
ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
|
||||
ipaadmin_password)
|
||||
api_connect()
|
||||
|
||||
commands = []
|
||||
|
||||
for name in names:
|
||||
# Make sure user exists
|
||||
res_find = find_user(ansible_module, name)
|
||||
# Also search for preserved user
|
||||
res_find_preserved = find_user(ansible_module, name,
|
||||
preserved=True)
|
||||
#ansible_module.warn("res_find: %s" % repr(res_find))
|
||||
|
||||
# Create command
|
||||
if state == "present":
|
||||
# Generate args
|
||||
args = gen_args(
|
||||
first, last, fullname, displayname, homedir, shell, emails,
|
||||
principalname, passwordexpiration, password, uid, gid,
|
||||
phones, title, sshpubkey)
|
||||
|
||||
# Also check preserved users
|
||||
if res_find is None and res_find_preserved is not None:
|
||||
res_find = res_find_preserved
|
||||
|
||||
# Found the user
|
||||
if res_find is not None:
|
||||
# Ignore password with update_password == on_create
|
||||
if update_password == "on_create" and \
|
||||
"userpassword" in args:
|
||||
del args["userpassword"]
|
||||
|
||||
# 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, "user_mod", args])
|
||||
else:
|
||||
commands.append([name, "user_add", args])
|
||||
|
||||
elif state == "absent":
|
||||
# Also check preserved users
|
||||
if res_find is None and res_find_preserved is not None:
|
||||
res_find = res_find_preserved
|
||||
|
||||
if res_find is not None:
|
||||
args = {}
|
||||
if preserve is not None:
|
||||
args["preserve"] = preserve
|
||||
commands.append([name, "user_del", args])
|
||||
|
||||
elif state == "undeleted":
|
||||
if res_find_preserved is not None:
|
||||
commands.append([name, "user_undel", {}])
|
||||
else:
|
||||
raise ValueError("No preserved user '%s'" % name)
|
||||
|
||||
elif state == "enabled":
|
||||
if res_find is not None:
|
||||
if res_find["nsaccountlock"] == True:
|
||||
commands.append([name, "user_enable", {}])
|
||||
else:
|
||||
raise ValueError("No disabled user '%s'" % name)
|
||||
|
||||
elif state == "disabled":
|
||||
if res_find is not None:
|
||||
if res_find["nsaccountlock"] == False:
|
||||
commands.append([name, "user_disable", {}])
|
||||
else:
|
||||
raise ValueError("No user '%s'" % name)
|
||||
|
||||
elif state == "unlocked":
|
||||
if res_find is not None:
|
||||
commands.append([name, "user_unlock", {}])
|
||||
|
||||
else:
|
||||
ansible_module.fail_json(msg="Unkown state '%s'" % state)
|
||||
|
||||
# Execute commands
|
||||
|
||||
for name, command, args in commands:
|
||||
try:
|
||||
result = api_command(ansible_module, command,
|
||||
to_text(name), args)
|
||||
changed = True
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(msg="%s: %s: %s" % (command, name,
|
||||
str(e)))
|
||||
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(msg=str(e))
|
||||
|
||||
finally:
|
||||
temp_kdestroy(ccache_dir, ccache_name)
|
||||
|
||||
# Done
|
||||
|
||||
ansible_module.exit_json(changed=changed, **exit_args)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -32,6 +32,7 @@ Requirements
|
||||
|
||||
**Controller**
|
||||
* Ansible version: 2.8+
|
||||
* /usr/bin/kinit is required on the controller if a one time password (OTP) is used
|
||||
* python3-gssapi is required on the controller if a one time password (OTP) is used to install the client.
|
||||
|
||||
**Node**
|
||||
|
||||
@@ -52,7 +52,8 @@ def run_cmd(args, stdin=None):
|
||||
close_fds=True)
|
||||
stdout, stderr = p.communicate(stdin)
|
||||
|
||||
return p.returncode
|
||||
if p.returncode != 0:
|
||||
raise RuntimeError(stderr)
|
||||
|
||||
|
||||
def kinit_password(principal, password, ccache_name, config):
|
||||
@@ -197,12 +198,14 @@ class ActionModule(ActionBase):
|
||||
f.write(content)
|
||||
|
||||
if password:
|
||||
# perform kinit -c ccache_name -l 1h principal
|
||||
res = kinit_password(principal, password, ccache_name,
|
||||
krb5conf_name)
|
||||
if res:
|
||||
try:
|
||||
# perform kinit -c ccache_name -l 1h principal
|
||||
kinit_password(principal, password, ccache_name,
|
||||
krb5conf_name)
|
||||
except Exception as e:
|
||||
result['failed'] = True
|
||||
result['msg'] = 'kinit %s with password failed' % principal
|
||||
result['msg'] = 'kinit %s with password failed: %s' % \
|
||||
(principal, to_native(e))
|
||||
return result
|
||||
|
||||
else:
|
||||
|
||||
@@ -90,7 +90,6 @@ def main():
|
||||
os.environ['KRB5CCNAME'] = paths.IPA_DNS_CCACHE
|
||||
|
||||
options.ca_cert_file = None
|
||||
options.unattended = True
|
||||
options.principal = None
|
||||
options.force = False
|
||||
options.password = None
|
||||
|
||||
@@ -180,7 +180,6 @@ def main():
|
||||
sssd = True
|
||||
|
||||
options.ca_cert_file = ca_cert_file
|
||||
options.unattended = True
|
||||
options.principal = principal
|
||||
options.force = False
|
||||
options.password = password
|
||||
|
||||
@@ -124,7 +124,12 @@ def main():
|
||||
if sync_time is not None:
|
||||
if options.conf_ntp:
|
||||
# Attempt to configure and sync time with NTP server (chrony).
|
||||
synced_ntp = sync_time(options, fstore, statestore)
|
||||
argspec = inspect.getargspec(sync_time)
|
||||
if "options" not in argspec.args:
|
||||
synced_ntp = sync_time(options.ntp_servers, options.ntp_pool,
|
||||
fstore, statestore)
|
||||
else:
|
||||
synced_ntp = sync_time(options, fstore, statestore)
|
||||
elif options.on_master:
|
||||
# If we're on master skipping the time sync here because it was done
|
||||
# in ipa-server-install
|
||||
|
||||
@@ -413,7 +413,6 @@ def main():
|
||||
|
||||
# root_logger
|
||||
options.debug = False
|
||||
options.unattended = not installer.interactive
|
||||
if options.domain_name:
|
||||
options.domain = normalize_hostname(installer.domain_name)
|
||||
else:
|
||||
@@ -493,9 +492,10 @@ def main():
|
||||
try:
|
||||
timeconf.check_timedate_services()
|
||||
except timeconf.NTPConflictingService as e:
|
||||
logger.info("WARNING: conflicting time&date synchronization service '{}'"
|
||||
" will be disabled".format(e.conflicting_service))
|
||||
logger.info("in favor of chronyd")
|
||||
logger.info(
|
||||
"WARNING: conflicting time&date synchronization service "
|
||||
"'%s' will be disabled in favor of chronyd" % \
|
||||
e.conflicting_service)
|
||||
logger.info("")
|
||||
except timeconf.NTPConfigurationError:
|
||||
pass
|
||||
@@ -801,6 +801,13 @@ def main():
|
||||
# "Proceed with fixed values and no DNS discovery?", False):
|
||||
# raise ScriptError(rval=CLIENT_INSTALL_ERROR)
|
||||
|
||||
# Do not ask for time source
|
||||
#if options.conf_ntp:
|
||||
# if not options.on_master and not options.unattended and not (
|
||||
# options.ntp_servers or options.ntp_pool):
|
||||
# options.ntp_servers, options.ntp_pool = \
|
||||
# timeconf.get_time_source()
|
||||
|
||||
cli_realm = ds.realm
|
||||
cli_realm_source = ds.realm_source
|
||||
logger.debug("will use discovered realm: %s", cli_realm)
|
||||
@@ -830,6 +837,14 @@ def main():
|
||||
logger.info("BaseDN: %s", cli_basedn)
|
||||
logger.debug("BaseDN source: %s", cli_basedn_source)
|
||||
|
||||
if not options.on_master:
|
||||
if options.ntp_servers:
|
||||
for server in options.ntp_servers:
|
||||
logger.info("NTP server: %s", server)
|
||||
|
||||
if options.ntp_pool:
|
||||
logger.info("NTP pool: %s", options.ntp_pool)
|
||||
|
||||
# ipa-join would fail with IP address instead of a FQDN
|
||||
for srv in cli_server:
|
||||
try:
|
||||
@@ -895,6 +910,8 @@ def main():
|
||||
client_domain=client_domain,
|
||||
dnsok=dnsok,
|
||||
sssd=options.sssd,
|
||||
ntp_servers=options.ntp_servers,
|
||||
ntp_pool=options.ntp_pool,
|
||||
client_already_configured=client_already_configured,
|
||||
ipa_python_version=IPA_PYTHON_VERSION)
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ installer = installer_obj()
|
||||
# Create options
|
||||
options = installer
|
||||
options.interactive = False
|
||||
options.unattended = not options.interactive
|
||||
|
||||
if NUM_VERSION >= 40400:
|
||||
# IPA version >= 4.4
|
||||
|
||||
@@ -63,8 +63,8 @@
|
||||
- name: Install - Configure NTP
|
||||
ipaclient_setup_ntp:
|
||||
### basic ###
|
||||
ntp_servers: "{{ ipaclient_ntp_servers | default(omit) }}"
|
||||
ntp_pool: "{{ ipaclient_ntp_pool | default(omit) }}"
|
||||
ntp_servers: "{{ result_ipaclient_test.ntp_servers | default(omit) }}"
|
||||
ntp_pool: "{{ result_ipaclient_test.ntp_pool | default(omit) }}"
|
||||
no_ntp: "{{ ipaclient_no_ntp }}"
|
||||
# force_ntpd: "{{ ipaclient_force_ntpd }}"
|
||||
on_master: "{{ ipaclient_on_master }}"
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
- "{{ role_path }}/vars/default.yml"
|
||||
|
||||
- name: Install IPA client
|
||||
include_tasks: tasks/install.yml
|
||||
include_tasks: install.yml
|
||||
when: state|default('present') == 'present'
|
||||
|
||||
- name: Uninstall IPA client
|
||||
include_tasks: tasks/uninstall.yml
|
||||
include_tasks: uninstall.yml
|
||||
when: state|default('present') == 'absent'
|
||||
|
||||
3
roles/ipaclient/vars/RedHat-8.yml
Normal file
3
roles/ipaclient/vars/RedHat-8.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
# defaults file for ipaclient
|
||||
# vars/RedHat-8.yml
|
||||
ipaclient_packages: [ "@idm:DL1/client" ]
|
||||
@@ -172,6 +172,7 @@ def main():
|
||||
### additional ###
|
||||
server=dict(required=True),
|
||||
config_master_host_name=dict(required=True),
|
||||
config_ca_host_name=dict(required=True),
|
||||
ccache=dict(required=True),
|
||||
installer_ccache=dict(required=True),
|
||||
_ca_enabled=dict(required=False, type='bool'),
|
||||
@@ -183,6 +184,7 @@ def main():
|
||||
_add_to_ipaservers = dict(required=True, type='bool'),
|
||||
_ca_subject=dict(required=True),
|
||||
_subject_base=dict(required=True),
|
||||
master=dict(required=False, default=None),
|
||||
|
||||
dirman_password=dict(required=True, no_log=True),
|
||||
),
|
||||
@@ -227,6 +229,7 @@ def main():
|
||||
# '_hostname_overridden')
|
||||
options.server = ansible_module.params.get('server')
|
||||
master_host_name = ansible_module.params.get('config_master_host_name')
|
||||
ca_host_name = ansible_module.params.get('config_ca_host_name')
|
||||
ccache = ansible_module.params.get('ccache')
|
||||
os.environ['KRB5CCNAME'] = ccache
|
||||
#os.environ['KRB5CCNAME'] = ansible_module.params.get('installer_ccache')
|
||||
@@ -246,6 +249,7 @@ def main():
|
||||
|
||||
options._ca_subject = ansible_module.params.get('_ca_subject')
|
||||
options._subject_base = ansible_module.params.get('_subject_base')
|
||||
master = ansible_module.params.get('master')
|
||||
|
||||
dirman_password = ansible_module.params.get('dirman_password')
|
||||
|
||||
@@ -267,6 +271,7 @@ def main():
|
||||
config = gen_ReplicaConfig()
|
||||
config.subject_base = options.subject_base
|
||||
config.dirman_password = dirman_password
|
||||
config.ca_host_name = ca_host_name
|
||||
|
||||
remote_api = gen_remote_api(master_host_name, paths.ETC_IPA)
|
||||
installer._remote_api = remote_api
|
||||
@@ -284,7 +289,7 @@ def main():
|
||||
# successful uninstallation
|
||||
# The configuration creation has to be here otherwise previous call
|
||||
# To config certmonger would try to connect to local server
|
||||
create_ipa_conf(fstore, config, ca_enabled)
|
||||
create_ipa_conf(fstore, config, ca_enabled, master)
|
||||
|
||||
# done #
|
||||
|
||||
|
||||
@@ -64,6 +64,12 @@ options:
|
||||
_ca_file:
|
||||
description:
|
||||
required: yes
|
||||
_kra_enabled:
|
||||
description:
|
||||
required: yes
|
||||
_kra_host_name:
|
||||
description:
|
||||
required: yes
|
||||
_dirsrv_pkcs12_info:
|
||||
description:
|
||||
required: yes
|
||||
@@ -103,6 +109,8 @@ def main():
|
||||
ccache=dict(required=True),
|
||||
_ca_enabled=dict(required=False, type='bool'),
|
||||
_ca_file=dict(required=False),
|
||||
_kra_enabled=dict(required=False, type='bool'),
|
||||
_kra_host_name=dict(required=False),
|
||||
_dirsrv_pkcs12_info = dict(required=False),
|
||||
_pkinit_pkcs12_info = dict(required=False),
|
||||
_top_dir = dict(required=True),
|
||||
@@ -135,6 +143,8 @@ def main():
|
||||
#os.environ['KRB5CCNAME'] = ansible_module.params.get('installer_ccache')
|
||||
#installer._ccache = ansible_module.params.get('installer_ccache')
|
||||
ca_enabled = ansible_module.params.get('_ca_enabled')
|
||||
kra_enabled = ansible_module.params.get('_kra_enabled')
|
||||
kra_host_name = ansible_module.params.get('_kra_host_name')
|
||||
dirsrv_pkcs12_info = ansible_module.params.get('_dirsrv_pkcs12_info')
|
||||
pkinit_pkcs12_info = ansible_module.params.get('_pkinit_pkcs12_info')
|
||||
options._top_dir = ansible_module.params.get('_top_dir')
|
||||
@@ -161,6 +171,8 @@ def main():
|
||||
config.ca_host_name = config_ca_host_name
|
||||
config.subject_base = options.subject_base
|
||||
config.promote = installer.promote
|
||||
config.kra_enabled = kra_enabled
|
||||
config.kra_host_name = kra_host_name
|
||||
|
||||
remote_api = gen_remote_api(config.master_host_name, paths.ETC_IPA)
|
||||
installer._remote_api = remote_api
|
||||
|
||||
@@ -49,6 +49,9 @@ options:
|
||||
setup_ca:
|
||||
description: Configure a dogtag CA
|
||||
required: yes
|
||||
setup_kra:
|
||||
description: Configure KRA
|
||||
required: yes
|
||||
config_master_host_name:
|
||||
description: The master host name
|
||||
required: yes
|
||||
@@ -77,6 +80,7 @@ def main():
|
||||
ccache=dict(required=True),
|
||||
_top_dir = dict(required=True),
|
||||
setup_ca=dict(required=True, type='bool'),
|
||||
setup_kra=dict(required=True, type='bool'),
|
||||
config_master_host_name=dict(required=True),
|
||||
),
|
||||
supports_check_mode = True,
|
||||
@@ -100,6 +104,7 @@ def main():
|
||||
os.environ['KRB5CCNAME'] = ccache
|
||||
options._top_dir = ansible_module.params.get('_top_dir')
|
||||
options.setup_ca = ansible_module.params.get('setup_ca')
|
||||
options.setup_kra = ansible_module.params.get('setup_kra')
|
||||
config_master_host_name = ansible_module.params.get('config_master_host_name')
|
||||
|
||||
# init #
|
||||
|
||||
@@ -133,6 +133,8 @@ def main():
|
||||
krb.init_info(api.env.realm, api.env.host,
|
||||
setup_pkinit=not options.no_pkinit,
|
||||
subject_base=options.subject_base)
|
||||
krb.pkcs12_info = options._pkinit_pkcs12_info
|
||||
krb.master_fqdn = master_host_name
|
||||
|
||||
ansible_log.debug("-- KRB ENABLE_SSL --")
|
||||
|
||||
|
||||
@@ -728,6 +728,7 @@ def main():
|
||||
config_setup_ca=config.setup_ca,
|
||||
config_master_host_name=config.master_host_name,
|
||||
config_ca_host_name=config.ca_host_name,
|
||||
config_kra_host_name=config.kra_host_name,
|
||||
config_ips=[ str(ip) for ip in config.ips ],
|
||||
### ad trust ###
|
||||
rid_base=options.rid_base,
|
||||
|
||||
@@ -61,6 +61,12 @@ options:
|
||||
_ca_file:
|
||||
description:
|
||||
required: yes
|
||||
_kra_enabled:
|
||||
description:
|
||||
required: yes
|
||||
_kra_host_name:
|
||||
description:
|
||||
required: yes
|
||||
_dirsrv_pkcs12_info:
|
||||
description:
|
||||
required: yes
|
||||
@@ -118,6 +124,8 @@ def main():
|
||||
ccache=dict(required=True),
|
||||
_ca_enabled=dict(required=False, type='bool'),
|
||||
_ca_file=dict(required=False),
|
||||
_kra_enabled=dict(required=False, type='bool'),
|
||||
_kra_host_name=dict(required=False),
|
||||
_dirsrv_pkcs12_info = dict(required=False),
|
||||
_pkinit_pkcs12_info = dict(required=False),
|
||||
_top_dir = dict(required=True),
|
||||
@@ -152,6 +160,8 @@ def main():
|
||||
#os.environ['KRB5CCNAME'] = ansible_module.params.get('installer_ccache')
|
||||
#installer._ccache = ansible_module.params.get('installer_ccache')
|
||||
ca_enabled = ansible_module.params.get('_ca_enabled')
|
||||
kra_enabled = ansible_module.params.get('_kra_enabled')
|
||||
kra_host_name = ansible_module.params.get('_kra_host_name')
|
||||
installer._dirsrv_pkcs12_info = ansible_module.params.get('_dirsrv_pkcs12_info')
|
||||
installer._pkinit_pkcs12_info = ansible_module.params.get('_pkinit_pkcs12_info')
|
||||
options._top_dir = ansible_module.params.get('_top_dir')
|
||||
@@ -190,6 +200,8 @@ def main():
|
||||
config.ca_host_name = config_ca_host_name
|
||||
config.ips = config_ips
|
||||
config.promote = options.promote
|
||||
config.kra_enabled = kra_enabled
|
||||
config.kra_host_name = kra_host_name
|
||||
|
||||
remote_api = gen_remote_api(config.master_host_name, paths.ETC_IPA)
|
||||
options._remote_api = remote_api
|
||||
@@ -213,7 +225,10 @@ def main():
|
||||
if not hasattr(custodiainstance, "get_custodia_instance"):
|
||||
ca.install(False, config, options)
|
||||
else:
|
||||
if ca_enabled:
|
||||
if kra_enabled:
|
||||
# A KRA peer always provides a CA, too.
|
||||
mode = custodiainstance.CustodiaModes.KRA_PEER
|
||||
elif ca_enabled:
|
||||
mode = custodiainstance.CustodiaModes.CA_PEER
|
||||
else:
|
||||
mode = custodiainstance.CustodiaModes.MASTER_PEER
|
||||
|
||||
@@ -64,6 +64,12 @@ options:
|
||||
_ca_file:
|
||||
description:
|
||||
required: yes
|
||||
_kra_enabled:
|
||||
description:
|
||||
required: yes
|
||||
_kra_host_name:
|
||||
description:
|
||||
required: yes
|
||||
_top_dir:
|
||||
description:
|
||||
required: yes
|
||||
@@ -98,6 +104,8 @@ def main():
|
||||
ccache=dict(required=True),
|
||||
_ca_enabled=dict(required=False, type='bool'),
|
||||
_ca_file=dict(required=False),
|
||||
_kra_enabled=dict(required=False, type='bool'),
|
||||
_kra_host_name=dict(required=False),
|
||||
_dirsrv_pkcs12_info = dict(required=False),
|
||||
_pkinit_pkcs12_info = dict(required=False),
|
||||
_top_dir = dict(required=True),
|
||||
@@ -127,6 +135,8 @@ def main():
|
||||
#os.environ['KRB5CCNAME'] = ansible_module.params.get('installer_ccache')
|
||||
#installer._ccache = ansible_module.params.get('installer_ccache')
|
||||
ca_enabled = ansible_module.params.get('_ca_enabled')
|
||||
kra_enabled = ansible_module.params.get('_kra_enabled')
|
||||
kra_host_name = ansible_module.params.get('_kra_host_name')
|
||||
dirsrv_pkcs12_info = ansible_module.params.get('_dirsrv_pkcs12_info')
|
||||
options._pkinit_pkcs12_info = ansible_module.params.get('_pkinit_pkcs12_info')
|
||||
options._top_dir = ansible_module.params.get('_top_dir')
|
||||
@@ -149,6 +159,8 @@ def main():
|
||||
config = gen_ReplicaConfig()
|
||||
config.dirman_password = dirman_password
|
||||
config.promote = installer.promote
|
||||
config.kra_enabled = kra_enabled
|
||||
config.kra_host_name = kra_host_name
|
||||
|
||||
remote_api = gen_remote_api(master_host_name, paths.ETC_IPA)
|
||||
#installer._remote_api = remote_api
|
||||
@@ -174,7 +186,10 @@ def main():
|
||||
ansible_log.debug("-- CUSTODIA CREATE_INSTANCE --")
|
||||
custodia.create_instance()
|
||||
else:
|
||||
if ca_enabled:
|
||||
if kra_enabled:
|
||||
# A KRA peer always provides a CA, too.
|
||||
mode = custodiainstance.CustodiaModes.KRA_PEER
|
||||
elif ca_enabled:
|
||||
mode = custodiainstance.CustodiaModes.CA_PEER
|
||||
else:
|
||||
mode = custodiainstance.CustodiaModes.MASTER_PEER
|
||||
|
||||
@@ -115,6 +115,7 @@ def main():
|
||||
installer_ccache=dict(required=True),
|
||||
_ca_enabled=dict(required=False, type='bool'),
|
||||
_kra_enabled=dict(required=False, type='bool'),
|
||||
_kra_host_name=dict(required=False),
|
||||
_dirsrv_pkcs12_info = dict(required=False),
|
||||
_http_pkcs12_info = dict(required=False),
|
||||
_pkinit_pkcs12_info = dict(required=False),
|
||||
@@ -176,6 +177,7 @@ def main():
|
||||
installer._ccache = ansible_module.params.get('installer_ccache')
|
||||
ca_enabled = ansible_module.params.get('_ca_enabled')
|
||||
kra_enabled = ansible_module.params.get('_kra_enabled')
|
||||
kra_host_name = ansible_module.params.get('_kra_host_name')
|
||||
|
||||
dirsrv_pkcs12_info = ansible_module.params.get('_dirsrv_pkcs12_info')
|
||||
http_pkcs12_info = ansible_module.params.get('_http_pkcs12_info')
|
||||
@@ -206,6 +208,8 @@ def main():
|
||||
config = gen_ReplicaConfig()
|
||||
config.subject_base = options.subject_base
|
||||
config.promote = installer.promote
|
||||
config.kra_enabled = kra_enabled
|
||||
config.kra_host_name = kra_host_name
|
||||
|
||||
remote_api = gen_remote_api(master_host_name, paths.ETC_IPA)
|
||||
installer._remote_api = remote_api
|
||||
|
||||
@@ -179,6 +179,14 @@ def main():
|
||||
ansible_module.fail_json(
|
||||
msg="Hidden replica is not supported in this version.")
|
||||
|
||||
# We need to point to the master in ipa default conf when certmonger
|
||||
# asks for HTTP certificate in newer ipa versions. In these versions
|
||||
# create_ipa_conf has the additional master argument.
|
||||
change_master_for_certmonger = False
|
||||
argspec = inspect.getargspec(create_ipa_conf)
|
||||
if "master" in argspec.args:
|
||||
change_master_for_certmonger = True
|
||||
|
||||
# From ipa installer classes
|
||||
|
||||
# pkinit is not supported on DL0, don't allow related options
|
||||
@@ -332,18 +340,20 @@ def main():
|
||||
|
||||
# done #
|
||||
|
||||
ansible_module.exit_json(changed=False,
|
||||
ipa_python_version=IPA_PYTHON_VERSION,
|
||||
### basic ###
|
||||
domain=options.domain_name,
|
||||
realm=options.realm_name,
|
||||
hostname=options.host_name,
|
||||
### server ###
|
||||
setup_adtrust=options.setup_adtrust,
|
||||
setup_kra=options.setup_kra,
|
||||
server=options.server,
|
||||
### additional ###
|
||||
client_enrolled=client_enrolled,
|
||||
ansible_module.exit_json(
|
||||
changed=False,
|
||||
ipa_python_version=IPA_PYTHON_VERSION,
|
||||
### basic ###
|
||||
domain=options.domain_name,
|
||||
realm=options.realm_name,
|
||||
hostname=options.host_name,
|
||||
### server ###
|
||||
setup_adtrust=options.setup_adtrust,
|
||||
setup_kra=options.setup_kra,
|
||||
server=options.server,
|
||||
### additional ###
|
||||
client_enrolled=client_enrolled,
|
||||
change_master_for_certmonger=change_master_for_certmonger,
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -240,7 +240,7 @@ options.kasp_db_file = None
|
||||
options.force = False
|
||||
|
||||
# ServerMasterInstall
|
||||
options.add_sids = True
|
||||
options.add_sids = False
|
||||
options.add_agents = False
|
||||
|
||||
# ServerReplicaInstall
|
||||
|
||||
@@ -314,6 +314,7 @@
|
||||
server: "{{ result_ipareplica_test.server }}"
|
||||
config_master_host_name:
|
||||
"{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
|
||||
config_ca_host_name: "{{ result_ipareplica_prepare.config_ca_host_name }}"
|
||||
ccache: "{{ result_ipareplica_prepare.ccache }}"
|
||||
installer_ccache: "{{ result_ipareplica_prepare.installer_ccache }}"
|
||||
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
|
||||
@@ -343,6 +344,54 @@
|
||||
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
|
||||
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
|
||||
|
||||
# We need to point to the master in ipa default conf when certmonger
|
||||
# asks for HTTP certificate in newer ipa versions. In these versions
|
||||
# create_ipa_conf has the additional master argument.
|
||||
- name: Install - Create override IPA conf
|
||||
ipareplica_create_ipa_conf:
|
||||
### basic ###
|
||||
dm_password: "{{ ipadm_password | default(omit) }}"
|
||||
password: "{{ ipaadmin_password | default(omit) }}"
|
||||
ip_addresses: "{{ ipareplica_ip_addresses | default([]) }}"
|
||||
domain: "{{ result_ipareplica_test.domain }}"
|
||||
realm: "{{ result_ipareplica_test.realm }}"
|
||||
hostname: "{{ result_ipareplica_test.hostname }}"
|
||||
ca_cert_files: "{{ ipareplica_ca_cert_files | default([]) }}"
|
||||
no_host_dns: "{{ ipareplica_no_host_dns }}"
|
||||
### replica ###
|
||||
setup_adtrust: "{{ result_ipareplica_test.setup_adtrust }}"
|
||||
setup_kra: "{{ result_ipareplica_test.setup_kra }}"
|
||||
setup_dns: "{{ ipareplica_setup_dns }}"
|
||||
### ssl certificate ###
|
||||
dirsrv_cert_files: "{{ ipareplica_dirsrv_cert_files | default([]) }}"
|
||||
### client ###
|
||||
force_join: "{{ ipaclient_force_join }}"
|
||||
### ad trust ###
|
||||
netbios_name: "{{ ipareplica_netbios_name | default(omit) }}"
|
||||
rid_base: "{{ ipareplica_rid_base | default(omit) }}"
|
||||
secondary_rid_base: "{{ ipareplica_secondary_rid_base | default(omit) }}"
|
||||
### additional ###
|
||||
server: "{{ result_ipareplica_test.server }}"
|
||||
config_master_host_name:
|
||||
"{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
|
||||
config_ca_host_name: "{{ result_ipareplica_prepare.config_ca_host_name }}"
|
||||
ccache: "{{ result_ipareplica_prepare.ccache }}"
|
||||
installer_ccache: "{{ result_ipareplica_prepare.installer_ccache }}"
|
||||
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
|
||||
_kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}"
|
||||
_dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info }}"
|
||||
_http_pkcs12_info: "{{ result_ipareplica_prepare._http_pkcs12_info }}"
|
||||
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
|
||||
subject_base: "{{ result_ipareplica_prepare.subject_base }}"
|
||||
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
|
||||
_add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
|
||||
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
|
||||
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
|
||||
dirman_password: "{{ ipareplica_dirman_password }}"
|
||||
master:
|
||||
"{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
|
||||
when: result_ipareplica_test.change_master_for_certmonger
|
||||
|
||||
- name: Install - DS enable SSL
|
||||
ipareplica_ds_enable_ssl:
|
||||
### server ###
|
||||
@@ -383,6 +432,50 @@
|
||||
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
|
||||
dirman_password: "{{ ipareplica_dirman_password }}"
|
||||
|
||||
# Need to point back to ourself after the cert for HTTP is obtained
|
||||
- name: Install - Create original IPA conf again
|
||||
ipareplica_create_ipa_conf:
|
||||
### basic ###
|
||||
dm_password: "{{ ipadm_password | default(omit) }}"
|
||||
password: "{{ ipaadmin_password | default(omit) }}"
|
||||
ip_addresses: "{{ ipareplica_ip_addresses | default([]) }}"
|
||||
domain: "{{ result_ipareplica_test.domain }}"
|
||||
realm: "{{ result_ipareplica_test.realm }}"
|
||||
hostname: "{{ result_ipareplica_test.hostname }}"
|
||||
ca_cert_files: "{{ ipareplica_ca_cert_files | default([]) }}"
|
||||
no_host_dns: "{{ ipareplica_no_host_dns }}"
|
||||
### replica ###
|
||||
setup_adtrust: "{{ result_ipareplica_test.setup_adtrust }}"
|
||||
setup_kra: "{{ result_ipareplica_test.setup_kra }}"
|
||||
setup_dns: "{{ ipareplica_setup_dns }}"
|
||||
### ssl certificate ###
|
||||
dirsrv_cert_files: "{{ ipareplica_dirsrv_cert_files | default([]) }}"
|
||||
### client ###
|
||||
force_join: "{{ ipaclient_force_join }}"
|
||||
### ad trust ###
|
||||
netbios_name: "{{ ipareplica_netbios_name | default(omit) }}"
|
||||
rid_base: "{{ ipareplica_rid_base | default(omit) }}"
|
||||
secondary_rid_base: "{{ ipareplica_secondary_rid_base | default(omit) }}"
|
||||
### additional ###
|
||||
server: "{{ result_ipareplica_test.server }}"
|
||||
config_master_host_name:
|
||||
"{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
|
||||
config_ca_host_name: "{{ result_ipareplica_prepare.config_ca_host_name }}"
|
||||
ccache: "{{ result_ipareplica_prepare.ccache }}"
|
||||
installer_ccache: "{{ result_ipareplica_prepare.installer_ccache }}"
|
||||
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
|
||||
_kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}"
|
||||
_dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info }}"
|
||||
_http_pkcs12_info: "{{ result_ipareplica_prepare._http_pkcs12_info }}"
|
||||
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
|
||||
subject_base: "{{ result_ipareplica_prepare.subject_base }}"
|
||||
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
|
||||
_add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
|
||||
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
|
||||
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
|
||||
dirman_password: "{{ ipareplica_dirman_password }}"
|
||||
when: result_ipareplica_test.change_master_for_certmonger
|
||||
|
||||
- name: Install - Setup otpd
|
||||
ipareplica_setup_otpd:
|
||||
### server ###
|
||||
@@ -415,6 +508,8 @@
|
||||
"{{ result_ipareplica_prepare.config_master_host_name }}"
|
||||
ccache: "{{ result_ipareplica_prepare.ccache }}"
|
||||
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
|
||||
_kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}"
|
||||
_kra_host_name: "{{ result_ipareplica_prepare.config_kra_host_name }}"
|
||||
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
|
||||
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
|
||||
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
|
||||
@@ -434,6 +529,8 @@
|
||||
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
|
||||
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
|
||||
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
|
||||
_kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}"
|
||||
_kra_host_name: "{{ result_ipareplica_prepare.config_kra_host_name }}"
|
||||
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
|
||||
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
|
||||
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
|
||||
@@ -516,6 +613,7 @@
|
||||
installer_ccache: "{{ result_ipareplica_prepare.installer_ccache }}"
|
||||
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
|
||||
_kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}"
|
||||
_kra_host_name: "{{ result_ipareplica_prepare.config_kra_host_name }}"
|
||||
_dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info }}"
|
||||
_http_pkcs12_info: "{{ result_ipareplica_prepare._http_pkcs12_info }}"
|
||||
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
|
||||
@@ -560,6 +658,8 @@
|
||||
ccache: "{{ result_ipareplica_prepare.ccache }}"
|
||||
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
|
||||
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
|
||||
_kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}"
|
||||
_kra_host_name: "{{ result_ipareplica_prepare.config_kra_host_name }}"
|
||||
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
|
||||
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
|
||||
dirman_password: "{{ ipareplica_dirman_password }}"
|
||||
@@ -640,6 +740,8 @@
|
||||
hostname: "{{ result_ipareplica_test.hostname }}"
|
||||
hidden_replica: "{{ ipareplica_hidden_replica }}"
|
||||
### server ###
|
||||
### replica ###
|
||||
setup_kra: "{{ result_ipareplica_test.setup_kra }}"
|
||||
### certificate system ###
|
||||
subject_base: "{{ result_ipareplica_prepare.subject_base }}"
|
||||
### additional ###
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
- "vars/default.yml"
|
||||
|
||||
- name: Install IPA replica
|
||||
include_tasks: tasks/install.yml
|
||||
include_tasks: install.yml
|
||||
when: state|default('present') == 'present'
|
||||
|
||||
- name: Uninstall IPA replica
|
||||
include_tasks: tasks/uninstall.yml
|
||||
include_tasks: uninstall.yml
|
||||
when: state|default('present') == 'absent'
|
||||
|
||||
5
roles/ipareplica/vars/RedHat-8.yml
Normal file
5
roles/ipareplica/vars/RedHat-8.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
# defaults file for ipareplica
|
||||
# vars/RedHat-8.yml
|
||||
ipareplica_packages: [ "@idm:DL1/server" ]
|
||||
ipareplica_packages_dns: [ "@idm:DL1/dns" ]
|
||||
ipareplica_packages_adtrust: [ "@idm:DL1/adtrust" ]
|
||||
@@ -42,11 +42,11 @@ Requirements
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
External CA
|
||||
External signed CA
|
||||
|
||||
External CA support is not supported or working. The currently needed two step process is an issue for the processing in the role. The configuration of the server is partly done already and needs to be continued after the CSR has been handled. This is for example breaking the deployment of a server with replicas or clients in one playbook.
|
||||
External signed CA is now supported. But the currently needed two step process is an issue for the processing in a simple playbook.
|
||||
|
||||
Work is planned to have a new method to handle CSR for external CAs in a separate step before starting the server installation.
|
||||
Work is planned to have a new method to handle CSR for external signed CAs in a separate step before starting the server installation.
|
||||
|
||||
|
||||
Usage
|
||||
@@ -106,6 +106,56 @@ Example playbook to setup the IPA server using admin and dirman passwords from i
|
||||
- role: ipaserver
|
||||
state: present
|
||||
|
||||
Example playbook to setup the IPA primary with external signed CA using the previous inventory file:
|
||||
|
||||
Server installation step 1: Generate CSR, copy to controller as `<ipaserver hostname>-ipa.csr`
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Playbook to configure IPA server step1
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
vars:
|
||||
ipaserver_external_ca: yes
|
||||
|
||||
roles:
|
||||
- role: ipaserver
|
||||
state: present
|
||||
|
||||
post_tasks:
|
||||
- name: Copy CSR /root/ipa.csr from node to "{{ groups.ipaserver[0] + '-ipa.csr' }}"
|
||||
fetch:
|
||||
src: /root/ipa.csr
|
||||
dest: "{{ groups.ipaserver[0] + '-ipa.csr' }}"
|
||||
flat: yes
|
||||
```
|
||||
|
||||
Sign with CA: This is up to you
|
||||
|
||||
Server installatin step 2: Copy `<ipaserver hostname>-chain.crt` to the IPA server and continue with installation of the primary.
|
||||
|
||||
```yaml
|
||||
- name: Playbook to configure IPA server step3
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
vars:
|
||||
ipaserver_external_cert_files: "/root/chain.crt"
|
||||
|
||||
pre_tasks:
|
||||
- name: Copy "{{ groups.ipaserver[0] + '-chain.crt' }}" to /root/chain.crt on node
|
||||
copy:
|
||||
src: "{{ groups.ipaserver[0] + '-chain.crt' }}"
|
||||
dest: "/root/chain.crt"
|
||||
force: yes
|
||||
|
||||
roles:
|
||||
- role: ipaserver
|
||||
state: present
|
||||
```
|
||||
|
||||
The files can also be copied automatically: Set `ipaserver_copy_csr_to_controller` to true in the server installation step 1 and set `ipaserver_external_cert_files_from_controller` to point to the `chain.crt` file in the server installatin step 2.
|
||||
|
||||
|
||||
Playbooks
|
||||
=========
|
||||
|
||||
@@ -192,13 +242,13 @@ Certificate system Variables
|
||||
|
||||
Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
~~`ipaserver_external_ca`~~ | ~~Generate a CSR for the IPA CA certificate to be signed by an external CA. (bool, default: false)~~ | ~~no~~
|
||||
~~`ipaserver_external_ca_type`~~ | ~~Type of the external CA. (choice: generic,ms-cs)~~ | ~~no~~
|
||||
~~`ipaserver_external_ca_profile`~~ | ~~Specify the certificate profile/template to use at the external CA. (string)~~ | ~~no~~
|
||||
~~`ipaserver_external_cert_files`~~ | ~~Files containing the IPA CA certificates and the external CA certificate chains (list of string)~~ | ~~no~~
|
||||
`ipaserver_external_ca` | Generate a CSR for the IPA CA certificate to be signed by an external CA. (bool, default: false) | no
|
||||
`ipaserver_external_ca_type` | Type of the external CA. (choice: generic,ms-cs) | no
|
||||
`ipaserver_external_ca_profile` | Specify the certificate profile/template to use at the external CA. (string) | no
|
||||
`ipaserver_external_cert_files` | Files containing the IPA CA certificates and the external CA certificate chains (list of string) | no
|
||||
`ipaserver_subject_base` | The certificate subject base (default O=<realm-name>). RDNs are in LDAP order (most specific RDN first). (string) | no
|
||||
`ipaserver_ca_subject` | The CA certificate subject DN (default CN=Certificate Authority,O=<realm-name>). RDNs are in LDAP order (most specific RDN first). (string) | no
|
||||
~~`ipaserver_ca_signing_algorithm`~~ | ~~Signing algorithm of the IPA CA certificate. (choice: SHA1withRSA,SHA256withRSA,SHA512withRSA)~~ | ~~no~~
|
||||
`ipaserver_ca_signing_algorithm` | Signing algorithm of the IPA CA certificate. (choice: SHA1withRSA,SHA256withRSA,SHA512withRSA) | no
|
||||
|
||||
DNS Variables
|
||||
-------------
|
||||
@@ -233,7 +283,8 @@ Variable | Description | Required
|
||||
-------- | ----------- | --------
|
||||
`ipaserver_install_packages` | The bool value defines if the needed packages are installed on the node. (bool, default: true) | no
|
||||
`ipaserver_setup_firewalld` | The value defines if the needed services will automatically be openen in the firewall managed by firewalld. (bool, default: true) | no
|
||||
|
||||
`ipaserver_external_cert_files_from_controller` | Files containing the IPA CA certificates and the external CA certificate chains on the controller that will be copied to the ipaserver host to `/root` folder. (list of string) | no
|
||||
`ipaserver_copy_csr_to_controller` | Copy the generated CSR from the ipaserver to the controller as `"{{ inventory_hostname }}-ipa.csr"`. (bool) | no
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
@@ -36,7 +36,7 @@ ipaserver_install_packages: yes
|
||||
ipaserver_setup_firewalld: yes
|
||||
|
||||
### additional ###
|
||||
ipaserver_allow_missing: [ ]
|
||||
ipaserver_copy_csr_to_controller: no
|
||||
|
||||
### uninstall ###
|
||||
ipaserver_ignore_topology_disconnect: no
|
||||
|
||||
@@ -97,7 +97,9 @@ def main():
|
||||
### ssl certificate ###
|
||||
### client ###
|
||||
### certificate system ###
|
||||
external_ca=dict(required=False),
|
||||
external_ca=dict(required=False, type='bool'),
|
||||
external_ca_type=dict(required=False),
|
||||
external_ca_profile=dict(required=False),
|
||||
external_cert_files=dict(required=False, type='list', default=[]),
|
||||
subject_base=dict(required=False),
|
||||
ca_subject=dict(required=False),
|
||||
@@ -152,6 +154,9 @@ def main():
|
||||
#options.no_ntp = ansible_module.params.get('no_ntp')
|
||||
### certificate system ###
|
||||
options.external_ca = ansible_module.params.get('external_ca')
|
||||
options.external_ca_type = ansible_module.params.get('external_ca_type')
|
||||
options.external_ca_profile = ansible_module.params.get(
|
||||
'external_ca_profile')
|
||||
options.external_cert_files = ansible_module.params.get(
|
||||
'external_cert_files')
|
||||
options.subject_base = ansible_module.params.get('subject_base')
|
||||
@@ -190,96 +195,102 @@ def main():
|
||||
if not options.ca_subject:
|
||||
options.ca_subject = str(default_ca_subject_dn(options.subject_base))
|
||||
|
||||
# Configuration for ipalib, we will bootstrap and finalize later, after
|
||||
# we are sure we have the configuration file ready.
|
||||
cfg = dict(
|
||||
context='installer',
|
||||
confdir=paths.ETC_IPA,
|
||||
in_server=True,
|
||||
# make sure host name specified by user is used instead of default
|
||||
host=options.host_name,
|
||||
)
|
||||
if options.setup_ca:
|
||||
# we have an IPA-integrated CA
|
||||
cfg['ca_host'] = options.host_name
|
||||
try:
|
||||
|
||||
# Create the management framework config file and finalize api
|
||||
target_fname = paths.IPA_DEFAULT_CONF
|
||||
fd = open(target_fname, "w")
|
||||
fd.write("[global]\n")
|
||||
fd.write("host=%s\n" % options.host_name)
|
||||
fd.write("basedn=%s\n" % ipautil.realm_to_suffix(options.realm_name))
|
||||
fd.write("realm=%s\n" % options.realm_name)
|
||||
fd.write("domain=%s\n" % options.domain_name)
|
||||
fd.write("xmlrpc_uri=https://%s/ipa/xml\n" % \
|
||||
ipautil.format_netloc(options.host_name))
|
||||
fd.write("ldap_uri=ldapi://%%2fvar%%2frun%%2fslapd-%s.socket\n" % \
|
||||
installutils.realm_to_serverid(options.realm_name))
|
||||
if options.setup_ca:
|
||||
fd.write("enable_ra=True\n")
|
||||
fd.write("ra_plugin=dogtag\n")
|
||||
fd.write("dogtag_version=10\n")
|
||||
else:
|
||||
fd.write("enable_ra=False\n")
|
||||
fd.write("ra_plugin=none\n")
|
||||
fd.write("mode=production\n")
|
||||
fd.close()
|
||||
# Configuration for ipalib, we will bootstrap and finalize later, after
|
||||
# we are sure we have the configuration file ready.
|
||||
cfg = dict(
|
||||
context='installer',
|
||||
confdir=paths.ETC_IPA,
|
||||
in_server=True,
|
||||
# make sure host name specified by user is used instead of default
|
||||
host=options.host_name,
|
||||
)
|
||||
if options.setup_ca:
|
||||
# we have an IPA-integrated CA
|
||||
cfg['ca_host'] = options.host_name
|
||||
|
||||
# Must be readable for everyone
|
||||
os.chmod(target_fname, 0o644)
|
||||
# Create the management framework config file and finalize api
|
||||
target_fname = paths.IPA_DEFAULT_CONF
|
||||
fd = open(target_fname, "w")
|
||||
fd.write("[global]\n")
|
||||
fd.write("host=%s\n" % options.host_name)
|
||||
fd.write("basedn=%s\n" % ipautil.realm_to_suffix(options.realm_name))
|
||||
fd.write("realm=%s\n" % options.realm_name)
|
||||
fd.write("domain=%s\n" % options.domain_name)
|
||||
fd.write("xmlrpc_uri=https://%s/ipa/xml\n" % \
|
||||
ipautil.format_netloc(options.host_name))
|
||||
fd.write("ldap_uri=ldapi://%%2fvar%%2frun%%2fslapd-%s.socket\n" % \
|
||||
installutils.realm_to_serverid(options.realm_name))
|
||||
if options.setup_ca:
|
||||
fd.write("enable_ra=True\n")
|
||||
fd.write("ra_plugin=dogtag\n")
|
||||
fd.write("dogtag_version=10\n")
|
||||
else:
|
||||
fd.write("enable_ra=False\n")
|
||||
fd.write("ra_plugin=none\n")
|
||||
fd.write("mode=production\n")
|
||||
fd.close()
|
||||
|
||||
api.bootstrap(**cfg)
|
||||
api.finalize()
|
||||
# Must be readable for everyone
|
||||
os.chmod(target_fname, 0o644)
|
||||
|
||||
if options.setup_ca:
|
||||
with redirect_stdout(ansible_log):
|
||||
ca.install_check(False, None, options)
|
||||
if options.setup_kra:
|
||||
with redirect_stdout(ansible_log):
|
||||
kra.install_check(api, None, options)
|
||||
api.bootstrap(**cfg)
|
||||
api.finalize()
|
||||
|
||||
if options.setup_dns:
|
||||
with redirect_stdout(ansible_log):
|
||||
dns.install_check(False, api, False, options, options.host_name)
|
||||
ip_addresses = dns.ip_addresses
|
||||
else:
|
||||
ip_addresses = get_server_ip_address(options.host_name,
|
||||
not options.interactive, False,
|
||||
options.ip_addresses)
|
||||
if options.setup_ca:
|
||||
with redirect_stdout(ansible_log):
|
||||
ca.install_check(False, None, options)
|
||||
if options.setup_kra:
|
||||
with redirect_stdout(ansible_log):
|
||||
kra.install_check(api, None, options)
|
||||
|
||||
# check addresses here, dns module is doing own check
|
||||
no_matching_interface_for_ip_address_warning(ip_addresses)
|
||||
options.ip_addresses = ip_addresses
|
||||
options.reverse_zones = dns.reverse_zones
|
||||
if options.setup_dns:
|
||||
with redirect_stdout(ansible_log):
|
||||
dns.install_check(False, api, False, options, options.host_name)
|
||||
ip_addresses = dns.ip_addresses
|
||||
else:
|
||||
ip_addresses = get_server_ip_address(options.host_name,
|
||||
not options.interactive, False,
|
||||
options.ip_addresses)
|
||||
|
||||
instance_name = "-".join(options.realm_name.split("."))
|
||||
dirsrv = services.knownservices.dirsrv
|
||||
if (options.external_cert_files
|
||||
and dirsrv.is_installed(instance_name)
|
||||
and not dirsrv.is_running(instance_name)):
|
||||
logger.debug('Starting Directory Server')
|
||||
services.knownservices.dirsrv.start(instance_name)
|
||||
# check addresses here, dns module is doing own check
|
||||
no_matching_interface_for_ip_address_warning(ip_addresses)
|
||||
options.ip_addresses = ip_addresses
|
||||
options.reverse_zones = dns.reverse_zones
|
||||
|
||||
if options.setup_adtrust:
|
||||
with redirect_stdout(ansible_log):
|
||||
adtrust.install_check(False, options, api)
|
||||
instance_name = "-".join(options.realm_name.split("."))
|
||||
dirsrv = services.knownservices.dirsrv
|
||||
if (options.external_cert_files
|
||||
and dirsrv.is_installed(instance_name)
|
||||
and not dirsrv.is_running(instance_name)):
|
||||
logger.debug('Starting Directory Server')
|
||||
services.knownservices.dirsrv.start(instance_name)
|
||||
|
||||
_update_hosts_file = False
|
||||
# options needs to update hosts file when DNS subsystem will be
|
||||
# installed or custom addresses are used
|
||||
if options.ip_addresses or options.setup_dns:
|
||||
_update_hosts_file = True
|
||||
if options.setup_adtrust:
|
||||
with redirect_stdout(ansible_log):
|
||||
adtrust.install_check(False, options, api)
|
||||
|
||||
if options._host_name_overridden:
|
||||
tasks.backup_hostname(fstore, sstore)
|
||||
tasks.set_hostname(options.host_name)
|
||||
_update_hosts_file = False
|
||||
# options needs to update hosts file when DNS subsystem will be
|
||||
# installed or custom addresses are used
|
||||
if options.ip_addresses or options.setup_dns:
|
||||
_update_hosts_file = True
|
||||
|
||||
if _update_hosts_file:
|
||||
update_hosts_file(ip_addresses, options.host_name, fstore)
|
||||
if options._host_name_overridden:
|
||||
tasks.backup_hostname(fstore, sstore)
|
||||
tasks.set_hostname(options.host_name)
|
||||
|
||||
if hasattr(tasks, "configure_pkcs11_modules"):
|
||||
if tasks.configure_pkcs11_modules(fstore):
|
||||
ansible_log.info("Disabled p11-kit-proxy")
|
||||
if _update_hosts_file:
|
||||
update_hosts_file(ip_addresses, options.host_name, fstore)
|
||||
|
||||
if hasattr(tasks, "configure_pkcs11_modules"):
|
||||
if tasks.configure_pkcs11_modules(fstore):
|
||||
ansible_log.info("Disabled p11-kit-proxy")
|
||||
|
||||
except (RuntimeError, ValueError, ScriptError,
|
||||
ipautil.CalledProcessError) as e:
|
||||
ansible_module.fail_json(msg=str(e))
|
||||
|
||||
ansible_module.exit_json(changed=True,
|
||||
### basic ###
|
||||
|
||||
@@ -106,7 +106,9 @@ def main():
|
||||
_dirsrv_pkcs12_info=dict(required=False),
|
||||
### certificate system ###
|
||||
external_ca=dict(required=False, type='bool', default=False),
|
||||
external_cert_files=dict(required=False, type='list', default=[]),
|
||||
external_ca_type=dict(required=False),
|
||||
external_ca_profile=dict(required=False),
|
||||
external_cert_files=dict(required=False, type='list', default=None),
|
||||
subject_base=dict(required=False),
|
||||
_subject_base=dict(required=False),
|
||||
ca_subject=dict(required=False),
|
||||
@@ -154,6 +156,9 @@ def main():
|
||||
'_dirsrv_pkcs12_info')
|
||||
### certificate system ###
|
||||
options.external_ca = ansible_module.params.get('external_ca')
|
||||
options.external_ca_type = ansible_module.params.get('external_ca_type')
|
||||
options.external_ca_profile = ansible_module.params.get(
|
||||
'external_ca_profile')
|
||||
options.external_cert_files = ansible_module.params.get(
|
||||
'external_cert_files')
|
||||
options.subject_base = ansible_module.params.get('subject_base')
|
||||
@@ -175,6 +180,13 @@ def main():
|
||||
|
||||
options.promote = False # first master, no promotion
|
||||
|
||||
# Repeat from ca.install_check
|
||||
# ca.external_cert_file and ca.external_ca_file need to be set
|
||||
if options.external_cert_files:
|
||||
ca.external_cert_file, ca.external_ca_file = \
|
||||
installutils.load_external_cert(
|
||||
options.external_cert_files, options._ca_subject)
|
||||
|
||||
fstore = sysrestore.FileStore(paths.SYSRESTORE)
|
||||
|
||||
api_Backend_ldap2(options.host_name, options.setup_ca, connect=True)
|
||||
@@ -190,53 +202,61 @@ def main():
|
||||
|
||||
# setup CA ##############################################################
|
||||
|
||||
with redirect_stdout(ansible_log):
|
||||
if hasattr(custodiainstance, "get_custodia_instance"):
|
||||
if hasattr(custodiainstance.CustodiaModes, "FIRST_MASTER"):
|
||||
mode = custodiainstance.CustodiaModes.FIRST_MASTER
|
||||
else:
|
||||
mode = custodiainstance.CustodiaModes.MASTER_PEER
|
||||
custodia = custodiainstance.get_custodia_instance(options, mode)
|
||||
if hasattr(custodiainstance, "get_custodia_instance"):
|
||||
if hasattr(custodiainstance.CustodiaModes, "FIRST_MASTER"):
|
||||
mode = custodiainstance.CustodiaModes.FIRST_MASTER
|
||||
else:
|
||||
mode = custodiainstance.CustodiaModes.MASTER_PEER
|
||||
custodia = custodiainstance.get_custodia_instance(options, mode)
|
||||
custodia.set_output(ansible_log)
|
||||
with redirect_stdout(ansible_log):
|
||||
custodia.create_instance()
|
||||
|
||||
if options.setup_ca:
|
||||
if not options.external_cert_files and options.external_ca:
|
||||
# stage 1 of external CA installation
|
||||
cache_vars = {n: options.__dict__[n] for o, n in options.knobs()
|
||||
if n in options.__dict__}
|
||||
write_cache(cache_vars)
|
||||
if options.setup_ca:
|
||||
if not options.external_cert_files and options.external_ca:
|
||||
# stage 1 of external CA installation
|
||||
cache_vars = {n: options.__dict__[n] for o, n in options.knobs()
|
||||
if n in options.__dict__}
|
||||
write_cache(cache_vars)
|
||||
|
||||
if hasattr(custodiainstance, "get_custodia_instance"):
|
||||
ca.install_step_0(False, None, options, custodia=custodia)
|
||||
else:
|
||||
ca.install_step_0(False, None, options)
|
||||
try:
|
||||
with redirect_stdout(ansible_log):
|
||||
if hasattr(custodiainstance, "get_custodia_instance"):
|
||||
ca.install_step_0(False, None, options, custodia=custodia)
|
||||
else:
|
||||
ca.install_step_0(False, None, options)
|
||||
except SystemExit:
|
||||
ansible_module.exit_json(changed=True,
|
||||
csr_generated=True)
|
||||
else:
|
||||
# Put the CA cert where other instances expect it
|
||||
x509.write_certificate(options._http_ca_cert, paths.IPA_CA_CRT)
|
||||
os.chmod(paths.IPA_CA_CRT, 0o444)
|
||||
|
||||
if not options.no_pkinit:
|
||||
x509.write_certificate(options._http_ca_cert,
|
||||
paths.KDC_CA_BUNDLE_PEM)
|
||||
else:
|
||||
# Put the CA cert where other instances expect it
|
||||
x509.write_certificate(options._http_ca_cert, paths.IPA_CA_CRT)
|
||||
os.chmod(paths.IPA_CA_CRT, 0o444)
|
||||
with open(paths.KDC_CA_BUNDLE_PEM, 'w'):
|
||||
pass
|
||||
os.chmod(paths.KDC_CA_BUNDLE_PEM, 0o444)
|
||||
|
||||
if not options.no_pkinit:
|
||||
x509.write_certificate(options._http_ca_cert,
|
||||
paths.KDC_CA_BUNDLE_PEM)
|
||||
else:
|
||||
with open(paths.KDC_CA_BUNDLE_PEM, 'w'):
|
||||
pass
|
||||
os.chmod(paths.KDC_CA_BUNDLE_PEM, 0o444)
|
||||
|
||||
x509.write_certificate(options._http_ca_cert, paths.CA_BUNDLE_PEM)
|
||||
os.chmod(paths.CA_BUNDLE_PEM, 0o444)
|
||||
x509.write_certificate(options._http_ca_cert, paths.CA_BUNDLE_PEM)
|
||||
os.chmod(paths.CA_BUNDLE_PEM, 0o444)
|
||||
|
||||
with redirect_stdout(ansible_log):
|
||||
# we now need to enable ssl on the ds
|
||||
ds.enable_ssl()
|
||||
|
||||
if options.setup_ca:
|
||||
with redirect_stdout(ansible_log):
|
||||
if hasattr(custodiainstance, "get_custodia_instance"):
|
||||
ca.install_step_1(False, None, options, custodia=custodia)
|
||||
else:
|
||||
ca.install_step_1(False, None, options)
|
||||
if options.setup_ca:
|
||||
with redirect_stdout(ansible_log):
|
||||
if hasattr(custodiainstance, "get_custodia_instance"):
|
||||
ca.install_step_1(False, None, options, custodia=custodia)
|
||||
else:
|
||||
ca.install_step_1(False, None, options)
|
||||
|
||||
ansible_module.exit_json(changed=True)
|
||||
ansible_module.exit_json(changed=True,
|
||||
csr_generated=False)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -51,12 +51,20 @@ from ansible.module_utils.ansible_ipa_server import *
|
||||
|
||||
def main():
|
||||
ansible_module = AnsibleModule(
|
||||
argument_spec = dict(),
|
||||
argument_spec = dict(
|
||||
ntp_servers=dict(required=False, type='list', default=None),
|
||||
ntp_pool=dict(required=False, default=None),
|
||||
),
|
||||
)
|
||||
|
||||
ansible_module._ansible_debug = True
|
||||
ansible_log = AnsibleModuleLog(ansible_module)
|
||||
|
||||
# set values ############################################################
|
||||
|
||||
options.ntp_servers = ansible_module.params.get('ntp_servers')
|
||||
options.ntp_pool = ansible_module.params.get('ntp_pool')
|
||||
|
||||
# init ##########################################################
|
||||
|
||||
fstore = sysrestore.FileStore(paths.SYSRESTORE)
|
||||
@@ -70,14 +78,19 @@ def main():
|
||||
# chrony will be handled here in uninstall() method as well by invoking
|
||||
# the ipa-server-install --uninstall
|
||||
ansible_module.log("Synchronizing time")
|
||||
options.ntp_servers = None
|
||||
options.ntp_pool = None
|
||||
if sync_time(options, fstore, sstore):
|
||||
ansible_module.log("Time synchronization was successful.")
|
||||
|
||||
argspec = inspect.getargspec(sync_time)
|
||||
if "options" not in argspec.args:
|
||||
synced_ntp = sync_time(options.ntp_servers, options.ntp_pool,
|
||||
fstore, sstore)
|
||||
else:
|
||||
ansible_module.warn("IPA was unable to sync time with chrony!")
|
||||
ansible_module.warn("Time synchronization is required for IPA "
|
||||
"to work correctly")
|
||||
synced_ntp = sync_time(options, fstore, sstore)
|
||||
if not synced_ntp:
|
||||
ansible_module.log(
|
||||
"Warning: IPA was unable to sync time with chrony!")
|
||||
ansible_module.log(
|
||||
" Time synchronization is required for IPA "
|
||||
"to work correctly")
|
||||
else:
|
||||
# Configure ntpd
|
||||
timeconf.force_ntpd(sstore)
|
||||
|
||||
@@ -77,17 +77,19 @@ def main():
|
||||
# no_ui_redirect
|
||||
dirsrv_config_file=dict(required=False),
|
||||
### ssl certificate ###
|
||||
dirsrv_cert_files=dict(required=False, type='list', default=[]),
|
||||
http_cert_files=dict(required=False, type='list', default=[]),
|
||||
pkinit_cert_files=dict(required=False, type='list', default=[]),
|
||||
# dirsrv_pin
|
||||
# http_pin
|
||||
# pkinit_pin
|
||||
# dirsrv_name
|
||||
# http_name
|
||||
# pkinit_name
|
||||
dirsrv_cert_files=dict(required=False, type='list', default=None),
|
||||
http_cert_files=dict(required=False, type='list', defaullt=None),
|
||||
pkinit_cert_files=dict(required=False, type='list', default=None),
|
||||
dirsrv_pin=dict(required=False),
|
||||
http_pin=dict(required=False),
|
||||
pkinit_pin=dict(required=False),
|
||||
dirsrv_cert_name=dict(required=False),
|
||||
http_cert_name=dict(required=False),
|
||||
pkinit_cert_name=dict(required=False),
|
||||
### client ###
|
||||
# mkhomedir
|
||||
ntp_servers=dict(required=False, type='list', default=None),
|
||||
ntp_pool=dict(required=False, default=None),
|
||||
no_ntp=dict(required=False, type='bool', default=False),
|
||||
# ssh_trust_dns
|
||||
# no_ssh
|
||||
@@ -96,7 +98,8 @@ def main():
|
||||
### certificate system ###
|
||||
external_ca=dict(required=False, type='bool', default=False),
|
||||
external_ca_type=dict(required=False),
|
||||
external_cert_files=dict(required=False, type='list', default=[]),
|
||||
external_ca_profile=dict(required=False),
|
||||
external_cert_files=dict(required=False, type='list', default=None),
|
||||
subject_base=dict(required=False),
|
||||
ca_subject=dict(required=False),
|
||||
# ca_signing_algorithm
|
||||
@@ -155,14 +158,16 @@ def main():
|
||||
options.dirsrv_cert_files = ansible_module.params.get('dirsrv_cert_files')
|
||||
options.http_cert_files = ansible_module.params.get('http_cert_files')
|
||||
options.pkinit_cert_files = ansible_module.params.get('pkinit_cert_files')
|
||||
# dirsrv_pin
|
||||
# http_pin
|
||||
# pkinit_pin
|
||||
# dirsrv_name
|
||||
# http_name
|
||||
# pkinit_name
|
||||
options.dirsrv_pin = ansible_module.params.get('dirsrv_pin'),
|
||||
options.http_pin = ansible_module.params.get('http_pin'),
|
||||
options.pkinit_pin = ansible_module.params.get('pkinit_pin'),
|
||||
options.dirsrv_cert_name = ansible_module.params.get('dirsrv_cert_name'),
|
||||
options.http_cert_name = ansible_module.params.get('http_cert_name'),
|
||||
options.pkinit_cert_name = ansible_module.params.get('pkinit_cert_name'),
|
||||
### client ###
|
||||
# mkhomedir
|
||||
options.ntp_servers = ansible_module.params.get('ntp_servers')
|
||||
options.ntp_pool = ansible_module.params.get('ntp_pool')
|
||||
options.no_ntp = ansible_module.params.get('no_ntp')
|
||||
# ssh_trust_dns
|
||||
# no_ssh
|
||||
@@ -171,6 +176,8 @@ def main():
|
||||
### certificate system ###
|
||||
options.external_ca = ansible_module.params.get('external_ca')
|
||||
options.external_ca_type = ansible_module.params.get('external_ca_type')
|
||||
options.external_ca_profile = ansible_module.params.get(
|
||||
'external_ca_profile')
|
||||
options.external_cert_files = ansible_module.params.get(
|
||||
'external_cert_files')
|
||||
options.subject_base = ansible_module.params.get('subject_base')
|
||||
@@ -226,25 +233,6 @@ def main():
|
||||
ansible_module.fail_json(
|
||||
msg="pki_config_override: %s" % str(e))
|
||||
|
||||
# validation #############################################################
|
||||
|
||||
if options.dm_password is None:
|
||||
ansible_module.fail_json(msg="Directory Manager password required")
|
||||
|
||||
if options.admin_password is None:
|
||||
ansible_module.fail_json(msg="IPA admin password required")
|
||||
|
||||
# This will override any settings passed in on the cmdline
|
||||
if os.path.isfile(paths.ROOT_IPA_CACHE):
|
||||
# dm_password check removed, checked already
|
||||
try:
|
||||
cache_vars = read_cache(options.dm_password)
|
||||
options.__dict__.update(cache_vars)
|
||||
if cache_vars.get('external_ca', False):
|
||||
options.external_ca = False
|
||||
options.interactive = False
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(msg="Cannot process the cache file: %s" % str(e))
|
||||
# default values ########################################################
|
||||
|
||||
# idstart and idmax
|
||||
@@ -253,50 +241,190 @@ def main():
|
||||
if options.idmax is None or options.idmax == 0:
|
||||
options.idmax = options.idstart + 199999
|
||||
|
||||
# validation ############################################################
|
||||
#class ServerInstallInterface(ServerCertificateInstallInterface,
|
||||
# client.ClientInstallInterface,
|
||||
# ca.CAInstallInterface,
|
||||
# kra.KRAInstallInterface,
|
||||
# dns.DNSInstallInterface,
|
||||
# adtrust.ADTrustInstallInterface,
|
||||
# conncheck.ConnCheckInterface,
|
||||
# ServerUninstallInterface):
|
||||
|
||||
# domain_level
|
||||
if options.domain_level < MIN_DOMAIN_LEVEL:
|
||||
ansible_module.fail_json(
|
||||
msg="Domain Level cannot be lower than %d" % MIN_DOMAIN_LEVEL)
|
||||
elif options.domain_level > MAX_DOMAIN_LEVEL:
|
||||
ansible_module.fail_json(
|
||||
msg="Domain Level cannot be higher than %d" % MAX_DOMAIN_LEVEL)
|
||||
# ServerInstallInterface.__init__ #######################################
|
||||
try:
|
||||
self = options
|
||||
|
||||
# dirsrv_config_file
|
||||
if options.dirsrv_config_file is not None:
|
||||
if not os.path.exists(options.dirsrv_config_file):
|
||||
ansible_module.fail_json(
|
||||
msg="File %s does not exist." % options.dirsrv_config_file)
|
||||
# If any of the key file options are selected, all are required.
|
||||
cert_file_req = (self.dirsrv_cert_files, self.http_cert_files)
|
||||
cert_file_opt = (self.pkinit_cert_files,)
|
||||
if not self.no_pkinit:
|
||||
cert_file_req += cert_file_opt
|
||||
if self.no_pkinit and self.pkinit_cert_files:
|
||||
raise RuntimeError(
|
||||
"--no-pkinit and --pkinit-cert-file cannot be specified "
|
||||
"together"
|
||||
)
|
||||
if any(cert_file_req + cert_file_opt) and not all(cert_file_req):
|
||||
raise RuntimeError(
|
||||
"--dirsrv-cert-file, --http-cert-file, and --pkinit-cert-file "
|
||||
"or --no-pkinit are required if any key file options are used."
|
||||
)
|
||||
|
||||
# domain_name
|
||||
if (options.setup_dns and not options.allow_zone_overlap and \
|
||||
options.domain_name is not None):
|
||||
try:
|
||||
check_zone_overlap(options.domain_name, False)
|
||||
except ValueError as e:
|
||||
ansible_module.fail_json(msg=str(e))
|
||||
if not self.interactive:
|
||||
if self.dirsrv_cert_files and self.dirsrv_pin is None:
|
||||
raise RuntimeError(
|
||||
"You must specify --dirsrv-pin with --dirsrv-cert-file")
|
||||
if self.http_cert_files and self.http_pin is None:
|
||||
raise RuntimeError(
|
||||
"You must specify --http-pin with --http-cert-file")
|
||||
if self.pkinit_cert_files and self.pkinit_pin is None:
|
||||
raise RuntimeError(
|
||||
"You must specify --pkinit-pin with --pkinit-cert-file")
|
||||
|
||||
# dm_password
|
||||
with redirect_stdout(ansible_log):
|
||||
validate_dm_password(options.dm_password)
|
||||
if not self.setup_dns:
|
||||
if self.forwarders:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --forwarder option without the "
|
||||
"--setup-dns option")
|
||||
if self.auto_forwarders:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --auto-forwarders option without "
|
||||
"the --setup-dns option")
|
||||
if self.no_forwarders:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --no-forwarders option without the "
|
||||
"--setup-dns option")
|
||||
if self.forward_policy:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --forward-policy option without the "
|
||||
"--setup-dns option")
|
||||
if self.reverse_zones:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --reverse-zone option without the "
|
||||
"--setup-dns option")
|
||||
if self.auto_reverse:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --auto-reverse option without the "
|
||||
"--setup-dns option")
|
||||
if self.no_reverse:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --no-reverse option without the "
|
||||
"--setup-dns option")
|
||||
if self.no_dnssec_validation:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --no-dnssec-validation option "
|
||||
"without the --setup-dns option")
|
||||
elif self.forwarders and self.no_forwarders:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --forwarder option together with "
|
||||
"--no-forwarders")
|
||||
elif self.auto_forwarders and self.no_forwarders:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --auto-forwarders option together with "
|
||||
"--no-forwarders")
|
||||
elif self.reverse_zones and self.no_reverse:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --reverse-zone option together with "
|
||||
"--no-reverse")
|
||||
elif self.auto_reverse and self.no_reverse:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --auto-reverse option together with "
|
||||
"--no-reverse")
|
||||
|
||||
# admin_password
|
||||
with redirect_stdout(ansible_log):
|
||||
validate_admin_password(options.admin_password)
|
||||
if not self.setup_adtrust:
|
||||
if self.add_agents:
|
||||
raise RuntimeError(
|
||||
"You cannot specify an --add-agents option without the "
|
||||
"--setup-adtrust option")
|
||||
|
||||
# pkinit is not supported on DL0, don't allow related options
|
||||
if self.enable_compat:
|
||||
raise RuntimeError(
|
||||
"You cannot specify an --enable-compat option without the "
|
||||
"--setup-adtrust option")
|
||||
|
||||
# replica install: if not self.replica_file is None:
|
||||
if (not options._replica_install and \
|
||||
not options.domain_level > DOMAIN_LEVEL_0) or \
|
||||
(options._replica_install and self.replica_file is not None):
|
||||
if (options.no_pkinit or options.pkinit_cert_files is not None or
|
||||
options.pkinit_pin is not None):
|
||||
ansible_module.fail_json(
|
||||
msg="pkinit on domain level 0 is not supported. Please "
|
||||
"don't use any pkinit-related options.")
|
||||
options.no_pkinit = True
|
||||
if self.netbios_name:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --netbios-name option without the "
|
||||
"--setup-adtrust option")
|
||||
|
||||
if self.no_msdcs:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --no-msdcs option without the "
|
||||
"--setup-adtrust option")
|
||||
|
||||
if not hasattr(self, 'replica_install'):
|
||||
if self.external_cert_files and self.dirsrv_cert_files:
|
||||
raise RuntimeError(
|
||||
"Service certificate file options cannot be used with the "
|
||||
"external CA options.")
|
||||
|
||||
if self.external_ca_type and not self.external_ca:
|
||||
raise RuntimeError(
|
||||
"You cannot specify --external-ca-type without "
|
||||
"--external-ca")
|
||||
|
||||
if self.external_ca_profile and not self.external_ca:
|
||||
raise RuntimeError(
|
||||
"You cannot specify --external-ca-profile without "
|
||||
"--external-ca")
|
||||
|
||||
if self.uninstalling:
|
||||
if (self.realm_name or self.admin_password or
|
||||
self.master_password):
|
||||
raise RuntimeError(
|
||||
"In uninstall mode, -a, -r and -P options are not "
|
||||
"allowed")
|
||||
elif not self.interactive:
|
||||
if (not self.realm_name or not self.dm_password or
|
||||
not self.admin_password):
|
||||
raise RuntimeError(
|
||||
"In unattended mode you need to provide at least -r, "
|
||||
"-p and -a options")
|
||||
if self.setup_dns:
|
||||
if (not self.forwarders and
|
||||
not self.no_forwarders and
|
||||
not self.auto_forwarders):
|
||||
raise RuntimeError(
|
||||
"You must specify at least one of --forwarder, "
|
||||
"--auto-forwarders, or --no-forwarders options")
|
||||
|
||||
any_ignore_option_true = any(
|
||||
[self.ignore_topology_disconnect, self.ignore_last_of_role])
|
||||
if any_ignore_option_true and not self.uninstalling:
|
||||
raise RuntimeError(
|
||||
"'--ignore-topology-disconnect/--ignore-last-of-role' "
|
||||
"options can be used only during uninstallation")
|
||||
|
||||
if self.idmax < self.idstart:
|
||||
raise RuntimeError(
|
||||
"idmax (%s) cannot be smaller than idstart (%s)" %
|
||||
(self.idmax, self.idstart))
|
||||
else:
|
||||
# replica installers
|
||||
if self.servers and not self.domain_name:
|
||||
raise RuntimeError(
|
||||
"The --server option cannot be used without providing "
|
||||
"domain via the --domain option")
|
||||
|
||||
if self.setup_dns:
|
||||
if (not self.forwarders and
|
||||
not self.no_forwarders and
|
||||
not self.auto_forwarders):
|
||||
raise RuntimeError(
|
||||
"You must specify at least one of --forwarder, "
|
||||
"--auto-forwarders, or --no-forwarders options")
|
||||
|
||||
except RuntimeError as e:
|
||||
ansible_module.fail_json(msg=e)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# #######################################################################
|
||||
|
||||
# If any of the key file options are selected, all are required.
|
||||
cert_file_req = (options.dirsrv_cert_files, options.http_cert_files)
|
||||
@@ -351,7 +479,7 @@ def main():
|
||||
ansible_module.fail_json(
|
||||
msg="You cannot specify auto-reverse together with no-reverse")
|
||||
|
||||
if not options._replica_install:
|
||||
if not hasattr(self, 'replica_install'):
|
||||
if options.external_cert_files and options.dirsrv_cert_files:
|
||||
ansible_module.fail_json(
|
||||
msg="Service certificate file options cannot be used with the "
|
||||
@@ -403,34 +531,63 @@ def main():
|
||||
ansible_module.fail_json(
|
||||
msg="idmax (%s) cannot be smaller than idstart (%s)" %
|
||||
(options.idmax, options.idstart))
|
||||
else:
|
||||
# replica install
|
||||
if options.replica_file is None:
|
||||
if options.servers and not options.domain_name:
|
||||
ansible_module.fail_json(
|
||||
msg="servers cannot be used without providing domain")
|
||||
|
||||
else:
|
||||
if not os.path.isfile(options.replica_file):
|
||||
ansible_module.fail_json(
|
||||
msg="Replica file %s does not exist" % options.replica_file)
|
||||
# validation #############################################################
|
||||
|
||||
if options.dm_password is None:
|
||||
ansible_module.fail_json(msg="Directory Manager password required")
|
||||
|
||||
if options.admin_password is None:
|
||||
ansible_module.fail_json(msg="IPA admin password required")
|
||||
|
||||
# validation ############################################################
|
||||
|
||||
# domain_level
|
||||
if options.domain_level < MIN_DOMAIN_LEVEL:
|
||||
ansible_module.fail_json(
|
||||
msg="Domain Level cannot be lower than %d" % MIN_DOMAIN_LEVEL)
|
||||
elif options.domain_level > MAX_DOMAIN_LEVEL:
|
||||
ansible_module.fail_json(
|
||||
msg="Domain Level cannot be higher than %d" % MAX_DOMAIN_LEVEL)
|
||||
|
||||
# dirsrv_config_file
|
||||
if options.dirsrv_config_file is not None:
|
||||
if not os.path.exists(options.dirsrv_config_file):
|
||||
ansible_module.fail_json(
|
||||
msg="File %s does not exist." % options.dirsrv_config_file)
|
||||
|
||||
# domain_name
|
||||
if (options.setup_dns and not options.allow_zone_overlap and \
|
||||
options.domain_name is not None):
|
||||
try:
|
||||
check_zone_overlap(options.domain_name, False)
|
||||
except ValueError as e:
|
||||
ansible_module.fail_json(str(e))
|
||||
|
||||
# dm_password
|
||||
with redirect_stdout(ansible_log):
|
||||
validate_dm_password(options.dm_password)
|
||||
|
||||
# admin_password
|
||||
with redirect_stdout(ansible_log):
|
||||
validate_admin_password(options.admin_password)
|
||||
|
||||
# pkinit is not supported on DL0, don't allow related options
|
||||
|
||||
"""
|
||||
# replica install: if not options.replica_file is None:
|
||||
if (not options._replica_install and \
|
||||
not options.domain_level > DOMAIN_LEVEL_0) or \
|
||||
(options._replica_install and options.replica_file is not None):
|
||||
if (options.no_pkinit or options.pkinit_cert_files is not None or
|
||||
options.pkinit_pin is not None):
|
||||
ansible_module.fail_json(
|
||||
msg="pkinit on domain level 0 is not supported. Please "
|
||||
"don't use any pkinit-related options.")
|
||||
options.no_pkinit = True
|
||||
"""
|
||||
|
||||
if any(cert_file_req + cert_file_opt):
|
||||
ansible_module.fail_json(
|
||||
msg="You cannot specify dirsrv-cert-file, http-cert-file, "
|
||||
"or pkinit-cert-file together with replica file")
|
||||
|
||||
conflicting = { "realm": options.realm_name,
|
||||
"domain": options.domain_name,
|
||||
"hostname": options.host_name,
|
||||
"servers": options.servers,
|
||||
"principal": options.principal }
|
||||
conflicting_names = [ name for name in conflicting
|
||||
if conflicting[name] is not None ]
|
||||
if len(conflicting_names) > 0:
|
||||
ansible_module.fail_json(
|
||||
msg="You cannot specify %s option(s) with replica file." % \
|
||||
", ".join(conflicting_names))
|
||||
|
||||
if options.setup_dns:
|
||||
if len(options.forwarders) < 1 and not options.no_forwarders and \
|
||||
@@ -439,11 +596,12 @@ def main():
|
||||
msg="You must specify at least one of forwarders, "
|
||||
"auto-forwarders or no-forwarders")
|
||||
|
||||
if NUM_VERSION >= 40200 and options.master_password:
|
||||
ansible_module.warn("Specifying master-password is deprecated")
|
||||
if NUM_VERSION >= 40200 and options.master_password and \
|
||||
not options.external_cert_files:
|
||||
ansible_module.warn("Specifying kerberos master-password is deprecated")
|
||||
|
||||
options._installation_cleanup = True
|
||||
if not options.external_ca and len(options.external_cert_files) < 1 and \
|
||||
if not options.external_ca and not options.external_cert_files and \
|
||||
is_ipa_configured():
|
||||
options._installation_cleanup = False
|
||||
ansible_module.log(
|
||||
@@ -495,10 +653,11 @@ def main():
|
||||
ansible_module.fail_json(msg=error)
|
||||
|
||||
# external cert file paths are absolute
|
||||
for path in options.external_cert_files:
|
||||
if not os.path.isabs(path):
|
||||
ansible_module.fail_json(
|
||||
msg="External cert file '%s' must use an absolute path" % path)
|
||||
if options.external_cert_files:
|
||||
for path in options.external_cert_files:
|
||||
if not os.path.isabs(path):
|
||||
ansible_module.fail_json(
|
||||
msg="External cert file '%s' must use an absolute path" % path)
|
||||
|
||||
options.setup_ca = True
|
||||
# We only set up the CA if the PKCS#12 options are not given.
|
||||
@@ -517,6 +676,18 @@ def main():
|
||||
ansible_module.fail_json(msg=
|
||||
"--setup-kra cannot be used with CA-less installation")
|
||||
|
||||
# This will override any settings passed in on the cmdline
|
||||
if os.path.isfile(paths.ROOT_IPA_CACHE):
|
||||
# dm_password check removed, checked already
|
||||
try:
|
||||
cache_vars = read_cache(options.dm_password)
|
||||
options.__dict__.update(cache_vars)
|
||||
if cache_vars.get('external_ca', False):
|
||||
options.external_ca = False
|
||||
options.interactive = False
|
||||
except Exception as e:
|
||||
ansible_module.fail_json(msg="Cannot process the cache file: %s" % str(e))
|
||||
|
||||
# ca_subject
|
||||
if options.ca_subject:
|
||||
ca.subject_validator(ca.VALID_SUBJECT_ATTRS, options.ca_subject)
|
||||
@@ -538,9 +709,10 @@ def main():
|
||||
try:
|
||||
timeconf.check_timedate_services()
|
||||
except timeconf.NTPConflictingService as e:
|
||||
ansible_module.log("Conflicting time&date synchronization service '%s'"
|
||||
" will be disabled in favor of %s" % \
|
||||
(e.conflicting_service, time_service))
|
||||
ansible_module.log(
|
||||
"WARNING: conflicting time&date synchronization service "
|
||||
"'%s' will be disabled in favor of chronyd" % \
|
||||
e.conflicting_service)
|
||||
except timeconf.NTPConfigurationError:
|
||||
pass
|
||||
|
||||
@@ -610,6 +782,11 @@ def main():
|
||||
"You will not be able to establish trusts with Active "
|
||||
"Directory.")
|
||||
|
||||
# Do not ask for time source
|
||||
#if not options.no_ntp and not options.unattended and not (
|
||||
# options.ntp_servers or options.ntp_pool):
|
||||
# options.ntp_servers, options.ntp_pool = timeconf.get_time_source()
|
||||
|
||||
#########################################################################
|
||||
|
||||
http_pkcs12_file = None
|
||||
@@ -697,9 +874,16 @@ def main():
|
||||
_pkinit_pkcs12_file=pkinit_pkcs12_file,
|
||||
_pkinit_pkcs12_info=pkinit_pkcs12_info,
|
||||
_pkinit_ca_cert=pkinit_ca_cert,
|
||||
### certificate system ###
|
||||
external_ca=options.external_ca,
|
||||
external_ca_type=options.external_ca_type,
|
||||
external_ca_profile=options.external_ca_profile,
|
||||
### ad trust ###
|
||||
rid_base=options.rid_base,
|
||||
secondary_rid_base=options.secondary_rid_base,
|
||||
### client ###
|
||||
ntp_servers=options.ntp_servers,
|
||||
ntp_pool=options.ntp_pool,
|
||||
### additional ###
|
||||
_installation_cleanup=_installation_cleanup,
|
||||
domainlevel=options.domainlevel)
|
||||
|
||||
@@ -100,7 +100,7 @@ if NUM_VERSION >= 40500:
|
||||
update_hosts_file)
|
||||
from ipaserver.install.server.install import (
|
||||
check_dirsrv, validate_admin_password, validate_dm_password,
|
||||
write_cache)
|
||||
read_cache, write_cache)
|
||||
try:
|
||||
from ipaserver.install.dogtaginstance import PKIIniLoader
|
||||
except ImportError:
|
||||
@@ -218,6 +218,39 @@ options.add_sids = True
|
||||
options.add_agents = False
|
||||
|
||||
|
||||
# Installable
|
||||
options.uninstalling = False
|
||||
|
||||
# ServerInstallInterface
|
||||
options.description = "Server"
|
||||
|
||||
options.kinit_attempts = 1
|
||||
options.fixed_primary = True
|
||||
options.permit = False
|
||||
options.enable_dns_updates = False
|
||||
options.no_krb5_offline_passwords = False
|
||||
options.preserve_sssd = False
|
||||
options.no_sssd = False
|
||||
|
||||
# ServerMasterInstall
|
||||
options.force_join = False
|
||||
options.servers = None
|
||||
options.no_wait_for_dns = True
|
||||
options.host_password = None
|
||||
options.keytab = None
|
||||
options.setup_ca = True
|
||||
# always run sidgen task and do not allow adding agents on first master
|
||||
options.add_sids = True
|
||||
options.add_agents = False
|
||||
|
||||
# ADTrustInstallInterface
|
||||
# no_msdcs is deprecated
|
||||
options.no_msdcs = False
|
||||
|
||||
# Uninstall
|
||||
options.ignore_topology_disconnect = False
|
||||
options.ignore_last_of_role = False
|
||||
|
||||
def api_Backend_ldap2(host_name, setup_ca, connect=False):
|
||||
# we are sure we have the configuration file ready.
|
||||
cfg = dict(context='installer', confdir=paths.ETC_IPA, in_server=True,
|
||||
|
||||
12
roles/ipaserver/tasks/copy_external_cert.yml
Normal file
12
roles/ipaserver/tasks/copy_external_cert.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
- name: Install - Initialize ipaserver_external_cert_files
|
||||
set_fact:
|
||||
ipaserver_external_cert_files: []
|
||||
when: ipaserver_external_cert_files is undefined
|
||||
- name: Install - Copy "{{ item }}" "{{ inventory_hostname }}':/root/'{{ item }}"
|
||||
copy:
|
||||
src: "{{ item }}"
|
||||
dest: "/root/{{ item }}"
|
||||
force: yes
|
||||
- name: Install - Extend ipaserver_external_cert_files with "/root/{{ item }}"
|
||||
set_fact:
|
||||
ipaserver_external_cert_files: "{{ ipaserver_external_cert_files }} + [ '/root/{{ item }}' ]"
|
||||
@@ -24,6 +24,12 @@
|
||||
#- name: Install - Include Python2/3 import test
|
||||
# import_tasks: "{{ role_path }}/tasks/python_2_3_test.yml"
|
||||
|
||||
- include_tasks: "{{ role_path }}/tasks/copy_external_cert.yml"
|
||||
with_items: "{{ ipaserver_external_cert_files_from_controller }}"
|
||||
when: ipaserver_external_cert_files_from_controller is defined and
|
||||
ipaserver_external_cert_files_from_controller|length > 0 and
|
||||
not ipaserver_external_cert_files is defined
|
||||
|
||||
- name: Install - Server installation test
|
||||
ipaserver_test:
|
||||
### basic ###
|
||||
@@ -47,9 +53,9 @@
|
||||
# no_ui_redirect: "{{ ipaserver_no_ui_redirect }}"
|
||||
dirsrv_config_file: "{{ ipaserver_dirsrv_config_file | default(omit) }}"
|
||||
### ssl certificate ###
|
||||
dirsrv_cert_files: "{{ ipaserver_dirsrv_cert_files | default([]) }}"
|
||||
http_cert_files: "{{ ipaserver_http_cert_files | default([]) }}"
|
||||
pkinit_cert_files: "{{ ipaserver_pkinit_cert_files | default([]) }}"
|
||||
dirsrv_cert_files: "{{ ipaserver_dirsrv_cert_files | default(omit) }}"
|
||||
http_cert_files: "{{ ipaserver_http_cert_files | default(omit) }}"
|
||||
pkinit_cert_files: "{{ ipaserver_pkinit_cert_files | default(omit) }}"
|
||||
# dirsrv_pin
|
||||
# http_pin
|
||||
# pkinit_pin
|
||||
@@ -58,6 +64,8 @@
|
||||
# pkinit_name
|
||||
### client ###
|
||||
# mkhomedir
|
||||
ntp_servers: "{{ ipaclient_ntp_servers | default(omit) }}"
|
||||
ntp_pool: "{{ ipaclient_ntp_pool | default(omit) }}"
|
||||
no_ntp: "{{ ipaclient_no_ntp }}"
|
||||
# ssh_trust_dns
|
||||
# no_ssh
|
||||
@@ -66,7 +74,8 @@
|
||||
### certificate system ###
|
||||
external_ca: "{{ ipaserver_external_ca }}"
|
||||
external_ca_type: "{{ ipaserver_external_ca_type | default(omit) }}"
|
||||
external_cert_files: "{{ ipaserver_external_cert_files | default([]) }}"
|
||||
external_ca_profile: "{{ ipaserver_external_ca_profile | default(omit) }}"
|
||||
external_cert_files: "{{ ipaserver_external_cert_files | default(omit) }}"
|
||||
subject_base: "{{ ipaserver_subject_base | default(omit) }}"
|
||||
ca_subject: "{{ ipaserver_ca_subject | default(omit) }}"
|
||||
# ca_signing_algorithm
|
||||
@@ -128,8 +137,12 @@
|
||||
setup_kra: "{{ ipaserver_setup_kra }}"
|
||||
setup_dns: "{{ ipaserver_setup_dns }}"
|
||||
### certificate system ###
|
||||
# external_ca
|
||||
# external_cert_files
|
||||
external_ca: "{{ ipaserver_external_ca }}"
|
||||
external_ca_type: "{{ ipaserver_external_ca_type | default(omit) }}"
|
||||
external_ca_profile:
|
||||
"{{ ipaserver_external_ca_profile | default(omit) }}"
|
||||
external_cert_files:
|
||||
"{{ ipaserver_external_cert_files | default(omit) }}"
|
||||
subject_base: "{{ ipaserver_subject_base | default(omit) }}"
|
||||
ca_subject: "{{ ipaserver_ca_subject | default(omit) }}"
|
||||
### dns ###
|
||||
@@ -155,6 +168,8 @@
|
||||
|
||||
- name: Install - Setup NTP
|
||||
ipaserver_setup_ntp:
|
||||
ntp_servers: "{{ result_ipaserver_test.ntp_servers | default(omit) }}"
|
||||
ntp_pool: "{{ result_ipaserver_test.ntp_pool | default(omit) }}"
|
||||
when: not ipaclient_no_ntp | bool and (ipaserver_external_cert_files
|
||||
is undefined or ipaserver_external_cert_files|length < 1)
|
||||
|
||||
@@ -174,8 +189,9 @@
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
# no_host_dns: "{{ result_ipaserver_test.no_host_dns }}"
|
||||
dirsrv_config_file: "{{ ipaserver_dirsrv_config_file | default(omit) }}"
|
||||
dirsrv_cert_files: "{{ ipaserver_dirsrv_cert_files | default([]) }}"
|
||||
external_cert_files: "{{ ipaserver_external_cert_files | default([]) }}"
|
||||
dirsrv_cert_files: "{{ ipaserver_dirsrv_cert_files | default(omit) }}"
|
||||
external_cert_files:
|
||||
"{{ ipaserver_external_cert_files | default(omit) }}"
|
||||
subject_base: "{{ result_ipaserver_prepare.subject_base }}"
|
||||
ca_subject: "{{ result_ipaserver_prepare.ca_subject }}"
|
||||
# no_reverse: "{{ ipaserver_no_reverse }}"
|
||||
@@ -200,7 +216,8 @@
|
||||
setup_dns: "{{ ipaserver_setup_dns }}"
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
no_host_dns: "{{ result_ipaserver_test.no_host_dns }}"
|
||||
external_cert_files: "{{ ipaserver_external_cert_files | default([]) }}"
|
||||
external_cert_files:
|
||||
"{{ ipaserver_external_cert_files | default(omit) }}"
|
||||
subject_base: "{{ result_ipaserver_prepare.subject_base }}"
|
||||
ca_subject: "{{ result_ipaserver_prepare.ca_subject }}"
|
||||
no_reverse: "{{ ipaserver_no_reverse }}"
|
||||
@@ -241,7 +258,11 @@
|
||||
dirsrv_cert_files: "{{ ipaserver_dirsrv_cert_files | default([]) }}"
|
||||
_dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info }}"
|
||||
external_ca: "{{ ipaserver_external_ca }}"
|
||||
external_cert_files: "{{ ipaserver_external_cert_files | default([]) }}"
|
||||
external_ca_type: "{{ ipaserver_external_ca_type | default(omit) }}"
|
||||
external_ca_profile:
|
||||
"{{ ipaserver_external_ca_profile | default(omit) }}"
|
||||
external_cert_files:
|
||||
"{{ ipaserver_external_cert_files | default(omit) }}"
|
||||
subject_base: "{{ result_ipaserver_prepare.subject_base }}"
|
||||
_subject_base: "{{ result_ipaserver_prepare._subject_base }}"
|
||||
ca_subject: "{{ result_ipaserver_prepare.ca_subject }}"
|
||||
@@ -251,150 +272,163 @@
|
||||
reverse_zones: "{{ result_ipaserver_prepare.reverse_zones }}"
|
||||
no_reverse: "{{ ipaserver_no_reverse }}"
|
||||
auto_forwarders: "{{ ipaserver_auto_forwarders }}"
|
||||
register: result_ipaserver_setup_ca
|
||||
|
||||
- name: Install - Setup otpd
|
||||
ipaserver_setup_otpd:
|
||||
realm: "{{ result_ipaserver_test.realm }}"
|
||||
hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
- name: Copy /root/ipa.csr to "{{ inventory_hostname }}-ipa.csr"
|
||||
fetch:
|
||||
src: /root/ipa.csr
|
||||
dest: "{{ inventory_hostname }}-ipa.csr"
|
||||
flat: yes
|
||||
when: result_ipaserver_setup_ca.csr_generated | bool and
|
||||
ipaserver_copy_csr_to_controller | bool
|
||||
|
||||
- name: Install - Setup HTTP
|
||||
ipaserver_setup_http:
|
||||
dm_password: "{{ ipadm_password }}"
|
||||
password: "{{ ipaadmin_password }}"
|
||||
master_password: "{{ ipaserver_master_password }}"
|
||||
domain: "{{ result_ipaserver_test.domain }}"
|
||||
realm: "{{ result_ipaserver_test.realm }}"
|
||||
hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
# ip_addresses: "{{ result_ipaserver_prepare.ip_addresses }}"
|
||||
reverse_zones: "{{ result_ipaserver_prepare.reverse_zones }}"
|
||||
setup_adtrust: "{{ result_ipaserver_test.setup_adtrust }}"
|
||||
setup_kra: "{{ result_ipaserver_test.setup_kra }}"
|
||||
setup_dns: "{{ ipaserver_setup_dns }}"
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
no_host_dns: "{{ result_ipaserver_test.no_host_dns }}"
|
||||
dirsrv_cert_files: "{{ ipaserver_dirsrv_cert_files | default([]) }}"
|
||||
external_cert_files: "{{ ipaserver_external_cert_files | default([]) }}"
|
||||
subject_base: "{{ result_ipaserver_prepare.subject_base }}"
|
||||
_subject_base: "{{ result_ipaserver_prepare._subject_base }}"
|
||||
ca_subject: "{{ result_ipaserver_prepare.ca_subject }}"
|
||||
_ca_subject: "{{ result_ipaserver_prepare._ca_subject }}"
|
||||
no_reverse: "{{ ipaserver_no_reverse }}"
|
||||
auto_forwarders: "{{ ipaserver_auto_forwarders }}"
|
||||
no_pkinit: "{{ result_ipaserver_test.no_pkinit }}"
|
||||
no_hbac_allow: "{{ ipaserver_no_hbac_allow }}"
|
||||
idstart: "{{ result_ipaserver_test.idstart }}"
|
||||
idmax: "{{ result_ipaserver_test.idmax }}"
|
||||
http_cert_files: "{{ ipaserver_http_cert_files | default([]) }}"
|
||||
no_ui_redirect: "{{ ipaserver_no_ui_redirect }}"
|
||||
- block:
|
||||
- name: Install - Setup otpd
|
||||
ipaserver_setup_otpd:
|
||||
realm: "{{ result_ipaserver_test.realm }}"
|
||||
hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
|
||||
- name: Install - Setup KRA
|
||||
ipaserver_setup_kra:
|
||||
hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
dm_password: "{{ ipadm_password }}"
|
||||
setup_kra: "{{ result_ipaserver_test.setup_kra }}"
|
||||
realm: "{{ result_ipaserver_test.realm }}"
|
||||
pki_config_override: "{{ ipaserver_pki_config_override |
|
||||
default(omit) }}"
|
||||
when: result_ipaserver_test.setup_kra | bool
|
||||
- name: Install - Setup HTTP
|
||||
ipaserver_setup_http:
|
||||
dm_password: "{{ ipadm_password }}"
|
||||
password: "{{ ipaadmin_password }}"
|
||||
master_password: "{{ ipaserver_master_password }}"
|
||||
domain: "{{ result_ipaserver_test.domain }}"
|
||||
realm: "{{ result_ipaserver_test.realm }}"
|
||||
hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
# ip_addresses: "{{ result_ipaserver_prepare.ip_addresses }}"
|
||||
reverse_zones: "{{ result_ipaserver_prepare.reverse_zones }}"
|
||||
setup_adtrust: "{{ result_ipaserver_test.setup_adtrust }}"
|
||||
setup_kra: "{{ result_ipaserver_test.setup_kra }}"
|
||||
setup_dns: "{{ ipaserver_setup_dns }}"
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
no_host_dns: "{{ result_ipaserver_test.no_host_dns }}"
|
||||
dirsrv_cert_files: "{{ ipaserver_dirsrv_cert_files | default([]) }}"
|
||||
external_cert_files:
|
||||
"{{ ipaserver_external_cert_files | default(omit) }}"
|
||||
subject_base: "{{ result_ipaserver_prepare.subject_base }}"
|
||||
_subject_base: "{{ result_ipaserver_prepare._subject_base }}"
|
||||
ca_subject: "{{ result_ipaserver_prepare.ca_subject }}"
|
||||
_ca_subject: "{{ result_ipaserver_prepare._ca_subject }}"
|
||||
no_reverse: "{{ ipaserver_no_reverse }}"
|
||||
auto_forwarders: "{{ ipaserver_auto_forwarders }}"
|
||||
no_pkinit: "{{ result_ipaserver_test.no_pkinit }}"
|
||||
no_hbac_allow: "{{ ipaserver_no_hbac_allow }}"
|
||||
idstart: "{{ result_ipaserver_test.idstart }}"
|
||||
idmax: "{{ result_ipaserver_test.idmax }}"
|
||||
http_cert_files: "{{ ipaserver_http_cert_files | default([]) }}"
|
||||
no_ui_redirect: "{{ ipaserver_no_ui_redirect }}"
|
||||
|
||||
- name: Install - Setup DNS
|
||||
ipaserver_setup_dns:
|
||||
hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
setup_dns: "{{ ipaserver_setup_dns }}"
|
||||
forwarders: "{{ result_ipaserver_prepare.forwarders }}"
|
||||
forward_policy: "{{ result_ipaserver_prepare.forward_policy }}"
|
||||
zonemgr: "{{ ipaserver_zonemgr | default(omit) }}"
|
||||
no_dnssec_validation: "{{ result_ipaserver_prepare.no_dnssec_validation }}"
|
||||
### additional ###
|
||||
dns_ip_addresses: "{{ result_ipaserver_prepare.dns_ip_addresses }}"
|
||||
dns_reverse_zones: "{{ result_ipaserver_prepare.dns_reverse_zones }}"
|
||||
when: ipaserver_setup_dns | bool
|
||||
- name: Install - Setup KRA
|
||||
ipaserver_setup_kra:
|
||||
hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
dm_password: "{{ ipadm_password }}"
|
||||
setup_kra: "{{ result_ipaserver_test.setup_kra }}"
|
||||
realm: "{{ result_ipaserver_test.realm }}"
|
||||
pki_config_override: "{{ ipaserver_pki_config_override |
|
||||
default(omit) }}"
|
||||
when: result_ipaserver_test.setup_kra | bool
|
||||
|
||||
- name: Install - Setup ADTRUST
|
||||
ipaserver_setup_adtrust:
|
||||
hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
setup_adtrust: "{{ result_ipaserver_test.setup_adtrust }}"
|
||||
### ad trust ###
|
||||
enable_compat: "{{ ipaserver_enable_compat }}"
|
||||
rid_base: "{{ result_ipaserver_test.rid_base }}"
|
||||
secondary_rid_base: "{{ result_ipaserver_test.secondary_rid_base }}"
|
||||
### additional ###
|
||||
adtrust_netbios_name: "{{ result_ipaserver_prepare.adtrust_netbios_name }}"
|
||||
adtrust_reset_netbios_name:
|
||||
"{{ result_ipaserver_prepare.adtrust_reset_netbios_name }}"
|
||||
when: result_ipaserver_test.setup_adtrust
|
||||
- name: Install - Setup DNS
|
||||
ipaserver_setup_dns:
|
||||
hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
setup_dns: "{{ ipaserver_setup_dns }}"
|
||||
forwarders: "{{ result_ipaserver_prepare.forwarders }}"
|
||||
forward_policy: "{{ result_ipaserver_prepare.forward_policy }}"
|
||||
zonemgr: "{{ ipaserver_zonemgr | default(omit) }}"
|
||||
no_dnssec_validation: "{{ result_ipaserver_prepare.no_dnssec_validation }}"
|
||||
### additional ###
|
||||
dns_ip_addresses: "{{ result_ipaserver_prepare.dns_ip_addresses }}"
|
||||
dns_reverse_zones: "{{ result_ipaserver_prepare.dns_reverse_zones }}"
|
||||
when: ipaserver_setup_dns | bool
|
||||
|
||||
- name: Install - Set DS password
|
||||
ipaserver_set_ds_password:
|
||||
dm_password: "{{ ipadm_password }}"
|
||||
password: "{{ ipaadmin_password }}"
|
||||
domain: "{{ result_ipaserver_test.domain }}"
|
||||
realm: "{{ result_ipaserver_test.realm }}"
|
||||
hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
subject_base: "{{ result_ipaserver_prepare.subject_base }}"
|
||||
ca_subject: "{{ result_ipaserver_prepare.ca_subject }}"
|
||||
no_pkinit: "{{ result_ipaserver_test.no_pkinit }}"
|
||||
no_hbac_allow: "{{ ipaserver_no_hbac_allow }}"
|
||||
idstart: "{{ result_ipaserver_test.idstart }}"
|
||||
idmax: "{{ result_ipaserver_test.idmax }}"
|
||||
dirsrv_config_file: "{{ ipaserver_dirsrv_config_file | default(omit) }}"
|
||||
_dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info }}"
|
||||
- name: Install - Setup ADTRUST
|
||||
ipaserver_setup_adtrust:
|
||||
hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
setup_adtrust: "{{ result_ipaserver_test.setup_adtrust }}"
|
||||
### ad trust ###
|
||||
enable_compat: "{{ ipaserver_enable_compat }}"
|
||||
rid_base: "{{ result_ipaserver_test.rid_base }}"
|
||||
secondary_rid_base: "{{ result_ipaserver_test.secondary_rid_base }}"
|
||||
### additional ###
|
||||
adtrust_netbios_name: "{{ result_ipaserver_prepare.adtrust_netbios_name }}"
|
||||
adtrust_reset_netbios_name:
|
||||
"{{ result_ipaserver_prepare.adtrust_reset_netbios_name }}"
|
||||
when: result_ipaserver_test.setup_adtrust
|
||||
|
||||
- name: Install - Setup client
|
||||
include_role:
|
||||
name: ipaclient
|
||||
vars:
|
||||
state: present
|
||||
ipaclient_on_master: yes
|
||||
ipaclient_domain: "{{ result_ipaserver_test.domain }}"
|
||||
ipaclient_realm: "{{ result_ipaserver_test.realm }}"
|
||||
ipaclient_servers: ["{{ result_ipaserver_test.hostname }}"]
|
||||
ipaclient_hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
ipaclient_no_ntp:
|
||||
"{{ 'true' if result_ipaserver_test.ipa_python_version >= 40690
|
||||
else 'false' }}"
|
||||
ipaclient_install_packages: "{{ ipaserver_install_packages }}"
|
||||
- name: Install - Set DS password
|
||||
ipaserver_set_ds_password:
|
||||
dm_password: "{{ ipadm_password }}"
|
||||
password: "{{ ipaadmin_password }}"
|
||||
domain: "{{ result_ipaserver_test.domain }}"
|
||||
realm: "{{ result_ipaserver_test.realm }}"
|
||||
hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
subject_base: "{{ result_ipaserver_prepare.subject_base }}"
|
||||
ca_subject: "{{ result_ipaserver_prepare.ca_subject }}"
|
||||
no_pkinit: "{{ result_ipaserver_test.no_pkinit }}"
|
||||
no_hbac_allow: "{{ ipaserver_no_hbac_allow }}"
|
||||
idstart: "{{ result_ipaserver_test.idstart }}"
|
||||
idmax: "{{ result_ipaserver_test.idmax }}"
|
||||
dirsrv_config_file: "{{ ipaserver_dirsrv_config_file | default(omit) }}"
|
||||
_dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info }}"
|
||||
|
||||
- name: Install - Enable IPA
|
||||
ipaserver_enable_ipa:
|
||||
hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
setup_dns: "{{ ipaserver_setup_dns }}"
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
register: result_ipaserver_enable_ipa
|
||||
- name: Install - Setup client
|
||||
include_role:
|
||||
name: ipaclient
|
||||
vars:
|
||||
state: present
|
||||
ipaclient_on_master: yes
|
||||
ipaclient_domain: "{{ result_ipaserver_test.domain }}"
|
||||
ipaclient_realm: "{{ result_ipaserver_test.realm }}"
|
||||
ipaclient_servers: ["{{ result_ipaserver_test.hostname }}"]
|
||||
ipaclient_hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
ipaclient_no_ntp:
|
||||
"{{ 'true' if result_ipaserver_test.ipa_python_version >= 40690
|
||||
else 'false' }}"
|
||||
ipaclient_install_packages: "{{ ipaserver_install_packages }}"
|
||||
|
||||
- name: Install - Cleanup root IPA cache
|
||||
file:
|
||||
path: "/root/.ipa_cache"
|
||||
state: absent
|
||||
when: result_ipaserver_enable_ipa.changed
|
||||
- name: Install - Enable IPA
|
||||
ipaserver_enable_ipa:
|
||||
hostname: "{{ result_ipaserver_test.hostname }}"
|
||||
setup_dns: "{{ ipaserver_setup_dns }}"
|
||||
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
|
||||
register: result_ipaserver_enable_ipa
|
||||
|
||||
- name: Install - Configure firewalld
|
||||
command: >
|
||||
firewall-cmd
|
||||
--permanent
|
||||
--add-service=freeipa-ldap
|
||||
--add-service=freeipa-ldaps
|
||||
{{ "--add-service=freeipa-trust" if ipaserver_setup_adtrust | bool
|
||||
else "" }}
|
||||
{{ "--add-service=dns" if ipaserver_setup_dns | bool else "" }}
|
||||
{{ "--add-service=ntp" if not ipaclient_no_ntp | bool else "" }}
|
||||
when: ipaserver_setup_firewalld | bool
|
||||
- name: Install - Cleanup root IPA cache
|
||||
file:
|
||||
path: "/root/.ipa_cache"
|
||||
state: absent
|
||||
when: result_ipaserver_enable_ipa.changed
|
||||
|
||||
- name: Install - Configure firewalld runtime
|
||||
command: >
|
||||
firewall-cmd
|
||||
--add-service=freeipa-ldap
|
||||
--add-service=freeipa-ldaps
|
||||
{{ "--add-service=freeipa-trust" if ipaserver_setup_adtrust | bool
|
||||
else "" }}
|
||||
{{ "--add-service=dns" if ipaserver_setup_dns | bool else "" }}
|
||||
{{ "--add-service=ntp" if not ipaclient_no_ntp | bool else "" }}
|
||||
when: ipaserver_setup_firewalld | bool
|
||||
- name: Install - Configure firewalld
|
||||
command: >
|
||||
firewall-cmd
|
||||
--permanent
|
||||
--add-service=freeipa-ldap
|
||||
--add-service=freeipa-ldaps
|
||||
{{ "--add-service=freeipa-trust" if ipaserver_setup_adtrust | bool
|
||||
else "" }}
|
||||
{{ "--add-service=dns" if ipaserver_setup_dns | bool else "" }}
|
||||
{{ "--add-service=ntp" if not ipaclient_no_ntp | bool else "" }}
|
||||
when: ipaserver_setup_firewalld | bool
|
||||
|
||||
- name: Install - Configure firewalld runtime
|
||||
command: >
|
||||
firewall-cmd
|
||||
--add-service=freeipa-ldap
|
||||
--add-service=freeipa-ldaps
|
||||
{{ "--add-service=freeipa-trust" if ipaserver_setup_adtrust | bool
|
||||
else "" }}
|
||||
{{ "--add-service=dns" if ipaserver_setup_dns | bool else "" }}
|
||||
{{ "--add-service=ntp" if not ipaclient_no_ntp | bool else "" }}
|
||||
when: ipaserver_setup_firewalld | bool
|
||||
|
||||
when: not result_ipaserver_setup_ca.csr_generated | bool
|
||||
|
||||
when: not ansible_check_mode and not
|
||||
(not result_ipaserver_test.changed and
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
- "vars/default.yml"
|
||||
|
||||
- name: Install IPA server
|
||||
include_tasks: tasks/install.yml
|
||||
include_tasks: install.yml
|
||||
when: state|default('present') == 'present'
|
||||
|
||||
- name: Uninstall IPA server
|
||||
include_tasks: tasks/uninstall.yml
|
||||
include_tasks: uninstall.yml
|
||||
when: state|default('present') == 'absent'
|
||||
|
||||
5
roles/ipaserver/vars/RedHat-8.yml
Normal file
5
roles/ipaserver/vars/RedHat-8.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
# defaults file for ipaserver
|
||||
# vars/RedHat-8.yml
|
||||
ipaserver_packages: [ "@idm:DL1/server" ]
|
||||
ipaserver_packages_dns: [ "@idm:DL1/dns" ]
|
||||
ipaserver_packages_adtrust: [ "@idm:DL1/adtrust" ]
|
||||
49
tests/external-signed-ca-with-automatic-copy/external-ca.sh
Normal file
49
tests/external-signed-ca-with-automatic-copy/external-ca.sh
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
master=$1
|
||||
if [ -z "$master" ]; then
|
||||
echo "ERROR: master is not set"
|
||||
echo
|
||||
echo "usage: $0 master-fqdn domain"
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
PASSWORD="SomeCApassword"
|
||||
DBDIR="${master}-nssdb"
|
||||
PWDFILE="$DBDIR/pwdfile.txt"
|
||||
NOISE="/etc/passwd"
|
||||
|
||||
domain=$2
|
||||
if [ -z "$domain" ]; then
|
||||
echo "ERROR: domain is not set"
|
||||
echo
|
||||
echo "usage: $0 master-fqdn domain"
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
if [ ! -f "${master}-ipa.csr" ]; then
|
||||
echo "ERROR: ${master}-ipa.csr missing"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
ROOT_KEY_ID=0x$(dd if=/dev/urandom bs=20 count=1 | xxd -p)
|
||||
IPA_CA_KEY_ID=0x$(dd if=/dev/urandom bs=20 count=1 | xxd -p)
|
||||
|
||||
rm -rf "$DBDIR"
|
||||
mkdir "$DBDIR"
|
||||
echo "$PASSWORD" > "$PWDFILE"
|
||||
certutil -N -d "$DBDIR" -f "$PWDFILE"
|
||||
echo -e "0\n1\n5\n6\n9\ny\ny\n\ny\n${ROOT_KEY_ID}\nn\n" \
|
||||
| certutil -d "$DBDIR" -f "$PWDFILE" -S -z "$NOISE" -n ca -x -t C,C,C \
|
||||
-s "CN=PRIMARY,O=$domain" -x -1 -2 --extSKID
|
||||
|
||||
openssl req -outform der -in "${master}-ipa.csr" -out "$DBDIR/req.csr"
|
||||
echo -e "0\n1\n5\n6\n9\ny\ny\n\ny\ny\n${ROOT_KEY_ID}\n\n\nn\n${IPA_CA_KEY_ID}\nn\n" \
|
||||
| certutil -d "$DBDIR" -f "$PWDFILE" -C -z "$NOISE" -c ca \
|
||||
-i "$DBDIR/req.csr" -o "$DBDIR/external.cer" -1 -2 -3 --extSKID
|
||||
|
||||
openssl x509 -inform der -in "$DBDIR/external.cer" -out "$DBDIR/external.pem"
|
||||
certutil -L -n ca -d "$DBDIR" -a > "$DBDIR/ca.crt"
|
||||
cat "$DBDIR/external.pem" "$DBDIR/ca.crt" > "$DBDIR/chain.crt"
|
||||
|
||||
cp "$DBDIR/chain.crt" "${master}-chain.crt"
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
- name: Playbook to configure IPA server step1
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
vars:
|
||||
ipaserver_external_ca: yes
|
||||
ipaserver_copy_csr_to_controller: yes
|
||||
|
||||
roles:
|
||||
- role: ipaserver
|
||||
state: present
|
||||
|
||||
- name: Create CA, get /root/ipa.csr signed by your CA, ..
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
- name: Run external-ca.sh
|
||||
command: >
|
||||
/bin/bash
|
||||
external-ca.sh
|
||||
"{{ groups.ipaserver[0] }}"
|
||||
"{{ ipaserver_domain | default(groups.ipaserver[0].split('.')[1:] | join ('.')) }}"
|
||||
args:
|
||||
chdir: "{{ playbook_dir }}"
|
||||
|
||||
- name: Playbook to configure IPA server step2
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
vars:
|
||||
ipaserver_external_cert_files_from_controller: "{{ groups.ipaserver[0] + '-chain.crt' }}"
|
||||
#ipaserver_external_ca_file: "{{ groups.ipaserver[0] + '-cacert.asc' }}"
|
||||
|
||||
roles:
|
||||
- role: ipaserver
|
||||
state: present
|
||||
8
tests/external-signed-ca-with-automatic-copy/inventory
Normal file
8
tests/external-signed-ca-with-automatic-copy/inventory
Normal file
@@ -0,0 +1,8 @@
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
|
||||
[ipaservcer:vars]
|
||||
ipaadmin_password=SomeADMINpassword
|
||||
ipadm_password=SomeDMpassword
|
||||
ipaserver_domain=test.local
|
||||
ipaserver_realm=TEST.LOCAL
|
||||
49
tests/external-signed-ca-with-manual-copy/external-ca.sh
Normal file
49
tests/external-signed-ca-with-manual-copy/external-ca.sh
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
master=$1
|
||||
if [ -z "$master" ]; then
|
||||
echo "ERROR: master is not set"
|
||||
echo
|
||||
echo "usage: $0 master-fqdn domain"
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
PASSWORD="SomeCApassword"
|
||||
DBDIR="${master}-nssdb"
|
||||
PWDFILE="$DBDIR/pwdfile.txt"
|
||||
NOISE="/etc/passwd"
|
||||
|
||||
domain=$2
|
||||
if [ -z "$domain" ]; then
|
||||
echo "ERROR: domain is not set"
|
||||
echo
|
||||
echo "usage: $0 master-fqdn domain"
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
if [ ! -f "${master}-ipa.csr" ]; then
|
||||
echo "ERROR: ${master}-ipa.csr missing"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
ROOT_KEY_ID=0x$(dd if=/dev/urandom bs=20 count=1 | xxd -p)
|
||||
IPA_CA_KEY_ID=0x$(dd if=/dev/urandom bs=20 count=1 | xxd -p)
|
||||
|
||||
rm -rf "$DBDIR"
|
||||
mkdir "$DBDIR"
|
||||
echo "$PASSWORD" > "$PWDFILE"
|
||||
certutil -N -d "$DBDIR" -f "$PWDFILE"
|
||||
echo -e "0\n1\n5\n6\n9\ny\ny\n\ny\n${ROOT_KEY_ID}\nn\n" \
|
||||
| certutil -d "$DBDIR" -f "$PWDFILE" -S -z "$NOISE" -n ca -x -t C,C,C \
|
||||
-s "CN=PRIMARY,O=$domain" -x -1 -2 --extSKID
|
||||
|
||||
openssl req -outform der -in "${master}-ipa.csr" -out "$DBDIR/req.csr"
|
||||
echo -e "0\n1\n5\n6\n9\ny\ny\n\ny\ny\n${ROOT_KEY_ID}\n\n\nn\n${IPA_CA_KEY_ID}\nn\n" \
|
||||
| certutil -d "$DBDIR" -f "$PWDFILE" -C -z "$NOISE" -c ca \
|
||||
-i "$DBDIR/req.csr" -o "$DBDIR/external.cer" -1 -2 -3 --extSKID
|
||||
|
||||
openssl x509 -inform der -in "$DBDIR/external.cer" -out "$DBDIR/external.pem"
|
||||
certutil -L -n ca -d "$DBDIR" -a > "$DBDIR/ca.crt"
|
||||
cat "$DBDIR/external.pem" "$DBDIR/ca.crt" > "$DBDIR/chain.crt"
|
||||
|
||||
cp "$DBDIR/chain.crt" "${master}-chain.crt"
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
- name: Playbook to configure IPA server step1
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
vars:
|
||||
ipaserver_external_ca: yes
|
||||
|
||||
roles:
|
||||
- role: ipaserver
|
||||
state: present
|
||||
|
||||
post_tasks:
|
||||
- name: Copy CSR /root/ipa.csr from node to "{{ groups.ipaserver[0] + '-ipa.csr' }}"
|
||||
fetch:
|
||||
src: /root/ipa.csr
|
||||
dest: "{{ groups.ipaserver[0] + '-ipa.csr' }}"
|
||||
flat: yes
|
||||
|
||||
- name: Get /root/ipa.csr, create CA, sign with our CA and copy to node
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
- name: Run external-ca.sh
|
||||
command: >
|
||||
/bin/bash
|
||||
external-ca.sh
|
||||
"{{ groups.ipaserver[0] }}"
|
||||
"{{ ipaserver_domain | default(groups.ipaserver[0].split('.')[1:] | join ('.')) }}"
|
||||
args:
|
||||
chdir: "{{ playbook_dir }}"
|
||||
|
||||
- name: Playbook to configure IPA server step2
|
||||
hosts: ipaserver
|
||||
become: true
|
||||
vars:
|
||||
ipaserver_external_cert_files: "/root/chain.crt"
|
||||
#ipaserver_external_ca_file: "cacert.asc"
|
||||
|
||||
pre_tasks:
|
||||
- name: Copy "{{ groups.ipaserver[0] + '-chain.crt' }}" to /root/chain.crt on node
|
||||
copy:
|
||||
src: "{{ groups.ipaserver[0] + '-chain.crt' }}"
|
||||
dest: "/root/chain.crt"
|
||||
force: yes
|
||||
|
||||
roles:
|
||||
- role: ipaserver
|
||||
state: present
|
||||
8
tests/external-signed-ca-with-manual-copy/inventory
Normal file
8
tests/external-signed-ca-with-manual-copy/inventory
Normal file
@@ -0,0 +1,8 @@
|
||||
[ipaserver]
|
||||
ipaserver.test.local
|
||||
|
||||
[ipaservcer:vars]
|
||||
ipaadmin_password=SomeADMINpassword
|
||||
ipadm_password=SomeDMpassword
|
||||
ipaserver_domain=test.local
|
||||
ipaserver_realm=TEST.LOCAL
|
||||
Reference in New Issue
Block a user