Allow to install the KRA on a promoted server

Signed-off-by: Simo Sorce <simo@redhat.com>
Reviewed-By: Martin Babinsky <mbabinsk@redhat.com>
Reviewed-By: Jan Cholasta <jcholast@redhat.com>
This commit is contained in:
Simo Sorce 2015-08-25 15:42:25 -04:00 committed by Martin Basti
parent a0b8415236
commit bc39cc9f81
9 changed files with 301 additions and 152 deletions

View File

@ -29,7 +29,7 @@ from ipaserver.install import certs
from ipaserver.install.installutils import create_replica_config
from ipaserver.install.installutils import check_creds, ReplicaConfig
from ipaserver.install import dsinstance, ca
from ipaserver.install import cainstance, custodiainstance
from ipaserver.install import cainstance, custodiainstance, service
from ipapython import dogtag
from ipapython import version
from ipalib import api
@ -162,7 +162,8 @@ def install_replica(safe_options, options, filename):
config.subject_base = attrs.get('ipacertificatesubjectbase')[0]
if config.master_host_name is None:
config.ca_host_name = cainstance.find_ca_server(api.env.ca_host, conn)
config.ca_host_name = \
service.find_providing_server('CA', conn, api.env.ca_host)
config.master_host_name = config.ca_host_name
else:
config.ca_host_name = config.master_host_name

View File

