Initial support for confiuguring a DNS Server during installation.

It's not perfect yet but good enough to include it.
This commit is contained in:
Simo Sorce 2007-09-20 15:10:21 -04:00
parent 7633abb9e4
commit 3fd4b9ba2c
5 changed files with 310 additions and 32 deletions

View File

@ -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)

View File

@ -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-"
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"
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-"
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
print "The Final KDC Host Name will be: " + host_name + ". With IP address: " + ip
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"])

View File

@ -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";
};

View File

@ -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()

View File

@ -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")