mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Initial replication setup.
This add replication setup through two new commands: ipa-replica-prepare and ipa-replica-install. The procedure is to run ipa-replica-prepare on an existing master. This will collect information about the realm and the current master and create a file storing all of the information. After copying that file to the new replica, ipa-replica-install is run (with -r to create a read-only replica). This version of the patch also includes fixes for the sasl mappings on the replicas. Remaining features: - ssl for replication. - automatic configuration of mesh topology for master (or a simpler way to replicate multiple masters. - tool for view / configuring current replication.
This commit is contained in:
@@ -25,6 +25,7 @@ import logging
|
||||
import subprocess
|
||||
import os
|
||||
import stat
|
||||
import socket
|
||||
|
||||
from string import lower
|
||||
import re
|
||||
@@ -36,7 +37,6 @@ def realm_to_suffix(realm_name):
|
||||
terms = ["dc=" + x.lower() for x in s]
|
||||
return ",".join(terms)
|
||||
|
||||
|
||||
def template_str(txt, vars):
|
||||
return string.Template(txt).substitute(vars)
|
||||
|
||||
|
||||
@@ -71,6 +71,8 @@ rm -rf %{buildroot}
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
%{_sbindir}/ipa-server-install
|
||||
%{_sbindir}/ipa-replica-install
|
||||
%{_sbindir}/ipa-replica-prepare
|
||||
%{_sbindir}/ipa_kpasswd
|
||||
%{_sbindir}/ipa-webgui
|
||||
%attr(755,root,root) %{_initrddir}/ipa-kpasswd
|
||||
|
||||
@@ -71,6 +71,8 @@ rm -rf %{buildroot}
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
%{_sbindir}/ipa-server-install
|
||||
%{_sbindir}/ipa-replica-install
|
||||
%{_sbindir}/ipa-replica-prepare
|
||||
%{_sbindir}/ipa_kpasswd
|
||||
%{_sbindir}/ipa-webgui
|
||||
%attr(755,root,root) %{_initrddir}/ipa-kpasswd
|
||||
|
||||
@@ -6,6 +6,8 @@ SUBDIRS = \
|
||||
|
||||
sbin_SCRIPTS = \
|
||||
ipa-server-install \
|
||||
ipa-replica-install \
|
||||
ipa-replica-prepare \
|
||||
$(NULL)
|
||||
|
||||
appdir = $(IPA_DATA_DIR)
|
||||
|
||||
142
ipa-server/ipa-install/ipa-replica-install
Normal file
142
ipa-server/ipa-install/ipa-replica-install
Normal file
@@ -0,0 +1,142 @@
|
||||
#! /usr/bin/python -E
|
||||
# Authors: Karl MacMillan <kmacmillan@mentalrootkit.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 sys
|
||||
sys.path.append("/usr/share/ipa")
|
||||
|
||||
import tempfile
|
||||
from ConfigParser import SafeConfigParser
|
||||
|
||||
from ipa import ipautil
|
||||
|
||||
from ipaserver import dsinstance, replication, installutils, krbinstance, service
|
||||
from ipaserver import httpinstance, webguiinstance, radiusinstance, ntpinstance
|
||||
|
||||
class ReplicaConfig:
|
||||
def __init__(self):
|
||||
self.realm_name = ""
|
||||
self.master_host_name = ""
|
||||
self.dirman_password = ""
|
||||
self.ds_user = ""
|
||||
self.host_name = ""
|
||||
self.repl_password = ""
|
||||
self.dir = ""
|
||||
|
||||
def parse_options():
|
||||
from optparse import OptionParser
|
||||
parser = OptionParser()
|
||||
parser.add_option("-r", "--read-only", dest="master", action="store_false",
|
||||
default=True, help="create read-only replica - default is master")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if len(args) != 1:
|
||||
parser.error("you must provide a file generated by ipa-replica-prepare")
|
||||
|
||||
return options, args[0]
|
||||
|
||||
def get_dirman_password():
|
||||
return installutils.read_password("Directory Manager (existing master)")
|
||||
|
||||
def expand_info(filename):
|
||||
top_dir = tempfile.mkdtemp("ipa")
|
||||
dir = top_dir + "/realm_info"
|
||||
ipautil.run(["tar", "xfz", filename, "-C", top_dir])
|
||||
|
||||
return top_dir, dir
|
||||
|
||||
def read_info(dir, rconfig):
|
||||
filename = dir + "/realm_info"
|
||||
fd = open(filename)
|
||||
config = SafeConfigParser()
|
||||
config.readfp(fd)
|
||||
|
||||
rconfig.realm_name = config.get("realm", "realm_name")
|
||||
rconfig.master_host_name = config.get("realm", "master_host_name")
|
||||
rconfig.ds_user = config.get("realm", "ds_user")
|
||||
|
||||
def get_host_name():
|
||||
hostname = installutils.get_fqdn()
|
||||
try:
|
||||
installutils.verify_fqdn(hostname)
|
||||
except RuntimeError, e:
|
||||
logging.error(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
return hostname
|
||||
|
||||
def install_ds(config):
|
||||
dsinstance.check_existing_installation()
|
||||
dsinstance.check_ports()
|
||||
|
||||
ds = dsinstance.DsInstance()
|
||||
ds.create_instance(config.ds_user, config.realm_name, config.host_name, config.dirman_password)
|
||||
|
||||
def install_krb(config):
|
||||
krb = krbinstance.KrbInstance()
|
||||
ldappwd_filename = config.dir + "/ldappwd"
|
||||
krb.create_replica(config.ds_user, config.realm_name, config.host_name,
|
||||
config.dirman_password, ldappwd_filename)
|
||||
|
||||
def install_http(config):
|
||||
http = httpinstance.HTTPInstance()
|
||||
http.create_instance(config.realm_name, config.host_name)
|
||||
|
||||
def main():
|
||||
options, filename = parse_options()
|
||||
top_dir, dir = expand_info(filename)
|
||||
|
||||
config = ReplicaConfig()
|
||||
read_info(dir, config)
|
||||
config.host_name = get_host_name()
|
||||
config.repl_password = "box"
|
||||
config.dir = dir
|
||||
|
||||
# get the directory manager password
|
||||
config.dirman_password = get_dirman_password()
|
||||
|
||||
install_ds(config)
|
||||
|
||||
repl = replication.ReplicationManager(config.host_name, config.dirman_password)
|
||||
repl.setup_replication(config.master_host_name, config.realm_name, options.master)
|
||||
|
||||
install_krb(config)
|
||||
install_http(config)
|
||||
|
||||
# Create a Web Gui instance
|
||||
webgui = webguiinstance.WebGuiInstance()
|
||||
webgui.create_instance()
|
||||
|
||||
# Create a radius instance
|
||||
radius = radiusinstance.RadiusInstance()
|
||||
# FIXME: ldap_server should be derived, not hardcoded to localhost, also should it be a URL?
|
||||
radius.create_instance(config.realm_name, config.host_name, 'localhost')
|
||||
|
||||
# Configure ntpd
|
||||
ntp = ntpinstance.NTPInstance()
|
||||
ntp.create_instance()
|
||||
|
||||
|
||||
service.restart("dirsrv")
|
||||
service.restart("krb5kdc")
|
||||
|
||||
main()
|
||||
|
||||
|
||||
114
ipa-server/ipa-install/ipa-replica-prepare
Normal file
114
ipa-server/ipa-install/ipa-replica-prepare
Normal file
@@ -0,0 +1,114 @@
|
||||
#! /usr/bin/python -E
|
||||
# Authors: Karl MacMillan <kmacmillan@mentalrootkit.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 sys
|
||||
sys.path.append("/usr/share/ipa")
|
||||
|
||||
import logging, tempfile, shutil, os, pwd
|
||||
from ConfigParser import SafeConfigParser
|
||||
import krbV
|
||||
|
||||
from ipa import ipautil
|
||||
from ipaserver import dsinstance
|
||||
from ipaserver import installutils
|
||||
|
||||
certutil = "/usr/bin/certutil"
|
||||
|
||||
def get_host_name():
|
||||
hostname = installutils.get_fqdn()
|
||||
try:
|
||||
installutils.verify_fqdn(hostname)
|
||||
except RuntimeError, e:
|
||||
logging.error(str(e))
|
||||
sys.exit(1)
|
||||
|
||||
return hostname
|
||||
|
||||
def get_realm_name():
|
||||
c = krbV.default_context()
|
||||
return c.default_realm
|
||||
|
||||
def check_ipa_configuration(realm_name):
|
||||
config_dir = dsinstance.config_dirname(realm_name)
|
||||
if not ipautil.dir_exists(config_dir):
|
||||
logging.error("could not find directory instance: %s" % config_dir)
|
||||
sys.exit(1)
|
||||
|
||||
def create_certdb(ds_dir, dir):
|
||||
# copy the passwd, noise, and pin files
|
||||
shutil.copyfile(ds_dir + "/pwdfile.txt", dir + "/pwdfile.txt")
|
||||
shutil.copyfile(ds_dir + "/noise.txt", dir + "/noise.txt")
|
||||
shutil.copyfile(ds_dir + "/pin.txt", dir + "/pin.txt")
|
||||
|
||||
# create a new cert db
|
||||
ipautil.run([certutil, "-N", "-d", dir, "-f", dir + "/pwdfile.txt"])
|
||||
|
||||
# Add the CA cert
|
||||
ipautil.run([certutil, "-A", "-d", dir, "-n", "CA certificate", "-t", "CT,CT", "-a", "-i",
|
||||
ds_dir + "/cacert.asc"])
|
||||
|
||||
def get_ds_user(ds_dir):
|
||||
uid = os.stat(ds_dir).st_uid
|
||||
user = pwd.getpwuid(uid)[0]
|
||||
|
||||
return user
|
||||
|
||||
def copy_files(realm_name, dir):
|
||||
shutil.copy("/var/kerberos/krb5kdc/ldappwd", dir + "/ldappwd")
|
||||
|
||||
|
||||
def save_config(dir, realm_name, host_name, ds_user):
|
||||
config = SafeConfigParser()
|
||||
config.add_section("realm")
|
||||
config.set("realm", "realm_name", realm_name)
|
||||
config.set("realm", "master_host_name", host_name)
|
||||
config.set("realm", "ds_user", ds_user)
|
||||
fd = open(dir + "/realm_info", "w")
|
||||
config.write(fd)
|
||||
|
||||
|
||||
def main():
|
||||
realm_name = get_realm_name()
|
||||
host_name = get_host_name()
|
||||
ds_dir = dsinstance.config_dirname(realm_name)
|
||||
ds_user = get_ds_user(ds_dir)
|
||||
|
||||
check_ipa_configuration(realm_name)
|
||||
|
||||
top_dir = tempfile.mkdtemp("ipa")
|
||||
dir = top_dir + "/realm_info"
|
||||
os.mkdir(dir, 0700)
|
||||
|
||||
create_certdb(ds_dir, dir)
|
||||
|
||||
copy_files(realm_name, dir)
|
||||
|
||||
save_config(dir, realm_name, host_name, ds_user)
|
||||
|
||||
ipautil.run(["/bin/tar", "cfz", "replica-info-" + realm_name, "-C", top_dir, "realm_info"])
|
||||
|
||||
shutil.rmtree(dir)
|
||||
|
||||
main()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ import socket
|
||||
import errno
|
||||
import logging
|
||||
import pwd
|
||||
import getpass
|
||||
import subprocess
|
||||
import signal
|
||||
import shutil
|
||||
@@ -51,8 +50,9 @@ import ipaserver.radiusinstance
|
||||
import ipaserver.webguiinstance
|
||||
|
||||
from ipaserver import service
|
||||
from ipaserver.installutils import *
|
||||
|
||||
from ipa.ipautil import run
|
||||
from ipa.ipautil import *
|
||||
|
||||
def parse_options():
|
||||
parser = OptionParser(version=VERSION)
|
||||
@@ -86,39 +86,6 @@ def parse_options():
|
||||
|
||||
return options
|
||||
|
||||
def logging_setup(options):
|
||||
# Always log everything (i.e., DEBUG) to the log
|
||||
# file.
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format='%(asctime)s %(levelname)s %(message)s',
|
||||
filename='ipaserver-install.log',
|
||||
filemode='w')
|
||||
|
||||
console = logging.StreamHandler()
|
||||
# If the debug option is set, also log debug messages to the console
|
||||
if options.debug:
|
||||
console.setLevel(logging.DEBUG)
|
||||
else:
|
||||
# Otherwise, log critical and error messages
|
||||
console.setLevel(logging.ERROR)
|
||||
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
|
||||
console.setFormatter(formatter)
|
||||
logging.getLogger('').addHandler(console)
|
||||
|
||||
def erase_ds_instance_data(serverid):
|
||||
try:
|
||||
shutil.rmtree("/etc/dirsrv/slapd-%s" % serverid)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
shutil.rmtree("/var/lib/dirsrv/slapd-%s" % serverid)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
shutil.rmtree("/var/lock/dirsrv/slapd-%s" % serverid)
|
||||
except:
|
||||
pass
|
||||
|
||||
def signal_handler(signum, frame):
|
||||
global ds
|
||||
print "\nCleaning up..."
|
||||
@@ -126,59 +93,9 @@ def signal_handler(signum, frame):
|
||||
print "Removing configuration for %s instance" % ds.serverid
|
||||
ds.stop()
|
||||
if ds.serverid:
|
||||
erase_ds_instance_data (ds.serverid)
|
||||
ipaserver.dsinstance.erase_ds_instance_data (ds.serverid)
|
||||
sys.exit(1)
|
||||
|
||||
def check_existing_installation():
|
||||
dirs = glob.glob("/etc/dirsrv/slapd-*")
|
||||
if not dirs:
|
||||
return
|
||||
print ""
|
||||
print "An existing Directory Server has been detected."
|
||||
yesno = raw_input("Do you wish to remove it and create a new one? [no]: ")
|
||||
if not yesno or yesno.lower()[0] != "y":
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
run(["/sbin/service", "dirsrv", "stop"])
|
||||
except:
|
||||
pass
|
||||
for d in dirs:
|
||||
serverid = os.path.basename(d).split("slapd-", 1)[1]
|
||||
if serverid:
|
||||
erase_ds_instance_data(serverid)
|
||||
|
||||
def check_ports():
|
||||
ds_unsecure = port_available(389)
|
||||
ds_secure = port_available(636)
|
||||
if not ds_unsecure or not ds_secure:
|
||||
print "IPA requires ports 389 and 636 for the Directory Server."
|
||||
print "These are currently in use:"
|
||||
if not ds_unsecure:
|
||||
print "\t389"
|
||||
if not ds_secure:
|
||||
print "\t636"
|
||||
sys.exit(1)
|
||||
|
||||
def get_fqdn():
|
||||
fqdn = ""
|
||||
try:
|
||||
fqdn = socket.getfqdn()
|
||||
except:
|
||||
try:
|
||||
fqdn = socket.gethostname()
|
||||
except:
|
||||
fqdn = ""
|
||||
return fqdn
|
||||
|
||||
def verify_fqdn(host_name):
|
||||
is_ok = True
|
||||
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"
|
||||
is_ok = False
|
||||
return is_ok
|
||||
|
||||
def read_host_name(host_default):
|
||||
host_ok = False
|
||||
host_name = ""
|
||||
@@ -198,7 +115,9 @@ def read_host_name(host_default):
|
||||
host_name = host_default
|
||||
else:
|
||||
host_name = host_input
|
||||
if not verify_fqdn(host_name):
|
||||
try:
|
||||
verify_fqdn(host_name)
|
||||
except:
|
||||
host_name = ""
|
||||
continue
|
||||
else:
|
||||
@@ -256,36 +175,6 @@ def read_ip_address(host_name):
|
||||
|
||||
return ip
|
||||
|
||||
def port_available(port):
|
||||
"""Try to bind to a port on the wildcard host
|
||||
Return 1 if the port is available
|
||||
Return 0 if the port is in use
|
||||
"""
|
||||
rv = 1
|
||||
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(('', port))
|
||||
s.shutdown(0)
|
||||
s.close()
|
||||
except socket.error, e:
|
||||
if e[0] == errno.EADDRINUSE:
|
||||
rv = 0
|
||||
|
||||
if rv:
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(('', port))
|
||||
s.shutdown(0)
|
||||
s.close()
|
||||
except socket.error, e:
|
||||
if e[0] == errno.EADDRINUSE:
|
||||
rv = 0
|
||||
|
||||
return rv
|
||||
|
||||
def read_ds_user():
|
||||
print "The server must run as a specific user in a specific group."
|
||||
print "It is strongly recommended that this user should have no privileges"
|
||||
@@ -333,23 +222,6 @@ def read_realm_name(domain_name):
|
||||
realm_name = upper_dom
|
||||
return realm_name
|
||||
|
||||
def read_password(user):
|
||||
correct = False
|
||||
pwd = ""
|
||||
while not correct:
|
||||
pwd = getpass.getpass(user + " password: ")
|
||||
if not pwd:
|
||||
continue
|
||||
pwd_confirm = getpass.getpass("Password (confirm): ")
|
||||
if pwd != pwd_confirm:
|
||||
print "Password mismatch!"
|
||||
print ""
|
||||
else:
|
||||
correct = True
|
||||
#TODO: check validity/length
|
||||
print ""
|
||||
return pwd
|
||||
|
||||
def read_dm_password():
|
||||
print "Certain directory server operations require an administrative user."
|
||||
print "This user is referred to as the Directory Manager and has full access"
|
||||
@@ -392,6 +264,8 @@ def main():
|
||||
global ds
|
||||
ds = None
|
||||
|
||||
options = parse_options()
|
||||
|
||||
if os.getegid() != 0:
|
||||
print "Must be root to setup server"
|
||||
return
|
||||
@@ -399,17 +273,17 @@ def main():
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
|
||||
standard_logging_setup("ipaserver-install.log", options.debug)
|
||||
|
||||
print "=============================================================================="
|
||||
print "This program will setup the FreeIPA Server."
|
||||
print ""
|
||||
print "To accept the default shown in brackets, press the Enter key."
|
||||
print ""
|
||||
|
||||
check_existing_installation()
|
||||
check_ports()
|
||||
ipaserver.dsinstance.check_existing_installation()
|
||||
ipaserver.dsinstance.check_ports()
|
||||
|
||||
options = parse_options()
|
||||
logging_setup(options)
|
||||
|
||||
ds_user = ""
|
||||
realm_name = ""
|
||||
@@ -439,10 +313,13 @@ def main():
|
||||
host_default = get_fqdn()
|
||||
|
||||
if options.unattended:
|
||||
if not verify_fqdn(host_default):
|
||||
try:
|
||||
verify_fqdn(host_default)
|
||||
except RuntimeError, e:
|
||||
logging.error(str(e) + "\n")
|
||||
return "-Fatal Error-"
|
||||
else:
|
||||
host_name = host_default
|
||||
|
||||
host_name = host_default
|
||||
else:
|
||||
host_name = read_host_name(host_default)
|
||||
|
||||
|
||||
@@ -14,22 +14,4 @@ objectClass: top
|
||||
cn: kerberos
|
||||
aci: (targetattr="*")(version 3.0; acl "KDC System Account"; allow (all) userdn= "ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
|
||||
|
||||
#sasl mapping
|
||||
dn: cn=Full Principal,cn=mapping,cn=sasl,cn=config
|
||||
changetype: add
|
||||
objectclass: top
|
||||
objectclass: nsSaslMapping
|
||||
cn: Full Principal
|
||||
nsSaslMapRegexString: \(.*\)@\(.*\)
|
||||
nsSaslMapBaseDNTemplate: $SUFFIX
|
||||
nsSaslMapFilterTemplate: (krbPrincipalName=\1@\2)
|
||||
|
||||
dn: cn=Name Only,cn=mapping,cn=sasl,cn=config
|
||||
changetype: add
|
||||
objectclass: top
|
||||
objectclass: nsSaslMapping
|
||||
cn: Name Only
|
||||
nsSaslMapRegexString: \(.*\)
|
||||
nsSaslMapBaseDNTemplate: $SUFFIX
|
||||
nsSaslMapFilterTemplate: (krbPrincipalName=\1@$REALM)
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ app_PYTHON = \
|
||||
radiusinstance.py \
|
||||
webguiinstance.py \
|
||||
service.py \
|
||||
installutils.py \
|
||||
replication.py \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
|
||||
@@ -24,10 +24,14 @@ import tempfile
|
||||
import shutil
|
||||
import logging
|
||||
import pwd
|
||||
import glob
|
||||
import sys
|
||||
|
||||
from ipa.ipautil import *
|
||||
import service
|
||||
|
||||
import installutils
|
||||
|
||||
SERVER_ROOT_64 = "/usr/lib64/dirsrv"
|
||||
SERVER_ROOT_32 = "/usr/lib/dirsrv"
|
||||
|
||||
@@ -35,9 +39,6 @@ def ldap_mod(fd, dn, pwd):
|
||||
args = ["/usr/bin/ldapmodify", "-h", "127.0.0.1", "-xv", "-D", dn, "-w", pwd, "-f", fd.name]
|
||||
run(args)
|
||||
|
||||
text = fd.read()
|
||||
print text
|
||||
|
||||
def realm_to_suffix(realm_name):
|
||||
s = realm_name.split(".")
|
||||
terms = ["dc=" + x.lower() for x in s]
|
||||
@@ -49,6 +50,61 @@ def find_server_root():
|
||||
else:
|
||||
return SERVER_ROOT_32
|
||||
|
||||
def realm_to_serverid(realm_name):
|
||||
return "-".join(realm_name.split("."))
|
||||
|
||||
def config_dirname(realm_name):
|
||||
return "/etc/dirsrv/slapd-" + realm_to_serverid(realm_name) + "/"
|
||||
|
||||
def schema_dirname(realm_name):
|
||||
return config_dirname(realm_name) + "/schema/"
|
||||
|
||||
def erase_ds_instance_data(serverid):
|
||||
try:
|
||||
shutil.rmtree("/etc/dirsrv/slapd-%s" % serverid)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
shutil.rmtree("/var/lib/dirsrv/slapd-%s" % serverid)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
shutil.rmtree("/var/lock/dirsrv/slapd-%s" % serverid)
|
||||
except:
|
||||
pass
|
||||
|
||||
def check_existing_installation():
|
||||
dirs = glob.glob("/etc/dirsrv/slapd-*")
|
||||
if not dirs:
|
||||
return
|
||||
print ""
|
||||
print "An existing Directory Server has been detected."
|
||||
yesno = raw_input("Do you wish to remove it and create a new one? [no]: ")
|
||||
if not yesno or yesno.lower()[0] != "y":
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
run(["/sbin/service", "dirsrv", "stop"])
|
||||
except:
|
||||
pass
|
||||
for d in dirs:
|
||||
serverid = os.path.basename(d).split("slapd-", 1)[1]
|
||||
if serverid:
|
||||
erase_ds_instance_data(serverid)
|
||||
|
||||
def check_ports():
|
||||
ds_unsecure = installutils.port_available(389)
|
||||
ds_secure = installutils.port_available(636)
|
||||
if not ds_unsecure or not ds_secure:
|
||||
print "IPA requires ports 389 and 636 for the Directory Server."
|
||||
print "These are currently in use:"
|
||||
if not ds_unsecure:
|
||||
print "\t389"
|
||||
if not ds_secure:
|
||||
print "\t636"
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
INF_TEMPLATE = """
|
||||
[General]
|
||||
FullMachineName= $FQHN
|
||||
@@ -72,20 +128,25 @@ class DsInstance(service.Service):
|
||||
self.dm_password = None
|
||||
self.sub_dict = None
|
||||
|
||||
def create_instance(self, ds_user, realm_name, host_name, dm_password):
|
||||
def create_instance(self, ds_user, realm_name, host_name, dm_password, ro_replica=False):
|
||||
self.ds_user = ds_user
|
||||
self.realm_name = realm_name.upper()
|
||||
self.serverid = "-".join(self.realm_name.split("."))
|
||||
self.serverid = realm_to_serverid(self.realm_name)
|
||||
self.suffix = realm_to_suffix(self.realm_name)
|
||||
self.host_name = host_name
|
||||
self.dm_password = dm_password
|
||||
self.__setup_sub_dict()
|
||||
|
||||
if ro_replica:
|
||||
self.start_creation(15, "Configuring directory server:")
|
||||
else:
|
||||
self.start_creation(15, "Configuring directory server:")
|
||||
|
||||
self.start_creation(15, "Configuring directory server:")
|
||||
self.__create_ds_user()
|
||||
self.__create_instance()
|
||||
self.__add_default_schemas()
|
||||
self.__add_memberof_module()
|
||||
if not ro_replica:
|
||||
self.__add_memberof_module()
|
||||
self.__add_referint_module()
|
||||
self.__add_dna_module()
|
||||
self.__create_indeces()
|
||||
@@ -97,10 +158,11 @@ class DsInstance(service.Service):
|
||||
except:
|
||||
# TODO: roll back here?
|
||||
logging.critical("Failed to restart the ds instance")
|
||||
self.__config_uidgid_gen_first_master()
|
||||
self.__add_default_layout()
|
||||
self.__add_master_entry_first_master()
|
||||
self.__init_memberof()
|
||||
if not ro_replica:
|
||||
self.__config_uidgid_gen_first_master()
|
||||
self.__add_master_entry_first_master()
|
||||
self.__init_memberof()
|
||||
|
||||
|
||||
self.step("configuring directoy to start on boot")
|
||||
@@ -108,14 +170,6 @@ class DsInstance(service.Service):
|
||||
|
||||
self.done_creation()
|
||||
|
||||
def config_dirname(self):
|
||||
if not self.serverid:
|
||||
raise RuntimeError("serverid not set")
|
||||
return "/etc/dirsrv/slapd-" + self.serverid + "/"
|
||||
|
||||
def schema_dirname(self):
|
||||
return self.config_dirname() + "/schema/"
|
||||
|
||||
def __setup_sub_dict(self):
|
||||
server_root = find_server_root()
|
||||
self.sub_dict = dict(FQHN=self.host_name, SERVERID=self.serverid,
|
||||
@@ -165,13 +219,13 @@ class DsInstance(service.Service):
|
||||
def __add_default_schemas(self):
|
||||
self.step("adding default schema")
|
||||
shutil.copyfile(SHARE_DIR + "60kerberos.ldif",
|
||||
self.schema_dirname() + "60kerberos.ldif")
|
||||
schema_dirname(self.realm_name) + "60kerberos.ldif")
|
||||
shutil.copyfile(SHARE_DIR + "60samba.ldif",
|
||||
self.schema_dirname() + "60samba.ldif")
|
||||
schema_dirname(self.realm_name) + "60samba.ldif")
|
||||
shutil.copyfile(SHARE_DIR + "60radius.ldif",
|
||||
self.schema_dirname() + "60radius.ldif")
|
||||
schema_dirname(self.realm_name) + "60radius.ldif")
|
||||
shutil.copyfile(SHARE_DIR + "60ipaconfig.ldif",
|
||||
self.schema_dirname() + "60ipaconfig.ldif")
|
||||
schema_dirname(self.realm_name) + "60ipaconfig.ldif")
|
||||
|
||||
def __add_memberof_module(self):
|
||||
self.step("enabling memboerof plugin")
|
||||
@@ -235,7 +289,7 @@ class DsInstance(service.Service):
|
||||
|
||||
def __enable_ssl(self):
|
||||
self.step("configuring ssl for ds instance")
|
||||
dirname = self.config_dirname()
|
||||
dirname = config_dirname(self.realm_name)
|
||||
args = ["/usr/share/ipa/ipa-server-setupssl", self.dm_password,
|
||||
dirname, self.host_name]
|
||||
try:
|
||||
@@ -273,7 +327,7 @@ class DsInstance(service.Service):
|
||||
|
||||
def __certmap_conf(self):
|
||||
self.step("configuring certmap.conf")
|
||||
dirname = self.config_dirname()
|
||||
dirname = config_dirname(self.realm_name)
|
||||
certmap_conf = template_file(SHARE_DIR+"certmap.conf.template", self.sub_dict)
|
||||
certmap_fd = open(dirname+"certmap.conf", "w+")
|
||||
certmap_fd.write(certmap_conf)
|
||||
@@ -281,7 +335,7 @@ class DsInstance(service.Service):
|
||||
|
||||
def change_admin_password(self, password):
|
||||
logging.debug("Changing admin password")
|
||||
dirname = self.config_dirname()
|
||||
dirname = config_dirname(self.realm_name)
|
||||
if dir_exists("/usr/lib64/mozldap"):
|
||||
app = "/usr/lib64/mozldap/ldappasswd"
|
||||
else:
|
||||
|
||||
108
ipa-server/ipaserver/installutils.py
Normal file
108
ipa-server/ipaserver/installutils.py
Normal file
@@ -0,0 +1,108 @@
|
||||
# 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 logging
|
||||
import socket
|
||||
import errno
|
||||
import getpass
|
||||
|
||||
def get_fqdn():
|
||||
fqdn = ""
|
||||
try:
|
||||
fqdn = socket.getfqdn()
|
||||
except:
|
||||
try:
|
||||
fqdn = socket.gethostname()
|
||||
except:
|
||||
fqdn = ""
|
||||
return fqdn
|
||||
|
||||
def verify_fqdn(host_name):
|
||||
if len(host_name.split(".")) < 2 or host_name == "localhost.localdomain":
|
||||
raise RuntimeError("Invalid hostname: " + host_name)
|
||||
|
||||
def port_available(port):
|
||||
"""Try to bind to a port on the wildcard host
|
||||
Return 1 if the port is available
|
||||
Return 0 if the port is in use
|
||||
"""
|
||||
rv = 1
|
||||
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(('', port))
|
||||
s.shutdown(0)
|
||||
s.close()
|
||||
except socket.error, e:
|
||||
if e[0] == errno.EADDRINUSE:
|
||||
rv = 0
|
||||
|
||||
if rv:
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(('', port))
|
||||
s.shutdown(0)
|
||||
s.close()
|
||||
except socket.error, e:
|
||||
if e[0] == errno.EADDRINUSE:
|
||||
rv = 0
|
||||
|
||||
return rv
|
||||
|
||||
def standard_logging_setup(log_filename, debug=False):
|
||||
# Always log everything (i.e., DEBUG) to the log
|
||||
# file.
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format='%(asctime)s %(levelname)s %(message)s',
|
||||
filename=log_filename,
|
||||
filemode='w')
|
||||
|
||||
console = logging.StreamHandler()
|
||||
# If the debug option is set, also log debug messages to the console
|
||||
if debug:
|
||||
console.setLevel(logging.DEBUG)
|
||||
else:
|
||||
# Otherwise, log critical and error messages
|
||||
console.setLevel(logging.ERROR)
|
||||
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
|
||||
console.setFormatter(formatter)
|
||||
logging.getLogger('').addHandler(console)
|
||||
|
||||
def read_password(user):
|
||||
correct = False
|
||||
pwd = ""
|
||||
while not correct:
|
||||
pwd = getpass.getpass(user + " password: ")
|
||||
if not pwd:
|
||||
continue
|
||||
if len(pwd) < 8:
|
||||
print "Password must be at least 8 characters long"
|
||||
continue
|
||||
pwd_confirm = getpass.getpass("Password (confirm): ")
|
||||
if pwd != pwd_confirm:
|
||||
print "Password mismatch!"
|
||||
print ""
|
||||
else:
|
||||
correct = True
|
||||
print ""
|
||||
return pwd
|
||||
|
||||
|
||||
@@ -176,25 +176,90 @@ def wrapper(f,name):
|
||||
return f(*args, **kargs)
|
||||
return inner
|
||||
|
||||
class LDIFConn(ldif.LDIFParser):
|
||||
def __init__(
|
||||
self,
|
||||
input_file,
|
||||
ignored_attr_types=None,max_entries=0,process_url_schemes=None
|
||||
):
|
||||
"""
|
||||
See LDIFParser.__init__()
|
||||
|
||||
Additional Parameters:
|
||||
all_records
|
||||
List instance for storing parsed records
|
||||
"""
|
||||
self.dndict = {} # maps dn to Entry
|
||||
self.dnlist = [] # contains entries in order read
|
||||
myfile = input_file
|
||||
if isinstance(input_file,str) or isinstance(input_file,unicode):
|
||||
myfile = open(input_file, "r")
|
||||
ldif.LDIFParser.__init__(self,myfile,ignored_attr_types,max_entries,process_url_schemes)
|
||||
self.parse()
|
||||
if isinstance(input_file,str) or isinstance(input_file,unicode):
|
||||
myfile.close()
|
||||
|
||||
def handle(self,dn,entry):
|
||||
"""
|
||||
Append single record to dictionary of all records.
|
||||
"""
|
||||
if not dn:
|
||||
dn = ''
|
||||
newentry = Entry((dn, entry))
|
||||
self.dndict[IPAdmin.normalizeDN(dn)] = newentry
|
||||
self.dnlist.append(newentry)
|
||||
|
||||
def get(self,dn):
|
||||
ndn = IPAdmin.normalizeDN(dn)
|
||||
return self.dndict.get(ndn, Entry(None))
|
||||
|
||||
class IPAdmin(SimpleLDAPObject):
|
||||
CFGSUFFIX = "o=NetscapeRoot"
|
||||
DEFAULT_USER_ID = "nobody"
|
||||
|
||||
def getDseAttr(self,attrname):
|
||||
conffile = self.confdir + '/dse.ldif'
|
||||
dseldif = LDIFConn(conffile)
|
||||
cnconfig = dseldif.get("cn=config")
|
||||
if cnconfig:
|
||||
return cnconfig.getValue(attrname)
|
||||
return None
|
||||
|
||||
def __initPart2(self):
|
||||
if self.binddn and len(self.binddn) and not hasattr(self,'sroot'):
|
||||
try:
|
||||
ent = self.getEntry('cn=config', ldap.SCOPE_BASE, '(objectclass=*)',
|
||||
[ 'nsslapd-instancedir', 'nsslapd-errorlog' ])
|
||||
instdir = ent.getValue('nsslapd-instancedir')
|
||||
self.sroot, self.inst = re.match(r'(.*)[\/]slapd-(\w+)$', instdir).groups()
|
||||
[ 'nsslapd-instancedir', 'nsslapd-errorlog',
|
||||
'nsslapd-certdir', 'nsslapd-schemadir' ])
|
||||
self.errlog = ent.getValue('nsslapd-errorlog')
|
||||
except (ldap.INSUFFICIENT_ACCESS, ldap.CONNECT_ERROR,
|
||||
ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND)):
|
||||
self.confdir = None
|
||||
if self.isLocal:
|
||||
self.confdir = ent.getValue('nsslapd-certdir')
|
||||
if not self.confdir or not os.access(self.confdir + '/dse.ldif', os.R_OK):
|
||||
self.confdir = ent.getValue('nsslapd-schemadir')
|
||||
if self.confdir:
|
||||
self.confdir = os.path.dirname(self.confdir)
|
||||
instdir = ent.getValue('nsslapd-instancedir')
|
||||
if not instdir:
|
||||
# get instance name from errorlog
|
||||
self.inst = re.match(r'(.*)[\/]slapd-([\w-]+)/errors', self.errlog).group(2)
|
||||
if self.confdir:
|
||||
instdir = self.getDseAttr('nsslapd-instancedir')
|
||||
else:
|
||||
if self.isLocal:
|
||||
print instdir
|
||||
self.sroot, self.inst = re.match(r'(.*)[\/]slapd-([\w-]+)$', instdir).groups()
|
||||
instdir = re.match(r'(.*/slapd-.*)/errors', self.errlog).group(1)
|
||||
#self.sroot, self.inst = re.match(r'(.*)[\/]slapd-([\w-]+)$', instdir).groups()
|
||||
ent = self.getEntry('cn=config,cn=ldbm database,cn=plugins,cn=config',
|
||||
ldap.SCOPE_BASE, '(objectclass=*)',
|
||||
[ 'nsslapd-directory' ])
|
||||
self.dbdir = os.path.dirname(ent.getValue('nsslapd-directory'))
|
||||
except (ldap.INSUFFICIENT_ACCESS, ldap.CONNECT_ERROR):
|
||||
pass # usually means
|
||||
# print "ignored exception"
|
||||
except ldap.LDAPError, e:
|
||||
print "caught exception ", e
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, None, e)
|
||||
raise
|
||||
|
||||
def __localinit__(self):
|
||||
"""If a CA certificate is provided then it is assumed that we are
|
||||
@@ -209,7 +274,7 @@ class IPAdmin(SimpleLDAPObject):
|
||||
else:
|
||||
SimpleLDAPObject.__init__(self,'ldap://%s:%d' % (self.host,self.port))
|
||||
|
||||
def __init__(self,host,port,cacert,bindcert,bindkey,proxydn=None,debug=None):
|
||||
def __init__(self,host,port=389,cacert=None,bindcert=None,bindkey=None,proxydn=None,debug=None):
|
||||
"""We just set our instance variables and wrap the methods - the real
|
||||
work is done in __localinit__ and __initPart2 - these are separated
|
||||
out this way so that we can call them from places other than
|
||||
@@ -223,7 +288,7 @@ class IPAdmin(SimpleLDAPObject):
|
||||
ldap.set_option(ldap.OPT_X_TLS_KEYFILE,bindkey)
|
||||
|
||||
self.__wrapmethods()
|
||||
self.port = port or 389
|
||||
self.port = port
|
||||
self.host = host
|
||||
self.cacert = cacert
|
||||
self.bindcert = bindcert
|
||||
@@ -272,6 +337,12 @@ class IPAdmin(SimpleLDAPObject):
|
||||
self.principal = principal
|
||||
self.proxydn = None
|
||||
|
||||
def do_simple_bind(self, binddn="cn=directory manager", bindpw=""):
|
||||
self.binddn = binddn
|
||||
self.bindpwd = bindpw
|
||||
self.simple_bind_s(binddn, bindpw)
|
||||
self.__initPart2()
|
||||
|
||||
def getEntry(self,*args):
|
||||
"""This wraps the search function. It is common to just get one entry"""
|
||||
|
||||
@@ -283,8 +354,9 @@ class IPAdmin(SimpleLDAPObject):
|
||||
try:
|
||||
res = self.search(*args)
|
||||
type, obj = self.result(res)
|
||||
|
||||
# res = self.search_ext(args[0], args[1], filterstr=args[2], attrlist=args[3], serverctrls=sctrl)
|
||||
except ldap.NO_SUCH_OBJECT:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND,
|
||||
"no such entry for " + str(args))
|
||||
except ldap.LDAPError, e:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, None, e)
|
||||
|
||||
@@ -538,7 +610,7 @@ class IPAdmin(SimpleLDAPObject):
|
||||
print "Export task %s for file %s completed successfully" % (cn,file)
|
||||
return rc
|
||||
|
||||
def waitForEntry(self, dn, timeout=7200, attr='', quiet=False):
|
||||
def waitForEntry(self, dn, timeout=7200, attr='', quiet=True):
|
||||
scope = ldap.SCOPE_BASE
|
||||
filter = "(objectclass=*)"
|
||||
attrlist = []
|
||||
@@ -560,7 +632,8 @@ class IPAdmin(SimpleLDAPObject):
|
||||
entry = self.getEntry(dn, scope, filter, attrlist)
|
||||
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
||||
pass # found entry, but no attr
|
||||
except ldap.NO_SUCH_OBJECT: pass # no entry yet
|
||||
except ldap.NO_SUCH_OBJECT:
|
||||
pass # no entry yet
|
||||
except ldap.LDAPError, e: # badness
|
||||
print "\nError reading entry", dn, e
|
||||
break
|
||||
@@ -574,7 +647,7 @@ class IPAdmin(SimpleLDAPObject):
|
||||
print "\nwaitForEntry timeout for %s for %s" % (self,dn)
|
||||
elif entry and not quiet:
|
||||
print "\nThe waited for entry is:", entry
|
||||
else:
|
||||
elif not entry:
|
||||
print "\nError: could not read entry %s from %s" % (dn,self)
|
||||
|
||||
return entry
|
||||
|
||||
@@ -32,16 +32,21 @@ import os
|
||||
import pwd
|
||||
import socket
|
||||
import time
|
||||
import shutil
|
||||
|
||||
import service
|
||||
from ipa.ipautil import *
|
||||
from ipa import ipaerror
|
||||
|
||||
import ipaldap
|
||||
|
||||
import ldap
|
||||
from ldap import LDAPError
|
||||
from ldap import ldapobject
|
||||
|
||||
from pyasn1.type import univ
|
||||
from pyasn1.type import univ, namedtype
|
||||
import pyasn1.codec.ber.encoder
|
||||
import pyasn1.codec.ber.decoder
|
||||
import struct
|
||||
import base64
|
||||
|
||||
@@ -88,18 +93,26 @@ class KrbInstance(service.Service):
|
||||
self.kdc_password = None
|
||||
self.sub_dict = None
|
||||
|
||||
def create_instance(self, ds_user, realm_name, host_name, admin_password, master_password):
|
||||
def __common_setup(self, ds_user, realm_name, host_name, admin_password):
|
||||
self.ds_user = ds_user
|
||||
self.fqdn = host_name
|
||||
self.ip = socket.gethostbyname(host_name)
|
||||
self.fqdn = host_name
|
||||
self.realm = realm_name.upper()
|
||||
self.host = host_name.split(".")[0]
|
||||
self.domain = host_to_domain(host_name)
|
||||
self.admin_password = admin_password
|
||||
self.master_password = master_password
|
||||
|
||||
self.ip = socket.gethostbyname(host_name)
|
||||
self.domain = host_to_domain(host_name)
|
||||
self.suffix = realm_to_suffix(self.realm)
|
||||
self.kdc_password = generate_kdc_password()
|
||||
self.admin_password = admin_password
|
||||
|
||||
self.__setup_sub_dict()
|
||||
|
||||
# get a connection to the DS
|
||||
try:
|
||||
self.conn = ipaldap.IPAdmin(self.fqdn)
|
||||
self.conn.do_simple_bind(bindpw=self.admin_password)
|
||||
except ipaerror.exception_for(ipaerror.LDAP_DATABASE_ERROR), e:
|
||||
logging.critical("Could not connect to DS")
|
||||
raise e
|
||||
|
||||
try:
|
||||
self.stop()
|
||||
@@ -107,22 +120,7 @@ class KrbInstance(service.Service):
|
||||
# It could have been not running
|
||||
pass
|
||||
|
||||
self.start_creation(10, "Configuring Kerberos KDC")
|
||||
|
||||
self.__configure_kdc_account_password()
|
||||
|
||||
self.__setup_sub_dict()
|
||||
|
||||
self.__configure_ldap()
|
||||
|
||||
self.__create_instance()
|
||||
|
||||
self.__create_ds_keytab()
|
||||
|
||||
self.__export_kadmin_changepw_keytab()
|
||||
|
||||
self.__add_pwd_extop_module()
|
||||
|
||||
def __common_post_setup(self):
|
||||
try:
|
||||
self.step("starting the KDC")
|
||||
self.start()
|
||||
@@ -138,8 +136,48 @@ class KrbInstance(service.Service):
|
||||
self.step("starting ipa-kpasswd")
|
||||
service.start("ipa-kpasswd")
|
||||
|
||||
|
||||
def create_instance(self, ds_user, realm_name, host_name, admin_password, master_password):
|
||||
self.master_password = master_password
|
||||
|
||||
self.__common_setup(ds_user, realm_name, host_name, admin_password)
|
||||
|
||||
self.start_creation(11, "Configuring Kerberos KDC")
|
||||
|
||||
self.__configure_kdc_account_password()
|
||||
self.__configure_sasl_mappings()
|
||||
self.__add_krb_entries()
|
||||
self.__create_instance()
|
||||
self.__create_ds_keytab()
|
||||
self.__export_kadmin_changepw_keytab()
|
||||
self.__add_pwd_extop_module()
|
||||
|
||||
self.__common_post_setup()
|
||||
|
||||
self.done_creation()
|
||||
|
||||
|
||||
def create_replica(self, ds_user, realm_name, host_name, admin_password, ldap_passwd_filename):
|
||||
|
||||
self.__common_setup(ds_user, realm_name, host_name, admin_password)
|
||||
|
||||
self.start_creation(9, "Configuring Kerberos KDC")
|
||||
self.__copy_ldap_passwd(ldap_passwd_filename)
|
||||
self.__configure_sasl_mappings()
|
||||
self.__write_stash_from_ds()
|
||||
self.__create_instance(replica=True)
|
||||
self.__create_ds_keytab()
|
||||
self.__export_kadmin_changepw_keytab()
|
||||
|
||||
self.__common_post_setup()
|
||||
|
||||
self.done_creation()
|
||||
|
||||
|
||||
def __copy_ldap_passwd(self, filename):
|
||||
shutil.copy(filename, "/var/kerberos/krb5kdc/ldappwd")
|
||||
|
||||
|
||||
def __configure_kdc_account_password(self):
|
||||
self.step("setting KDC account password")
|
||||
hexpwd = ''
|
||||
@@ -158,23 +196,60 @@ class KrbInstance(service.Service):
|
||||
HOST=self.host,
|
||||
REALM=self.realm)
|
||||
|
||||
def __configure_ldap(self):
|
||||
self.step("adding kerberos configuration to the directory")
|
||||
def __configure_sasl_mappings(self):
|
||||
self.step("adding sasl mappings to the directory")
|
||||
# we need to remove any existing SASL mappings in the directory as otherwise they
|
||||
# they may conflict. There is no way to define the order they are used in atm.
|
||||
try:
|
||||
lo = ldapobject.SimpleLDAPObject("ldap://127.0.0.1/")
|
||||
lo.bind("cn=Directory Manager", self.admin_password)
|
||||
msgid = lo.search("cn=mapping,cn=sasl,cn=config", ldap.SCOPE_ONELEVEL, "(objectclass=nsSaslMapping)")
|
||||
res = lo.result(msgid)
|
||||
for r in res[1]:
|
||||
mid = lo.delete(r[0])
|
||||
delres = lo.result(mid)
|
||||
lo.unbind()
|
||||
except LDAPError, e:
|
||||
logging.critical("Error during SASL mapping removal: %s" % str(e))
|
||||
|
||||
#TODO: test that the ldif is ok with any random charcter we may use in the password
|
||||
# FIXME: for some reason IPAdmin dies here, so we switch
|
||||
# it out for a regular ldapobject.
|
||||
conn = self.conn
|
||||
self.conn = ldapobject.SimpleLDAPObject("ldap://127.0.0.1/")
|
||||
self.conn.bind("cn=directory manager", self.admin_password)
|
||||
try:
|
||||
msgid = self.conn.search("cn=mapping,cn=sasl,cn=config", ldap.SCOPE_ONELEVEL, "(objectclass=nsSaslMapping)")
|
||||
res = self.conn.result(msgid)
|
||||
for r in res[1]:
|
||||
mid = self.conn.delete_s(r[0])
|
||||
#except LDAPError, e:
|
||||
# logging.critical("Error during SASL mapping removal: %s" % str(e))
|
||||
except Exception, e:
|
||||
print type(e)
|
||||
print dir(e)
|
||||
raise e
|
||||
|
||||
self.conn = conn
|
||||
|
||||
entry = ipaldap.Entry("cn=Full Principal,cn=mapping,cn=sasl,cn=config")
|
||||
entry.setValues("objectclass", "top", "nsSaslMapping")
|
||||
entry.setValues("cn", "Full Principal")
|
||||
entry.setValues("nsSaslMapRegexString", '\(.*\)@\(.*\)')
|
||||
entry.setValues("nsSaslMapBaseDNTemplate", self.suffix)
|
||||
entry.setValues("nsSaslMapFilterTemplate", '(krbPrincipalName=\\1@\\2)')
|
||||
|
||||
try:
|
||||
self.conn.add_s(entry)
|
||||
except ldap.ALREADY_EXISTS:
|
||||
logging.critical("failed to add Full Principal Sasl mapping")
|
||||
raise e
|
||||
|
||||
entry = ipaldap.Entry("cn=Name Only,cn=mapping,cn=sasl,cn=config")
|
||||
entry.setValues("objectclass", "top", "nsSaslMapping")
|
||||
entry.setValues("cn", "Name Only")
|
||||
entry.setValues("nsSaslMapRegexString", '\(.*\)')
|
||||
entry.setValues("nsSaslMapBaseDNTemplate", self.suffix)
|
||||
entry.setValues("nsSaslMapFilterTemplate", '(krbPrincipalName=\\1@%s)' % self.realm)
|
||||
|
||||
try:
|
||||
self.conn.add_s(entry)
|
||||
except ldap.ALREADY_EXISTS:
|
||||
logging.critical("failed to add Name Only Sasl mapping")
|
||||
raise e
|
||||
|
||||
def __add_krb_entries(self):
|
||||
self.step("adding kerberos entries to the DS")
|
||||
|
||||
#TODO: test that the ldif is ok with any random charcter we may use in the password
|
||||
kerberos_txt = template_file(SHARE_DIR + "kerberos.ldif", self.sub_dict)
|
||||
kerberos_fd = write_tmp_file(kerberos_txt)
|
||||
try:
|
||||
@@ -192,7 +267,7 @@ class KrbInstance(service.Service):
|
||||
logging.critical("Failed to load default-aci.ldif: %s" % str(e))
|
||||
aci_fd.close()
|
||||
|
||||
def __create_instance(self):
|
||||
def __create_instance(self, replica=False):
|
||||
self.step("configuring KDC")
|
||||
kdc_conf = template_file(SHARE_DIR+"kdc.conf.template", self.sub_dict)
|
||||
kdc_fd = open("/var/kerberos/krb5kdc/kdc.conf", "w+")
|
||||
@@ -220,12 +295,34 @@ class KrbInstance(service.Service):
|
||||
krb_fd.write(krb_realm)
|
||||
krb_fd.close()
|
||||
|
||||
#populate the directory with the realm structure
|
||||
args = ["/usr/kerberos/sbin/kdb5_ldap_util", "-D", "uid=kdc,cn=sysaccounts,cn=etc,"+self.suffix, "-w", self.kdc_password, "create", "-s", "-P", self.master_password, "-r", self.realm, "-subtrees", self.suffix, "-sscope", "sub"]
|
||||
if not replica:
|
||||
#populate the directory with the realm structure
|
||||
args = ["/usr/kerberos/sbin/kdb5_ldap_util", "-D", "uid=kdc,cn=sysaccounts,cn=etc,"+self.suffix, "-w", self.kdc_password, "create", "-s", "-P", self.master_password, "-r", self.realm, "-subtrees", self.suffix, "-sscope", "sub"]
|
||||
try:
|
||||
run(args)
|
||||
except subprocess.CalledProcessError, e:
|
||||
print "Failed to populate the realm structure in kerberos", e
|
||||
|
||||
def __write_stash_from_ds(self):
|
||||
self.step("writing stash file from DS")
|
||||
try:
|
||||
run(args)
|
||||
except subprocess.CalledProcessError, e:
|
||||
print "Failed to populate the realm structure in kerberos", e
|
||||
entry = self.conn.getEntry("cn=%s, cn=kerberos, %s" % (self.realm, self.suffix), ldap.SCOPE_SUBTREE)
|
||||
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND), e:
|
||||
logging.critical("Could not find master key in DS")
|
||||
raise e
|
||||
|
||||
krbMKey = pyasn1.codec.ber.decoder.decode(entry.krbmkey)
|
||||
keytype = int(krbMKey[0][1][0])
|
||||
keydata = str(krbMKey[0][1][1])
|
||||
|
||||
format = '=hi%ss' % len(keydata)
|
||||
s = struct.pack(format, keytype, len(keydata), keydata)
|
||||
try:
|
||||
fd = open("/var/kerberos/krb5kdc/.k5."+self.realm, "w")
|
||||
fd.write(s)
|
||||
except os.error, e:
|
||||
logging.critical("failed to write stash file")
|
||||
raise e
|
||||
|
||||
#add the password extop module
|
||||
def __add_pwd_extop_module(self):
|
||||
@@ -255,18 +352,14 @@ class KrbInstance(service.Service):
|
||||
krbMKey.setComponentByPosition(1, MasterKey)
|
||||
asn1key = pyasn1.codec.ber.encoder.encode(krbMKey)
|
||||
|
||||
#put the attribute in the Directory
|
||||
mod_txt = "dn: cn="+self.realm+",cn=kerberos,"+self.suffix+"\n"
|
||||
mod_txt += "changetype: modify\n"
|
||||
mod_txt += "add: krbMKey\n"
|
||||
mod_txt += "krbMKey:: "+base64.encodestring(asn1key)+"\n"
|
||||
mod_txt += "\n"
|
||||
mod_fd = write_tmp_file(mod_txt)
|
||||
entry = ipaldap.Entry("cn="+self.realm+",cn=kerberos,"+self.suffix)
|
||||
dn = "cn="+self.realm+",cn=kerberos,"+self.suffix
|
||||
mod = [(ldap.MOD_ADD, 'krbMKey', str(asn1key))]
|
||||
try:
|
||||
ldap_mod(mod_fd, "cn=Directory Manager", self.admin_password)
|
||||
except subprocess.CalledProcessError, e:
|
||||
logging.critical("Failed to load Master Key: %s" % str(e))
|
||||
mod_fd.close()
|
||||
self.conn.modify_s(dn, mod)
|
||||
except ldap.TYPE_OR_VALUE_EXISTS, e:
|
||||
logging.critical("failed to add master key to kerberos database\n")
|
||||
raise e
|
||||
|
||||
def __create_ds_keytab(self):
|
||||
self.step("creating a keytab for the directory")
|
||||
|
||||
@@ -25,6 +25,7 @@ import shutil
|
||||
import logging
|
||||
import pwd
|
||||
import time
|
||||
import sys
|
||||
from ipa.ipautil import *
|
||||
|
||||
import service
|
||||
@@ -149,7 +150,7 @@ class RadiusInstance(service.Service):
|
||||
retry += 1
|
||||
if retry > 15:
|
||||
print "Error timed out waiting for kadmin to finish operations\n"
|
||||
os.exit()
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
pent = pwd.getpwnam(RADIUS_USER)
|
||||
|
||||
Reference in New Issue
Block a user