Compare commits

...

25 Commits

Author SHA1 Message Date
Thomas Woerner
158fdb1876 ipatopologysegment: Use commands, not command
command has been used instead of commands. command is not defined.
2019-06-17 20:33:49 +02:00
Thomas Woerner
c905cdaf02 Update README-topology.md 2019-06-17 18:52:55 +02:00
Thomas Woerner
4378d161bc Update README.md 2019-06-17 18:35:42 +02:00
Thomas Woerner
1009c889b3 Update README.md 2019-06-17 18:34:01 +02:00
Thomas Woerner
56a8acedf0 ipatopologysegment: Allow domain+ca suffix, new state: checked
It is now possible to use domain+ca as suffix, That means that the segment
will be handled for the suffixes domain and also ca.

The new state checked is returning two lists found and not-found. If a
segment exists, the ckecked suffix is added to the found list. If a segment
from suffix is not found, it is added to the not-found list.

New example playbooks have been added:
   playbooks/topology/add-topologysegments.yml
   playbooks/topology/check-topologysegments.yml
   playbooks/topology/delete-topologysegments.yml

The cluster playbook has been extended by the
2019-06-17 18:23:44 +02:00
Thomas Woerner
8ac1a6e590 ipareplica: Add default(omit) for ipaclient role varaibles
To make sure that there will be no issue with undefined output from
ipareplica_test, the default(omit) has been added.
2019-06-17 16:49:14 +02:00
Thomas Woerner
76d436ec0b ipareplica: Use ipareplica_server if set.
The variable has been ignored and was not used. The servers are now
properly set from ipareplica_servers now.
2019-06-17 16:42:37 +02:00
Thomas Woerner
438f09bad9 ipareplica: The dm password is not needed for ipareplica_master_password
The module ipareplica_master_password has been a copy from ipaserver role
and still contained code to read the cache file. This is not needed for
the replica. Therefore there is no need also to provide the dm password
to ipareplica_master_password any more.
2019-06-17 16:40:14 +02:00
Thomas Woerner
0f73362ef5 Merge pull request #60 from dawud/feature/improve_package_installation_tasks
Fixes #17 Improve how tasks manage package installation
2019-06-17 15:49:33 +02:00
Thomas Woerner
2372e5b98d ipaclient: Report error message if ipaclient_get_otp failed
Currently the error message of ipaclient_get_otp is not visble if it failed
due to the use of no_log. The no_log option is needed and useful if the
one-time password has successfully been generated, but is bad if there was
an error while generating the password, for example if a clock skew has been
detected.

There is now a new task to print the error message if ipaclient_get_otp. The
task for ipaclient_get_otp has been marked with "ignore_errors: yes" and
the new task "Install - Report error for OTP generation" will always fail
and only be used if result_ipaclient_get_otp is failed.
2019-06-17 11:14:02 +02:00
Thomas Woerner
50046a7348 ipaclient: Fix test of newly supported ipaadmin_keytab use in ipaclient_join
The check for the ipaadmin_keytab was not using "is defined". Because of this
the playbook processing failed if the variable was not defined.
2019-06-17 10:25:02 +02:00
Thomas Woerner
79d0ac9d47 Update README.md 2019-06-17 09:39:59 +02:00
Thomas Woerner
ca43b427a8 Update README.md 2019-06-17 09:38:10 +02:00
Thomas Woerner
b89112cf81 Update README.md
Fixes #87 (ipareplica README seems to have incorrect info)
2019-06-14 20:13:22 +02:00
Thomas Woerner
215359e377 Update README.md 2019-06-14 19:28:51 +02:00
Thomas Woerner
a79437d39a Update README.md 2019-06-14 19:27:52 +02:00
Thomas Woerner
4829399ef3 Update README.md 2019-06-14 18:26:50 +02:00
Thomas Woerner
e218441c39 Update README.md 2019-06-14 18:18:33 +02:00
Thomas Woerner
8ffe818b7f Update README.md 2019-06-14 18:15:07 +02:00
Thomas Woerner
100b7eabaf ipaclient: Do not fail if ipaadmin_password is not used
This is for example the case if ipaadmin_keytab is used instead without
ipaclient_use_otp.
2019-06-14 17:34:04 +02:00
Thomas Woerner
ac24f9c067 ipaclient_join: Support to use ipaadmin_keytab without ipaclient_use_otp
ipaadmin_keytab has been supported only with with ipaclient_use_otp. But
it can also be used without for ipa-join.

Important is that ipaadmin_keytab needs to be placed on the cliend node
and ipaadmin_keytab needs to be a full path. Otherwise the file will not
be found.
2019-06-14 17:30:53 +02:00
Thomas Woerner
da14fa29bb ipaclient: Use omit (None) for password, keytab, no string length checks
Password and keytab do not need to be set explicitely to an empty string
when they are not set. Also there is no need to have string length checks
in the role tasks.
2019-06-14 17:26:12 +02:00
Thomas Woerner
813d5bbf97 ipaclient_get_otp: Remove ansible_python_interpreter handling
With the deactivation of the Python2/3 test the handling of
ansible_python_interpreter needs to be removed as the setting might
not exist and is not changed in with the Python2/3 test any more.
2019-06-12 11:06:38 +02:00
Thomas Woerner
3de056bc60 ipaclient_test: Fix Python2 decode use with Python3
This is a remain of the Python2 version and has been fixed.

Fixed: #86 (AttributeError: 'str' object has no attribute 'decode')
2019-06-12 11:00:59 +02:00
David Sastre Medina
20e5338ad5 Fixes #17 Improve how tasks manage package installation
The use of squash_actions to invoke a package module, such as “yum”, to
only invoke the module once is deprecated, and will be removed in
Ansible 2.11.
Instead of relying on implicit squashing, tasks should instead supply
the list directly to the name, pkg or package parameter of the module.

See [1] for a reference to the upstream documentation.

The ipa-krb5 and ipa-sssd modules include *_packages variables in both
defaults/ and vars/, additionally, the list of packages in ipa-sssd differs
from one to the other.
Unify list of packages into vars/

[1]: https://docs.ansible.com/ansible/latest/porting_guides/porting_guide_2.7.html#using-a-loop-on-a-package-module-via-squash-actions
2019-06-04 12:42:19 +02:00
20 changed files with 792 additions and 396 deletions

View File

@@ -116,6 +116,38 @@ Example playbook to verify a topology suffix:
state: verified
```
Example playbook to add a list of topology segments:
```yaml
---
- name: Add topology segments
hosts: ipaserver
become: true
gather_facts: false
vars:
ipaadmin_password: password1
ipatopology_segments:
- {suffix: domain, left: replica1.test.local, right: replica2.test.local}
- {suffix: domain, left: replica2.test.local, right: replica3.test.local}
- {suffix: domain, left: replica3.test.local, right: replica4.test.local}
- {suffix: domain+ca, left: replica4.test.local, right: replica1.test.local}
tasks:
- name: Add topology segment
ipatopologysegment:
password: "{{ ipaadmin_password }}"
suffix: "{{ item.suffix }}"
name: "{{ item.name | default(omit) }}"
left: "{{ item.left }}"
right: "{{ item.right }}"
#state: present
#state: absent
#state: checked
state: reinitialized
loop: "{{ ipatopology_segments | default([]) }}"
```
Variables
=========
@@ -127,12 +159,12 @@ 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
`suffix` | The topology suffix to be used, this can either be `domain` or `ca` | yes
`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
`right` \| `rightnode` | The right replication node string - an IPA server | no
`direction` | The direction a segment will be reinitialized. It can either be `left-to-right` or `right-to-left` and only used with `state: reinitialized` |
`state` | The state to ensure. It can be one of `present`, `absent`, `enabled`, `disabled` or `reinitialized` | yes
`state` | The state to ensure. It can be one of `present`, `absent`, `enabled`, `disabled`, `checked` or `reinitialized` | yes
ipatopologysuffix

View File

@@ -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`.
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.
**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.
@@ -234,6 +234,42 @@ ipaserver_realm=TEST.LOCAL
```
All these settings will be available in the ```[ipaserver]```, ```[ipareplicas]``` and ```[ipaclient]``` groups.
**Topology**
With this playbook it is possible to add a list of topology segments using the `ipatopologysegment` module.
```yaml
---
- name: Add topology segments
hosts: ipaserver
become: true
gather_facts: false
vars:
ipaadmin_password: password1
ipatopology_segments:
- {suffix: domain, left: replica1.test.local, right: replica2.test.local}
- {suffix: domain, left: replica2.test.local, right: replica3.test.local}
- {suffix: domain, left: replica3.test.local, right: replica4.test.local}
- {suffix: domain+ca, left: replica4.test.local, right: replica1.test.local}
tasks:
- name: Add topology segment
ipatopologysegment:
password: "{{ ipaadmin_password }}"
suffix: "{{ item.suffix }}"
name: "{{ item.name | default(omit) }}"
left: "{{ item.left }}"
right: "{{ item.right }}"
#state: present
#state: absent
#state: checked
state: reinitialized
loop: "{{ ipatopology_segments | default([]) }}"
```
Playbooks
=========
@@ -309,7 +345,7 @@ Roles
* [Replica](roles/ipareplica/README.md)
* [Client](roles/ipaclient/README.md)
Plugins in plugin/modules
Modules in plugin/modules
=========================
* [ipatopologysegment](README-topology.md)

