Modify ipahost module: the authentication is done locally on the controller

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.
This commit is contained in:
Florence Blanc-Renaud
2017-08-10 16:54:44 +02:00
parent 09f45e4acd
commit 38d7223376
10 changed files with 430 additions and 66 deletions

175
library/ipa_facts.py Normal file
View File

@@ -0,0 +1,175 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import re
import six
from six.moves.configparser import RawConfigParser
from ansible.module_utils.basic import AnsibleModule
try:
from ipalib import api
except ImportError:
HAS_IPALIB = False
else:
HAS_IPALIB = True
from ipaplatform.paths import paths
try:
# FreeIPA >= 4.5
from ipalib.install import sysrestore
except ImportError:
# FreeIPA 4.4 and older
from ipapython import sysrestore
try:
import ipaserver
except ImportError:
HAS_IPASERVER = False
else:
HAS_IPASERVER = True
SERVER_SYSRESTORE_STATE = "/var/lib/ipa/sysrestore/sysrestore.state"
NAMED_CONF = "/etc/named.conf"
VAR_LIB_PKI_TOMCAT = "/var/lib/pki/pki-tomcat"
def is_ntpd_configured():
# ntpd is configured when sysrestore.state contains the line
# [ntpd]
ntpd_conf_section = re.compile('^\s*\[ntpd\]\s*$')
try:
with open(SERVER_SYSRESTORE_STATE) as f:
for line in f.readlines():
if ntpd_conf_section.match(line):
return True
return False
except IOError:
return False
def is_dns_configured():
# dns is configured when /etc/named.conf contains the line
# dyndb "ipa" "/usr/lib64/bind/ldap.so" {
bind_conf_section = re.compile('^\s*dyndb\s+"ipa"\s+"[^"]+"\s+{$')
try:
with open(NAMED_CONF) as f:
for line in f.readlines():
if bind_conf_section.match(line):
return True
return False
except IOError:
return False
def is_dogtag_configured(subsystem):
# ca / kra is configured when the directory /var/lib/pki/pki-tomcat/[ca|kra]
# exists
available_subsystems = { 'ca', 'kra' }
assert subsystem in available_subsystems
return os.path.isdir(os.path.join(VAR_LIB_PKI_TOMCAT, subsystem))
def is_ca_configured():
return is_dogtag_configured('ca')
def is_kra_configured():
return is_dogtag_configured('kra')
def is_client_configured():
# IPA Client is configured when /etc/ipa/default.conf exists
# and /var/lib/ipa-client/sysrestore/sysrestore.state exists
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
return (os.path.isfile(paths.IPA_DEFAULT_CONF) and fstore.has_files())
def is_server_configured():
# IPA server is configured when /etc/ipa/default.conf exists
# and /var/lib/ipa/sysrestore/sysrestore.state exists
return (os.path.isfile(paths.IPA_DEFAULT_CONF) and
os.path.isfile(SERVER_SYSRESTORE_STATE))
def get_ipa_conf():
# Extract basedn, realm and domain from /etc/ipa/default.conf
parser = RawConfigParser()
parser.read(paths.IPA_DEFAULT_CONF)
basedn = parser.get('global', 'basedn')
realm = parser.get('global', 'realm')
domain = parser.get('global', 'domain')
return dict(
basedn=basedn,
realm=realm,
domain=domain
)
def get_ipa_version():
try:
from ipapython import version
except ImportError:
return None
else:
version_info = []
for part in version.VERSION.split('.'):
# DEV versions look like:
# 4.4.90.201610191151GITd852c00
# 4.4.90.dev201701071308+git2e43db1
if part.startswith('dev') or 'GIT' in part:
version_info.append(part)
else:
version_info.append(int(part))
return dict(
api_version=version.API_VERSION,
num_version=version.NUM_VERSION,
vendor_version=version.VENDOR_VERSION,
version=version.VERSION,
version_info=version_info
)
def main():
module = AnsibleModule(
argument_spec = dict(),
supports_check_mode=True
)
# The module does not change anything, meaning that
# check mode is supported
ipa_facts = dict(
packages= dict(
ipalib=HAS_IPALIB,
ipaserver=HAS_IPASERVER,
),
configured=dict(
client=False,
server=False,
dns=False,
ca=False,
kra=False,
ntpd=False
)
)
if HAS_IPALIB:
if is_client_configured():
ipa_facts['configured']['client'] = True
ipa_facts['version'] = get_ipa_version()
for key,value in six.iteritems(get_ipa_conf()):
ipa_facts[key] = value
if HAS_IPASERVER:
if is_server_configured():
ipa_facts['configured']['server'] = True
ipa_facts['configured']['dns'] = is_dns_configured()
ipa_facts['configured']['ca'] = is_ca_configured()
ipa_facts['configured']['kra'] = is_kra_configured()
ipa_facts['configured']['ntpd'] = is_ntpd_configured()
module.exit_json(
changed=False,
ansible_facts=dict(ipa=ipa_facts)
)
if __name__ == '__main__':
main()

