Files
ansible-freeipa/plugins/modules/ipaconfig.py
Thomas Woerner c089c010e6 ipaconfig: Add choices pkinit, hardened and idp to user_auth_type
The parameter user_auth_type has been updated in FreeIPA. The choices
pkinit, hardened and idp have been missing and are now added.

An additional check was added to verify that the values of the
user_auth_type list are valid for the used IPA version.
2023-07-19 11:38:14 +02:00

647 lines
24 KiB
Python

# -*- coding: utf-8 -*-
# Authors:
# Chris Procter <cprocter@redhat.com>
# Thomas Woerner <twoerner@redhat.com>
#
# Copyright (C) 2020-2022 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
"status": ["preview"],
}
DOCUMENTATION = '''
---
module: ipaconfig
author:
- Chris Procter (@chr15p)
- Thomas Woerner (@t-woerner)
short_description: Modify IPA global config options
description:
- Modify IPA global config options
extends_documentation_fragment:
- ipamodule_base_docs
options:
maxusername:
description: Set the maximum username length between 1-255
required: false
type: int
aliases: ['ipamaxusernamelength']
maxhostname:
description: Set the maximum hostname length between 64-255
required: false
type: int
aliases: ['ipamaxhostnamelength']
homedirectory:
description: Set the default location of home directories
required: false
type: str
aliases: ['ipahomesrootdir']
defaultshell:
description: Set the default shell for new users
required: false
type: str
aliases: ['ipadefaultloginshell', 'loginshell']
defaultgroup:
description: Set the default group for new users
required: false
type: str
aliases: ['ipadefaultprimarygroup']
emaildomain:
description: Set the default e-mail domain
required: false
type: str
aliases: ['ipadefaultemaildomain']
searchtimelimit:
description:
- Set maximum amount of time (seconds) for a search
- values -1 to 2147483647 (-1 or 0 is unlimited)
required: false
type: int
aliases: ['ipasearchtimelimit']
searchrecordslimit:
description:
- Set maximum number of records to search
- values -1 to 2147483647 (-1 or 0 is unlimited)
required: false
type: int
aliases: ['ipasearchrecordslimit']
usersearch:
description:
- Set comma-separated list of fields to search for user search
required: false
type: list
elements: str
aliases: ['ipausersearchfields']
groupsearch:
description:
- Set comma-separated list of fields to search for group search
required: false
type: list
elements: str
aliases: ['ipagroupsearchfields']
enable_migration:
description: Enable migration mode
type: bool
required: false
aliases: ['ipamigrationenabled']
groupobjectclasses:
description: Set default group objectclasses (comma-separated list)
required: false
type: list
elements: str
aliases: ['ipagroupobjectclasses']
userobjectclasses:
description: Set default user objectclasses (comma-separated list)
required: false
type: list
elements: str
aliases: ['ipauserobjectclasses']
pwdexpnotify:
description:
- Set number of days's notice of impending password expiration
- values 0 to 2147483647
required: false
type: int
aliases: ['ipapwdexpadvnotify']
configstring:
description: Set extra hashes to generate in password plug-in
required: false
type: list
elements: str
choices:
- "AllowNThash"
- "KDC:Disable Last Success"
- "KDC:Disable Lockout"
- "KDC:Disable Default Preauth for SPNs"
- ""
aliases: ['ipaconfigstring']
selinuxusermaporder:
description: Set order in increasing priority of SELinux users
required: false
type: list
elements: str
aliases: ['ipaselinuxusermaporder']
selinuxusermapdefault:
description: Set default SELinux user when no match found in map rule
required: false
type: str
aliases: ['ipaselinuxusermapdefault']
pac_type:
description: set default types of PAC supported for services
required: false
type: list
elements: str
choices: ["MS-PAC", "PAD", "nfs:NONE", ""]
aliases: ["ipakrbauthzdata"]
user_auth_type:
description: set default types of supported user authentication
required: false
type: list
elements: str
choices: ["password", "radius", "otp", "pkinit", "hardened", "idp",
"disabled", ""]
aliases: ["ipauserauthtype"]
ca_renewal_master_server:
description: Renewal master for IPA certificate authority.
required: false
type: str
domain_resolution_order:
description: set list of domains used for short name qualification
required: false
type: list
elements: str
aliases: ["ipadomainresolutionorder"]
enable_sid:
description: >
New users and groups automatically get a SID assigned.
Cannot be deactivated once activated. Requires IPA 4.9.8+.
required: false
type: bool
netbios_name:
description: >
NetBIOS name of the IPA domain. Requires IPA 4.9.8+
and SID generation to be activated.
required: false
type: str
add_sids:
description: >
Add SIDs for existing users and groups. Requires IPA 4.9.8+
and SID generation to be activated.
required: false
type: bool
'''
EXAMPLES = '''
---
- name: Playbook to handle global configuration options
hosts: ipaserver
become: true
tasks:
- name: return current values of the global configuration options
ipaconfig:
ipaadmin_password: SomeADMINpassword
register: result
- name: display default login shell
debug:
msg: '{{ result.config.defaultshell[0] }}'
- name: set defaultshell and maxusername
ipaconfig:
ipaadmin_password: SomeADMINpassword
defaultshell: /bin/bash
maxusername: 64
- name: Playbook to enable SID and generate users and groups SIDs
hosts: ipaserver
tasks:
- name: Enable SID and generate users and groups SIDS
ipaconfig:
ipaadmin_password: SomeADMINpassword
enable_sid: yes
add_sids: yes
- name: Playbook to change IPA domain netbios name
hosts: ipaserver
tasks:
- name: Enable SID and generate users and groups SIDS
ipaconfig:
ipaadmin_password: SomeADMINpassword
enable_sid: yes
netbios_name: IPADOM
'''
RETURN = '''
config:
description: Dict of all global config options
returned: When no options are set
type: dict
contains:
maxusername:
description: maximum username length
type: int
returned: always
maxhostname:
description: maximum hostname length
type: int
returned: always
homedirectory:
description: default location of home directories
type: str
returned: always
defaultshell:
description: default shell for new users
type: str
returned: always
defaultgroup:
description: default group for new users
type: str
returned: always
emaildomain:
description: default e-mail domain
type: str
returned: always
searchtimelimit:
description: maximum amount of time (seconds) for a search
type: int
returned: always
searchrecordslimit:
description: maximum number of records to search
type: int
returned: always
usersearch:
description: list of fields to search in user search
type: list
elements: str
returned: always
groupsearch:
description: list of fields to search in group search
type: list
elements: str
returned: always
enable_migration:
description: Enable migration mode
type: bool
returned: always
groupobjectclasses:
description: default group objectclasses (comma-separated list)
type: list
elements: str
returned: always
userobjectclasses:
description: default user objectclasses (comma-separated list)
type: list
elements: str
returned: always
pwdexpnotify:
description: number of days's notice of impending password expiration
type: str
returned: always
configstring:
description: extra hashes to generate in password plug-in
type: list
elements: str
returned: always
selinuxusermaporder:
description: order in increasing priority of SELinux users
type: list
elements: str
returned: always
selinuxusermapdefault:
description: default SELinux user when no match is found in map rule
type: str
returned: always
pac_type:
description: default types of PAC supported for services
type: list
elements: str
returned: always
user_auth_type:
description: default types of supported user authentication
type: str
returned: always
ca_renewal_master_server:
description: master for IPA certificate authority.
type: str
returned: always
domain_resolution_order:
description: list of domains used for short name qualification
type: list
elements: str
returned: always
enable_sid:
description: >
new users and groups automatically get a SID assigned.
Requires IPA 4.9.8+.
type: str
returned: always
netbios_name:
description: NetBIOS name of the IPA domain. Requires IPA 4.9.8+.
type: str
returned: if enable_sid is True
'''
from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa, ipalib_errors
def config_show(module):
_result = module.ipa_command_no_name("config_show", {"all": True})
return _result["result"]
def get_netbios_name(module):
try:
_result = module.ipa_command_no_name("trustconfig_show", {"all": True})
except Exception: # pylint: disable=broad-except
return None
else:
return _result["result"]["ipantflatname"][0]
def is_enable_sid(module):
"""When 'enable_sid' is true admin user and admins group have SID set."""
_result = module.ipa_command("user_show", "admin", {"all": True})
sid = _result["result"].get("ipantsecurityidentifier", [""])
if not sid[0].endswith("-500"):
return False
_result = module.ipa_command("group_show", "admins", {"all": True})
sid = _result["result"].get("ipantsecurityidentifier", [""])
if not sid[0].endswith("-512"):
return False
return True
def main():
ansible_module = IPAAnsibleModule(
argument_spec=dict(
# general
maxusername=dict(type="int", required=False,
aliases=['ipamaxusernamelength']),
maxhostname=dict(type="int", required=False,
aliases=['ipamaxhostnamelength']),
homedirectory=dict(type="str", required=False,
aliases=['ipahomesrootdir']),
defaultshell=dict(type="str", required=False,
aliases=['ipadefaultloginshell',
'loginshell']),
defaultgroup=dict(type="str", required=False,
aliases=['ipadefaultprimarygroup']),
emaildomain=dict(type="str", required=False,
aliases=['ipadefaultemaildomain']),
searchtimelimit=dict(type="int", required=False,
aliases=['ipasearchtimelimit']),
searchrecordslimit=dict(type="int", required=False,
aliases=['ipasearchrecordslimit']),
usersearch=dict(type="list", elements="str", required=False,
aliases=['ipausersearchfields']),
groupsearch=dict(type="list", elements="str", required=False,
aliases=['ipagroupsearchfields']),
enable_migration=dict(type="bool", required=False,
aliases=['ipamigrationenabled']),
groupobjectclasses=dict(type="list", elements="str",
required=False,
aliases=['ipagroupobjectclasses']),
userobjectclasses=dict(type="list", elements="str", required=False,
aliases=['ipauserobjectclasses']),
pwdexpnotify=dict(type="int", required=False,
aliases=['ipapwdexpadvnotify']),
configstring=dict(type="list", elements="str", required=False,
aliases=['ipaconfigstring'],
choices=["AllowNThash",
"KDC:Disable Last Success",
"KDC:Disable Lockout",
"KDC:Disable Default Preauth for SPNs",
""]), # noqa E128
selinuxusermaporder=dict(type="list", elements="str",
required=False,
aliases=['ipaselinuxusermaporder']),
selinuxusermapdefault=dict(type="str", required=False,
aliases=['ipaselinuxusermapdefault']),
pac_type=dict(type="list", elements="str", required=False,
aliases=["ipakrbauthzdata"],
choices=["MS-PAC", "PAD", "nfs:NONE", ""]),
user_auth_type=dict(type="list", elements="str", required=False,
choices=["password", "radius", "otp",
"pkinit", "hardened", "idp",
"disabled", ""],
aliases=["ipauserauthtype"]),
ca_renewal_master_server=dict(type="str", required=False),
domain_resolution_order=dict(type="list", elements="str",
required=False,
aliases=["ipadomainresolutionorder"]),
enable_sid=dict(type="bool", required=False),
add_sids=dict(type="bool", required=False),
netbios_name=dict(type="str", required=False),
),
supports_check_mode=True,
)
ansible_module._ansible_debug = True
# Get parameters
# general
field_map = {
"maxusername": "ipamaxusernamelength",
"maxhostname": "ipamaxhostnamelength",
"homedirectory": "ipahomesrootdir",
"defaultshell": "ipadefaultloginshell",
"defaultgroup": "ipadefaultprimarygroup",
"emaildomain": "ipadefaultemaildomain",
"searchtimelimit": "ipasearchtimelimit",
"searchrecordslimit": "ipasearchrecordslimit",
"usersearch": "ipausersearchfields",
"groupsearch": "ipagroupsearchfields",
"enable_migration": "ipamigrationenabled",
"groupobjectclasses": "ipagroupobjectclasses",
"userobjectclasses": "ipauserobjectclasses",
"pwdexpnotify": "ipapwdexpadvnotify",
"configstring": "ipaconfigstring",
"selinuxusermaporder": "ipaselinuxusermaporder",
"selinuxusermapdefault": "ipaselinuxusermapdefault",
"pac_type": "ipakrbauthzdata",
"user_auth_type": "ipauserauthtype",
"ca_renewal_master_server": "ca_renewal_master_server",
"domain_resolution_order": "ipadomainresolutionorder",
"enable_sid": "enable_sid",
"netbios_name": "netbios_name",
"add_sids": "add_sids",
}
allow_empty_string = ["pac_type", "user_auth_type", "configstring"]
reverse_field_map = {v: k for k, v in field_map.items()}
params = {}
for x in field_map:
val = ansible_module.params_get(
x, allow_empty_string=(x in allow_empty_string))
if val is not None:
params[field_map.get(x, x)] = val
if params.get("ipamigrationenabled") is not None:
params["ipamigrationenabled"] = \
str(params["ipamigrationenabled"]).upper()
if params.get("ipaselinuxusermaporder", None):
params["ipaselinuxusermaporder"] = \
"$".join(params["ipaselinuxusermaporder"])
if params.get("ipadomainresolutionorder", None):
params["ipadomainresolutionorder"] = \
":".join(params["ipadomainresolutionorder"])
if params.get("ipausersearchfields", None):
params["ipausersearchfields"] = \
",".join(params["ipausersearchfields"])
if params.get("ipagroupsearchfields", None):
params["ipagroupsearchfields"] = \
",".join(params["ipagroupsearchfields"])
# verify limits on INT values.
args_with_limits = [
("ipamaxusernamelength", 1, 255),
("ipamaxhostnamelength", 64, 255),
("ipasearchtimelimit", -1, 2147483647),
("ipasearchrecordslimit", -1, 2147483647),
("ipapwdexpadvnotify", 0, 2147483647),
]
for arg, minimum, maximum in args_with_limits:
if arg in params and (params[arg] > maximum or params[arg] < minimum):
ansible_module.fail_json(
msg="Argument '%s' must be between %d and %d."
% (arg, minimum, maximum))
changed = False
exit_args = {}
# Connect to IPA API (enable_sid requires context == 'client')
with ansible_module.ipa_connect(context="client"):
has_enable_sid = ansible_module.ipa_command_param_exists(
"config_mod", "enable_sid")
result = config_show(ansible_module)
if params:
# Verify ipauserauthtype(s)
if "ipauserauthtype" in params and params["ipauserauthtype"]:
_invalid = ansible_module.ipa_command_invalid_param_choices(
"config_mod", "ipauserauthtype", params["ipauserauthtype"])
if _invalid:
ansible_module.fail_json(
msg="The use of userauthtype '%s' is not "
"supported by your IPA version" % "','".join(_invalid))
enable_sid = params.get("enable_sid")
sid_is_enabled = has_enable_sid and is_enable_sid(ansible_module)
if sid_is_enabled and enable_sid is False:
ansible_module.fail_json(msg="SID cannot be disabled.")
netbios_name = params.get("netbios_name")
add_sids = params.get("add_sids")
if has_enable_sid:
if (
netbios_name
and netbios_name == get_netbios_name(ansible_module)
):
del params["netbios_name"]
netbios_name = None
if not add_sids and "add_sids" in params:
del params["add_sids"]
if any([netbios_name, add_sids]):
if sid_is_enabled:
params["enable_sid"] = True
else:
if not enable_sid:
ansible_module.fail_json(
msg="SID generation must be enabled for "
"'netbios_name' and 'add_sids'. Use "
"'enable_sid: yes'."
)
else:
if sid_is_enabled and "enable_sid" in params:
del params["enable_sid"]
else:
if any([enable_sid, netbios_name, add_sids is not None]):
ansible_module.fail_json(
msg="This version of IPA does not support enable_sid, "
"add_sids or netbios_name setting through the "
"config module"
)
params = {
k: v for k, v in params.items()
if k not in result or result[k] != v
}
# Remove empty string args from params if result arg is not set
for k in ["ipakrbauthzdata", "ipauserauthtype", "ipaconfigstring"]:
if k not in result and k in params and params[k] == [""]:
del params[k]
if params \
and not compare_args_ipa(ansible_module, params, result):
changed = True
if not ansible_module.check_mode:
try:
ansible_module.ipa_command_no_name("config_mod",
params)
except ipalib_errors.EmptyModlist:
changed = False
else:
del result['dn']
type_map = {"str": str, "int": int, "list": list, "bool": bool}
for key, value in result.items():
k = reverse_field_map.get(key, key)
if ansible_module.argument_spec.get(k):
arg_type = ansible_module.argument_spec[k]['type']
if k in (
'ipaselinuxusermaporder', 'domain_resolution_order'
):
exit_args[k] = result.get(key)[0].split('$')
elif k in (
'usersearch', 'groupsearch'
):
exit_args[k] = result.get(key)[0].split(',')
elif isinstance(value, str) and arg_type == "list":
exit_args[k] = [value]
elif (
isinstance(value, (tuple, list))
and arg_type in ("str", "int")
):
exit_args[k] = type_map[arg_type](value[0])
elif (
isinstance(value, (tuple, list)) and arg_type == "bool"
):
# FreeIPA 4.9.10+ and 4.10 use proper mapping for
# boolean values, so we need to convert it to str
# for comparison.
# See: https://github.com/freeipa/freeipa/pull/6294
exit_args[k] = (str(value[0]).upper() == "TRUE")
else:
if arg_type not in type_map:
raise ValueError(
"Unexpected attribute type: %s" % arg_type)
exit_args[k] = type_map[arg_type](value)
# Add empty pac_type and user_auth_type if they are not set
for key in ["pac_type", "user_auth_type"]:
if key not in exit_args:
exit_args[key] = ""
# Add empty domain_resolution_order if it is not set
if "domain_resolution_order" not in exit_args:
exit_args["domain_resolution_order"] = []
# Set enable_sid
if has_enable_sid:
exit_args["enable_sid"] = is_enable_sid(ansible_module)
exit_args["netbios_name"] = get_netbios_name(ansible_module)
# Done
ansible_module.exit_json(changed=changed, config=exit_args)
if __name__ == "__main__":
main()