@ -269,32 +269,6 @@ def is_step_one_done():
return False
def find_ca_server(host_name, conn, api=api):
"""
:param host_name: the preferred server
:param conn: a connection to the LDAP server
:return: the selected host name
Find a server that is a CA.
"""
dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
query_filter = conn.make_filter({'objectClass': 'ipaConfigObject',
'ipaConfigString': 'enabledService',
'cn': 'CA'}, rules='&')
try:
entries, trunc = conn.find_entries(filter=query_filter, base_dn=dn)
except errors.NotFound:
return None
if len(entries):
if host_name is not None:
for entry in entries:
if entry.dn[1].value == host_name:
return host_name
# if the preferred is not found, return the first in the list
return entries[0].dn[1].value
return None
def is_ca_installed_locally():
"""Check if CA is installed locally by checking for existence of CS.cfg
:return:True/False
@ -1540,83 +1514,6 @@ class CAInstance(DogtagInstance):
# Activate Topology for o=ipaca segments
self.__update_topology()
def __add_admin_to_group(self, group):
dn = DN(('cn', group), ('ou', 'groups'), ('o', 'ipaca'))
entry = self.admin_conn.get_entry(dn)
members = entry.get('uniqueMember', [])
members.append(self.admin_dn)
mod = [(ldap.MOD_REPLACE, 'uniqueMember', members)]
try:
self.admin_conn.modify_s(dn, mod)
except ldap.TYPE_OR_VALUE_EXISTS:
# already there
pass
def __setup_admin(self):
self.admin_user = "admin-%s" % self.fqdn
self.admin_password = binascii.hexlify(os.urandom(16))
if not self.admin_conn:
self.ldap_connect()
self.admin_dn = DN(('uid', self.admin_user),
('ou', 'people'), ('o', 'ipaca'))
# remove user if left-over exists
try:
entry = self.admin_conn.delete_entry(self.admin_dn)
except errors.NotFound:
pass
# add user
entry = self.admin_conn.make_entry(
self.admin_dn,
objectclass=["top", "person", "organizationalPerson",
"inetOrgPerson", "cmsuser"],
uid=[self.admin_user],
cn=[self.admin_user],
sn=[self.admin_user],
usertype=['adminType'],
mail=['root@localhost'],
userPassword=[self.admin_password],
userstate=['1']
)
self.admin_conn.add_entry(entry)
for group in ADMIN_GROUPS:
self.__add_admin_to_group(group)
# Now wait until the other server gets replicated this data
master_conn = ipaldap.IPAdmin(self.master_host,
port=replication.DEFAULT_PORT,
protocol='ldap')
master_conn.do_sasl_gssapi_bind()
replication.wait_for_entry(master_conn, entry)
del master_conn
def __remove_admin_from_group(self, group):
dn = DN(('cn', group), ('ou', 'groups'), ('o', 'ipaca'))
entry = self.admin_conn.get_entry(dn)
mod = [(ldap.MOD_DELETE, 'uniqueMember', self.admin_dn)]
try:
self.admin_conn.modify_s(dn, mod)
except ldap.NO_SUCH_ATTRIBUTE:
# already removed
pass
def __teardown_admin(self):
if not self.admin_conn:
self.ldap_connect()
for group in ADMIN_GROUPS:
self.__remove_admin_from_group(group)
self.admin_conn.delete_entry(self.admin_dn)
def __restart_ds_instance(self):
self.ldap_disconnect()
services.knownservices.dirsrv.restart()
def __client_auth_to_db(self):
self.enable_client_auth_to_db(self.dogtag_constants.CS_CFG_PATH)
@ -1651,6 +1548,7 @@ class CAInstance(DogtagInstance):
else:
self.ca_type = 'generic'
self.admin_groups = ADMIN_GROUPS
self.pkcs12_info = ca_cert_bundle
self.no_db_setup = True
self.clone = True
@ -1664,7 +1562,7 @@ class CAInstance(DogtagInstance):
self.step("creating certificate server db", self.__create_ds_db)
self.step("setting up initial replication", self.__setup_replication)
self.step("creating installation admin user", self.__setup_admin)
self.step("creating installation admin user", self.setup_admin)
# Setup instance
self.step("setting up certificate server", self.__spawn_instance)
@ -1675,7 +1573,7 @@ class CAInstance(DogtagInstance):
self.step("enable PKIX certificate path discovery and validation",
self.enable_pkix)
self.step("set up client auth to db", self.__client_auth_to_db)
self.step("destroying installation admin user", self.__teardown_admin)
self.step("destroying installation admin user", self.teardown_admin)
self.step("starting instance", self.start_instance)
self.step("importing CA chain to RA certificate database",

View File

@ -78,13 +78,12 @@ class CustodiaInstance(SimpleServiceInstance):
cli = CustodiaClient(self.fqdn, master_host_name, self.realm)
cli.fetch_key('dm/DMHash')
def get_ca_keys(self, ca_host, cacerts_file, cacerts_pwd):
def __get_keys(self, ca_host, cacerts_file, cacerts_pwd, data):
# Fecth all needed certs one by one, then combine them in a single
# p12 file
certlist = ['caSigningCert cert-pki-ca',
'ocspSigningCert cert-pki-ca',
'auditSigningCert cert-pki-ca',
'subsystemCert cert-pki-ca']
prefix = data['prefix']
certlist = data['list']
cli = CustodiaClient(self.fqdn, ca_host, self.realm)
@ -104,7 +103,7 @@ class CustodiaInstance(SimpleServiceInstance):
f.flush()
for nickname in certlist:
value = cli.fetch_key(os.path.join('ca', nickname), False)
value = cli.fetch_key(os.path.join(prefix, nickname), False)
v = json_decode(value)
pk12pwfile = os.path.join(tmpnssdir, 'pk12pwfile')
with open(pk12pwfile, 'w+') as f:
@ -129,6 +128,24 @@ class CustodiaInstance(SimpleServiceInstance):
finally:
shutil.rmtree(tmpnssdir)
def get_ca_keys(self, ca_host, cacerts_file, cacerts_pwd):
certlist = ['caSigningCert cert-pki-ca',
'ocspSigningCert cert-pki-ca',
'auditSigningCert cert-pki-ca',
'subsystemCert cert-pki-ca']
data = {'prefix': 'ca',
'list': certlist}
self.__get_keys(ca_host, cacerts_file, cacerts_pwd, data)
def get_kra_keys(self, ca_host, cacerts_file, cacerts_pwd):
certlist = ['auditSigningCert cert-pki-kra',
'storageCert cert-pki-kra',
'subsystemCert cert-pki-ca',
'transportCert cert-pki-kra']
data = {'prefix': 'ca',
'list': certlist}
self.__get_keys(ca_host, cacerts_file, cacerts_pwd, data)
def __start(self):
super(CustodiaInstance, self).__start()

View File

@ -18,6 +18,8 @@
#
import base64
import binascii
import ldap
import os
import shutil
import tempfile
@ -28,6 +30,8 @@ import pwd
from pki.client import PKIConnection
import pki.system
from ipalib import errors
from ipaplatform import services
from ipaplatform.paths import paths
from ipapython import certmonger
@ -37,6 +41,7 @@ from ipapython import ipautil
from ipapython.dn import DN
from ipaserver.install import service
from ipaserver.install import installutils
from ipaserver.install import replication
from ipaserver.install.installutils import stopped_service
from ipapython.ipa_log_manager import log_mgr
@ -144,7 +149,10 @@ class DogtagInstance(service.Service):
self.clone = False
self.basedn = DN(('o', 'ipa%s' % subsystem.lower()))
self.admin_user = DN(('uid', 'admin'), ('ou', 'people'), ('o', 'ipaca'))
self.admin_user = "admin"
self.admin_dn = DN(('uid', self.admin_user),
('ou', 'people'), ('o', 'ipaca'))
self.admin_groups = None
self.agent_db = tempfile.mkdtemp(prefix="tmp-")
self.ds_port = DEFAULT_DSPORT
self.server_root = dogtag_constants.SERVER_ROOT
@ -435,12 +443,10 @@ class DogtagInstance(service.Service):
conn = None
try:
conn = ipaldap.IPAdmin(self.fqdn, self.ds_port)
conn.do_simple_bind(
DN(('cn', 'Directory Manager')),
self.dm_password)
conn = ipaldap.IPAdmin(self.fqdn, ldapi=True, realm=self.realm)
conn.do_external_bind('root')
entry_attrs = conn.get_entry(self.admin_user, ['usercertificate'])
entry_attrs = conn.get_entry(self.admin_dn, ['usercertificate'])
admin_cert = entry_attrs.get('usercertificate')[0]
# TODO(edewata) Add check to warn if there is more than one cert.
@ -462,3 +468,76 @@ class DogtagInstance(service.Service):
self.log.critical(" %s" % log)
raise RuntimeError("%s configuration failed." % self.subsystem)
def __add_admin_to_group(self, group):
dn = DN(('cn', group), ('ou', 'groups'), ('o', 'ipaca'))
entry = self.admin_conn.get_entry(dn)
members = entry.get('uniqueMember', [])
members.append(self.admin_dn)
mod = [(ldap.MOD_REPLACE, 'uniqueMember', members)]
try:
self.admin_conn.modify_s(dn, mod)
except ldap.TYPE_OR_VALUE_EXISTS:
# already there
pass
def setup_admin(self):
self.admin_user = "admin-%s" % self.fqdn
self.admin_password = binascii.hexlify(os.urandom(16))
if not self.admin_conn:
self.ldap_connect()
self.admin_dn = DN(('uid', self.admin_user),
('ou', 'people'), ('o', 'ipaca'))
# remove user if left-over exists
try:
entry = self.admin_conn.delete_entry(self.admin_dn)
except errors.NotFound:
pass
# add user
entry = self.admin_conn.make_entry(
self.admin_dn,
objectclass=["top", "person", "organizationalPerson",
"inetOrgPerson", "cmsuser"],
uid=[self.admin_user],
cn=[self.admin_user],
sn=[self.admin_user],
usertype=['adminType'],
mail=['root@localhost'],
userPassword=[self.admin_password],
userstate=['1']
)
self.admin_conn.add_entry(entry)
for group in self.admin_groups:
self.__add_admin_to_group(group)
# Now wait until the other server gets replicated this data
master_conn = ipaldap.IPAdmin(self.master_host,
port=DEFAULT_DSPORT,
protocol='ldap')
master_conn.do_sasl_gssapi_bind()
replication.wait_for_entry(master_conn, entry)
del master_conn
def __remove_admin_from_group(self, group):
dn = DN(('cn', group), ('ou', 'groups'), ('o', 'ipaca'))
entry = self.admin_conn.get_entry(dn)
mod = [(ldap.MOD_DELETE, 'uniqueMember', self.admin_dn)]
try:
self.admin_conn.modify_s(dn, mod)
except ldap.NO_SUCH_ATTRIBUTE:
# already removed
pass
def teardown_admin(self):
if not self.admin_conn:
self.ldap_connect()
for group in self.admin_groups:
self.__remove_admin_from_group(group)
self.admin_conn.delete_entry(self.admin_dn)

60
ipaserver/install/ipa_kra_install.py Normal file → Executable file
View File

@ -20,6 +20,8 @@
from __future__ import print_function
import tempfile
from textwrap import dedent
from ipalib import api
from ipaplatform import services
@ -28,11 +30,14 @@ from ipapython import admintool
from ipapython import dogtag
from ipapython import ipautil
from ipapython.dn import DN
from ipaserver.install import service
from ipaserver.install import krainstance
from ipaserver.install import dsinstance
from ipaserver.install import installutils
from ipaserver.install.installutils import create_replica_config
from ipaserver.install import dogtaginstance
from ipaserver.install import kra
from ipaserver.install.installutils import ReplicaConfig
class KRAInstall(admintool.AdminTool):
@ -129,8 +134,14 @@ class KRAInstaller(KRAInstall):
)
self.installing_replica = dogtaginstance.is_installing_replica("KRA")
self.options.promote = False
if self.installing_replica:
domain_level = dsinstance.get_domain_level(api)
if domain_level > 0:
self.options.promote = True
return
if not self.args:
self.option_parser.error("A replica file is required.")
if len(self.args) > 1:
@ -160,29 +171,48 @@ class KRAInstaller(KRAInstall):
super(KRAInstaller, self).run()
print(dedent(self.INSTALLER_START_MESSAGE))
if not self.installing_replica:
replica_config = None
else:
replica_config = create_replica_config(
self.options.password,
self.replica_file,
self.options)
self.options.dm_password = self.options.password
self.options.setup_ca = False
api.Backend.ldap2.connect(bind_dn=DN('cn=Directory Manager'),
bind_pw=self.options.dm_password)
conn = api.Backend.ldap2
conn.connect(bind_dn=DN(('cn', 'Directory Manager')),
bind_pw=self.options.password)
config = None
if self.installing_replica:
if self.options.promote:
config = ReplicaConfig()
config.master_host_name = None
config.realm_name = api.env.realm
config.host_name = api.env.host
config.domain_name = api.env.domain
config.dirman_password = self.options.password
config.ca_ds_port = dogtag.install_constants.DS_PORT
config.top_dir = tempfile.mkdtemp("ipa")
config.dir = config.top_dir
else:
config = create_replica_config(
self.options.password,
self.replica_file,
self.options)
if config.subject_base is None:
attrs = conn.get_ipa_config()
config.subject_base = attrs.get('ipacertificatesubjectbase')[0]
if config.master_host_name is None:
config.kra_host_name = \
service.find_providing_server('KRA', conn, api.env.ca_host)
config.master_host_name = config.kra_host_name
else:
config.kra_host_name = config.master_host_name
try:
kra.install_check(api, replica_config, self.options)
kra.install_check(api, config, self.options)
except RuntimeError as e:
raise admintool.ScriptError(str(e))
kra.install(api, replica_config, self.options)
# Restart apache for new proxy config file
services.knownservices.httpd.restart(capture_output=True)
kra.install(api, config, self.options)
def run(self):
try:

View File

@ -2,11 +2,15 @@
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
#
import os
from ipalib import api, errors
from ipaplatform import services
from ipapython import certdb
from ipapython import dogtag
from ipapython import ipautil
from ipapython.dn import DN
from ipaserver.install import custodiainstance
from ipaserver.install import cainstance
from ipaserver.install import krainstance
from ipaserver.install import dsinstance
@ -36,6 +40,9 @@ def install_check(api, replica_config, options):
if not api.Command.kra_is_enabled()['result']:
raise RuntimeError("KRA is not installed on the master system")
if options.promote:
return
with certdb.NSSDatabase() as tmpdb:
pw = ipautil.write_tmp_file(ipautil.ipa_generate_password())
tmpdb.create_db(pw.name)
@ -62,7 +69,26 @@ def install(api, replica_config, options):
api.env.realm, api.env.host, options.dm_password,
options.dm_password, subject_base=subject)
else:
kra = krainstance.install_replica_kra(replica_config)
if options.promote:
ca_data = (os.path.join(replica_config.dir, 'kracert.p12'),
replica_config.dirman_password)
custodia = custodiainstance.CustodiaInstance(
replica_config.host_name, replica_config.realm_name)
custodia.get_kra_keys(replica_config.kra_host_name,
ca_data[0], ca_data[1])
kra = krainstance.KRAInstance(
replica_config.realm_name,
dogtag_constants=dogtag.install_constants)
kra.configure_replica(replica_config.host_name,
replica_config.kra_host_name,
replica_config.dirman_password,
kra_cert_bundle=ca_data)
return
else:
kra = krainstance.install_replica_kra(replica_config)
service.print_msg("Restarting the directory server")
ds = dsinstance.DsInstance()
@ -72,6 +98,9 @@ def install(api, replica_config, options):
kra.enable_client_auth_to_db(kra.dogtag_constants.KRA_CS_CFG_PATH)
# Restart apache for new proxy config file
services.knownservices.httpd.restart(capture_output=True)
def uninstall(standalone):
dogtag_constants = dogtag.configured_constants(api)

View File

@ -47,6 +47,11 @@ from ipapython.ipa_log_manager import log_mgr
# replicas with KRA configured
IPA_KRA_RECORD = "ipa-kra"
ADMIN_GROUPS = [
'Enterprise CA Administrators',
'Enterprise KRA Administrators',
'Security Domain Administrators'
]
class KRAInstance(DogtagInstance):
"""
@ -147,7 +152,7 @@ class KRAInstance(DogtagInstance):
# Security Domain Authentication
config.set("KRA", "pki_security_domain_https_port", "443")
config.set("KRA", "pki_security_domain_password", self.admin_password)
config.set("KRA", "pki_security_domain_user", "admin")
config.set("KRA", "pki_security_domain_user", self.admin_user)
# issuing ca
config.set("KRA", "pki_issuing_ca_uri", "https://%s" %
@ -166,8 +171,8 @@ class KRAInstance(DogtagInstance):
config.set("KRA", "pki_client_pkcs12_password", self.admin_password)
# Administrator
config.set("KRA", "pki_admin_name", "admin")
config.set("KRA", "pki_admin_uid", "admin")
config.set("KRA", "pki_admin_name", self.admin_user)
config.set("KRA", "pki_admin_uid", self.admin_user)
config.set("KRA", "pki_admin_email", "root@localhost")
config.set("KRA", "pki_admin_password", self.admin_password)
config.set("KRA", "pki_admin_nickname", "ipa-ca-agent")
@ -227,15 +232,16 @@ class KRAInstance(DogtagInstance):
pent = pwd.getpwnam(PKI_USER)
os.chown(p12_tmpfile_name, pent.pw_uid, pent.pw_gid)
# create admin cert file if it does not exist
cert = DogtagInstance.get_admin_cert(self)
with open(paths.ADMIN_CERT_PATH, "w") as admin_path:
admin_path.write(cert)
# FIXME
# # create admin cert file if it does not exist
# cert = DogtagInstance.get_admin_cert(self)
# with open(paths.ADMIN_CERT_PATH, "w") as admin_path:
# admin_path.write(cert)
# Security domain registration
config.set("KRA", "pki_security_domain_hostname", self.master_host)
config.set("KRA", "pki_security_domain_https_port", "443")
config.set("KRA", "pki_security_domain_user", "admin")
config.set("KRA", "pki_security_domain_user", self.admin_user)
config.set("KRA", "pki_security_domain_password",
self.admin_password)
@ -342,6 +348,54 @@ class KRAInstance(DogtagInstance):
dogtag.configured_constants().KRA_CS_CFG_PATH,
dogtag_constants)
def __enable_instance(self):
self.ldap_enable('KRA', self.fqdn, None, self.suffix)
def configure_replica(self, host_name, master_host, dm_password,
kra_cert_bundle=None, subject_base=None):
"""Create a KRA instance.
To create a clone, pass in pkcs12_info.
"""
self.fqdn = host_name
self.dm_password = dm_password
self.ds_port = DEFAULT_DSPORT
self.master_host = master_host
if subject_base is None:
self.subject_base = DN(('O', self.realm))
else:
self.subject_base = subject_base
self.suffix = ipautil.realm_to_suffix(self.realm)
self.pkcs12_info = kra_cert_bundle
self.clone = True
self.admin_groups = ADMIN_GROUPS
# Confirm that a KRA does not already exist
if self.is_installed():
raise RuntimeError(
"KRA already installed.")
# Confirm that a Dogtag 10 CA instance already exists
ca = cainstance.CAInstance(self.realm, certs.NSS_DIR,
dogtag_constants=dogtag.Dogtag10Constants)
if not ca.is_installed():
raise RuntimeError(
"KRA configuration failed. "
"A Dogtag CA must be installed first")
self.step("creating installation admin user", self.setup_admin)
self.step("configuring KRA instance", self.__spawn_instance)
self.step("destroying installation admin user", self.teardown_admin)
self.step("restarting KRA", self.restart_instance)
self.step("configure certmonger for renewals",
self.configure_certmonger_renewal)
self.step("configure certificate renewals", self.configure_renewal)
self.step("add vault container", self.__add_vault_container)
self.step("enabling KRA instance", self.__enable_instance)
self.start_creation(runtime=126)
def install_replica_kra(config, postinstall=False):
"""

View File

@ -28,8 +28,8 @@ import ipaclient.ipachangeconf
import ipaclient.ntpconf
from ipaserver.install import (
bindinstance, ca, cainstance, certs, dns, dsinstance, httpinstance,
installutils, kra, krbinstance, memcacheinstance, ntpinstance,
otpdinstance, custodiainstance, service)
installutils, kra, krainstance, krbinstance, memcacheinstance,
ntpinstance, otpdinstance, custodiainstance, service)
from ipaserver.install.installutils import create_replica_config
from ipaserver.install.installutils import ReplicaConfig
from ipaserver.install.replication import (
@ -772,10 +772,6 @@ def promote_check(installer):
installer._top_dir = tempfile.mkdtemp("ipa")
# FIXME: to implement yet
if options.setup_kra:
raise NotImplementedError
tasks.check_selinux_status()
client_fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
@ -922,7 +918,7 @@ def promote_check(installer):
config.subject_base = DN(subject_base)
# Find if any server has a CA
ca_host = cainstance.find_ca_server(api.env.server, conn)
ca_host = service.find_providing_server('CA', conn, api.env.server)
if ca_host is not None:
config.ca_host_name = ca_host
ca_enabled = True
@ -932,6 +928,13 @@ def promote_check(installer):
"installed, can't proceed without certs")
sys.exit(3)
config.kra_host_name = service.find_providing_server('KRA', conn,
api.env.server)
if options.setup_kra and config.kra_host_name is None:
root_logger.error("There is no KRA server in the domain, can't "
"setup a KRA clone")
sys.exit(3)
if options.setup_ca:
if not ca_enabled:
root_logger.error("The remote master does not have a CA "
@ -1083,7 +1086,17 @@ def promote(installer):
ca_cert_bundle=ca_data)
if options.setup_kra:
kra.install(api, config, options)
ca_data = (os.path.join(config.dir, 'kracert.p12'),
config.dirman_password)
custodia.get_kra_keys(config.kra_host_name, ca_data[0], ca_data[1])
constants = dogtag.install_constants
kra = krainstance.KRAInstance(config.realm_name,
dogtag_constants=constants)
kra.configure_replica(config.host_name, config.kra_host_name,
config.dirman_password,
kra_cert_bundle=ca_data)
ds.replica_populate()

View File

@ -27,7 +27,7 @@ import traceback
from ipapython import sysrestore, ipautil, dogtag, ipaldap
from ipapython.dn import DN
from ipapython.ipa_log_manager import *
from ipalib import errors, certstore
from ipalib import api, errors, certstore
from ipaplatform import services
from ipaplatform.paths import paths
@ -100,6 +100,34 @@ def add_principals_to_group(admin_conn, group, member_attr, principals):
# If there are no changes just pass
pass
def find_providing_server(svcname, conn, host_name=None, api=api):
"""
:param svcname: The service to find
:param conn: a connection to the LDAP server
:param host_name: the preferred server
:return: the selected host name
Find a server that is a CA.
"""
dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
query_filter = conn.make_filter({'objectClass': 'ipaConfigObject',
'ipaConfigString': 'enabledService',
'cn': svcname}, rules='&')
try:
entries, trunc = conn.find_entries(filter=query_filter, base_dn=dn)
except errors.NotFound:
return None
if len(entries):
if host_name is not None:
for entry in entries:
if entry.dn[1].value == host_name:
return host_name
# if the preferred is not found, return the first in the list
return entries[0].dn[1].value
return None
class Service(object):
def __init__(self, service_name, service_desc=None, sstore=None,
dm_password=None, ldapi=True, autobind=ipaldap.AUTOBIND_AUTO,