diff options
author | Simo Sorce <ssorce@redhat.com> | 2007-09-20 15:10:21 -0400 |
---|---|---|
committer | Simo Sorce <ssorce@redhat.com> | 2007-09-20 15:10:21 -0400 |
commit | 3fd4b9ba2ce40ff068bdfb8cd5ff3d04d26119aa (patch) | |
tree | 81b8328482058ea734c497de449f602ff0cc9938 | |
parent | 7633abb9e4869d1ad35f33f36e184481e07804c4 (diff) | |
download | freeipa-3fd4b9ba2ce40ff068bdfb8cd5ff3d04d26119aa.tar.gz freeipa-3fd4b9ba2ce40ff068bdfb8cd5ff3d04d26119aa.tar.xz freeipa-3fd4b9ba2ce40ff068bdfb8cd5ff3d04d26119aa.zip |
Initial support for confiuguring a DNS Server during installation.
It's not perfect yet but good enough to include it.
-rw-r--r-- | ipa-client/ipa-install/ipa-client-install | 1 | ||||
-rw-r--r-- | ipa-server/ipa-install/ipa-server-install | 177 | ||||
-rw-r--r-- | ipa-server/ipa-install/share/bind.named.conf.template | 41 | ||||
-rw-r--r-- | ipa-server/ipaserver/bindinstance.py | 113 | ||||
-rw-r--r-- | ipa-server/ipaserver/krbinstance.py | 12 |
5 files changed, 311 insertions, 33 deletions
diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install index c1f3ed5be..895756c66 100644 --- a/ipa-client/ipa-install/ipa-client-install +++ b/ipa-client/ipa-install/ipa-client-install @@ -31,7 +31,6 @@ from optparse import OptionParser import ipaclient.ipadiscovery import ipaclient.ipachangeconf from ipa.ipautil import run -import shutil def parse_options(): parser = OptionParser(version=VERSION) diff --git a/ipa-server/ipa-install/ipa-server-install b/ipa-server/ipa-install/ipa-server-install index e4d859950..439a8b3fe 100644 --- a/ipa-server/ipa-install/ipa-server-install +++ b/ipa-server/ipa-install/ipa-server-install @@ -36,6 +36,7 @@ import getpass from optparse import OptionParser import ipaserver.dsinstance import ipaserver.krbinstance +import ipaserver.bindinstance from ipa.ipautil import run def parse_options(): @@ -51,10 +52,13 @@ def parse_options(): parser.add_option("-a", "--admin-password", dest="admin_password", help="admin user kerberos password") 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("--hostname", dest="host_name", help="fully qualified name of server") - parser.add_option("-U", "--unattended", dest="unattended", - help="unattended installation never prompts the user") + parser.add_option("--ip-address", dest="ip_address", help="Master Server IP Address") + parser.add_option("--setup-bind", dest="setup_bind", action="store_true", + default=False, help="configure bind with our zone file") + parser.add_option("-U", "--unattended", dest="unattended", action="store_true", + default=False, help="unattended installation never prompts the user") options, args = parser.parse_args() @@ -63,7 +67,7 @@ def parse_options(): not options.dm_password or not options.admin_password or not options.master_password): - parser.error("error: In unattended mode you need to provide -u, -r, -p and -P options") + parser.error("error: In unattended mode you need to provide iat least -u, -r, -p and -P options") return options @@ -93,34 +97,140 @@ def main(): ds_user = "" realm_name = "" host_name = "" + domain_name = "" + ip_address = "" master_password = "" dm_password = "" admin_password = "" + # check bind packages are installed + bind = ipaserver.bindinstance.BindInstance() + if options.setup_bind: + if not bind.check_inst(): + print "--setup-bind was specified but bind is not installed on the system" + print "Please install bind (you also need the package 'caching-nameserver') and restart the setup program" + return "-Fatal Error-" + # check the hostname is correctly configured, it must be as the kldap # utilities just use the hostname as returned by gethostbyname to set # up some of the standard entries + host_name = "" if options.host_name: host_name = options.host_name else: - host_name = socket.gethostname() - if len(host_name.split(".")) < 2: - print "Invalid hostname <"+host_name+">" - print "Check the /etc/hosts file and make sure to have a valid FQDN" - return "-Fatal Error-" - - ip = socket.gethostbyname(host_name) - if ip == "127.0.0.1": - print "The hostname resolves to the localhost address (127.0.0.1)" - print "Please change your /etc/hosts file or your DNS so that the" - print "hostname resolves to the ip address of your network interface." - print "The KDC service does not listen on 127.0.0.1" - print "" - print "Please fix your /etc/hosts file and restart the setup program" - return "-Fatal Error-" - - print "The Final KDC Host Name will be: " + host_name + ". With IP address: " + ip + try: + host_name = socket.gethostname() + except: + pass + if options.unattended: + if len(host_name.split(".")) < 2 or host_name == "localhost.localdomain": + print "Invalid hostname: "+host_name + print "This host name can't be used as a hostname for an IPA Server" + return "-Fatal Error-" + else: + host_ok = False + while not host_ok: + if host_name == "": + print "" + host_name = raw_input("Please provide a Fully Qualified name to use for your system [master.example.com]: ") + if host_name != "": + host_name = "master.example.com" + + if len(host_name.split(".")) < 2 or host_name == "localhost.localdomain": + print "Invalid hostname: "+host_name + print "This host name can't be used as a hostname for an IPA Server" + host_name = "" + continue + else: + host_ok = True + + yesno = raw_input("Please confirm this ["+host_name+"] is the server hostname you want to use [Y/n]: ") + if yesno != "" and yesno.lower() != 'y': + host_name = "" + host_ok = False + + domain_name = host_name[host_name.find(".")+1:] + + # Check we have a public IP that is associated with the hostname + ip = "" + askip = False + try: + ip = socket.gethostbyname(host_name) + + if ip == "127.0.0.1" or ip == "::1": + print "The hostname resolves to the localhost address (127.0.0.1/::1)" + print "Please change your /etc/hosts file so that the hostname" + print "resolves to the ip address of your network interface." + print "The KDC service does not listen on localhost" + print "" + print "Please fix your /etc/hosts file and restart the setup program" + return "-Fatal Error-" + + except: + print "The provided hostname can't actually be use to resolve the IP address" + if options.ip_address: + ip = options.ip_address + else: + askip = True + + if ip != "": + try: + socket.inet_pton(socket.AF_INET, ip) + except: + try: + socket.inet_pton(socket.AF_INET6, ip) + except: + print "Invalid IP format" + if options.unattended: + return "-Fatal Error-" + else: + ip = "" + askip = True + + if options.ip_address and options.ip_address != ip: + if options.setup_bind: + ip = options.ip_address + else: + print "Error: the hostname resolves to an IP that is different from the one provided on the command line" + print "Please fix your DNS or /etc/hosts file to provide consistent information and restart the setup program" + return "-Fatal Error-" + + if options.unattended: + if askip or ip == "": + print "Unable to resolve IP address" + return "-Fatal Error-" + + while askip: + ip = raw_input("Please provide the IP address to be used for this host name: ") + + if ip == "": + print "An empty IP is not acceptable" + continue + if ip == "127.0.0.1" or ip == "::1": + print "The IPA Server can't use localhost as a valid IP" + continue + + try: + socket.inet_pton(socket.AF_INET, ip) + except: + try: + socket.inet_pton(socket.AF_INET6, ip) + except: + print "Invalid IP format" + continue + + print "Adding ["+ip+" "+host_name+"] to your /etc/hosts file" + hosts_fd = open('/etc/hosts', 'r+') + hosts_fd.seek(0, 2) + hosts_fd.write(ip+'\t'+host_name+' '+host_name[:host_name.find('.')]+'\n') + hosts_fd.close() + askip = False + + ip_address = ip + + print "The IPA Master Server Name will be: " + host_name + ". With IP address: " + ip_address + print "The IPA Domain Name will be: " + domain_name print "" if not options.ds_user: @@ -152,7 +262,7 @@ def main(): print "The kerberos protocol requires a Realm name to be defined." print "Usually the domain name all in uppercase is used as realm name." print "" - upper_dom = (host_name[host_name.find(".")+1:]).upper() + upper_dom = domain_name.upper() realm_name = raw_input("Please provide a realm name ["+upper_dom+"]: ") print "" if realm_name == "": @@ -227,6 +337,11 @@ def main(): else: admin_password = options.admin_password + if not options.unattended: + print "" + print "The following operations may take some minutes to complete." + print "Please wait until the prompt is returned." + # Create a directory server instance ds = ipaserver.dsinstance.DsInstance() ds.create_instance(ds_user, realm_name, host_name, dm_password) @@ -235,8 +350,24 @@ def main(): krb = ipaserver.krbinstance.KrbInstance() krb.create_instance(ds_user, realm_name, host_name, dm_password, master_password) - # Restart ds after the krb instance has changed ds configurations + bind.setup(host_name, ip_address, realm_name) + if options.setup_bind: + skipbind = False + if not options.unattended: + print "This program is about to replace the DNS Server configuration," + print "with an automatically generated one, based on the data gathered so far." + print "This will REPLACE any existing configuration." + yesno = raw_input("Are you sure you want to configure the DNS Server ? [y/N]: ") + if yesno.lower() != 'y': + skipbind = True + if not skipbind: + bind.create_instance() + else: + bind.create_sample_bind_zone() + + # Restart ds and krb after configurations have been changed ds.restart() + krb.restart() # Restart apache run(["/sbin/service", "httpd", "restart"]) diff --git a/ipa-server/ipa-install/share/bind.named.conf.template b/ipa-server/ipa-install/share/bind.named.conf.template new file mode 100644 index 000000000..c1d2817e0 --- /dev/null +++ b/ipa-server/ipa-install/share/bind.named.conf.template @@ -0,0 +1,41 @@ +options { + /* make named use port 53 for the source of all queries, to allow + * firewalls to block all ports except 53: + */ + query-source port 53; + query-source-v6 port 53; + + // Put files that named is allowed to write in the data/ directory: + directory "/var/named"; // the default + dump-file "data/cache_dump.db"; + statistics-file "data/named_stats.txt"; + memstatistics-file "data/named_mem_stats.txt"; + + /* Not used yet, support only on very recent bind versions */ +# tkey-gssapi-credential "DNS/$FQDN"; +# tkey-domain "$REALM"; +}; + +logging { +/* If you want to enable debugging, eg. using the 'rndc trace' command, + * By default, SELinux policy does not allow named to modify the /var/named directory, + * so put the default debug log file in data/ : + */ + channel default_debug { + file "data/named.run"; + severity dynamic; + }; +}; + +zone "." IN { + type hint; + file "named.ca"; +}; + +include "/etc/named.rfc1912.zones"; + +zone "$DOMAIN" { + type master; + file "$DOMAIN.zone.db"; +}; + diff --git a/ipa-server/ipaserver/bindinstance.py b/ipa-server/ipaserver/bindinstance.py new file mode 100644 index 000000000..708cc9337 --- /dev/null +++ b/ipa-server/ipaserver/bindinstance.py @@ -0,0 +1,113 @@ +#! /usr/bin/python -E +# Authors: Simo Sorce <ssorce@redhat.com> +# +# Copyright (C) 2007 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; version 2 or later +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import string +import tempfile +import shutil +import os +import socket +from ipa.ipautil import * + +class BindInstance: + def __init__(self): + self.fqdn = None + self.domain = None + self.host = None + self.ip_address = None + self.realm = None + self.sub_dict = None + + def setup(self, fqdn, ip_address, realm_name): + self.fqdn = fqdn + self.ip_address = ip_address + self.realm = realm_name + self.domain = fqdn[fqdn.find(".")+1:] + self.host = fqdn[:fqdn.find(".")] + + self.__setup_sub_dict() + + def check_inst(self): + # So far this file is always present in both RHEL5 and Fedora if all the necessary + # bind packages are installed (RHEL5 requires also the pkg: caching-nameserver) + if not os.path.exists('/etc/named.rfc1912.zones'): + return False + + return True + + def create_sample_bind_zone(self): + bind_txt = template_file(SHARE_DIR + "bind.zone.db.template", self.sub_dict) + [bind_fd, bind_name] = tempfile.mkstemp(".db","sample.zone.") + os.write(bind_fd, bind_txt) + os.close(bind_fd) + print "Sample zone file for bind has been created in "+bind_name + + def create_instance(self): + + try: + self.stop() + except: + pass + + self.__setup_zone() + self.__setup_named_conf() + + self.start() + + def stop(self): + run(["/sbin/service", "named", "stop"]) + + def start(self): + run(["/sbin/service", "named", "start"]) + + def restart(self): + run(["/sbin/service", "named", "restart"]) + + def __setup_sub_dict(self): + self.sub_dict = dict(FQDN=self.fqdn, + IP=self.ip_address, + DOMAIN=self.domain, + HOST=self.host, + REALM=self.realm) + + def __setup_zone(self): + zone_txt = template_file(SHARE_DIR + "bind.zone.db.template", self.sub_dict) + zone_fd = open('/var/named/'+self.domain+'.zone.db', 'w') + zone_fd.write(zone_txt) + zone_fd.close() + + def __setup_named_conf(self): + if os.path.exists('/etc/named.conf'): + shutil.copy2('/etc/named.conf', '/etc/named.conf.ipabkp') + named_txt = template_file(SHARE_DIR + "bind.named.conf.template", self.sub_dict) + named_fd = open('/etc/named.conf', 'w') + named_fd.seek(0) + named_fd.truncate(0) + named_fd.write(named_txt) + named_fd.close() + + if os.path.exists('/etc/resolve.conf'): + shutil.copy2('/etc/resolve.conf', '/etc/resolv.conf.ipabkp') + resolve_txt = "search "+self.domain+"\nnameserver "+self.ip_address+"\n" + resolve_fd = open('/etc/resolve.conf', 'w') + resolve_fd.seek(0) + resolve_fd.truncate(0) + resolve_fd.write(resolve_txt) + resolve_fd.close() + diff --git a/ipa-server/ipaserver/krbinstance.py b/ipa-server/ipaserver/krbinstance.py index e87f845f2..18f0db927 100644 --- a/ipa-server/ipaserver/krbinstance.py +++ b/ipa-server/ipaserver/krbinstance.py @@ -73,6 +73,9 @@ class KrbInstance: self.suffix = realm_to_suffix(self.realm) self.kdc_password = generate_kdc_password() + + self.stop() + self.__configure_kdc_account_password() self.__setup_sub_dict() @@ -89,8 +92,6 @@ class KrbInstance: self.__export_kadmin_changepw_keytab() - self.__create_sample_bind_zone() - self.__add_pwd_extop_module() self.start() @@ -161,13 +162,6 @@ class KrbInstance: args = ["/usr/bin/setfacl", "-m", "u:"+self.ds_user+":r", "/var/kerberos/krb5kdc/.k5."+self.realm] run(args) - def __create_sample_bind_zone(self): - bind_txt = template_file(SHARE_DIR + "bind.zone.db.template", self.sub_dict) - [bind_fd, bind_name] = tempfile.mkstemp(".db","sample.zone.") - os.write(bind_fd, bind_txt) - os.close(bind_fd) - print "Sample zone file for bind has been created in "+bind_name - def __create_ds_keytab(self): (kwrite, kread, kerr) = os.popen3("/usr/kerberos/sbin/kadmin.local") kwrite.write("addprinc -randkey ldap/"+self.fqdn+"@"+self.realm+"\n") |