View File

@@ -151,7 +151,10 @@ def get_ipa_conf():
parser.read(paths.IPA_DEFAULT_CONF)
result = dict()
for item in ['basedn', 'realm', 'domain', 'server', 'host', 'xmlrpc_uri']:
value = parser.get('global', item)
if parser.has_option('global', item):
value = parser.get('global', item)
else:
value = None
if value:
result[item] = value
@@ -251,6 +254,7 @@ def ensure_ipa_client(module):
if keytab:
cmd.append("--keytab")
cmd.append(keytab)
cmd.append("-d")
if otp:
cmd.append("--password")
cmd.append(otp)

View File

@@ -36,7 +36,7 @@ description:
options:
principal:
description: Kerberos principal used to manage the host
required: false
required: true
default: admin
password:
description: Password for the kerberos principal
@@ -44,6 +44,10 @@ options:
keytab:
description: Keytab file containing the Kerberos principal and encrypted key
required: false
lifetime:
description: Sets the default lifetime for initial ticket requests
required: false
default: 1h
fqdn:
description: the fully-qualified hostname of the host to add/modify/remove
required: true
@@ -251,9 +255,10 @@ def main():
"""
module = AnsibleModule(
argument_spec=dict(
keytab = dict(required=False, type='path'),
#keytab = dict(required=False, type='path'),
principal = dict(default='admin'),
password = dict(required=False, no_log=True),
#password = dict(required=False, no_log=True),
ccache = dict(required=False, type='path'),
fqdn = dict(required=True),
certificates = dict(required=False, type='list'),
sshpubkey= dict(required=False),
@@ -261,27 +266,21 @@ def main():
random = dict(default=False, type='bool'),
state = dict(default='present', choices=[ 'present', 'absent' ]),
),
required_one_of=[ [ 'password', 'keytab'], ],
mutually_exclusive=[ [ 'password', 'keytab' ], ],
#mutually_exclusive=[['password','keytab']],
#required_one_of=[['[password','keytab']],
supports_check_mode=True,
)
principal = module.params.get('principal', 'admin')
password = module.params.get('password')
keytab = module.params.get('keytab')
ccache = module.params.get('ccache')
fqdn = unicode(module.params.get('fqdn'))
state = module.params.get('state')
try:
ccache_dir = tempfile.mkdtemp(prefix='krbcc')
ccache_name = os.path.join(ccache_dir, 'ccache')
os.environ['KRB5CCNAME']=ccache
if keytab:
kinit_keytab(principal, keytab, ccache_name)
elif password:
kinit_password(principal, password, ccache_name)
os.environ['KRB5CCNAME'] = ccache_name
cfg = dict(
context='ansible_module',
confdir=paths.ETC_IPA,