mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
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:
parent
7633abb9e4
commit
3fd4b9ba2c
@ -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)
|
||||
|
@ -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"
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
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"])
|
||||
|
41
ipa-server/ipa-install/share/bind.named.conf.template
Normal file
41
ipa-server/ipa-install/share/bind.named.conf.template
Normal 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";
|
||||
};
|
||||
|
113
ipa-server/ipaserver/bindinstance.py
Normal file
113
ipa-server/ipaserver/bindinstance.py
Normal 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()
|
||||
|
@ -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")
|
||||
|
Loading…
Reference in New Issue
Block a user