Complete autodiscovery with autoconfiguration

The code is still not perfect and rely on a yet unreleased
nss_ldap package that fix dns discovery problems within nss_ldap
itself.
Also the manipulation of krb5.conf need to be improved
This commit is contained in:
Simo Sorce
2007-08-30 19:40:54 -04:00
parent a9b9a55392
commit 12b46527c6
5 changed files with 148 additions and 45 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
Name: freeipa-client
Version: 0.1.0
Release: 1%{?dist}
Release: 2%{?dist}
Summary: FreeIPA client
Group: System Environment/Base
@@ -9,7 +9,7 @@ URL: http://www.freeipa.org
Source0: %{name}-%{version}.tgz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Requires: python python-ldap freeipa-python
Requires: python python-ldap python-krbV freeipa-python
%description
FreeIPA is a server for identity, policy, and audit.
+2 -2
View File
@@ -1,6 +1,6 @@
Name: freeipa-client
Version: VERSION
Release: 1%{?dist}
Release: 2%{?dist}
Summary: FreeIPA client
Group: System Environment/Base
@@ -9,7 +9,7 @@ URL: http://www.freeipa.org
Source0: %{name}-%{version}.tgz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Requires: python python-ldap freeipa-python
Requires: python python-ldap python-krbV freeipa-python
%description
FreeIPA is a server for identity, policy, and audit.
+95 -16
View File
@@ -24,10 +24,12 @@ VERSION = "%prog .1"
import sys
sys.path.append("/usr/share/ipa")
import krbV
import socket
import logging
from optparse import OptionParser
import ipaclient.ipadiscovery
import ipaclient.ipachangeconf
from ipaserver.util import run
def parse_options():
@@ -35,10 +37,12 @@ def parse_options():
parser.add_option("--domain", dest="domain", help="domain name")
parser.add_option("--server", dest="server", help="IPA server")
parser.add_option("--realm", dest="realm_name", help="realm name")
parser.add_option("-f", "--force", dest="force", action="store_true",
default=False, help="force setting of ldap/kerberos conf")
parser.add_option("-d", "--debug", dest="debug", action="store_true",
dest="debug", default=False, help="print debugging information")
default=False, help="print debugging information")
parser.add_option("-U", "--unattended", dest="unattended",
help="unattended installation never prompts the user")
help="unattended installation never prompts the user")
options, args = parser.parse_args()
@@ -66,6 +70,7 @@ def logging_setup(options):
def main():
options = parse_options()
logging_setup(options)
dnsok = True
# Create the discovery instance
ds = ipaclient.ipadiscovery.IPADiscovery()
@@ -85,24 +90,98 @@ def main():
print "Failed to determine your DNS domain (DNS misconfigured?)"
dom = raw_input("Please provide your domain name (ex: example.com): ")
ret = ds.search(domain=dom)
if ret == -2:
logging.debug("IPA Server not found")
if options.server:
srv = options.server
elif options.unattended:
return ret
else:
print "Failed to find the IPA Server (DNS misconfigured?)"
srv = raw_input("Please provide your server name (ex: ipa.example.com): ")
ret = ds.search(domain=dom, server=srv)
if ret != 0:
print "Failed to verify that "+srv+" is an IPA Server, aborting!"
return ret
if ret == -2:
dnsok = False
logging.debug("IPA Server not found")
if options.server:
srv = options.server
elif options.unattended:
return ret
else:
print "Failed to find the IPA Server (DNS misconfigured?)"
srv = raw_input("Please provide your server name (ex: ipa.example.com): ")
ret = ds.search(domain=dom, server=srv)
if ret != 0:
print "Failed to verify that "+srv+" is an IPA Server, aborting!"
return ret
if dnsok:
print "Discovery was successful!"
elif not options.unattended:
print "\nThe failure to use DNS to find your IPA server indicates that your resolv.conf file is not properly configured\n."
print "Autodiscovery of servers for failover cannot work with this configuration.\n"
print "If you proceed with the installation, services will be configured to always access the discovered server for all operation and will not fail over to other servers in case of failure\n"
yesno = raw_input("Do you want to proceed and configure the system with fixed values with no DNS discovery? [y/N] ")
if yesno.lower() != "y":
return ret
print "\n"
print "Discovery was successful!"
print "Realm: "+ds.getRealmName()
print "DNS Domain: "+ds.getDomainName()
print "IPA Server: "+ds.getServerName()
print "BaseDN: "+ds.getBaseDN()
# Configure ldap.conf
ldapconf = ipaclient.ipachangeconf.IPAChangeConf("IPA Installer")
opts = [{'name':'host', 'action':'comment'},
{'name':'port', 'action':'comment'},
{'name':'binddn', 'action':'comment'},
{'name':'bindpw', 'action':'comment'},
{'name':'rootbinddn', 'action':'comment'},
{'name':'nss_base_passwd', 'value':ds.getBaseDN()+'?sub', 'action':'set'},
{'name':'nss_base_group', 'value':ds.getBaseDN()+'?sub', 'action':'set'},
{'name':'base', 'value':ds.getBaseDN(), 'action':'set'},
{'name':'ldap_version', 'value':'3', 'action':'set'}]
if dnsok and not options.force:
opts.insert(0, {'name':'uri', 'action':'comment'})
else:
opts.append({'name':'uri', 'value':'ldap://'+ds.getServerName(), 'action':'set'})
ldapconf.setOptionAssignment(" ")
ldapconf.changeConf("/etc/ldap.conf", opts)
#Check if kerberos is already configured properly
krbctx = krbV.default_context()
# If we find our domain assume we are properly configured
#(ex. we are configuring the client side of a Master)
if not krbctx.default_realm == ds.getRealmName() or options.force:
#Configure krb5.conf
krbconf = ipaclient.ipachangeconf.IPAChangeConf("IPA Installer")
krbconf.setOptionAssignment(" = ")
krbconf.setSectionNameDelimiters(("[","]"))
#[libdefaults]
opts = [{'name':'default_realm', 'value':ds.getRealmName(), 'action':'set'},
{'name':'ticket_lifetime', 'value':'24h', 'action':'set'},
{'name':'forwardable', 'value':'yes', 'action':'set'}]
if dnsok and not options.force:
opts.insert(1, {'name':'dns_lookup_realm', 'value':'true', 'action':'set'})
opts.insert(2, {'name':'dns_lookup_kdc', 'value':'true', 'action':'set'})
else:
opts.insert(1, {'name':'dns_lookup_realm', 'value':'false', 'action':'set'})
opts.insert(2, {'name':'dns_lookup_kdc', 'value':'false', 'action':'set'})
krbconf.changeConf("/etc/krb5.conf", opts, "libdefaults");
#the following are necessary only if DNS discovery does not work
if not dnsok or options.force:
#[realms]
opts = [{'name':ds.getRealmName(), 'value':'{', 'action':'set'},
{'name':'kdc', 'value':ds.getServerName()+':88', 'action':'set'},
{'name':'admin_server', 'value':ds.getServerName()+':749', 'action':'set'},
# adding '\n}' is a dirty hack because we still don't have subsections support
{'name':'default_domain', 'value':ds.getDomainName()+'\n}', 'action':'set'}]
krbconf.changeConf("/etc/krb5.conf", opts, "realms");
#[domain_realm]
opts = [{'name':'.'+ds.getDomainName(), 'value':ds.getRealmName(), 'action':'set'},
{'name':ds.getDomainName(), 'value':ds.getRealmName(), 'action':'set'}]
krbconf.changeConf("/etc/krb5.conf", opts, "domain_realm");
#Modify nsswitch to add nss_ldap
run(["/usr/sbin/authconfig", "--enableldap", "--update"])
#Modify pam to add pam_krb5
run(["/usr/sbin/authconfig", "--enablekrb5", "--update"])
return 0
+40 -19
View File
@@ -39,42 +39,61 @@ def openLocked(filename, perms):
raise IOError(errno, strerr)
return os.fdopen(fd, "r+")
#TODO: add subsection as a concept
# (ex. REALM.NAME = { foo = x bar = y } )
#TODO: put section delimiters as separating element of the list
# so that we can process multiple sections in one go
#TODO: add a comment all but provided options as a section option
class IPAChangeConf:
def __init__(self, name):
self.progname = name
self.optpre = (" ",)
self.optpre = ("",)
self.doptpre = self.optpre[0]
self.assign = ("=",)
self.assign = (" = ",)
self.dassign = self.assign[0]
self.comment = ("#",)
self.dcomment = self.comment[0]
self.eol = ("\n",)
self.deol = self.eol[0]
#self.sectdel = ("[","]")
self.sectdel = ()
#self.sectnamdel = ("[","]")
self.sectnamdel = ()
self.newsection = False
def setProgName(self, name):
self.progname = name
def setOptionPrefix(self, prefix):
self.optpre = prefix
if type(prefix) is list:
self.optpre = prefix
else:
self.optpre = (prefix, )
self.doptpre = self.optpre[0]
def setOptionAssignment(self, assign):
self.assign = assign
if type(assign) is list:
self.assign = assign
else:
self.assign = (assign, )
self.dassign = self.assign[0]
def setCommentPrefix(self, comment):
self.comment = comment
if type(comment) is list:
self.comment = comment
else:
self.comment = (comment, )
self.dcomment = self.comment[0]
def setEndLine(self, eol):
self.eol = eol
if type(eol) is list:
self.eol = eol
else:
self.eol = (eol, )
self.deol = self.eol[0]
def setSectionDelimiters(self, delims):
self.sectdel = delims
def setSectionNameDelimiters(self, delims):
self.sectnamdel = delims
def confDump(self, options):
output = ""
@@ -82,16 +101,18 @@ class IPAChangeConf:
#pre conf options delimiter
output += self.deol
output += self.dcomment+"["+self.progname+"]--start-line--"+self.deol
output += self.deol
output += self.dcomment+" Generated by authconfig on " + time.strftime("%Y/%m/%d %H:%M:%S") + self.deol
output += self.dcomment+" DO NOT EDIT THIS SECTION (delimited by --start-line--/--end-line--)"+self.deol
output += self.dcomment+" Any modification may be deleted or altered by authconfig in future"+self.deol
output += self.deol
if self.newsection:
output += getSectionLine(section)
#set options
for opt in options:
if opt['action'] == "set":
output += self.doptpre+opt['name']+" "+self.dassign+" "+opt['value']+self.deol
output += self.doptpre+opt['name']+self.dassign+opt['value']+self.deol
#post conf options delimiter
output += self.deol
@@ -127,18 +148,18 @@ class IPAChangeConf:
def matchSection(self, line):
cl = "".join(line.strip().split()).lower()
if len(self.sectdel) != 2:
if len(self.sectnamdel) != 2:
return False
if not cl.startswith(self.sectdel[0]):
if not cl.startswith(self.sectnamdel[0]):
return False
if not cl.endswith(self.sectdel[1]):
if not cl.endswith(self.sectnamdel[1]):
return False
return cl[len(self.sectdel[0]):-len(self.sectdel[1])]
return cl[len(self.sectnamdel[0]):-len(self.sectnamdel[1])]
def getSectionLine(self, section):
if len(self.sectdel) != 2:
if len(self.sectnamdel) != 2:
return section
return self.sectdel[0]+section+self.sectdel[1]+self.deol
return self.sectnamdel[0]+section+self.sectnamdel[1]+self.deol
def checkLineOption(self, line, options):
output = ""
@@ -211,7 +232,7 @@ class IPAChangeConf:
if not done:
if section:
output += getSectionLine(section)
self.newsection = True
output += self.confDump(options)
# Write it out and close it.
+9 -6
View File
@@ -30,6 +30,7 @@ class IPADiscovery:
self.realm = None
self.domain = None
self.server = None
self.basedn = None
def getServerName(self):
return str(self.server)
@@ -40,6 +41,9 @@ class IPADiscovery:
def getRealmName(self):
return str(self.realm)
def getBaseDN(self):
return str(self.basedn)
def search(self, domain = "", server = ""):
hostname = ""
qname = ""
@@ -127,10 +131,10 @@ class IPADiscovery:
lret = lh.search_s("", ldap.SCOPE_BASE, "(objectClass=*)")
for lattr in lret[0][1]:
if lattr.lower() == "namingcontexts":
lbase = lret[0][1][lattr][0]
self.basedn = lret[0][1][lattr][0]
logging.debug("Search for (info=*) in "+lbase+"(base)")
lret = lh.search_s(lbase, ldap.SCOPE_BASE, "(info=IPA*)")
logging.debug("Search for (info=*) in "+self.basedn+"(base)")
lret = lh.search_s(self.basedn, ldap.SCOPE_BASE, "(info=IPA*)")
if not lret:
return []
logging.debug("Found: "+str(lret))
@@ -144,8 +148,8 @@ class IPADiscovery:
return []
#search and return known realms
logging.debug("Search for (objectClass=krbRealmContainer) in "+lbase+"(sub)")
lret = lh.search_s("cn=kerberos,"+lbase, ldap.SCOPE_SUBTREE, "(objectClass=krbRealmContainer)")
logging.debug("Search for (objectClass=krbRealmContainer) in "+self.basedn+"(sub)")
lret = lh.search_s("cn=kerberos,"+self.basedn, ldap.SCOPE_SUBTREE, "(objectClass=krbRealmContainer)")
if not lret:
#something very wrong
return []
@@ -235,5 +239,4 @@ class IPADiscovery:
else:
kdc = qname
print "["+realm+", "+kdc+"]"
return [realm, kdc]