diff --git a/library/sssd.py b/library/sssd.py new file mode 100644 index 0000000000000000000000000000000000000000..3c9c9dce63f6a7b51499b1d94cb460d579173207 --- /dev/null +++ b/library/sssd.py @@ -0,0 +1,268 @@ +#!/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', + 'supported_by': 'community', + 'status': ['preview'], +} + +DOCUMENTATION = ''' +--- +module: sssd_conf +short description: Configure sssd +description: +Configure sssd +options: + servers: + description: The FQDN of the IPA servers to connect to. + required: false + domain: + description: The primary DNS domain of an existing IPA deployment. + 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 + services: + description: The services that should be enabled in the ssd configuration. + required: true + krb5_offline_passwords: + description: Whether user passwords are stored when the server is offline. + required: false + on_master: + description: Whether the configuration is done on the maseter or not. + required: false + primary: + description: Whether to use fixed server as primary IPA server. + required: false + preserve_sssd: + description: Preserve old SSSD configuration if possible. + required: false + permit: + description: Disable access rules by default, permit all access. + required: false + dns_updates: + description: Configures the machine to attempt dns updates when the ip address changes. + required: false + all_ip_addresses: + description: All routable IP addresses configured on any interface will be added to DNS. + required: false +author: + - Thomas Woerner +''' + +EXAMPLES = ''' +- name: Configure SSSD + sssd: + servers: ["server1.example.com","server2.example.com"] + domain: example.com + realm: EXAMPLE.COM + hostname: client1.example.com + services: ["ssh", "sudo"] + cache_credentials: yes + krb5_offline_passwords: yes +''' + +RETURN = ''' +''' + +import os +import SSSDConfig +from ansible.module_utils.basic import AnsibleModule +from ipalib.install import sysrestore +from ipaplatform.paths import paths +from ipapython.ipautil import file_exists +from ipaclient.install.client import get_server_connection_interface, \ + configure_nsswitch_database + + +def sssd_enable_service(module, sssdconfig, service): + try: + sssdconfig.new_service(service) + except SSSDConfig.ServiceAlreadyExists: + pass + except SSSDConfig.ServiceNotRecognizedError: + module.fail_json( + msg="Unable to activate the %s service in SSSD config." % service) + sssdconfig.activate_service(service) + +def main(): + module = AnsibleModule( + argument_spec = dict( + servers=dict(required=True, type='list'), + domain=dict(required=True), + realm=dict(required=True), + hostname=dict(required=True), + services=dict(required=True, type='list'), + krb5_offline_passwords=dict(required=False, type='bool'), + on_master=dict(required=False, type='bool'), + primary=dict(required=False, type='bool'), + preserve_sssd=dict(required=False, type='bool'), + permit=dict(required=False, type='bool'), + dns_updates=dict(required=False, type='bool'), + all_ip_addresses=dict(required=False, type='bool'), + ), + # required_one_of = ( [ '', '' ] ), + supports_check_mode = True, + ) + + module._ansible_debug = True + cli_servers = module.params.get('servers') + cli_domain = module.params.get('domain') + cli_realm = module.params.get('realm') + client_hostname = module.params.get('hostname') + services = module.params.get('services') + krb5_offline_passwords = module.params.get('krb5_offline_passwords') + on_master = module.params.get('on_master') + primary = module.params.get('primary') + preserve_sssd = module.params.get('preserve_sssd') + permit = module.params.get('permit') + dns_updates = module.params.get('dns_updates') + all_ip_addresses = module.params.get('all_ip_addresses') + + fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE) + client_domain = client_hostname[client_hostname.find(".")+1:] + + try: + sssdconfig = SSSDConfig.SSSDConfig() + sssdconfig.import_config() + except Exception as e: + if os.path.exists(paths.SSSD_CONF) and preserve_sssd: + # SSSD config is in place but we are unable to read it + # In addition, we are instructed to preserve it + # This all means we can't use it and have to bail out + module.fail_json( + msg="SSSD config exists but cannot be parsed: %s" % str(e)) + + # SSSD configuration does not exist or we are not asked to preserve it, + # create new one + # We do make new SSSDConfig instance because IPAChangeConf-derived + # classes have no means to reset their state and ParseError exception + # could come due to parsing error from older version which cannot be + # upgraded anymore, leaving sssdconfig instance practically unusable + # Note that we already backed up sssd.conf before going into this + # routine + if isinstance(e, IOError): + pass + else: + # It was not IOError so it must have been parsing error + module.fail_json(msg="Unable to parse existing SSSD config.") + + module.log("New SSSD config will be created") + sssdconfig = SSSDConfig.SSSDConfig() + sssdconfig.new_config() + + try: + domain = sssdconfig.new_domain(cli_domain) + except SSSDConfig.DomainAlreadyExistsError: + module.log("Domain %s is already configured in existing SSSD " + "config, creating a new one." % cli_domain) + sssdconfig = SSSDConfig.SSSDConfig() + sssdconfig.new_config() + domain = sssdconfig.new_domain(cli_domain) + + if on_master: + sssd_enable_service(module, sssdconfig, 'ifp') + + if (("ssh" in services and file_exists(paths.SSH_CONFIG)) or + ("sshd" in services and file_exists(paths.SSHD_CONFIG))): + sssd_enable_service(module, sssdconfig, 'ssh') + + if "sudo" in services: + sssd_enable_service(module, sssdconfig, 'sudo') + configure_nsswitch_database(fstore, 'sudoers', ['sss'], + default_value=['files']) + + domain.add_provider('ipa', 'id') + + # add discovery domain if client domain different from server domain + # do not set this config in server mode (#3947) + if not on_master and cli_domain != client_domain: + domain.set_option('dns_discovery_domain', cli_domain) + + if not on_master: + if primary: + domain.set_option('ipa_server', ', '.join(cli_servers)) + else: + domain.set_option('ipa_server', + '_srv_, %s' % ', '.join(cli_servers)) + else: + domain.set_option('ipa_server_mode', 'True') + # the master should only use itself for Kerberos + domain.set_option('ipa_server', cli_servers[0]) + + # increase memcache timeout to 10 minutes when in server mode + try: + nss_service = sssdconfig.get_service('nss') + except SSSDConfig.NoServiceError: + nss_service = sssdconfig.new_service('nss') + + nss_service.set_option('memcache_timeout', 600) + sssdconfig.save_service(nss_service) + + domain.set_option('ipa_domain', cli_domain) + domain.set_option('ipa_hostname', client_hostname) + if cli_domain.lower() != cli_realm.lower(): + domain.set_option('krb5_realm', cli_realm) + + # Might need this if /bin/hostname doesn't return a FQDN + # domain.set_option('ipa_hostname', 'client.example.com') + + domain.add_provider('ipa', 'auth') + domain.add_provider('ipa', 'chpass') + if not permit: + domain.add_provider('ipa', 'access') + else: + domain.add_provider('permit', 'access') + + domain.set_option('cache_credentials', True) + + # SSSD will need TLS for checking if ipaMigrationEnabled attribute is set + # Note that SSSD will force StartTLS because the channel is later used for + # authentication as well if password migration is enabled. Thus set + # the option unconditionally. + domain.set_option('ldap_tls_cacert', paths.IPA_CA_CRT) + + if dns_updates: + domain.set_option('dyndns_update', True) + if all_ip_addresses: + domain.set_option('dyndns_iface', '*') + else: + iface = get_server_connection_interface(cli_servers[0]) + domain.set_option('dyndns_iface', iface) + if krb5_offline_passwords: + domain.set_option('krb5_store_password_if_offline', True) + + domain.set_active(True) + + sssdconfig.save_domain(domain) + sssdconfig.write(paths.SSSD_CONF) + + module.exit_json(changed=True) + +if __name__ == '__main__': + main()