mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-03-26 21:33:05 +00:00
node and the credential cache is copied to the managed node ipahost module is also using facts gathered from the server to find the domain and realm.
303 lines
8.8 KiB
Python
303 lines
8.8 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Authors:
|
|
# Florence Blanc-Renaud <frenaud@redhat.com>
|
|
#
|
|
# Copyright (C) 2017 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',
|
|
'status': ['preview'],
|
|
'supported_by': 'community'}
|
|
|
|
DOCUMENTATION = '''
|
|
---
|
|
module: ipaclient
|
|
short description: Configures a client machine as IPA client
|
|
description:
|
|
Configures a client machine to use IPA for authentication and
|
|
identity services.
|
|
The enrollment requires one authentication method among the 3 following:
|
|
- Kerberos principal and password (principal/password)
|
|
- Kerberos keytab file (keytab)
|
|
- One-Time-Password (otp)
|
|
options:
|
|
state:
|
|
description: the client state
|
|
required: false
|
|
default: present
|
|
choices: [ "present", "absent"]
|
|
domain:
|
|
description: The primary DNS domain of an existing IPA deployment.
|
|
required: false
|
|
realm:
|
|
description: The Kerberos realm of an existing IPA deployment.
|
|
required: false
|
|
server:
|
|
description: The FQDN of the IPA server to connect to.
|
|
required: false
|
|
principal:
|
|
description: The authorized kerberos principal used to join the IPA realm.
|
|
required: false
|
|
default: admin
|
|
password:
|
|
description: The password for the kerberos principal.
|
|
required: false
|
|
keytab:
|
|
description: The pathto a backed-up host keytab from previous enrollment.
|
|
required: false
|
|
otp:
|
|
description: The One-Time-Password used to join the IPA realm.
|
|
required: false
|
|
extr_args:
|
|
description: The list of extra arguments to provide to ipa-client-install.
|
|
required: false
|
|
type: list
|
|
author:
|
|
- Florence Blanc-Renaud
|
|
'''
|
|
|
|
EXAMPLES = '''
|
|
# Example from Ansible Playbooks
|
|
# Unenroll client
|
|
- ipaclient:
|
|
state: absent
|
|
|
|
# Enroll client using admin credentials, with auto-discovery
|
|
- ipaclient:
|
|
principal: admin
|
|
password: MySecretPassword
|
|
extraargs: [ '--no-ntp', '--kinit-attempts=5']
|
|
|
|
# Enroll client using admin credentials, with specified domain and
|
|
# autodiscovery of the IPA server
|
|
- ipaclient:
|
|
principal: admin
|
|
password: MySecretPassword
|
|
domain: ipa.domain.com
|
|
extraargs: [ '--no-ntp', '--kinit-attempts=5']
|
|
|
|
# Enroll client using admin credentials, with specified server
|
|
- ipaclient:
|
|
principal: admin
|
|
password: MySecretPassword
|
|
domain: ipa.domain.com
|
|
server: ipaserver.ipa.domain.com
|
|
extraargs: [ '--no-ntp', '--kinit-attempts=5']
|
|
|
|
# Enroll client using One-Time-Password, with specified domain and realm
|
|
- ipaclient:
|
|
domain: ipa.domain.com
|
|
realm: IPA.DOMAIN.com
|
|
otp: 9Mn*Jm8z[%n]|:CJeu>Y~K
|
|
|
|
# Re-enroll client using keytab stored on the managed node
|
|
- ipaclient:
|
|
domain: ipa.domain.com
|
|
realm: IPA.DOMAIN.com
|
|
keytab: /path/to/host.keytab
|
|
'''
|
|
|
|
RETURN = '''
|
|
tbd
|
|
'''
|
|
|
|
import os
|
|
from six.moves.configparser import RawConfigParser
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
|
|
from ipalib.install.sysrestore import SYSRESTORE_STATEFILE
|
|
from ipaplatform.paths import paths
|
|
|
|
|
|
def is_client_configured():
|
|
"""
|
|
Check if ipa client is configured.
|
|
|
|
IPA client is configured when /etc/ipa/default.conf exists and
|
|
/var/lib/ipa-client/sysrestore/sysrestore.state exists.
|
|
|
|
:returns: boolean
|
|
"""
|
|
|
|
return (os.path.isfile(paths.IPA_DEFAULT_CONF) and
|
|
os.path.isfile(os.path.join(paths.IPA_CLIENT_SYSRESTORE,
|
|
SYSRESTORE_STATEFILE)))
|
|
|
|
|
|
def get_ipa_conf():
|
|
"""
|
|
Return IPA configuration read from /etc/ipa/default.conf
|
|
|
|
:returns: dict containing key,value
|
|
"""
|
|
|
|
parser = RawConfigParser()
|
|
parser.read(paths.IPA_DEFAULT_CONF)
|
|
result = dict()
|
|
for item in ['basedn', 'realm', 'domain', 'server', 'host', 'xmlrpc_uri']:
|
|
if parser.has_option('global', item):
|
|
value = parser.get('global', item)
|
|
else:
|
|
value = None
|
|
if value:
|
|
result[item] = value
|
|
|
|
return result
|
|
|
|
|
|
def ensure_not_ipa_client(module):
|
|
"""
|
|
Module for client uninstallation
|
|
|
|
If IPA client is installed, calls ipa-client-install --uninstall -U
|
|
:param module: AnsibleModule
|
|
"""
|
|
|
|
# Check if IPA client is already configured
|
|
if not is_client_configured():
|
|
# Nothing to do
|
|
module.exit_json(changed=False)
|
|
|
|
# Client is configured
|
|
# If in check mode, do nothing but return changed=True
|
|
if module.check_mode:
|
|
module.exit_json(changed=True)
|
|
|
|
# Client is configured and we want to remove it
|
|
cmd = [
|
|
module.get_bin_path('ipa-client-install'),
|
|
"--uninstall",
|
|
"-U",
|
|
]
|
|
retcode, stdout, stderr = module.run_command(cmd)
|
|
if retcode != 0:
|
|
module.fail_json(msg="Failed to uninstall IPA client: %s" % stderr)
|
|
|
|
module.exit_json(changed=True)
|
|
|
|
|
|
def ensure_ipa_client(module):
|
|
"""
|
|
Module for client installation
|
|
|
|
If IPA client is not installed, calls ipa-client-install
|
|
:param module: AnsibleModule
|
|
"""
|
|
|
|
domain = module.params.get('domain')
|
|
realm = module.params.get('realm')
|
|
server = module.params.get('server')
|
|
principal = module.params.get('principal')
|
|
password = module.params.get('password')
|
|
keytab = module.params.get('keytab')
|
|
otp = module.params.get('otp')
|
|
extra_args = module.params.get('extra_args')
|
|
|
|
# Ensure that at least one auth method is specified
|
|
if not (password or keytab or otp):
|
|
module.fail_json(msg="At least one of password, keytab or otp "
|
|
"must be specified")
|
|
|
|
# Check if ipa client is already configured
|
|
if is_client_configured():
|
|
# Check that realm and domain match
|
|
current_config = get_ipa_conf()
|
|
if domain and domain != current_config.get('domain'):
|
|
return module.fail_json(msg="IPA client already installed "
|
|
"with a conflicting domain")
|
|
if realm and realm != current_config.get('realm'):
|
|
return module.fail_json(msg="IPA client already installed "
|
|
"with a conflicting realm")
|
|
|
|
# client is already configured and no inconsistency detected
|
|
return module.exit_json(changed=False, domain=domain, realm=realm)
|
|
|
|
# ipa client not installed
|
|
if module.check_mode:
|
|
# Do nothing, just return changed=True
|
|
return module.exit_json(changed=True)
|
|
|
|
cmd = [
|
|
module.get_bin_path("ipa-client-install"),
|
|
"-U",
|
|
]
|
|
if domain:
|
|
cmd.append("--domain")
|
|
cmd.append(domain)
|
|
if realm:
|
|
cmd.append("--realm")
|
|
cmd.append(realm)
|
|
if server:
|
|
cmd.append("--server")
|
|
cmd.append(server)
|
|
if password:
|
|
cmd.append("--password")
|
|
cmd.append(password)
|
|
cmd.append("--principal")
|
|
cmd.append(principal)
|
|
if keytab:
|
|
cmd.append("--keytab")
|
|
cmd.append(keytab)
|
|
cmd.append("-d")
|
|
if otp:
|
|
cmd.append("--password")
|
|
cmd.append(otp)
|
|
if extra_args:
|
|
for extra_arg in extra_args:
|
|
cmd.append(extra_arg)
|
|
|
|
retcode, stdout, stderr = module.run_command(cmd)
|
|
if retcode != 0:
|
|
module.fail_json(msg="Failed to install IPA client: %s" % stderr)
|
|
|
|
# If autodiscovery was used, need to read /etc/ipa/default.conf to
|
|
# find domain and realm
|
|
new_config = get_ipa_conf()
|
|
module.exit_json(changed=True,
|
|
domain=new_config.get('domain'),
|
|
realm=new_config.get('realm'))
|
|
|
|
|
|
def main():
|
|
module = AnsibleModule(
|
|
supports_check_mode=True,
|
|
argument_spec=dict(
|
|
state=dict(default='present', choices=['present', 'absent']),
|
|
domain=dict(required=False),
|
|
realm=dict(required=False),
|
|
server=dict(required=False),
|
|
principal=dict(default='admin'),
|
|
password=dict(required=False, no_log=True),
|
|
keytab=dict(required=False, type='path'),
|
|
otp=dict(required=False),
|
|
extra_args=dict(default=None, type='list')
|
|
),
|
|
)
|
|
|
|
module._ansible_debug = True
|
|
state = module.params.get('state')
|
|
|
|
if state == 'present':
|
|
ensure_ipa_client(module)
|
|
else:
|
|
ensure_not_ipa_client(module)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|