View File

@@ -0,0 +1,23 @@
---
- name: Add topology segments
hosts: ipaserver
become: true
gather_facts: false
vars:
ipatopology_segments:
- {suffix: domain, left: replica1.test.local, right: replica2.test.local}
- {suffix: domain, left: replica2.test.local, right: replica3.test.local}
- {suffix: domain, left: replica3.test.local, right: replica4.test.local}
- {suffix: domain+ca, left: replica4.test.local, right: replica1.test.local}
tasks:
- name: Add topology segment
ipatopologysegment:
password: "{{ ipaadmin_password }}"
suffix: "{{ item.suffix }}"
name: "{{ item.name | default(omit) }}"
left: "{{ item.left }}"
right: "{{ item.right }}"
state: present
loop: "{{ ipatopology_segments | default([]) }}"

View File

@@ -0,0 +1,23 @@
---
- name: Add topology segments
hosts: ipaserver
become: true
gather_facts: false
vars:
ipatopology_segments:
- {suffix: domain, left: replica1.test.local, right: replica2.test.local}
- {suffix: domain, left: replica2.test.local, right: replica3.test.local}
- {suffix: domain, left: replica3.test.local, right: replica4.test.local}
- {suffix: domain+ca, left: replica4.test.local, right: replica1.test.local}
tasks:
- name: Add topology segment
ipatopologysegment:
password: "{{ ipaadmin_password }}"
suffix: "{{ item.suffix }}"
name: "{{ item.name | default(omit) }}"
left: "{{ item.left }}"
right: "{{ item.right }}"
state: checked
loop: "{{ ipatopology_segments | default([]) }}"

View File

@@ -0,0 +1,23 @@
---
- name: Add topology segments
hosts: ipaserver
become: true
gather_facts: false
vars:
ipatopology_segments:
- {suffix: domain, left: replica1.test.local, right: replica2.test.local}
- {suffix: domain, left: replica2.test.local, right: replica3.test.local}
- {suffix: domain, left: replica3.test.local, right: replica4.test.local}
- {suffix: domain+ca, left: replica4.test.local, right: replica1.test.local}
tasks:
- name: Add topology segment
ipatopologysegment:
password: "{{ ipaadmin_password }}"
suffix: "{{ item.suffix }}"
name: "{{ item.name | default(omit) }}"
left: "{{ item.left }}"
right: "{{ item.right }}"
state: absent
loop: "{{ ipatopology_segments | default([]) }}"

View File

