diff --git a/library/ipaapi.py b/library/ipaapi.py new file mode 100644 index 0000000000000000000000000000000000000000..aaa8299b3d51b0bcee568e0a79ca0533088011da --- /dev/null +++ b/library/ipaapi.py @@ -0,0 +1,188 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Authors: +# Thomas Woerner <twoerner@redhat.com> +# +# Based on ipa-client-install code +# +# 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: ipaapi +short description: Create temporary NSS database, call IPA API for remaining enrollment parts +description: +Create temporary NSS database, call IPA API for remaining enrollment parts +options: + servers: + description: The FQDN of the IPA servers to connect to. + required: false + realm: + description: The Kerberos realm of an existing IPA deployment. + required: true + hostname: + description: The hostname of the machine to join (FQDN). + required: true + debug: + description: Turn on extra debugging + required: false +author: + - Thomas Woerner +''' + +EXAMPLES = ''' +- name: IPA API calls for remaining enrollment parts + ipaapi: + servers: ["server1.example.com","server2.example.com"] + domain: example.com + hostname: client1.example.com + register: ipaapi +''' + +RETURN = ''' +ca_enabled: + description: Wheter the Certificate Authority is enabled or not. + returned: always + type: bool +''' + +import os +import time +import gssapi + +from ansible.module_utils.basic import AnsibleModule +from ipalib import api, errors, x509 +from ipalib.install import sysrestore +from ipalib.rpc import delete_persistent_client_session_data +from ipaplatform.paths import paths +from ipapython import certdb +from ipapython.ipautil import CalledProcessError +from ipaclient.install.client import SECURE_PATH, CCACHE_FILE, disable_ra + +def main(): + module = AnsibleModule( + argument_spec = dict( + servers=dict(required=True, type='list'), + realm=dict(required=True), + hostname=dict(required=True), + debug=dict(required=False, type='bool', default="false") + ), + # required_one_of = ( [ '', '' ] ), + supports_check_mode = True, + ) + + module._ansible_debug = True + realm = module.params.get('realm') + hostname = module.params.get('hostname') + servers = module.params.get('servers') + debug = module.params.get('debug') + + fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE) + statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE) + host_principal = 'host/%s@%s' % (hostname, realm) + os.environ['KRB5CCNAME'] = CCACHE_FILE + + with certdb.NSSDatabase() as tmp_db: + api.bootstrap(context='cli_installer', + confdir=paths.ETC_IPA, + debug=debug, + delegate=False, + nss_dir=tmp_db.secdir) + if 'config_loaded' not in api.env: + module.fail_json(msg="Failed to initialize IPA API.") + + # Clear out any current session keyring information + try: + delete_persistent_client_session_data(host_principal) + except ValueError: + pass + + # Add CA certs to a temporary NSS database + ca_certs = x509.load_certificate_list_from_file(paths.IPA_CA_CRT) + try: + tmp_db.create_db() + + for i, cert in enumerate(ca_certs): + tmp_db.add_cert(cert, + 'CA certificate %d' % (i + 1), + certdb.EXTERNAL_CA_TRUST_FLAGS) + except CalledProcessError: + module.fail_json(msg="Failed to add CA to temporary NSS database.") + + api.finalize() + + # Now, let's try to connect to the server's RPC interface + connected = False + try: + api.Backend.rpcclient.connect() + connected = True + module.debug("Try RPC connection") + api.Backend.rpcclient.forward('ping') + except errors.KerberosError as e: + if connected: + api.Backend.rpcclient.disconnect() + module.log( + "Cannot connect to the server due to Kerberos error: %s. " + "Trying with delegate=True" % e) + try: + api.Backend.rpcclient.connect(delegate=True) + module.debug("Try RPC connection") + api.Backend.rpcclient.forward('ping') + + module.log("Connection with delegate=True successful") + + # The remote server is not capable of Kerberos S4U2Proxy + # delegation. This features is implemented in IPA server + # version 2.2 and higher + module.warn( + "Target IPA server has a lower version than the enrolled " + "client") + module.warn( + "Some capabilities including the ipa command capability " + "may not be available") + except errors.PublicError as e2: + module.fail_json( + msg="Cannot connect to the IPA server RPC interface: %s" % e2) + except errors.PublicError as e: + module.fail_json( + msg="Cannot connect to the server due to generic error: %s" % e) + # Use the RPC directly so older servers are supported + try: + result = api.Backend.rpcclient.forward( + 'ca_is_enabled', + version=u'2.107', + ) + ca_enabled = result['result'] + except (errors.CommandError, errors.NetworkError): + result = api.Backend.rpcclient.forward( + 'env', + server=True, + version=u'2.0', + ) + ca_enabled = result['result']['enable_ra'] + if not ca_enabled: + disable_ra() + + module.exit_json(changed=True, ca_enabled=ca_enabled) + +if __name__ == '__main__': + main()