Files
ansible-freeipa/library/ipatest.py
Thomas Woerner c78507409c library/ipatest.py: New validation tests: krb5.conf and ipa ping
The first validation test of the krb5.keytab is now done using the system
krb5.conf file. If this test failed, then the validation will be done with
the temporary krb5.conf file.

An additionally IPA test has been added. For now this is "ipa ping" as there
seems not to be a more comprehensive validation test for proper IPA
configuration.
2017-10-04 17:13:19 +02:00

281 lines
8.7 KiB
Python

#!/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: ipatest
short description: Test if the krb5.keytab on the machine is valid and can be used.
description:
Test if the krb5.keytab on the machine is valid and can be used.
A temporary krb5.conf file will be generated to not fail on an invalid one.
options:
servers:
description: The FQDN of the IPA servers to connect to.
required: true
domain:
description: The primary DNS domain of an existing IPA deployment.
required: true
realm:
description: The Kerberos realm of an existing IPA deployment.
required: true
hostname:
description: The hostname of the machine to join (FQDN).
required: true
kdc:
description: The name or address of the host running the KDC.
required: true
principal:
description: The authorized kerberos principal used to join the IPA realm.
required: false
kinit_attempts:
description: Repeat the request for host Kerberos ticket X times.
required: false
default: 5
author:
- Thomas Woerner
'''
EXAMPLES = '''
# Join IPA to get the keytab
- name: Test IPA in force mode with maximum 5 kinit attempts
ipatest:
servers: ["server1.example.com","server2.example.com"]
domain: example.com
realm: EXAMPLE.COM
kdc: server1.example.com
hostname: client1.example.com
principal: admin
kinit_attempts: 5
# Join IPA to get the keytab using ipadiscovery return values
- name: Join IPA
ipajoin:
servers: "{{ ipadiscovery.servers }}"
domain: "{{ ipadiscovery.domain }}"
realm: "{{ ipadiscovery.realm }}"
kdc: "{{ ipadiscovery.kdc }}"
hostname: "{{ ipadiscovery.hostname }}"
principal: admin
'''
RETURN = '''
krb5_keytab_ok:
description: The flag describes if krb5.keytab on the host is usable.
returned: always
type: bool
ca_crt_exists:
description: The flag describes if ca.crt exists.
returned: always
krb5_conf_ok:
description: The flag describes if krb5.conf on the host is usable.
returned: always
type: bool
ipa_test_ok:
description: The flag describes if ipa ping test succeded.
returned: always
type: bool
'''
class Object(object):
pass
options = Object()
import os
import sys
import gssapi
import tempfile
import inspect
from ansible.module_utils.basic import AnsibleModule
from ipapython.version import NUM_VERSION, VERSION
if NUM_VERSION < 40400:
raise Exception("freeipa version '%s' is too old" % VERSION)
from ipaplatform.paths import paths
try:
from ipalib.install.kinit import kinit_keytab
except ImportError:
from ipapython.ipautil import kinit_keytab
try:
from ipaclient.install.client import configure_krb5_conf, SECURE_PATH
except ImportError:
# Create temporary copy of ipa-client-install script (as
# ipa_client_install.py) to be able to import the script easily and also
# to remove the global finally clause in which the generated ccache file
# gets removed. The ccache file will be needed in the next step.
# This is done in a temporary directory that gets removed right after
# ipa_client_install has been imported.
import shutil
temp_dir = tempfile.mkdtemp(dir="/tmp")
sys.path.append(temp_dir)
temp_file = "%s/ipa_client_install.py" % temp_dir
with open("/usr/sbin/ipa-client-install", "r") as f_in:
with open(temp_file, "w") as f_out:
for line in f_in:
if line.startswith("finally:"):
break
f_out.write(line)
import ipa_client_install
shutil.rmtree(temp_dir, ignore_errors=True)
sys.path.remove(temp_dir)
argspec = inspect.getargspec(ipa_client_install.configure_krb5_conf)
if argspec.keywords is None:
def configure_krb5_conf(
cli_realm, cli_domain, cli_server, cli_kdc, dnsok,
filename, client_domain, client_hostname, force,
configure_sssd):
global options
options.force = force
options.sssd = configure_sssd
return ipa_client_install.configure_krb5_conf(
cli_realm, cli_domain, cli_server, cli_kdc, dnsok, options,
filename, client_domain, client_hostname)
else:
configure_krb5_conf = ipa_client_install.configure_krb5_conf
SECURE_PATH = ("/bin:/sbin:/usr/kerberos/bin:/usr/kerberos/sbin:/usr/bin:/usr/sbin")
from ipapython.ipautil import realm_to_suffix, run
import logging
logger = logging.getLogger("ipa-client-install")
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),
kdc=dict(required=True),
principal=dict(required=False),
kinit_attempts=dict(required=False, type='int', default=5),
),
supports_check_mode = True,
)
module._ansible_debug = True
servers = module.params.get('servers')
domain = module.params.get('domain')
realm = module.params.get('realm')
hostname = module.params.get('hostname')
kdc = module.params.get('kdc')
principal = module.params.get('principal')
kinit_attempts = module.params.get('kinit_attempts')
client_domain = hostname[hostname.find(".")+1:]
host_principal = 'host/%s@%s' % (hostname, realm)
sssd = True
# Remove IPA_DNS_CCACHE remain if it exists
try:
os.remove(paths.IPA_DNS_CCACHE)
except OSError:
pass
krb5_keytab_ok = False
krb5_conf_ok = False
ipa_test_ok = False
ca_crt_exists = os.path.exists(paths.IPA_CA_CRT)
env = {'PATH': SECURE_PATH, 'KRB5CCNAME': paths.IPA_DNS_CCACHE}
# First try: Validate krb5 keytab with system krb5 configuraiton
try:
kinit_keytab(host_principal, paths.KRB5_KEYTAB,
paths.IPA_DNS_CCACHE,
config=paths.KRB5_CONF,
attempts=kinit_attempts)
krb5_keytab_ok = True
krb5_conf_ok = True
# Test IPA
try:
result = run(["/usr/bin/ipa", "ping"], raiseonerr=False, env=env)
if result.returncode == 0:
ipa_test_ok = True
except OSError:
pass
except gssapi.exceptions.GSSError as e:
pass
# Second try: Validate krb5 keytab with temporary krb5
# configuration
if not krb5_conf_ok:
try:
(krb_fd, krb_name) = tempfile.mkstemp()
os.close(krb_fd)
configure_krb5_conf(
cli_realm=realm,
cli_domain=domain,
cli_server=servers,
cli_kdc=kdc,
dnsok=False,
filename=krb_name,
client_domain=client_domain,
client_hostname=hostname,
configure_sssd=sssd,
force=False)
try:
kinit_keytab(host_principal, paths.KRB5_KEYTAB,
paths.IPA_DNS_CCACHE,
config=krb_name,
attempts=kinit_attempts)
krb5_keytab_ok = True
# Test IPA
env['KRB5_CONFIG'] = krb_name
try:
result = run(["/usr/bin/ipa", "ping"], raiseonerr=False,
env=env)
if result.returncode == 0:
ipa_test_ok = True
except OSError:
pass
except gssapi.exceptions.GSSError as e:
pass
finally:
try:
os.remove(krb_name)
except OSError:
module.fail_json(msg="Could not remove %s" % krb_name)
module.exit_json(changed=False,
krb5_keytab_ok=krb5_keytab_ok,
krb5_conf_ok=krb5_conf_ok,
ca_crt_exists=ca_crt_exists,
ipa_test_ok=ipa_test_ok)
if __name__ == '__main__':
main()