Skip to content
Snippets Groups Projects
Select Git revision
  • edd590cad9bd1c22e63ae23745006ed9e9ea9b38
  • master default protected
  • v1.15.0
  • v1.14.7
  • v1.14.6
  • v1.14.5
  • v1.14.4
  • v1.14.3
  • v1.14.2
  • v1.14.1
  • v1.14.0
  • v1.13.2
  • v1.13.1
  • v1.13.0
  • v1.12.1
  • v1.12.0
  • v1.11.1
  • v1.11.0
  • v1.10.0
  • v1.9.2
  • v1.9.1
  • v1.9.0
22 results

ipaclient.py

Blame
  • ipaclient.py 10.03 KiB
    #!/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
      servers:
        description: The FQDN of the IPA servers 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 path to a backed-up host keytab from previous enrollment.
        required: false
      otp:
        description: The One-Time-Password used to join the IPA realm.
        required: false
      force_join:
        description: Set force_join to yes to join the host even if it is already enrolled.
        required: false
        choices: [ "yes", "force" ]
        default: yes
      kinit_attempts:
        description: Repeat the request for host Kerberos ticket X times.
        required: false
      ntp:
        description: Set to no to not configure and enable NTP
        required: false
        default: yes
      mkhomedir:
        description: Set to yes to configure PAM to create a users home directory if it does not exist.
        required: false
        default: no
      extr_args:
        description: The list of extra arguments to provide to ipa-client-install.
        required: false
        type: list
    author:
        - Florence Blanc-Renaud
        - Thomas Woerner
    '''
    
    EXAMPLES = '''
    # Example from Ansible Playbooks
    # Unenroll client
    - ipaclient:
      state: absent
    
    # Enroll client using admin credentials, with auto-discovery
    - ipaclient:
        principal: admin
        password: MySecretPassword
        ntp: no
        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
        ntp: no
        kinit_attempts: 5
    
    # Enroll client using admin credentials, with specified server
    - ipaclient:
        principal: admin
        password: MySecretPassword
        domain: ipa.domain.com
        servers: ipaserver.ipa.domain.com
        ntp: no
        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')
        servers = module.params.get('servers')
        principal = module.params.get('principal')
        password = module.params.get('password')
        keytab = module.params.get('keytab')
        otp = module.params.get('otp')
        force_join = module.params.get('force_join')
        kinit_attempts = module.params.get('kinit_attempts')
        ntp = module.params.get('ntp')
        mkhomedir = module.params.get('mkhomedir')
        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 servers:
            for server in servers:
                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 force_join:
            cmd.append("--force-join")
        if kinit_attempts:
            cmd.append("--kinit-attempts")
            cmd.append(str(kinit_attempts))
        if not ntp:
            cmd.append("--no-ntp")
        if mkhomedir:
            cmd.append("--mkhomedir")
        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),
                servers=dict(required=False, type='list'),
                principal=dict(default='admin'),
                password=dict(required=False, no_log=True),
                keytab=dict(required=False, type='path'),
                otp=dict(required=False),
                force_join=dict(required=False, type='bool', default=False),
                kinit_attempts=dict(required=False, type='int'),
                ntp=dict(required=False, type='bool', default=True),
                mkhomedir=dict(required=False, type='bool', default=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()