summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <ssorce@redhat.com>2007-09-20 15:10:21 -0400
committerSimo Sorce <ssorce@redhat.com>2007-09-20 15:10:21 -0400
commit3fd4b9ba2ce40ff068bdfb8cd5ff3d04d26119aa (patch)
tree81b8328482058ea734c497de449f602ff0cc9938
parent7633abb9e4869d1ad35f33f36e184481e07804c4 (diff)
downloadfreeipa-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-install1
-rw-r--r--ipa-server/ipa-install/ipa-server-install177
-rw-r--r--ipa-server/ipa-install/share/bind.named.conf.template41
-rw-r--r--ipa-server/ipaserver/bindinstance.py113
-rw-r--r--ipa-server/ipaserver/krbinstance.py12
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")