@@ -41,7 +41,7 @@ options:
suffix:
description: Topology suffix
required: true
choices: ["domain", "ca"]
choices: ["domain", "ca", "domain+ca"]
name:
description: Topology segment name, unique identifier.
required: false
@@ -59,7 +59,8 @@ options:
state:
description: State to ensure
default: present
choices: ["present", "absent", "enabled", "disabled", "reinitialized"]
choices: ["present", "absent", "enabled", "disabled", "reinitialized"
"checked" ]
author:
- Thomas Woerner
"""
@@ -87,9 +88,29 @@ EXAMPLES = """
name: ipaserver.test.local-to-replica1.test.local
direction: left-to-right
state: reinitialized
- ipatopologysegment:
suffix: domain+ca
left: ipaserver.test.local
right: ipareplica1.test.local
state: absent
- ipatopologysegment:
suffix: domain+ca
left: ipaserver.test.local
right: ipareplica1.test.local
state: checked
"""
RETURN = """
found:
description: List of found segments
returned: if state is checked
type: list
not-found:
description: List of not found segments
returned: if state is checked
type: list
"""
from ansible.module_utils.basic import AnsibleModule
@@ -128,13 +149,33 @@ def find_cn(module, suffix, name):
else:
return None
def find_left_right_cn(module, suffix, left, right, name):
if left is not None and right is not None:
left_right = find_left_right(module, suffix, left, right)
if left_right is not None:
if name is not None and \
left_right["cn"][0] != to_text(name):
module.fail_json(
msg="Left and right nodes do not match "
"given name name (cn) '%s'" % name)
return left_right
# else: Nothing to change
elif name is not None:
cn = find_cn(module, suffix, name)
if cn is not None:
return cn
# else: Nothing to change
else:
module.fail_json(
msg="Either left and right or name need to be set.")
return None
def main():
ansible_module = AnsibleModule(
argument_spec=dict(
principal=dict(type="str", default="admin"),
password=dict(type="str", required=False, no_log=True),
suffix=dict(choices=["domain", "ca"], required=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),
right=dict(type="str", aliases=["rightnode"], default=None),
@@ -142,7 +183,7 @@ def main():
choices=["left-to-right", "right-to-left"]),
state=dict(type="str", default="present",
choices=["present", "absent", "enabled", "disabled",
"reinitialized"]),
"reinitialized", "checked"]),
),
supports_check_mode=True,
)
@@ -153,7 +194,7 @@ def main():
principal = ansible_module.params.get("principal")
password = ansible_module.params.get("password")
suffix = ansible_module.params.get("suffix")
suffixes = ansible_module.params.get("suffix")
name = ansible_module.params.get("name")
left = ansible_module.params.get("left")
right = ansible_module.params.get("right")
@@ -169,6 +210,7 @@ def main():
# Init
changed = False
exit_args = { }
ccache_dir = None
ccache_name = None
try:
@@ -176,100 +218,100 @@ def main():
ccache_dir, ccache_name = temp_kinit(principal, password)
api_connect()
command = None
commands = []
# Get name (cn) from left and right node if set for absent, disabled
# or reinitialized.
if state in ["absent", "disabled", "reinitialized"]:
if left is not None and right is not None:
left_right = find_left_right(ansible_module, suffix,
left, right)
if left_right is not None:
if name is not None and \
left_right["cn"][0] != to_text(name):
ansible_module.fail_json(
msg="Left and right nodes do not match "
"given name name (cn) '%s'" % name)
args = {
"cn": left_right["cn"][0]
}
# else: Nothing to change
elif name is not None:
result = find_cn(ansible_module, suffix, name)
if result is not None:
args = {
"cn": result["cn"][0]
}
# else: Nothing to change
else:
ansible_module.fail_json(
msg="Either left and right or name need to be set.")
for suffix in suffixes.split("+"):
# Create command
if state in ["present", "enabled"]:
# Make sure topology segment exists
# Create command
if state in ["present", "enabled"]:
# Make sure topology segment exists
if left is None or right is None:
ansible_module.fail_json(
msg="Left and right need to be set.")
args = {
"iparepltoposegmentleftnode": to_text(left),
"iparepltoposegmentrightnode": to_text(right),
}
if name is not None:
args["cn"] = to_text(name)
res_left_right = find_left_right(ansible_module, suffix,
left, right)
if res_left_right is not None:
if name is not None and \
res_left_right["cn"][0] != to_text(name):
if left is None or right is None:
ansible_module.fail_json(
msg="Left and right nodes already used with "
"different name (cn) '%s'" % res_left_right["cn"])
msg="Left and right need to be set.")
args = {
"iparepltoposegmentleftnode": to_text(left),
"iparepltoposegmentrightnode": to_text(right),
}
if name is not None:
args["cn"] = to_text(name)
# Left and right nodes and also the name can not be
# changed
for key in [ "iparepltoposegmentleftnode",
"iparepltoposegmentrightnode" ]:
if key in args:
del args[key]
if len(args) > 1:
# cn needs to be in args always
command = "topologysegment_mod"
# else: Nothing to change
else:
if name is None:
args["cn"] = to_text("%s-to-%s" % (left, right))
command = "topologysegment_add"
res_left_right = find_left_right(ansible_module, suffix,
left, right)
if res_left_right is not None:
if name is not None and \
res_left_right["cn"][0] != to_text(name):
ansible_module.fail_json(
msg="Left and right nodes already used with "
"different name (cn) '%s'" % res_left_right["cn"])
elif state in ["absent", "disabled"]:
# Make sure topology segment does not exist
if len(args) > 0:
# Either name defined or found name from left and right node
command = "topologysegment_del"
elif state == "reinitialized":
# Reinitialize segment
if len(args) > 0:
# Either name defined or found name from left and right node
command = "topologysegment_reinitialize"
if direction == "left-to-right":
args["left"] = True
elif direction == "right-to-left":
args["right"] = True
# Left and right nodes and also the name can not be
# changed
for key in [ "iparepltoposegmentleftnode",
"iparepltoposegmentrightnode" ]:
if key in args:
del args[key]
if len(args) > 1:
# cn needs to be in args always
commands.append(["topologysegment_mod", args])
# else: Nothing to change
else:
if name is None:
args["cn"] = to_text("%s-to-%s" % (left, right))
commands.append(["topologysegment_add", args])
elif state in ["absent", "disabled"]:
# Make sure topology segment does not exist
res_find = find_left_right_cn(ansible_module, suffix,
left, right, name)
if res_find is not None:
# Found either given name or found name from left and right
# node
args = {
"cn": res_find["cn"][0]
}
commands.append(["topologysegment_del", args])
elif state == "checked":
# Check if topology segment does exists
res_find = find_left_right_cn(ansible_module, suffix,
left, right, name)
if res_find is not None:
# Found either given name or found name from left and right
# node
exit_args.setdefault("found", []).append(suffix)
else:
# Not found
exit_args.setdefault("not-found", []).append(suffix)
elif state == "reinitialized":
# Reinitialize segment
if direction not in [ "left-to-right", "right-to-left" ]:
ansible_module.fail_json(msg="Unknown direction '%s'" %
direction)
else:
ansible_module.fail_json(msg="Unkown state '%s'" % state)
res_find = find_left_right_cn(ansible_module, suffix,
left, right, name)
if res_find is not None:
# Found either given name or found name from left and right
# node
args = {
"cn": res_find["cn"][0]
}
if direction == "left-to-right":
args["left"] = True
elif direction == "right-to-left":
args["right"] = True
commands.append(["topologysegment_reinitialize", args])
else:
ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Execute command
if command is not None:
for command, args in commands:
result = api_command(ansible_module, command,
to_text(suffix), args)
changed = True
@@ -282,7 +324,7 @@ def main():
# Done
ansible_module.exit_json(changed=changed)
ansible_module.exit_json(changed=changed, **exit_args)
if __name__ == "__main__":
main()

View File

@@ -1,13 +1,46 @@
ipaclient role
==============
Description
-----------
This [Ansible](https://www.ansible.com/) role allows to join hosts as clients to an IPA domain. This can be done in differnt ways using auto-discovery of the servers, domain and other settings or by specifying them.
**Note**: The ansible playbooks and role 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.
Features
--------
* Client deployment
* One-time-password (OTP) support
* Repair mode
Supported FreeIPA Versions
--------------------------
FreeIPA versions 4.5 and up are supported by the client role. There is also limited support for verison 4.4.
Supported Distributions
-----------------------
* RHEL/CentOS 7.4+
* Fedora 26+
* Ubuntu
Requirements
------------
**Controller**
* Ansible version: 2.8+
* python3-gssapi is required on the controller if a one time password (OTP) is used to install the client.
**Node**
* Supported FreeIPA version (see above)
* Supported distribution (needed for package installation only, see above)
This role allows to join hosts as clients to an IPA domain. This can be done in differnt ways using auto-discovery of the servers, domain and other settings or by specifying them.
Usage
-----
=====
Example inventory file with fixed principal using auto-discovery with DNS records:
@@ -74,78 +107,74 @@ Example playbook to setup the IPA client(s) using principal and password from in
state: present
```
Playbooks
=========
The playbooks needed to deploy or undeploy a client are part of the repository in the playbooks folder. There are also playbooks to deploy and undeploy clusters.
```
install-client.yml
uninstall-client.yml
```
Please remember to link or copy the playbooks to the base directory of ansible-freeipa if you want to use the roles within the source archive.
How to setup a client
---------------------
```bash
ansible-playbook -v -i inventory/hosts install-client.yml
```
This will deploy the clients defined in the inventory file.
Variables
---------
=========
### `ipaclients`
Base Variables
--------------
The mandatory `ipaclients` group is a list of the names of the IPA clients in FQDN form. All these clients will be installed or configured using the playbook.
Variable | Description | Required
-------- | ----------- | --------
`ipaclients` | This group is a list of the names of the IPA clients in FQDN form. All these clients will be installed or configured using the playbook. | yes
`ipaclient_domain` | This string value sets the DNS domain that will be used for client installation. Usually the DNS domain is a lower-cased name of the Kerberos realm. If the role is for example used in a cluster inventory and `ipaserver_domain` is set, then it will be used. | no
`ipaclient_realm` | This string value sets the Kerberos realm that will be used for client installation. Usually the Kerberos realm is an upper-cased name of the DNS domain. If the role is for example used in a cluster inventory and `ipaserver_realm` is set, then it will be used. If `ipaclient_realm` is not set, then it will be generated from `ipaclient_domain` if this is set. | no
`ipaclient_mkhomedir` | This bool value defines if PAM will be configured to create a users home directory if it does not exist. `ipaclient_mkhomedir` defaults to `no`. | no
`ipaclient_force_join` | This bool value defines if an already enrolled host can join again. `ipaclient_force_join` defaults to `no`. | no
`ipaclient_kinit_attempts` | The int value defines the number of tries to repeat the request for a failed host Kerberos ticket. `ipaclient_kinit_attempts` defaults to 5.| no
`ipaclient_ntp_servers` | The list defines the NTP servers to be used. | no
`ipaclient_ntp_pool` | The string value defines the ntp server pool to be used. | no
`ipaclient_no_ntp` | The bool value defines if NTP will not be configured and enabled. `ipaclient_no_ntp` defaults to `no`. | no
`ipaclient_ssh_trust_dns` | The bool value defines if OpenSSH client will be configured to trust DNS SSHFP records. `ipaclient_ssh_trust_dns` defaults to `no`. | no
`ipaclient_no_ssh` | The bool value defines if OpenSSH client will be configured. `ipaclient_no_ssh` defaults to `no`. | no
`ipaclient_no_sshd` | The bool value defines if OpenSSH server will be configured. `ipaclient_no_sshd` defaults to `no`. | no
`ipaclient_no_sudo` | The bool value defines if SSSD will be configured as a data source for sudo. `ipaclient_no_sudo` defaults to `no`. | no
`ipaclient_no_dns_sshfp` | The bool value defines if DNS SSHFP records will not be created automatically. `ipaclient_no_dns_sshfp` defaults to `no`. | no
`ipaclient_force` | The bool value defines if settings will be forced even in the error case. `ipaclient_force` defaults to `no`. | no
`ipaclient_force_ntpd` | The bool value defines if ntpd usage will be forced. This is not supported anymore and leads to a warning. `ipaclient_force_ntpd` defaults to `no`. | no
`ipaclient_nisdomain` | This string value defines the NIS domain name. | no
`ipaclient_no_nisdomain` | The bool value defines if the NIS domain name will not be configured. `ipaclient_no_nisdomain` defaults to `no`. | no
`ipaclient_configure_firefox` | The bool value defines if Firefox will be configured to use IPA domain credentials. `ipaclient_configure_firefox` defaults to `no`. | no
`ipaclient_firefox_dir` | The string value defines the Firefox installation directory. For example: '/usr/lib/firefox'. | no
`ipaclient_all_ip_addresses` | The bool value defines if DNS A/AAAA records for each IP address on the client will be created. `ipaclient_all_ip_addresses` defaults to `no`. | no
`ipasssd_fixed_primary` | The bool value defines if SSSD will be configured to use a fixed server as the primary IPA server. `ipasssd_fixed_primary` defaults to `no`. | no
`ipasssd_permit` | The bool value defines if SSSD will be configured to permit all access. Otherwise the machine will be controlled by the Host-based Access Controls (HBAC) on the IPA server. `ipasssd_permit` defaults to `no`. | no
`ipasssd_enable_dns_updates` | The bool value tells SSSD to automatically update DNS with the IP address of this client. `ipasssd_enable_dns_updates` defaults to `no`. | no
`ipasssd_no_krb5_offline_passwords` | The bool value defines if SSSD will be configured not to store user password when the server is offline . `ipasssd_no_krb5_offline_passwords` defaults to `no`. | no
`ipasssd_preserve_sssd` | The bool value defines if the old SSSD configuration will be preserved if it is not possible to merge it with a new one. `ipasssd_preserve_sssd` defaults to `no`. | no
`ipaclient_request_cert` | The bool value defines if the certificate for the machine wil be requested. The certificate will be stored in /etc/ipa/nssdb under the nickname "Local IPA host". . `ipaclient_request_cert` defaults to `no`. The option is deprecated and will be removed in a future release. | no
`ipaclient_keytab` | The string value contains the path on the node of a backup host keytab from a previous enrollment. | no
### `ipaclient_domain`
The optional `ipaclient_domain` variable sets the DNS domain that will be used for client installation. Usually the DNS domain is a lower-cased name of the Kerberos realm.
If `ipaclient_domain` is not set, then it will be generated from the domain part of the first entry from the `ipaservers` FQDN group if the group is defined and contains at least one entry. If `ipaservers` is not defined, then the domain will be tried to gather using DNS autodiscovery. `ipaclient_domain` needs to be set if the primary DNS domain is different from domain part of the server FQDN.
### `ipaclient_realm`
The optional `ipaclient_realm` sets the Kerberos realm that will be used for client installation. Usually the Kerberos realm is an upper-cased name of the DNS domain.
If `ipaclient_realm` is not set, then it will be generated from `ipaclient_domain` if this is set. If both are not set, then this
### `ipaclient_keytab`
The optional `ipaclient_keytab` contains the path of a backup host keytab from a previous enrollment.
### `ipaclient_force_join`
The `ipaclient_force_join` bool value defines if an already enrolled host can join again. `ipaclient_force_join` defaults to `no`.
### `ipaclient_use_otp`
The `ipaclient_use_otp` bool value defines if a one-time password will be generated to join a new or existing host. `ipaclient_use_otp` defaults to `no`.
The enforcement on an existing host is not done if there is a working krb5.keytab on the host. If the generation of an otp is enforced for an existing host entry, then the host gets diabled and the containing keytab gets removed.
### `ipaclient_allow_repair`
The `ipaclient_allow_repair` bool value defines if an already joined or partly set-up client can be repaired. `ipaclient_allow_repair` defaults to `no`.
Contrary to `ipaclient_force_join=yes` the host entry will not be changed on the server.
### `ipaclient_kinit_attempts`
The optional `ipaclient_kinit_attempts` defines the number of tries to repeat the request for a failed host Kerberos ticket. `ipaclient_kinit_attempts` defaults to 3.
### `ipaclient_no_ntp`
The `ipaclient_no_ntp` bool value defines if NTP will not be configured and enabled. `ipaclient_no_ntp` defaults to `no`.
### `ipaclient_mkhomedir`
The `ipaclient_mkhomedir` bool value defines if PAM will be configured to create a users home directory if it does not exist. `ipaclient_mkhomedir` defaults to `no`.
Server Variables
----------------
### `ipaservers`
The optional `ipaservers` group is a list of the IPA server full qualified host names. In a topology with a chain of servers and replicas, it is important to use the right server or replica as the server for the client. If there is a need to overwrite the setting for a client in the `ipaclients` group, please use the list `ipaclient_servers` explained below.
If no `ipaservers` group is defined than the installation preparation step will try to use DNS autodiscovery to identify the the IPA server using DNS txt records.
### `ipaadmin_keytab`
The `ipaadmin_keytab` variable enables the use of an admin keytab as an alternativce authentication method. The variable needs to contain the local path to the keytab file. If `ipaadmin_keytab` is used, then `ipaadmin_password` does not need to be set.
### `ipaadmin_principal`
The optional `ipaadmin_principal` variable only needs to be set if the name of the Kerberos admin principal is not "admin". If `ipaadmin_principal` is not set it will be set internally to "admin".
### `ipaadmin_password`
The `ipaadmin_password` variable contains the Kerberos password of the Kerberos admin principal. If `ipaadmin_keytab` is used, then `ipaadmin_password` does not need to be set.
Variable | Description | Required
-------- | ----------- | --------
`ipaservers` | This group is a list of the IPA server full qualified host names. In a topology with a chain of servers and replicas, it is important to use the right server or replica as the server for the client. If there is a need to overwrite the setting for a client in the `ipaclients` group, please use the list `ipaclient_servers` explained below. If no `ipaservers` group is defined than the installation preparation step will try to use DNS autodiscovery to identify the the IPA server using DNS txt records. | mostly
`ipaadmin_keytab` | The string variable enables the use of an admin keytab as an alternativce authentication method. The variable needs to contain the local path to the keytab file. If `ipaadmin_keytab` is used, then `ipaadmin_password` does not need to be set. If `ipaadmin_keytab` is used with `ipaclient_use_otp: yes` then the keytab needs to be available on the contoller, else on the client node. The use of full path names is recommended. | no
`ipaadmin_principal` | The string variable only needs to be set if the name of the Kerberos admin principal is not "admin". If `ipaadmin_principal` is not set it will be set internally to "admin". | no
`ipaadmin_password` | The string variable contains the Kerberos password of the Kerberos admin principal. If `ipaadmin_keytab` is used, then `ipaadmin_password` does not need to be set. | mostly
Topology Variables
@@ -153,21 +182,25 @@ Topology Variables
These variables can be used to define or change how clients are arranged within a cluster for example.
### `ipaclient_no_dns_lookup`
Variable | Description | Required
-------- | ----------- | --------
`ipaclient_no_dns_lookup` | The bool value defines if the `ipaservers` group will be used as servers for the clients automatically. If enabled this deactivates DNS lookup in Kerberos in client installations. `ipaclient_no_dns_lookup` defauults to `no`. | no
`ipaclient_servers` | The optional list can be used to manually override list of servers on a per client basis. The list of servers is normally taken from from `ipaservers` group. | no
The `ipaclient_no_dns_lookup` bool value defines if the `ipaservers` group will be used as servers for the clients automatically. If enabled this deactivates DNS lookup in Kerberos in client installations. `ipaclient_no_dns_lookup` defauults to `no`.
### `ipaclient_servers`
Special Variables
-----------------
The optional `ipaclient_servers` varaible can be used to manually override list of servers on a per client basis. The list of servers is normally taken from from `ipaservers` group.
Variable | Description | Required
-------- | ----------- | --------
`ipaclient_use_otp` | The bool value defines if a one-time password will be generated to join a new or existing host. `ipaclient_use_otp` defaults to `no`. The enforcement on an existing host is not done if there is a working krb5.keytab on the host. If the generation of an otp is enforced for an existing host entry, then the host gets diabled and the containing keytab gets removed. | no
`ipaclient_allow_repair` | The bool value defines if an already joined or partly set-up client can be repaired. `ipaclient_allow_repair` defaults to `no`. Contrary to `ipaclient_force_join=yes` the host entry will not be changed on the server. | no
`ipaclient_install_packages` | The bool value defines if the needed packages are installed on the node. `ipaclient_install_packages` defaults to `yes`. | no
`ipaclient_on_master` | The bool value is only used in the server and replica installation process to install the client part. It should not be set otherwise. `ipaclient_on_master` defaults to `no`. | no
Requirements
------------
freeipa-client v4.4 or later
Authors
-------
=======
Florence Blanc-Renaud

View File

@@ -155,8 +155,6 @@ class ActionModule(ActionBase):
keytab = self._task.args.get('keytab', None)
password = self._task.args.get('password', None)
lifetime = self._task.args.get('lifetime', '1h')
ansible_python_interpreter = self._task.args.get('ansible_python_interpreter', None)
task_vars["ansible_python_interpreter"] = ansible_python_interpreter
if (not keytab and not password):
result['failed'] = True
@@ -169,7 +167,7 @@ class ActionModule(ActionBase):
return result
data = self._execute_module(module_name='ipaclient_get_facts', module_args=dict(),
task_vars={ "ansible_python_interpreter": ansible_python_interpreter })
task_vars=None)
try:
domain = data['ansible_facts']['ipa']['domain']
realm = data['ansible_facts']['ipa']['realm']

View File

@@ -71,9 +71,6 @@ options:
ipaddress:
description: the IP address for the host
required: false
ansible_python_interpreter:
desciption: The ansible python interpreter used in the action plugin part, ignored here
required: false
requirements:
- gssapi on the Ansible controller
@@ -318,7 +315,6 @@ def main():
ipaddress = dict(required=False),
random = dict(default=False, type='bool'),
state = dict(default='present', choices=[ 'present', 'absent' ]),
ansible_python_interpreter = dict(required=False),
),
supports_check_mode=True,
)

View File

@@ -60,6 +60,9 @@ options:
password:
description: The password to use if not using Kerberos to authenticate.
required: false
admin_keytab:
description: The path to a local admin keytab.
required: false
keytab:
description: The path to a backed-up host keytab from previous enrollment.
required: false
@@ -138,6 +141,7 @@ def main():
principal=dict(required=False),
password=dict(required=False, no_log=True),
keytab=dict(required=False),
admin_keytab=dict(required=False),
ca_cert_file=dict(required=False),
force_join=dict(required=False, type='bool'),
kinit_attempts=dict(required=False, type='int', default=5),
@@ -157,14 +161,17 @@ def main():
principal = module.params.get('principal')
password = module.params.get('password')
keytab = module.params.get('keytab')
admin_keytab = module.params.get('admin_keytab')
ca_cert_file = module.params.get('ca_cert_file')
kinit_attempts = module.params.get('kinit_attempts')
debug = module.params.get('debug')
if password is not None and password != "" and \
keytab is not None and keytab != "":
if password is not None and keytab is not None:
module.fail_json(msg="Password and keytab cannot be used together")
if password is None and admin_keytab is None:
module.fail_json(msg="Password or admin_keytab is needed")
client_domain = hostname[hostname.find(".")+1:]
nolog = tuple()
env = {'PATH': SECURE_PATH}
@@ -174,7 +181,7 @@ def main():
options.ca_cert_file = ca_cert_file
options.unattended = True
options.principal = principal if principal != "" else None
options.principal = principal
options.force = False
options.password = password
@@ -207,15 +214,32 @@ def main():
env['XMLRPC_TRACE_CURL'] = 'yes'
if force_join:
join_args.append("-f")
if principal:
if principal is not None:
if principal.find('@') == -1:
principal = '%s@%s' % (principal, realm)
try:
kinit_password(principal, password, ccache_name,
config=krb_name)
except RuntimeError as e:
module.fail_json(
msg="Kerberos authentication failed: {}".format(e))
if admin_keytab:
join_args.append("-f")
if not os.path.exists(admin_keytab):
module.fail_json(
msg="Keytab file could not be found: %s" % \
admin_keytab)
try:
kinit_keytab(principal,
admin_keytab,
ccache_name,
config=krb_name,
attempts=kinit_attempts)
except GSSError as e:
module.fail_json(
msg="Kerberos authentication failed: %s" % str(e))
else:
try:
kinit_password(principal, password, ccache_name,
config=krb_name)
except RuntimeError as e:
module.fail_json(
msg="Kerberos authentication failed: {}".format(e))
elif keytab:
join_args.append("-f")
if os.path.exists(keytab):

View File

@@ -3,9 +3,8 @@
- name: Install - Ensure that IPA client packages are installed
package:
name: "{{ item }}"
name: "{{ ipaclient_packages }}"
state: present
with_items: "{{ ipaclient_packages }}"
when: ipaclient_install_packages | bool
#- name: Install - Include Python2/3 import test
@@ -107,10 +106,6 @@
fail: msg="Keytab or password is required for otp"
when: ipaadmin_keytab is undefined and ipaadmin_password is undefined
- name: Install - Save client ansible_python_interpreter setting
set_fact:
ipaclient_ansible_python_interpreter: "{{ ansible_python_interpreter }}"
#- name: Install - Include Python2/3 import test
# import_tasks: "{{ role_path }}/tasks/python_2_3_test.yml"
# delegate_to: "{{ result_ipaclient_test.servers[0] }}"
@@ -119,13 +114,12 @@
no_log: yes
ipaclient_get_otp:
state: present
principal: "{{ ipaadmin_principal | default('admin') }}"
principal: "{{ ipaadmin_principal | default(omit) }}"
password: "{{ ipaadmin_password | default(omit) }}"
keytab: "{{ ipaadmin_keytab | default(omit) }}"
fqdn: "{{ result_ipaclient_test.hostname }}"
lifetime: "{{ ipaclient_lifetime | default(omit) }}"
random: True
ansible_python_interpreter: "{{ ansible_python_interpreter }}"
register: result_ipaclient_get_otp
# If the host is already enrolled, this command will exit on error
# The error can be ignored
@@ -134,18 +128,21 @@
in result_ipaclient_get_otp.msg
delegate_to: "{{ result_ipaclient_test.servers[0] }}"
delegate_facts: yes
ignore_errors: yes
- name: Install - Report error for OTP generation
debug:
msg: "{{ result_ipaclient_get_otp.msg }}"
when: result_ipaclient_get_otp is failed
failed_when: yes
- name: Install - Store the previously obtained OTP
no_log: yes
set_fact:
ipaadmin_orig_password: "{{ ipaadmin_password }}"
ipaadmin_orig_password: "{{ ipaadmin_password | default(omit) }}"
ipaadmin_password: "{{ result_ipaclient_get_otp.host.randompassword
if result_ipaclient_get_otp.host is defined }}"
- name: Install - Restore client ansible_python_interpreter setting
set_fact:
ansible_python_interpreter: "{{ ipaclient_ansible_python_interpreter }}"
when: ipaclient_use_otp | bool
- block:
@@ -159,14 +156,14 @@
- name: Install - Check if principal and keytab are set
fail: msg="Principal and keytab cannot be used together"
when: ipaadmin_principal is defined and ipaadmin_principal|length > 0
and ipaclient_keytab is defined and ipaclient_keytab|length > 0
when: ipaadmin_principal is defined and ipaclient_keytab is defined
- name: Install - Check if one of password and keytab are set
fail: msg="At least one of password or keytab must be specified"
when: not result_ipaclient_test_keytab.krb5_keytab_ok and
(ipaadmin_password is undefined or ipaadmin_password|length == 0)
and (ipaclient_keytab is undefined or ipaclient_keytab|length == 0)
- name: Install - Check if one of password or keytabs are set
fail: msg="At least one of password or keytabs must be specified"
when: not result_ipaclient_test_keytab.krb5_keytab_ok
and ipaadmin_password is undefined
and ipaadmin_keytab is undefined
and ipaclient_keytab is undefined
when: not ipaclient_on_master | bool
- name: Install - Purge {{ result_ipaclient_test.realm }} from host keytab
@@ -197,9 +194,10 @@
hostname: "{{ result_ipaclient_test.hostname }}"
force_join: "{{ ipaclient_force_join | default(omit) }}"
principal: "{{ ipaadmin_principal if not ipaclient_use_otp | bool and
ipaclient_keytab is not defined else '' }}"
ipaclient_keytab is not defined else omit }}"
password: "{{ ipaadmin_password | default(omit) }}"
keytab: "{{ ipaclient_keytab | default(omit) }}"
admin_keytab: "{{ ipaadmin_keytab if ipaadmin_keytab is defined and not ipaclient_use_otp | bool else omit }}"
# ca_cert_file: "{{ ipaclient_ca_cert_file | default(omit) }}"
kinit_attempts: "{{ ipaclient_kinit_attempts | default(omit) }}"
register: result_ipaclient_join

View File

@@ -14,8 +14,7 @@
failed_when: uninstall.rc != 0 and uninstall.rc != 2
changed_when: uninstall.rc == 0
# - name: Remove IPA client package
# package:
# name: "{{ item }}"
# state: absent
# with_items: "{{ ipaclient_packages }}"
#- name: Remove IPA client package
# package:
# name: "{{ ipaclient_packages }}"
# state: absent

View File

@@ -9,8 +9,41 @@ Changes made to any master are automatically replicated to other masters.
This can be done in differnt ways using auto-discovery of the servers, domain and other settings or by specifying them.
**Note**: The ansible playbooks and role 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.
Features
--------
* Replica deployment
Supported FreeIPA Versions
--------------------------
FreeIPA versions 4.6 and up are supported by the replica role.
Supported Distributions
-----------------------
* RHEL/CentOS 7.6+
* Fedora 26+
* Ubuntu
Requirements
------------
**Controller**
* Ansible version: 2.8+
**Node**
* Supported FreeIPA version (see above)
* Supported distribution (needed for package installation only, see above)
Usage
-----
=====
Example inventory file with fixed principal using auto-discovery with DNS records:
@@ -68,68 +101,145 @@ Example playbook to setup the IPA client(s) using principal and password from in
- role: ipareplica
state: present
Playbooks
=========
The playbooks needed to deploy or undeploy a replica are part of the repository in the playbooks folder. There are also playbooks to deploy and undeploy clusters.
```
install-replica.yml
uninstall-replica.yml
```
Please remember to link or copy the playbooks to the base directory of ansible-freeipa if you want to use the roles within the source archive.
How to setup replicas
---------------------
```bash
ansible-playbook -v -i inventory/hosts install-replica.yml
```
This will deploy the replicas defined in the inventory file.
Variables
---------
=========
**ipaserver** - Group with IPA server hostname.
(list of strings, optional)
Base Variables
--------------
**ipaclients** - Group of IPA client hostnames.
(list of strings)
Variable | Description | Required
-------- | ----------- | --------
`ipaservers` | This group with the IPA master full qualified hostnames. (list of strings) | mostly
`ipareplicas` | Group of IPA replica hostnames. (list of strings) | yes
`ipaadmin_password` | The password for the IPA admin user (string) | mostly
`ipareplica_ip_addresses` | The list of master server IP addresses. (list of strings) | no
`ipareplica_domain` | The primary DNS domain of an existing IPA deployment. (string) | no
`ipaserver_realm` | The Kerberos realm of an existing IPA deployment. (string) | no
`ipaserver_hostname` | Fully qualified name of the server. (string) | no
`ipaadmin_principal` | The authorized kerberos principal used to join the IPA realm. (string) | no
`ipareplica_no_host_dns` | Do not use DNS for hostname lookup during installation. (bool, default: false) | no
`ipareplica_skip_conncheck` | Skip connection check to remote master. (bool, default: false) | no
**ipaadmin_keytab** - The path to the admin keytab used for alternative authentication.
(string, optional)
Server Vaiables
---------------
**ipaadmin_principal** - The authorized kerberos principal used to join the IPA realm.
(string, optional)
Variable | Description | Required
-------- | ----------- | --------
`ipadm_password` | The password for the Directory Manager. (string) | mostly
`ipareplica_setup_adtrust` | Configure AD trust capability. (bool, default: false) | no
`ipareplica_setup_ca` | Configure a dogtag CA. (bool, default: false) | no
`ipareplica_setup_kra` | Configure a dogtag KRA. (bool, default: false) | no
`ipareplica_setup_dns` | Configure bind with our zone. (bool, default: false) | no
`ipareplica_no_pkinit` | Disables pkinit setup steps. (bool, default: false) | no
`ipareplica_no_ui_redirect` | Do not automatically redirect to the Web UI. (bool, default: false) | no
`ipareplica_dirsrv_config_file` | The path to LDIF file that will be used to modify configuration of dse.ldif during installation of the directory server instance. (string)| no
**ipaadmin_password** - The password for the kerberos principal.
(string, optional)
**ipaclient_domain** - The primary DNS domain of an existing IPA deployment.
(string, optional)
SSL certificate Variables
-------------------------
**ipaclient_realm** - The Kerberos realm of an existing IPA deployment.
(string, optional)
Variable | Description | Required
-------- | ----------- | --------
`ipareplica_dirsrv_cert_files` | Files containing the Directory Server SSL certificate and private keys. (list of strings) | no
`ipareplica_http_cert_file` | File containing the Apache Server SSL certificate and private key. (string) | no
`ipareplica_pkinit_cert_file` | File containing the Kerberos KDC SSL certificate and private key. (string) | no
`ipareplica_dirsrv_pin` | The password to unlock the Directory Server private key. (string) | no
`ipareplica_http_pin` | The password to unlock the Apache Server private key. (string) | no
`ipareplica_pkinit_pin` | The password to unlock the Kerberos KDC private key. (string) | no
`ipareplica_dirsrv_cert_name` | Name of the Directory Server SSL certificate to install. (string) | no
`ipareplica_http_cert_name` | Name of the Apache Server SSL certificate to install. (string) | no
`ipareplica_pkinit_cert_name` | Name of the Kerberos KDC SSL certificate to install. (string) | no
**ipaclient_keytab** - The path to a backed-up host keytab from previous enrollment.
(string, optional)
Client Variables
----------------
**ipaclient_force_join** - Set force_join to yes to join the host even if it is already enrolled.
(bool, optional)
Variable | Description | Required
-------- | ----------- | --------
`ipaclient_keytab` | Path to backed up keytab from previous enrollment. (string) | no
`ipaclient_mkhomedir` | Set to yes to configure PAM to create a users home directory if it does not exist. (string) | no
`ipaclient_force_join` | Force client enrollment even if already enrolled. (bool, default: false) | no
`ipaclient_ntp_servers` | The list defines the NTP servers to be used. (list of strings) | no
`ipaclient_ntp_pool` | The string value defines the ntp server pool to be used. (string) | no
`ipaclient_no_ntp` | The bool value defines if NTP will not be configured and enabled. (bool, default: false) | no
`ipaclient_ssh_trust_dns` | The bool value defines if OpenSSH client will be configured to trust DNS SSHFP records. (bool, default: false) | no
`ipaclient_no_ssh` | The bool value defines if OpenSSH client will be configured. (bool, default: false) | no
`ipaclient_no_sshd` | The bool value defines if OpenSSH server will be configured. (bool, default: false) | no
`ipaclient_no_sudo` | The bool value defines if SSSD will be configured as a data source for sudo. (bool, default: false) | no
`ipaclient_no_dns_sshfp` | The bool value defines if DNS SSHFP records will not be created automatically. (bool, default: false) | no
**ipaclient_use_otp** - Enforce the generation of a one time password to configure new and existing hosts. The enforcement on an existing host is not done if there is a working krb5.keytab on the host. If the generation of an otp is enforced for an existing host entry, then the host gets diabled and the containing keytab gets removed.
(bool, optional)
Certificate system Variables
----------------------------
**ipaclient_allow_repair** - Allow repair of already joined hosts. Contrary to ipaclient_force_join the host entry will not be changed on the server.
(bool, optional)
Variable | Description | Required
-------- | ----------- | --------
~~`ipareplica_skip_schema_check`~~ | ~~Skip check for updated CA DS schema on the remote master. (bool, default: false)~~ | ~~no~~
**ipaclient_kinit_attempts** - Repeat the request for host Kerberos ticket X times if it fails.
(int, optional)
DNS Variables
-------------
**ipaclient_no_ntp** - Set to yes to not configure and enable NTP
(bool, optional)
Variable | Description | Required
-------- | ----------- | --------
`ipareplica_allow_zone_overlap` | Allow creation of (reverse) zone even if the zone is already resolvable. (bool, default: false) | no
`ipareplica_reverse_zones` | The reverse DNS zones to use. (list of strings) | no
`ipareplica_no_reverse` | Do not create reverse DNS zone. (bool, default: false) | no
`ipareplica_auto_reverse` | Try to resolve reverse records and reverse zones for server IP addresses. (bool, default: false) | no
`ipareplica_zonemgr` | The e-mail address of the DNS zone manager. (string, default: hostmaster@DOMAIN.) | no
`ipareplica_forwarders` | Add DNS forwarders to the DNS configuration. (list of strings) | no
`ipareplica_no_forwarders` | Do not add any DNS forwarders. Root DNS servers will be used instead. (bool, default: false) | no
`ipareplica_auto_forwarders` | Add DNS forwarders configured in /etc/resolv.conf to the list of forwarders used by IPA DNS. (bool, default: false) | no
`ipareplica_forward_policy` | DNS forwarding policy for global forwarders specified using other options. (choice: first,only) | no
`ipareplica_no_dnssec_validation` | Disable DNSSEC validation on this server. (bool, default: false) | no
**ipaclient_mkhomedir** - Set to yes to configure PAM to create a users home directory if it does not exist.
(string, optional)
AD trust Variables
------------------
Variable | Description | Required
-------- | ----------- | --------
~~`ipareplica_add_sids`~~ | ~~Add SIDs for existing users and groups as the final step. (bool, default: false)~~ | ~~no~~
~~`ipareplica_add_agents`~~ | ~~Add IPA masters to a list of hosts allowed to serve information about users from trusted forests. (bool, default: false)~~ | ~~no~~
`ipareplica_enable_compat`| Enables support for trusted domains users for old clients through Schema Compatibility plugin. (bool, default: false) | no
`ipareplica_netbios_name` | The NetBIOS name for the IPA domain. (string) | no
`ipareplica_rid_base` | First RID value of the local domain. (integer) | no
`ipareplica_secondary_rid_base` | Start value of the secondary RID range. (integer) | no
Cluster Specific Variables
--------------------------
**ipaclient_no_dns_lookup** - Set to 'yes' to use groups.ipaserver in cluster environments as servers for the clients. This deactivates DNS lookup in krb5.
(bool, optional, default: 'no')
Variable | Description | Required
-------- | ----------- | --------
`ipareplica_servers` | Manually override list of servers for example in a cluster environment on a per replica basis. The list of servers is normally taken from from groups.ipaserver in cluster environments. (list of strings) | no
`ipaserver_domain` | Used if set in a cliuster environment to overload `ipareplica_domain` | no
**ipareplica_servers** - Manually override list of servers for example in a cluster environment on a per client basis. The list of servers is normally taken from from groups.ipaserver in cluster environments.
(list of strings, optional)
Special Variables
-----------------
Requirements
------------
Variable | Description | Required
-------- | ----------- | --------
`ipareplica_install_packages` | The bool value defines if the needed packages are installed on the node. (bool, default: true) | no
`ipareplica_setup_firewalld` | The value defines if the needed services will automatically be openen in the firewall managed by firewalld. (bool, default: true) | no
freeipa-server v4.6 or later
Authors
-------
Florence Blanc-Renaud
=======
Thomas Woerner

View File

@@ -60,7 +60,6 @@ def main():
module = AnsibleModule(
argument_spec = dict(
#basic
dm_password=dict(required=True, no_log=True),
master_password=dict(required=False, no_log=True),
),
supports_check_mode = True,
@@ -68,21 +67,11 @@ def main():
module._ansible_debug = True
options.dm_password = module.params.get('dm_password')
options.master_password = module.params.get('master_password')
fstore = sysrestore.FileStore(paths.SYSRESTORE)
sstore = sysrestore.StateFile(paths.SYSRESTORE)
# 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)
except Exception as e:
module.fail_json(msg="Cannot process the cache file: %s" % str(e))
if not options.master_password:
options.master_password = ipa_generate_password()

View File

@@ -5,22 +5,19 @@
- name: Install - Ensure IPA replica packages are installed
package:
name: "{{ item }}"
name: "{{ ipareplica_packages }}"
state: present
with_items: "{{ ipareplica_packages }}"
- name: Install - Ensure IPA replica packages for dns are installed
package:
name: "{{ item }}"
name: "{{ ipareplica_packages_dns }}"
state: present
with_items: "{{ ipareplica_packages_dns }}"
when: ipareplica_setup_dns | bool
- name: Install - Ensure IPA replica packages for adtrust are installed
package:
name: "{{ item }}"
name: "{{ ipareplica_packages_adtrust }}"
state: present
with_items: "{{ ipareplica_packages_adtrust }}"
when: ipareplica_setup_adtrust | bool
when: ipareplica_install_packages | bool
@@ -28,6 +25,11 @@
#- name: Install - Include Python2/3 import test
# import_tasks: "{{ role_path }}/tasks/python_2_3_test.yml"
- name: Install - Set ipareplica_servers
set_fact:
ipareplica_servers: "{{ groups['ipaservers'] | list }}"
when: groups.ipaservers is defined and ipareplica_servers is not defined
- name: Install - Set default principal if no keytab is given
set_fact:
ipaadmin_principal: admin
@@ -41,8 +43,7 @@
ip_addresses: "{{ ipareplica_ip_addresses | default([]) }}"
domain: "{{ ipareplica_domain | default(ipaserver_domain) |
default(omit) }}"
servers: "{{ groups.ipaservers | default(groups.ipaserver) |
default(omit) }}"
servers: "{{ ipareplica_servers | default(omit) }}"
realm: "{{ ipareplica_realm | default(omit) }}"
hostname: "{{ ipareplica_hostname | default(ansible_fqdn) }}"
ca_cert_files: "{{ ipareplica_ca_cert_files | default([]) }}"
@@ -82,9 +83,9 @@
name: ipaclient
vars:
state: present
ipaclient_domain: "{{ result_ipareplica_test.domain }}"
ipaclient_realm: "{{ result_ipareplica_test.realm }}"
ipaclient_servers: ["{{ result_ipareplica_test.server }}"]
ipaclient_domain: "{{ result_ipareplica_test.domain | default(omit) }}"
ipaclient_realm: "{{ result_ipareplica_test.realm | default(omit) }}"
ipaclient_servers: "{{ ipareplica_servers | default(omit) }}"
ipaclient_hostname: "{{ result_ipareplica_test.hostname }}"
ipaclient_no_ntp: "{{ result_ipareplica_test.ipa_python_version
< 40690 }}"
@@ -181,7 +182,6 @@
- name: Install - Create dirman password
no_log: yes
ipareplica_master_password:
dm_password: "{{ ipadm_password }}"
master_password: "{{ ipareplica_master_password | default(omit) }}"
register: result_ipareplica_master_password

View File

@@ -34,8 +34,7 @@
# failed_when: False
# delegate_to: "{{ groups.ipaserver[0] | default(fail) }}"
# - name: Remove IPA replica packages
# package:
# name: "{{ item }}"
# state: absent
# with_items: "{{ ipareplica_packages }}"
#- name: Remove IPA replica packages
# package:
# name: "{{ ipareplica_packages }}"
# state: absent

View File

@@ -6,8 +6,51 @@ Description
This role allows to configure and IPA server.
**Note**: The ansible playbooks and role 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.
Features
--------
* Server deployment
Supported FreeIPA Versions
--------------------------
FreeIPA versions 4.5 and up are supported by the server role.
Supported Distributions
-----------------------
* RHEL/CentOS 7.6+
* Fedora 26+
* Ubuntu
Requirements
------------
**Controller**
* Ansible version: 2.8+
**Node**
* Supported FreeIPA version (see above)
* Supported distribution (needed for package installation only, see above)
Limitations
-----------
External 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.
Work is planned to have a new method to handle CSR for external CAs in a separate step before starting the server installation.
Usage
-----
=====
Example inventory file with fixed domain and realm, setting up of the DNS server and using forwarders from /etc/resolv.conf:
@@ -63,111 +106,136 @@ Example playbook to setup the IPA server using admin and dirman passwords from i
- role: ipaserver
state: present
Playbooks
=========
The playbooks needed to deploy or undeploy a server are part of the repository in the playbooks folder. There are also playbooks to deploy and undeploy clusters.
```
install-server.yml
uninstall-server.yml
```
Please remember to link or copy the playbooks to the base directory of ansible-freeipa if you want to use the roles within the source archive.
How to setup a server
---------------------
```bash
ansible-playbook -v -i inventory/hosts install-server.yml
```
This will deploy the server defined in the inventory file.
Variables
---------
=========
**ipaserver** - Group with the IPA server hostname
(list of strings)
Base Variables
--------------
**ipaadmin_password** - The password for the IPA admin user.
(string, optional)
**ipadm_password** - The password for the Directory Manager.
(string, optional)
**ipaserver_domain** - The primary DNS domain of an existing IPA deployment.
(string)
Variable | Description | Required
-------- | ----------- | --------
`ipaserver` | This group with the single IPA server full qualified hostname. (list of strings) | yes
`ipadm_password` | The password for the Directory Manager. (string) | no
`ipaadmin_password` | The password for the IPA admin user (string) | no
`ipaserver_ip_addresses` | The list of master server IP addresses. (list of strings) | no
`ipaserver_domain` | The primary DNS domain of an existing IPA deployment. (string) | no
`ipaserver_realm` | The Kerberos realm of an existing IPA deployment. (string) | no
`ipaserver_hostname` | Fully qualified name of the server. (string) | no
`ipaserver_no_host_dns` | Do not use DNS for hostname lookup during installation. (bool, default: false) | no
**ipaserver_realm** - The Kerberos realm of an existing IPA deployment.
(string)
Server Variables
----------------
**ipaserver_idstart** - The starting user and group id number (default random).
(integer, optional)
Variable | Description | Required
-------- | ----------- | --------
`ipaserver_setup_adtrust` | Configure AD Trust capability. (bool, default: false) | no
`ipaserver_setup_kra` | Install and configure a KRA on this server. (bool, default: false) | no
`ipaserver_setup_dns` | Configure an integrated DNS server, create DNS zone specified by domain. (bool, default: false) | no
`ipaserver_idstart` | The starting user and group id number. (integer, default: random) | no
`ipaserver_idmax` | The maximum user and group id number. (integer, default: idstart+199999) | no
`ipaserver_no_hbac_allow` | Do not install allow_all HBAC rule. (bool) | no
`ipaserver_no_ui_redirect` | Do not automatically redirect to the Web UI. (bool) | no
`ipaserver_dirsrv_config_file` | The path to LDIF file that will be used to modify configuration of dse.ldif during installation. (string) | no
`ipaserver_pki_config_override` | Path to ini file with config overrides. This is only usable with recent FreeIPA versions. (string) | no
**ipaserver_idmax** - The maximum user and group id number (default: idstart+199999).
(integer, optional)
SSL certificate Variables
-------------------------
**ipaserver_no_hbac_allow** - Do not install allow_all HBAC rule.
(bool, optional)
Variable | Description | Required
-------- | ----------- | --------
`ipaserver_dirsrv_cert_files` | Files containing the Directory Server SSL certificate and private keys. (list of strings) | no
`ipaserver_http_cert_file` | File containing the Apache Server SSL certificate and private key. (string) | no
`ipaserver_pkinit_cert_file` | File containing the Kerberos KDC SSL certificate and private key. (string) | no
`ipaserver_dirsrv_pin` | The password to unlock the Directory Server private key. (string) | no
`ipaserver_http_pin` | The password to unlock the Apache Server private key. (string) | no
`ipaserver_pkinit_pin` | The password to unlock the Kerberos KDC private key. (string) | no
`ipaserver_dirsrv_cert_name` | Name of the Directory Server SSL certificate to install. (string) | no
`ipaserver_http_cert_name` | Name of the Apache Server SSL certificate to install. (string) | no
`ipaserver_pkinit_cert_name` | Name of the Kerberos KDC SSL certificate to install. (string) | no
**ipaserver_no_ui_redirect** - Do not automatically redirect to the Web UI.
(bool, optional)
Client Variables
----------------
**ipaserver_dirsrv_config_file** - The path to LDIF file that will be used to modify configuration of dse.ldif during installation.
(string, optional)
Variable | Description | Required
-------- | ----------- | --------
`ipaclient_ntp_servers` | The list defines the NTP servers to be used. | no
`ipaclient_ntp_pool` | The string value defines the ntp server pool to be used. | no
`ipaclient_no_ntp` | The bool value defines if NTP will not be configured and enabled. `ipaclient_no_ntp` defaults to `no`. | no
`ipaclient_ssh_trust_dns` | The bool value defines if OpenSSH client will be configured to trust DNS SSHFP records. `ipaclient_ssh_trust_dns` defaults to `no`. | no
`ipaclient_no_ssh` | The bool value defines if OpenSSH client will be configured. `ipaclient_no_ssh` defaults to `no`. | no
`ipaclient_no_sshd` | The bool value defines if OpenSSH server will be configured. `ipaclient_no_sshd` defaults to `no`. | no
`ipaclient_no_sudo` | The bool value defines if SSSD will be configured as a data source for sudo. `ipaclient_no_sudo` defaults to `no`. | no
`ipaclient_no_dns_sshfp` | The bool value defines if DNS SSHFP records will not be created automatically. `ipaclient_no_dns_sshfp` defaults to `no`. | no
**ipaserver_setup_kra** - Install and configure a KRA on this server.
(bool, optional)
Certificate system Variables
----------------------------
**ipaserver_setup_dns** - Configure an integrated DNS server, create DNS zone specified by domain
(string, optional)
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_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_forwarders** - Add DNS forwarders to the DNS configuration.
(list of strings, optional)
DNS Variables
-------------
**ipaserver_no_forwarders** - Do not add any DNS forwarders. Root DNS servers will be used instead.
(bool, optional)
Variable | Description | Required
-------- | ----------- | --------
`ipaserver_allow_zone_overlap` | Allow creation of (reverse) zone even if the zone is already resolvable. (bool, default: false) | no
`ipaserver_reverse_zones` | The reverse DNS zones to use. (list of strings) | no
`ipaserver_no_reverse` | Do not create reverse DNS zone. (bool, default: false) | no
`ipaserver_auto_reverse` | Try to resolve reverse records and reverse zones for server IP addresses. (bool, default: false) | no
`ipaserver_zonemgr` | The e-mail address of the DNS zone manager. (string, default: hostmaster@DOMAIN.) | no
`ipaserver_forwarders` | Add DNS forwarders to the DNS configuration. (list of strings) | no
`ipaserver_no_forwarders` | Do not add any DNS forwarders. Root DNS servers will be used instead. (bool, default: false) | no
`ipaserver_auto_forwarders` | Add DNS forwarders configured in /etc/resolv.conf to the list of forwarders used by IPA DNS. (bool, default: false) | no
`ipaserver_forward_policy` | DNS forwarding policy for global forwarders specified using other options. (choice: first|only) | no
`ipaserver_no_dnssec_validation` | Disable DNSSEC validation on this server. (bool, default: false) | no
**ipaserver_auto_forwarders** - Add DNS forwarders configured in /etc/resolv.conf to the list of forwarders used by IPA DNS.
(bool, optional)
AD trust Variables
------------------
**ipaserver_forward_policy** - DNS forwarding policy for global forwarders specified using other options. first|only
(choice, optional)
Variable | Description | Required
-------- | ----------- | --------
`ipaserver_enable_compat`| Enables support for trusted domains users for old clients through Schema Compatibility plugin. (bool, default: false) | no
`ipaserver_netbios_name` | The NetBIOS name for the IPA domain. (string) | no
`ipaserver_rid_base` | First RID value of the local domain. (integer) | no
`ipaserver_secondary_rid_base` | Start value of the secondary RID range. (integer) | no
**ipaserver_reverse_zones** - The reverse DNS zones to use.
(list of strings, optional)
Special Variables
-----------------
**ipaserver_no_reverse** - Do not create reverse DNS zone.
(bool, optional)
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_auto_reverse** - Try to resolve reverse records and reverse zones for server IP addresses.
(bool, optional)
**ipaserver_zonemgr** - The e-mail address of the DNS zone manager. Defaults to hostmaster@DOMAIN.
(string, optional)
**ipaserver_no_host_dns** - Do not use DNS for hostname lookup during installation.
(bool, optional)
**ipaserver_pki_config_override** - Path to ini file with config overrides.
(string, optional)
**ipaserver_no_dnssec_validation** - Disable DNSSEC validation on this server.
(bool, optional)
**ipaserver_allow_zone_overlap** - Allow creation of (reverse) zone even if the zone is already resolvable.
(bool, optional)
**ipaserver_setup_adtrust** - Configure AD Trust capability.
(bool, optional)
**ipaserver_netbios_name** - The NetBIOS name for the IPA domain.
(string, optional)
**ipaserver_rid_base** - First RID value of the local domain.
(integer, optional)
**ipaserver_secondary_rid_base** - Start value of the secondary RID range.
(integer, optional)
**ipaserver_enable_compat** - Enables support for trusted domains users for old clients through Schema Compatibility plugin.
(bool, optional)
**ipaclient_force_join** - Set force_join to yes to join the host even if it is already enrolled.
(bool, optional)
**ipaclient_no_ntp** - Set to no to not configure and enable NTP
(bool, optional)
**ipaclient_mkhomedir** - Set to yes to configure PAM to create a users home directory if it does not exist.
(string, optional)
Requirements
------------
freeipa-server v4.5 or later
Authors
-------
=======
Thomas Woerner

View File

@@ -469,23 +469,30 @@ def main():
# validate zonemgr
if options.zonemgr:
try:
# IDNA support requires unicode
encoding = getattr(sys.stdin, 'encoding', None)
if encoding is None:
encoding = 'utf-8'
value = options.zonemgr.decode(encoding)
if six.PY3:
with redirect_stdout(ansible_log):
bindinstance.validate_zonemgr_str(value)
except ValueError as e:
# FIXME we can do this in better way
# https://fedorahosted.org/freeipa/ticket/4804
# decode to proper stderr encoding
stderr_encoding = getattr(sys.stderr, 'encoding', None)
if stderr_encoding is None:
stderr_encoding = 'utf-8'
error = unicode(e).encode(stderr_encoding)
ansible_module.fail_json(msg=error)
bindinstance.validate_zonemgr_str(options.zonemgr)
else:
try:
# IDNA support requires unicode
encoding = getattr(sys.stdin, 'encoding', None)
if encoding is None:
encoding = 'utf-8'
if not isinstance(value, unicode):
value = options.zonemgr.decode(encoding)
else:
value = options.zonemgr
with redirect_stdout(ansible_log):
bindinstance.validate_zonemgr_str(value)
except ValueError as e:
# FIXME we can do this in better way
# https://fedorahosted.org/freeipa/ticket/4804
# decode to proper stderr encoding
stderr_encoding = getattr(sys.stderr, 'encoding', None)
if stderr_encoding is None:
stderr_encoding = 'utf-8'
error = unicode(e).encode(stderr_encoding)
ansible_module.fail_json(msg=error)
# external cert file paths are absolute
for path in options.external_cert_files:

View File

@@ -4,22 +4,19 @@
- block:
- name: Install - Ensure that IPA server packages are installed
package:
name: "{{ item }}"
name: "{{ ipaserver_packages }}"
state: present
with_items: "{{ ipaserver_packages }}"
- name: Install - Ensure that IPA server packages for dns are installed
package:
name: "{{ item }}"
name: "{{ ipaserver_packages_dns }}"
state: present
with_items: "{{ ipaserver_packages_dns }}"
when: ipaserver_setup_dns | bool
- name: Install - Ensure that IPA server packages for adtrust are installed
package:
name: "{{ item }}"
name: "{{ ipaserver_packages_adtrust }}"
state: present
with_items: "{{ ipaserver_packages_adtrust }}"
when: ipaserver_setup_adtrust | bool
when: ipaserver_install_packages | bool

View File

@@ -17,8 +17,7 @@
failed_when: uninstall.rc != 0 and uninstall.rc != 1
changed_when: uninstall.rc == 0
# - name: Remove IPA server packages
# package:
# name: "{{ item }}"
# state: absent
# with_items: "{{ ipaserver_packages }}"
#- name: Remove IPA server packages
# package:
# name: "{{ ipaserver_packages }}"
# state: absent