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.ipadiscovery
|
||||||
import ipaclient.ipachangeconf
|
import ipaclient.ipachangeconf
|
||||||
from ipa.ipautil import run
|
from ipa.ipautil import run
|
||||||
import shutil
|
|
||||||
|
|
||||||
def parse_options():
|
def parse_options():
|
||||||
parser = OptionParser(version=VERSION)
|
parser = OptionParser(version=VERSION)
|
||||||
|
@ -36,6 +36,7 @@ import getpass
|
|||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
import ipaserver.dsinstance
|
import ipaserver.dsinstance
|
||||||
import ipaserver.krbinstance
|
import ipaserver.krbinstance
|
||||||
|
import ipaserver.bindinstance
|
||||||
from ipa.ipautil import run
|
from ipa.ipautil import run
|
||||||
|
|
||||||
def parse_options():
|
def parse_options():
|
||||||
@ -51,10 +52,13 @@ def parse_options():
|
|||||||
parser.add_option("-a", "--admin-password", dest="admin_password",
|
parser.add_option("-a", "--admin-password", dest="admin_password",
|
||||||
help="admin user kerberos password")
|
help="admin user kerberos password")
|
||||||
parser.add_option("-d", "--debug", dest="debug", action="store_true",
|
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("--hostname", dest="host_name", help="fully qualified name of server")
|
||||||
parser.add_option("-U", "--unattended", dest="unattended",
|
parser.add_option("--ip-address", dest="ip_address", help="Master Server IP Address")
|
||||||
help="unattended installation never prompts the user")
|
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()
|
options, args = parser.parse_args()
|
||||||
|
|
||||||
@ -63,7 +67,7 @@ def parse_options():
|
|||||||
not options.dm_password or
|
not options.dm_password or
|
||||||
not options.admin_password or
|
not options.admin_password or
|
||||||
not options.master_password):
|
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
|
return options
|
||||||
|
|
||||||
@ -93,34 +97,140 @@ def main():
|
|||||||
ds_user = ""
|
ds_user = ""
|
||||||
realm_name = ""
|
realm_name = ""
|
||||||
host_name = ""
|
host_name = ""
|
||||||
|
domain_name = ""
|
||||||
|
ip_address = ""
|
||||||
master_password = ""
|
master_password = ""
|
||||||
dm_password = ""
|
dm_password = ""
|
||||||
admin_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
|
# check the hostname is correctly configured, it must be as the kldap
|
||||||
# utilities just use the hostname as returned by gethostbyname to set
|
# utilities just use the hostname as returned by gethostbyname to set
|
||||||
# up some of the standard entries
|
# up some of the standard entries
|
||||||
|
|
||||||
|
host_name = ""
|
||||||
if options.host_name:
|
if options.host_name:
|
||||||
host_name = options.host_name
|
host_name = options.host_name
|
||||||
else:
|
else:
|
||||||
host_name = socket.gethostname()
|
try:
|
||||||
if len(host_name.split(".")) < 2:
|
host_name = socket.gethostname()
|
||||||
print "Invalid hostname <"+host_name+">"
|
except:
|
||||||
print "Check the /etc/hosts file and make sure to have a valid FQDN"
|
pass
|
||||||
return "-Fatal Error-"
|
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)
|
domain_name = host_name[host_name.find(".")+1:]
|
||||||
if ip == "127.0.0.1":
|
|
||||||
print "The hostname resolves to the localhost address (127.0.0.1)"
|
# Check we have a public IP that is associated with the hostname
|
||||||
print "Please change your /etc/hosts file or your DNS so that the"
|
ip = ""
|
||||||
print "hostname resolves to the ip address of your network interface."
|
askip = False
|
||||||
print "The KDC service does not listen on 127.0.0.1"
|
try:
|
||||||
print ""
|
ip = socket.gethostbyname(host_name)
|
||||||
print "Please fix your /etc/hosts file and restart the setup program"
|
|
||||||
return "-Fatal Error-"
|
if ip == "127.0.0.1" or ip == "::1":
|
||||||
|
print "The hostname resolves to the localhost address (127.0.0.1/::1)"
|
||||||
print "The Final KDC Host Name will be: " + host_name + ". With IP address: " + ip
|
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 ""
|
print ""
|
||||||
|
|
||||||
if not options.ds_user:
|
if not options.ds_user:
|
||||||
@ -152,7 +262,7 @@ def main():
|
|||||||
print "The kerberos protocol requires a Realm name to be defined."
|
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 "Usually the domain name all in uppercase is used as realm name."
|
||||||
print ""
|
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+"]: ")
|
realm_name = raw_input("Please provide a realm name ["+upper_dom+"]: ")
|
||||||
print ""
|
print ""
|
||||||
if realm_name == "":
|
if realm_name == "":
|
||||||
@ -227,6 +337,11 @@ def main():
|
|||||||
else:
|
else:
|
||||||
admin_password = options.admin_password
|
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
|
# Create a directory server instance
|
||||||
ds = ipaserver.dsinstance.DsInstance()
|
ds = ipaserver.dsinstance.DsInstance()
|
||||||
ds.create_instance(ds_user, realm_name, host_name, dm_password)
|
ds.create_instance(ds_user, realm_name, host_name, dm_password)
|
||||||
@ -235,8 +350,24 @@ def main():
|
|||||||
krb = ipaserver.krbinstance.KrbInstance()
|
krb = ipaserver.krbinstance.KrbInstance()
|
||||||
krb.create_instance(ds_user, realm_name, host_name, dm_password, master_password)
|
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()
|
ds.restart()
|
||||||
|
krb.restart()
|
||||||
|
|
||||||
# Restart apache
|
# Restart apache
|
||||||
run(["/sbin/service", "httpd", "restart"])
|
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.suffix = realm_to_suffix(self.realm)
|
||||||
self.kdc_password = generate_kdc_password()
|
self.kdc_password = generate_kdc_password()
|
||||||
|
|
||||||
|
self.stop()
|
||||||
|
|
||||||
self.__configure_kdc_account_password()
|
self.__configure_kdc_account_password()
|
||||||
|
|
||||||
self.__setup_sub_dict()
|
self.__setup_sub_dict()
|
||||||
@ -89,8 +92,6 @@ class KrbInstance:
|
|||||||
|
|
||||||
self.__export_kadmin_changepw_keytab()
|
self.__export_kadmin_changepw_keytab()
|
||||||
|
|
||||||
self.__create_sample_bind_zone()
|
|
||||||
|
|
||||||
self.__add_pwd_extop_module()
|
self.__add_pwd_extop_module()
|
||||||
|
|
||||||
self.start()
|
self.start()
|
||||||
@ -161,13 +162,6 @@ class KrbInstance:
|
|||||||
args = ["/usr/bin/setfacl", "-m", "u:"+self.ds_user+":r", "/var/kerberos/krb5kdc/.k5."+self.realm]
|
args = ["/usr/bin/setfacl", "-m", "u:"+self.ds_user+":r", "/var/kerberos/krb5kdc/.k5."+self.realm]
|
||||||
run(args)
|
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):
|
def __create_ds_keytab(self):
|
||||||
(kwrite, kread, kerr) = os.popen3("/usr/kerberos/sbin/kadmin.local")
|
(kwrite, kread, kerr) = os.popen3("/usr/kerberos/sbin/kadmin.local")
|
||||||
kwrite.write("addprinc -randkey ldap/"+self.fqdn+"@"+self.realm+"\n")
|
kwrite.write("addprinc -randkey ldap/"+self.fqdn+"@"+self.realm+"\n")
|
||||||
|
Loading…
Reference in New Issue
Block a user