Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/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: ipanss
short description: Create IPA NSS database
description:
Create IPA NSS database
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
basedn:
description: The basedn of the IPA server (of the form dc=example,dc=com).
required: true
principal:
description: The authorized kerberos principal used to join the IPA realm.
subject_base:
description: The subject base, needed for certmonger
required: true
ca_enabled:
description: Whether the Certificate Authority is enabled or not.
type: bool
default: no
mkhomedir:
description: Whether to create home directories for users on their first login.
required: false
type: bool
default: no
description: Whether the configuration is done on the master or not.
type: bool
default: no
author:
- Thomas Woerner
'''
EXAMPLES = '''
- name: Create IPA NSS database
ipanss:
servers: ["server1.example.com","server2.example.com"]
domain: example.com
realm: EXAMPLE.COM
basedn: dc=example,dc=com
hostname: client1.example.com
subject_base: O=EXAMPLE.COM
principal: admin
ca_enabled: yes
'''
RETURN = '''
'''
import os
import sys
import tempfile
import inspect
Thomas Woerner
committed
import logging
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 ipalib import api, errors, x509
try:
from ipalib.install import certmonger
except ImportError:
from ipapython import certmonger
try:
from ipalib.install import certstore
except ImportError:
from ipalib import certstore
try:
from ipalib.install import sysrestore
except ImportError:
from ipapython import sysrestore
try:
from ipalib.install.kinit import kinit_keytab, kinit_password
except ImportError:
from ipapython.ipautil import kinit_keytab, kinit_password
from ipalib.rpc import delete_persistent_client_session_data
from ipapython.dn import DN
from ipaplatform import services
from ipaplatform.paths import paths
from ipaplatform.tasks import tasks
from ipapython import certdb, ipautil
from ipapython.ipautil import CalledProcessError
Thomas Woerner
committed
from ipapython.ipa_log_manager import standard_logging_setup
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
try:
from ipaclient.install.client import CCACHE_FILE, client_dns, configure_certmonger, update_ssh_keys, configure_openldap_conf, hardcode_ldap_server, get_certs_from_ldap, save_state, disable_ra, create_ipa_nssdb
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)
CCACHE_FILE = paths.IPA_DNS_CCACHE
client_dns = ipa_client_install.client_dns
configure_certmonger = ipa_client_install.configure_certmonger
update_ssh_keys = ipa_client_install.update_ssh_keys
configure_openldap_conf = ipa_client_install.configure_openldap_conf
hardcode_ldap_server = ipa_client_install.hardcode_ldap_server
get_certs_from_ldap = ipa_client_install.get_certs_from_ldap
save_state = ipa_client_install.save_state
disable_ra = ipa_client_install.disable_ra
from ipapython.certdb import create_ipa_nssdb
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),
basedn=dict(required=True),
principal=dict(required=False),
subject_base=dict(required=True),
ca_enabled=dict(required=True, type='bool'),
mkhomedir=dict(required=False, type='bool'),
on_master=dict(required=False, type='bool'),
),
supports_check_mode = True,
)
module._ansible_debug = True
servers = module.params.get('servers')
realm = module.params.get('realm')
hostname = module.params.get('hostname')
basedn = module.params.get('basedn')
domain = module.params.get('domain')
principal = module.params.get('principal')
subject_base = module.params.get('subject_base')
ca_enabled = module.params.get('ca_enabled')
mkhomedir = module.params.get('mkhomedir')
on_master = module.params.get('on_master')
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE)
Thomas Woerner
committed
logger = logging.getLogger("ipa-client-install")
standard_logging_setup(
paths.IPACLIENT_INSTALL_LOG, verbose=True, debug=False,
filemode='a', console_format='%(message)s')
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
os.environ['KRB5CCNAME'] = CCACHE_FILE
class Object(object):
pass
options = Object()
options.dns_updates = False
options.all_ip_addresses = False
options.ip_addresses = None
options.request_cert = False
options.hostname = hostname
options.preserve_sssd = False
options.on_master = False
options.conf_ssh = True
options.conf_sshd = True
options.conf_sudo = True
options.primary = False
options.permit = False
options.krb5_offline_passwords = False
options.create_sshfp = True
##########################################################################
# Create IPA NSS database
try:
create_ipa_nssdb()
except ipautil.CalledProcessError as e:
module.fail_json(msg="Failed to create IPA NSS database: %s" % e)
# Get CA certificates from the certificate store
try:
ca_certs = get_certs_from_ldap(servers[0], basedn, realm,
ca_enabled)
except errors.NoCertificateError:
if ca_enabled:
ca_subject = DN(('CN', 'Certificate Authority'), subject_base)
else:
ca_subject = None
ca_certs = certstore.make_compat_ca_certs(ca_certs, realm,
ca_subject)
ca_certs_trust = [(c, n, certstore.key_policy_to_trust_flags(t, True, u))
for (c, n, t, u) in ca_certs]
if hasattr(paths, "KDC_CA_BUNDLE_PEM"):
x509.write_certificate_list(
[c for c, n, t, u in ca_certs if t is not False],
paths.KDC_CA_BUNDLE_PEM)
if hasattr(paths, "CA_BUNDLE_PEM"):
x509.write_certificate_list(
[c for c, n, t, u in ca_certs if t is not False],
paths.CA_BUNDLE_PEM)
# Add the CA certificates to the IPA NSS database
module.debug("Adding CA certificates to the IPA NSS database.")
ipa_db = certdb.NSSDatabase(paths.IPA_NSSDB_DIR)
for cert, nickname, trust_flags in ca_certs_trust:
try:
ipa_db.add_cert(cert, nickname, trust_flags)
except CalledProcessError as e:
module.fail_json(msg="Failed to add %s to the IPA NSS database." % nickname)
# Add the CA certificates to the platform-dependant systemwide CA store
tasks.insert_ca_certs_into_systemwide_ca_store(ca_certs)
if not on_master:
client_dns(servers[0], hostname, options)
configure_certmonger(fstore, subject_base, realm, hostname,
options, ca_enabled)
if hasattr(paths, "SSH_CONFIG_DIR"):
ssh_config_dir = paths.SSH_CONFIG_DIR
else:
ssh_config_dir = services.knownservices.sshd.get_config_dir()
update_ssh_keys(hostname, ssh_config_dir, options.create_sshfp)
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
try:
os.remove(CCACHE_FILE)
except Exception:
pass
##########################################################################
# Name Server Caching Daemon. Disable for SSSD, use otherwise
# (if installed)
nscd = services.knownservices.nscd
if nscd.is_installed():
save_state(nscd, statestore)
try:
nscd_service_action = 'stop'
nscd.stop()
except Exception:
module.warn("Failed to %s the %s daemon" %
(nscd_service_action, nscd.service_name))
try:
nscd.disable()
except Exception:
module.warn("Failed to disable %s daemon. Disable it manually." %
nscd.service_name)
nslcd = services.knownservices.nslcd
if nslcd.is_installed():
save_state(nslcd, statestore)
##########################################################################
# Modify nsswitch/pam stack
tasks.modify_nsswitch_pam_stack(sssd=True,
mkhomedir=mkhomedir,
statestore=statestore)
module.log("SSSD enabled")
argspec = inspect.getargspec(services.service)
if len(argspec.args) > 1:
sssd = services.service('sssd', api)
else:
sssd = services.service('sssd')
try:
sssd.restart()
except CalledProcessError:
module.warn("SSSD service restart was unsuccessful.")
try:
sssd.enable()
except CalledProcessError as e:
module.warn(
"Failed to enable automatic startup of the SSSD daemon: "
"%s", e)
if configure_openldap_conf(fstore, basedn, servers):
module.log("Configured /etc/openldap/ldap.conf")
else:
module.log("Failed to configure /etc/openldap/ldap.conf")
# Check that nss is working properly
if not on_master:
user = principal
if user is None or user == "":
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
user = "admin@%s" % domain
module.log("Principal is not set when enrolling with OTP"
"; using principal '%s' for 'getent passwd'" % user)
elif '@' not in user:
user = "%s@%s" % (user, domain)
n = 0
found = False
# Loop for up to 10 seconds to see if nss is working properly.
# It can sometimes take a few seconds to connect to the remote
# provider.
# Particulary, SSSD might take longer than 6-8 seconds.
while n < 10 and not found:
try:
ipautil.run(["getent", "passwd", user])
found = True
except Exception as e:
time.sleep(1)
n = n + 1
if not found:
module.fail_json(msg="Unable to find '%s' user with 'getent "
"passwd %s'!" % (user.split("@")[0], user))
if conf:
module.log("Recognized configuration: %s" % conf)
else:
module.fail_json(msg=
"Unable to reliably detect "
"configuration. Check NSS setup manually.")
try:
hardcode_ldap_server(servers)
except Exception as e:
module.fail_json(msg="Adding hardcoded server name to "
"/etc/ldap.conf failed: %s" % str(e))
##########################################################################
module.exit_json(changed=True,
ca_enabled_ra=ca_enabled)
if __name__ == '__main__':
main()