On Fri, Aug 26, 2011 at 09:14:27AM -0400, Simo Sorce wrote: > More comments. > > On Fri, 2011-08-26 at 11:39 +0200, Sumit Bose wrote: > > [..] > > > + if not options.unattended: > > + print "" > > + print "The following operations may take some minutes to > > complete." > > + print "Please wait until the prompt is returned." > > + print "" > > + > > + # Create a BIND instance > > comment seem to be wrong here :)
fixed > > > + if options.unattended and not options.dm_password: > > + sys.exit("\nIn unattended mode you need to provide at least > > the -p option") > > + > > + dm_password = options.dm_password or read_password("Directory > > Manager", > > + confirm=False, > > validate=False) > > + smb = smbinstance.SMBInstance(fstore, dm_password) > > [..] > > > diff --git a/ipaserver/install/service.py > > b/ipaserver/install/service.py > > index > > a7f6ff4eea1b67f714e18f882a082d4ad7d83026..7e0d2bd314f00ccf0b0ee37a9d572bdd5ee89414 > > 100644 > > --- a/ipaserver/install/service.py > > +++ b/ipaserver/install/service.py > > @@ -37,7 +37,8 @@ SERVICE_LIST = { > > 'KPASSWD':('kadmin', 20), > > 'DNS':('named', 30), > > 'HTTP':('httpd', 40), > > - 'CA':('pki-cad', 50) > > + 'CA':('pki-cad', 50), > > + 'SMB':('smb', 60) > > Please do not use SMB (in general I do not think SMB is the right > prefix). Use something like ADTRUST or MSRPC or WINCOMPAT. > changed to ADTRUST > > } > > > > def stop(service_name, instance_name="", capture_output=True): > > diff --git a/ipaserver/install/smbinstance.py > > b/ipaserver/install/smbinstance.py > > new file mode 100644 > > index > > 0000000000000000000000000000000000000000..5988f1e056d29af6686d53237b82d460cdc719da > > --- /dev/null > > +++ b/ipaserver/install/smbinstance.py > > @@ -0,0 +1,261 @@ > > +# Authors: Sumit Bose <sb...@redhat.com> > > +# > > +# Copyright (C) 2011 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, either version 3 of the License, or > > +# (at your option) any later version. > > +# > > +# 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, see > > <http://www.gnu.org/licenses/>. > > +# > > + > > +import logging > > + > > +import os > > +import ldap > > +import service > > +import tempfile > > +from ipaserver import ipaldap > > +from ipalib import errors > > +from ipapython import sysrestore > > +from ipapython import ipautil > > + > > +import random > > +import string > > +import struct > > + > > +def check_inst(unattended): > > + has_smb = True > > + > > + if not os.path.exists('/usr/sbin/smbd'): > > + print "Samba was not found on this system" > > + print "Please install the 'samba' package and start the > > installation again" > > + has_smb = False > > + > > + #TODO: Add check for needed samba4 libraries > > + > > + return has_smb > > + > > +def ipa_smb_conf_exists(): > > + if os.path.exists('/etc/ipa/smb.conf'): > > + print "Samba is already configured for this IPA server." > > + return True > > + > > + return False > > + > > +def random_password(length=16): > > + myrg = random.SystemRandom() > > + alphabet = string.letters[0:52] + string.digits + > > string.punctuation > > + pw = str().join(myrg.choice(alphabet) for _ in range(length)) > > + return pw > > We have a utility function to generate a proper random password IIRC. fixed > > > +class SMBInstance(service.Service): > > + def __init__(self, fstore=None, dm_password=None): > > + service.Service.__init__(self, "smb", > > dm_password=dm_password) > > + > > + if fstore: > > + self.fstore = fstore > > + else: > > + self.fstore = > > sysrestore.FileStore('/var/lib/ipa/sysrestore') > > + > > + def __create_samba_user(self): > > + print "The user for Samba is %s" % self.smb_dn > > + try: > > + self.admin_conn.getEntry(self.smb_dn, ldap.SCOPE_BASE) > > + print "Samba user entry exists, not resetting password" > > + return > > + except errors.NotFound: > > + pass > > + > > + # The user doesn't exist, add it > > + entry = ipaldap.Entry(self.smb_dn) > > + entry.setValues("objectclass", ["account", > > "simplesecurityobject"]) > > + entry.setValues("uid", "samba") > > + entry.setValues("userPassword", self.smb_dn_pwd) > > + self.admin_conn.add_s(entry) > > + > > + # And finally grant it permission to read NT passwords, we do > > not want > > + # to support LM passwords so there is no need to allow access > > to them > > + mod = [(ldap.MOD_ADD, 'aci', > > + str(['(targetattr = "sambaNTPassword")(version 3.0; acl > > "Samba user can read NT passwords"; allow (read) userdn="ldap:///% > > s";)' % self.smb_dn]))] > > + try: > > + self.admin_conn.modify_s(self.suffix, mod) > > + except ldap.TYPE_OR_VALUE_EXISTS: > > + logging.debug("samba user aci already exists in suffix %s > > on %s" % (self.suffix, self.admin_conn.host)) > > + > > + def __gen_sid_string(self): > > + sub_ids = struct.unpack("<LLL", os.urandom(12)) > > + return "S-1-5-21-%d-%d-%d" % (sub_ids[0], sub_ids[1], > > sub_ids[2]) > > + > > + def __create_samba_domain_object(self): > > + trust_dn = "cn=trusts,%s" % self.suffix > > + smb_dom_dn = "cn=ad,%s" % trust_dn > > + > > + try: > > + self.admin_conn.getEntry(smb_dom_dn, ldap.SCOPE_BASE) > > + print "Samba domain object already exists" > > + return > > + except errors.NotFound: > > + pass > > + > > + try: > > + self.admin_conn.getEntry(trust_dn, ldap.SCOPE_BASE) > > + except errors.NotFound: > > + entry = ipaldap.Entry(trust_dn) > > + entry.setValues("objectclass", ["nsContainer"]) > > + entry.setValues("cn", "trusts") > > + self.admin_conn.add_s(entry) > > + > > + entry = ipaldap.Entry(smb_dom_dn) > > + entry.setValues("objectclass", ["sambaDomain", > > "nsContainer"]) > > + entry.setValues("cn", "ad") > > + entry.setValues("sambaDomainName", self.domain_name) > > The sambaDomainName is generally a netbios name (or short name), it > appears you are setting the DNS domain name here. > Should we prompt the user for a short domain name to be used instead ? > Or maybe default to the first DNS domain component and allow the user to > override ? I added a --netbios-name option, for unattended installations a sanitized version of the first DNS domain component is taken. Currently I only allow upper case letters and numbers. This might be a bit too strict, but I haven't found a good list of safe characters for NetBIOS names. Please tell me if I should add more characters. > > > + entry.setValues("sambaSID", self.__gen_sid_string()) > > + #TODO: which MAY attributes do we want to set ? > > + self.admin_conn.add_s(entry) > > + > > + def __write_sysconfig_samba(self): > > + self.fstore.backup_file(self.sysconfig_file) > > + > > + fd = open(self.sysconfig_file, "w") > > + fd.write('### Added by IPA Installer ###\n') > > + fd.write('# Options to smbd\n') > > + fd.write('SMBDOPTIONS="-D -s /etc/ipa/smb.conf"\n') > > + fd.write('# Options to nmbd\n') > > + fd.write('NMBDOPTIONS="-D"\n') > > + fd.write('# Options for winbindd\n') > > + fd.write('WINBINDOPTIONS=""\n') > > + fd.close() > > If we are running nmbd and/or winbindd we need to pass them the proper > smb.conf file too. > > > > + def __write_smb_conf(self): > > + fd = open(self.smb_conf, "w") > > + fd.write('### Added by IPA Installer ###\n') > > + fd.write('[global]\n') > > + fd.write('config backend = registry\n') > > + fd.close() > > + > > + > > + def __write_smb_registry(self): > > + [fd, tmp_name] = tempfile.mkstemp() > > + > > + os.write(fd, '[global]\n') > > + os.write(fd, 'workgroup = %s\n' % self.domain_name) > > This again shuld be the netbios domain name, not the fqdn. > The workgroup name can't be longer than 15 chars IIRC. fixed > > > + os.write(fd, 'realm = %s\n' % self.realm_name) > > + os.write(fd, 'security = ads\n') > > Why ADS ? This should probably be security = user fixed > > > + os.write(fd, 'domain master = yes\n') > > + os.write(fd, 'domain logons = yes\n') > > + os.write(fd, 'passdb backend = IPA_ldapsam:ldap://%s\n' % > > self.fqdn) > > We should use ldapi here, not ldap:// fixed > > > + os.write(fd, 'ldapsam:trusted=yes\n') > > + os.write(fd, 'ldapsam:editposix=yes\n') > > We do not allow to create users so editposix should not be necessary. fixed > > > + os.write(fd, 'ldap ssl = startTLS\n') > > We shouldn't need SSL if we use ldapi above. fixed > > > + os.write(fd, 'ldap admin dn = %s\n' % self.smb_dn) > > + os.write(fd, 'ldap suffix = cn=accounts,dc=ipa,dc=test\n') > > + os.write(fd, 'ldap user suffix = cn=users\n') > > + os.write(fd, 'ldap group suffix = cn=groups\n') > > + os.write(fd, 'ldap machine suffix = cn=computers\n') > > + os.write(fd, 'ldap idmap suffix = cn=idmap\n') > > We probably won't use this. fixed > > > + os.write(fd, 'rpc_server:epmapper = external\n') > > + os.write(fd, 'rpc_server:lsarpc = external\n') > > We may need also the alias 'lsass' pipe name configured here. fixed > > > + os.write(fd, 'rpc_server:samr = external\n') > > + os.write(fd, 'rpc_server:netlogon = external\n') > > + os.write(fd, 'rpc_daemon:epmd = fork\n') > > + os.write(fd, 'rpc_daemon:lsasd = fork\n') > > + os.close(fd) > > We may also want to set some defaults for logging > (log level, max size of logs before rotating, log name format ?) > fixed > > + args = ["/usr/bin/net", "conf", "import", tmp_name] > > + > > + try: > > + ipautil.run(args) > > + finally: > > + os.remove(tmp_name) > > + > > + def __set_smb_ldap_password(self): > > + args = ["/usr/bin/smbpasswd", "-c", self.smb_conf, "-w", > > self.smb_dn_pwd ] > > We should either pass this password in via stdin (using -W) or you > should at least pass ipautil.run below the password to be blacked out of > install logs. fixed > > > + ipautil.run(args) > > + > > [..] > > Do we want to run winbindd at all on ipa servers ? > Should we join it to ourselves ? (Would require creation of a computer > account). I don't think that we should run winbind. I also changed the path to the smb.conf file from /etc/ipa to /etc/samba which makes the change to /etc/sysconfig/samba unnecessary. Thanks for review. bye, Sumit > > Simo. > > -- > Simo Sorce * Red Hat, Inc * New York > > _______________________________________________ > Freeipa-devel mailing list > Freeipa-devel@redhat.com > https://www.redhat.com/mailman/listinfo/freeipa-devel
From 7929cb97ad2f7df9506ee97dbfd1213f4273c66c Mon Sep 17 00:00:00 2001 From: sbose <sb...@ipa-devel.ipa.devel> Date: Wed, 24 Aug 2011 11:18:56 +0200 Subject: [PATCH] Add ipa-adtrust-install utility https://fedorahosted.org/freeipa/ticket/1619 --- freeipa.spec.in | 2 + install/po/Makefile.in | 1 + install/tools/Makefile.am | 1 + install/tools/ipa-adtrust-install | 213 ++++++++++++++++++ install/tools/man/Makefile.am | 1 + install/tools/man/ipa-adtrust-install.1 | 44 ++++ ipaserver/install/Makefile.am | 1 + ipaserver/install/service.py | 3 +- ipaserver/install/smbinstance.py | 259 ++++++++++++++++++++++ tests/test_ipaserver/install/test_smbinstance.py | 59 +++++ 10 files changed, 583 insertions(+), 1 deletions(-) create mode 100755 install/tools/ipa-adtrust-install create mode 100644 install/tools/man/ipa-adtrust-install.1 create mode 100644 ipaserver/install/smbinstance.py create mode 100755 tests/test_ipaserver/install/test_smbinstance.py diff --git a/freeipa.spec.in b/freeipa.spec.in index 1bf52b952177598bd24afc0560802f1883fa5a60..a67e9329f8230c05b0e9feeb321fb07e4f41a350 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -370,6 +370,7 @@ fi %doc COPYING README Contributors.txt %{_sbindir}/ipa-ca-install %{_sbindir}/ipa-dns-install +%{_sbindir}/ipa-adtrust-install %{_sbindir}/ipa-server-install %{_sbindir}/ipa-replica-conncheck %{_sbindir}/ipa-replica-install @@ -448,6 +449,7 @@ fi %{_mandir}/man1/ipa-server-certinstall.1.gz %{_mandir}/man1/ipa-server-install.1.gz %{_mandir}/man1/ipa-dns-install.1.gz +%{_mandir}/man1/ipa-adtrust-install.1.gz %{_mandir}/man1/ipa-ca-install.1.gz %{_mandir}/man1/ipa-compat-manage.1.gz %{_mandir}/man1/ipa-nis-manage.1.gz diff --git a/install/po/Makefile.in b/install/po/Makefile.in index a5468752723636b005c1d0876f10326e5c970814..d20a5d9628cb119f7704fcdb2917f1c897194147 100644 --- a/install/po/Makefile.in +++ b/install/po/Makefile.in @@ -54,6 +54,7 @@ PY_EXPLICIT_FILES = \ install/tools/ipa-server-install \ install/tools/ipa-ldap-updater \ install/tools/ipa-dns-install \ + install/tools/ipa-adtrust-install \ install/tools/ipa-ca-install \ ipa-client/ipa-install/ipa-client-install diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am index fc615ec04f324c2d9c98dc8cf674938e1064bec6..96da7531764598878f94b6abd54c27a74671c028 100644 --- a/install/tools/Makefile.am +++ b/install/tools/Makefile.am @@ -8,6 +8,7 @@ sbin_SCRIPTS = \ ipa-ca-install \ ipa-dns-install \ ipa-server-install \ + ipa-adtrust-install \ ipa-replica-conncheck \ ipa-replica-install \ ipa-replica-prepare \ diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install new file mode 100755 index 0000000000000000000000000000000000000000..883f9d608f6f4f62788009842b1fbd8f8cbe72d4 --- /dev/null +++ b/install/tools/ipa-adtrust-install @@ -0,0 +1,213 @@ +#! /usr/bin/python +# +# Authors: Sumit Bose <sb...@redhat.com> +# Based on ipa-server-install by Karl MacMillan <kmacmil...@mentalrootkit.com> +# and ipa-dns-install by Martin Nagy +# +# Copyright (C) 2011 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, either version 3 of the License, or +# (at your option) any later version. +# +# 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, see <http://www.gnu.org/licenses/>. +# + +import traceback + +from ipaserver.plugins.ldap2 import ldap2 +from ipaserver.install import smbinstance +from ipaserver.install.installutils import * +from ipaserver.install import installutils +from ipapython import version +from ipapython import ipautil, sysrestore +from ipalib import api, errors, util +from ipapython.config import IPAOptionParser +import krbV +import ldap + +def parse_options(): + parser = IPAOptionParser(version=version.VERSION) + parser.add_option("-p", "--ds-password", dest="dm_password", + sensitive=True, help="directory manager password") + parser.add_option("-d", "--debug", dest="debug", action="store_true", + default=False, help="print debugging information") + parser.add_option("--ip-address", dest="ip_address", + type="ip", ip_local=True, help="Master Server IP Address") + parser.add_option("--netbios-name", dest="netbios_name", + help="NetBIOS name of the IPA domain") + parser.add_option("-U", "--unattended", dest="unattended", action="store_true", + default=False, help="unattended installation never prompts the user") + + options, args = parser.parse_args() + safe_options = parser.get_safe_opts(options) + + return safe_options, options + +def main(): + safe_options, options = parse_options() + + if os.getegid() != 0: + sys.exit("Must be root to setup AD trusts on server") + + installutils.check_server_configuration() + + standard_logging_setup("/var/log/ipaserver-install.log", options.debug, filemode='a') + print "\nThe log file for this installation can be found in /var/log/ipaserver-install.log" + + logging.debug('%s was invoked with options: %s' % (sys.argv[0], safe_options)) + logging.debug("missing options might be asked for interactively later\n") + + global fstore + fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore') + + print "==============================================================================" + print "This program will setup components needed to establish trust to AD domains for" + print "the FreeIPA Server." + print "" + print "This includes:" + print " * Configure Samba" + print " * Add trust related objects to FreeIPA LDAP server" + #TODO: + #print " * Add a SID to all users and Posix groups" + print "" + print "To accept the default shown in brackets, press the Enter key." + print "" + + # Check if samba packages are installed + if not smbinstance.check_inst(options.unattended): + sys.exit("Aborting installation.") + + # Initialize the ipalib api + cfg = dict( + in_server=True, + debug=options.debug, + ) + api.bootstrap(**cfg) + api.finalize() + + if smbinstance.ipa_smb_conf_exists(): + sys.exit("Aborting installation.") + + # Check we have a public IP that is associated with the hostname + try: + if options.ip_address: + ip = ipautil.CheckedIPAddress(options.ip_address, match_local=True) + else: + hostaddr = resolve_host(api.env.host) + ip = hostaddr and ipautil.CheckedIPAddress(hostaddr, match_local=True) + except Exception, e: + print "Error: Invalid IP Address %s: %s" % (ip, e) + ip = None + + if not ip: + if options.unattended: + sys.exit("Unable to resolve IP address for host name") + else: + read_ip = read_ip_address(api.env.host, fstore) + try: + ip = ipautil.CheckedIPAddress(read_ip, match_local=True) + except Exception, e: + print "Error: Invalid IP Address %s: %s" % (ip, e) + sys.exit("Aborting installation.") + + ip_address = str(ip) + logging.debug("will use ip_address: %s\n", ip_address) + + if not options.unattended: + print "" + print "The following operations may take some minutes to complete." + print "Please wait until the prompt is returned." + print "" + + # Create a Samba instance + if options.unattended and not options.dm_password: + sys.exit("\nIn unattended mode you need to provide at least the -p option") + + netbios_name = options.netbios_name + if options.unattended and not netbios_name: + netbios_name = smbinstance.make_netbios_name(api.env.domain) + + if not smbinstance.check_netbios_name(netbios_name): + print "Illegal NetBIOS name [%s].\n" % netbios_name + print "Up to 15 characters and only uppercase ASCII letter and digits are allowed." + sys.exit("Aborting installation.") + + dm_password = options.dm_password or read_password("Directory Manager", + confirm=False, validate=False) + smb = smbinstance.SMBInstance(fstore, dm_password) + + # try the connection + try: + smb.ldap_connect() + smb.ldap_disconnect() + except ldap.INVALID_CREDENTIALS, e: + sys.exit("Password is not valid!") + + if smb.dm_password: + api.Backend.ldap2.connect(bind_dn="cn=Directory Manager", bind_pw=smb.dm_password) + else: + # See if our LDAP server is up and we can talk to it over GSSAPI + ccache = krbV.default_context().default_ccache().name + api.Backend.ldap2.connect(ccache) + + smb.setup(api.env.host, ip_address, api.env.realm, api.env.domain, + netbios_name) + smb.create_instance() + + print "==============================================================================" + print "Setup complete" + print "" + print "\tYou must make sure these network ports are open:" + print "\t\tTCP Ports:" + print "\t\t * 138: netbios-dgm" + print "\t\t * 139: netbios-ssn" + print "\t\t * 445: microsoft-ds" + print "\t\tUDP Ports:" + print "\t\t * 138: netbios-dgm" + print "\t\t * 139: netbios-ssn" + print "\t\t * 445: microsoft-ds" + print "" + print "\tAdditionally you have to make sure the FreeIPA LDAP server cannot reached" + print "\tby any domain controller in the Active Directory domain by closing the" + print "\tfollowing ports for these servers:" + print "\t\tTCP Ports:" + print "\t\t * 389, 636: LDAP/LDAPS" + print "\t\tUDP Ports:" + print "\t\t * 389: (C)LDAP" + print "\tYou may want to choose to REJECT the network packets instead of DROPing them" + print "\tto avoid timeouts on the AD domain controllers." + + return 0 + +try: + sys.exit(main()) +except SystemExit, e: + sys.exit(e) +except KeyboardInterrupt: + print "Installation cancelled." +except RuntimeError, e: + print str(e) +except HostnameLocalhost: + 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" +except Exception, e: + message = "Unexpected error - see ipaserver-install.log for details:\n %s" % str(e) + print message + message = str(e) + for str in traceback.format_tb(sys.exc_info()[2]): + message = message + "\n" + str + logging.debug(message) + sys.exit(1) diff --git a/install/tools/man/Makefile.am b/install/tools/man/Makefile.am index 71d9b29c87d2b24c51d3048dc1050e099a89835d..d5b5976b0fd8c8e6683d09e7ade575fda2527832 100644 --- a/install/tools/man/Makefile.am +++ b/install/tools/man/Makefile.am @@ -13,6 +13,7 @@ man1_MANS = \ ipa-server-certinstall.1 \ ipa-server-install.1 \ ipa-dns-install.1 \ + ipa-adtrust-install.1 \ ipa-ca-install.1 \ ipa-ldap-updater.1 \ ipa-compat-manage.1 \ diff --git a/install/tools/man/ipa-adtrust-install.1 b/install/tools/man/ipa-adtrust-install.1 new file mode 100644 index 0000000000000000000000000000000000000000..9e976d83bcd16abfca4e8eedfccf23a908c43400 --- /dev/null +++ b/install/tools/man/ipa-adtrust-install.1 @@ -0,0 +1,44 @@ +.\" A man page for ipa-adtrust-install +.\" Copyright (C) 2011 Red Hat, Inc. +.\" +.\" 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, either version 3 of the License, or +.\" (at your option) any later version. +.\" +.\" 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, see <http://www.gnu.org/licenses/>. +.\" +.\" Author: Sumit Bose <sb...@redhat.com> +.\" +.TH "ipa-adtrust-install" "1" "Aug 23, 2011" "freeipa" "" +.SH "NAME" +ipa\-adtrust\-install \- Prepare an IPA server to be able to establish trust relationships with AD domains +.SH "SYNOPSIS" +ipa\-adtrust\-install [\fIOPTION\fR]... +.SH "DESCRIPTION" +Adds all necesary objects and configuration to allow an IPA server to create a +trust to an Active Directory domain. This requires that the IPA server is +already installed and configured. +.SH "OPTIONS" +.TP +\fB\-p\fR \fIDM_PASSWORD\fR, \fB\-\-ds\-password\fR=\fIDM_PASSWORD\fR +The password to be used by the Directory Server for the Directory Manager user +.TP +\fB\-d\fR, \fB\-\-debug\fR +Enable debug logging when more verbose output is needed +.TP +\fB\-\-ip\-address\fR=\fIIP_ADDRESS\fR +The IP address of the IPA server. If not provided then this is determined based on the hostname of the server. +.TP +\fB\-U\fR, \fB\-\-unattended\fR +An unattended installation that will never prompt for user input +.SH "EXIT STATUS" +0 if the installation was successful + +1 if an error occurred diff --git a/ipaserver/install/Makefile.am b/ipaserver/install/Makefile.am index 8932eadbb7ace71372277259a557884d989ea2c1..398551bd78aa4ba893a3953f0c7ee7bcb23d1a14 100644 --- a/ipaserver/install/Makefile.am +++ b/ipaserver/install/Makefile.am @@ -10,6 +10,7 @@ app_PYTHON = \ krbinstance.py \ httpinstance.py \ ntpinstance.py \ + smbinstance.py \ service.py \ installutils.py \ replication.py \ diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py index a7f6ff4eea1b67f714e18f882a082d4ad7d83026..dcce0b1e86071458d353c59e40d56460ff48837c 100644 --- a/ipaserver/install/service.py +++ b/ipaserver/install/service.py @@ -37,7 +37,8 @@ SERVICE_LIST = { 'KPASSWD':('kadmin', 20), 'DNS':('named', 30), 'HTTP':('httpd', 40), - 'CA':('pki-cad', 50) + 'CA':('pki-cad', 50), + 'ADTRUST':('smb', 60) } def stop(service_name, instance_name="", capture_output=True): diff --git a/ipaserver/install/smbinstance.py b/ipaserver/install/smbinstance.py new file mode 100644 index 0000000000000000000000000000000000000000..31b6136ec45dac92c45f40a65e202a6ea90757ae --- /dev/null +++ b/ipaserver/install/smbinstance.py @@ -0,0 +1,259 @@ +# Authors: Sumit Bose <sb...@redhat.com> +# +# Copyright (C) 2011 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, either version 3 of the License, or +# (at your option) any later version. +# +# 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, see <http://www.gnu.org/licenses/>. +# + +import logging + +import os +import ldap +import service +import tempfile +from ipaserver import ipaldap +from ipalib import errors +from ipapython import sysrestore +from ipapython import ipautil + +import random +import string +import struct + +allowed_netbios_chars = string.ascii_uppercase + string.digits + +def check_inst(unattended): + has_smb = True + + if not os.path.exists('/usr/sbin/smbd'): + print "Samba was not found on this system" + print "Please install the 'samba' package and start the installation again" + has_smb = False + + #TODO: Add check for needed samba4 libraries + + return has_smb + +def ipa_smb_conf_exists(): + fd = open('/etc/samba/smb.conf', 'r') + lines = fd.readlines() + fd.close() + for line in lines: + if line.startswith('### Added by IPA Installer ###'): + return True + return False + + +def check_netbios_name(s): + # NetBIOS names may not be longer than 15 allowed characters + if not s or len(s) > 15 or ''.join([c for c in s if c not in allowed_netbios_chars]): + return False + + return True + +def make_netbios_name(s): + return ''.join([c for c in s.split('.')[0].upper() if c in allowed_netbios_chars])[:15] + +class SMBInstance(service.Service): + def __init__(self, fstore=None, dm_password=None): + service.Service.__init__(self, "smb", dm_password=dm_password) + + if fstore: + self.fstore = fstore + else: + self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore') + + def __create_samba_user(self): + print "The user for Samba is %s" % self.smb_dn + try: + self.admin_conn.getEntry(self.smb_dn, ldap.SCOPE_BASE) + print "Samba user entry exists, not resetting password" + return + except errors.NotFound: + pass + + # The user doesn't exist, add it + entry = ipaldap.Entry(self.smb_dn) + entry.setValues("objectclass", ["account", "simplesecurityobject"]) + entry.setValues("uid", "samba") + entry.setValues("userPassword", self.smb_dn_pwd) + self.admin_conn.add_s(entry) + + # And finally grant it permission to read NT passwords, we do not want + # to support LM passwords so there is no need to allow access to them + mod = [(ldap.MOD_ADD, 'aci', + str(['(targetattr = "sambaNTPassword")(version 3.0; acl "Samba user can read NT passwords"; allow (read) userdn="ldap:///%s";)' % self.smb_dn]))] + try: + self.admin_conn.modify_s(self.suffix, mod) + except ldap.TYPE_OR_VALUE_EXISTS: + logging.debug("samba user aci already exists in suffix %s on %s" % (self.suffix, self.admin_conn.host)) + + def __gen_sid_string(self): + sub_ids = struct.unpack("<LLL", os.urandom(12)) + return "S-1-5-21-%d-%d-%d" % (sub_ids[0], sub_ids[1], sub_ids[2]) + + def __create_samba_domain_object(self): + trust_dn = "cn=trusts,%s" % self.suffix + smb_dom_dn = "cn=ad,%s" % trust_dn + + try: + self.admin_conn.getEntry(smb_dom_dn, ldap.SCOPE_BASE) + print "Samba domain object already exists" + return + except errors.NotFound: + pass + + try: + self.admin_conn.getEntry(trust_dn, ldap.SCOPE_BASE) + except errors.NotFound: + entry = ipaldap.Entry(trust_dn) + entry.setValues("objectclass", ["nsContainer"]) + entry.setValues("cn", "trusts") + self.admin_conn.add_s(entry) + + entry = ipaldap.Entry(smb_dom_dn) + entry.setValues("objectclass", ["sambaDomain", "nsContainer"]) + entry.setValues("cn", "ad") + entry.setValues("sambaDomainName", self.netbios_name) + entry.setValues("sambaSID", self.__gen_sid_string()) + #TODO: which MAY attributes do we want to set ? + self.admin_conn.add_s(entry) + + def __write_smb_conf(self): + self.fstore.backup_file(self.smb_conf) + + fd = open(self.smb_conf, "w") + fd.write('### Added by IPA Installer ###\n') + fd.write('[global]\n') + fd.write('config backend = registry\n') + fd.close() + + def __write_smb_registry(self): + [fd, tmp_name] = tempfile.mkstemp() + + os.write(fd, '[global]\n') + os.write(fd, 'workgroup = %s\n' % self.netbios_name) + os.write(fd, 'realm = %s\n' % self.realm_name) + os.write(fd, 'security = user\n') + os.write(fd, 'domain master = yes\n') + os.write(fd, 'domain logons = yes\n') + os.write(fd, 'log level = 1\n') + os.write(fd, 'max log size = 100000\n') + os.write(fd, 'log file = /var/log/samba/log.%d\n') + os.write(fd, 'passdb backend = IPA_ldapsam:ldapi://%s\n' % self.fqdn) + os.write(fd, 'ldapsam:trusted=yes\n') + os.write(fd, 'ldap admin dn = %s\n' % self.smb_dn) + os.write(fd, 'ldap suffix = cn=accounts,dc=ipa,dc=test\n') + os.write(fd, 'ldap user suffix = cn=users\n') + os.write(fd, 'ldap group suffix = cn=groups\n') + os.write(fd, 'ldap machine suffix = cn=computers\n') + os.write(fd, 'rpc_server:epmapper = external\n') + os.write(fd, 'rpc_server:lsarpc = external\n') + os.write(fd, 'rpc_server:lsass = external\n') + os.write(fd, 'rpc_server:lsasd = external\n') + os.write(fd, 'rpc_server:samr = external\n') + os.write(fd, 'rpc_server:netlogon = external\n') + os.write(fd, 'rpc_server:tcpip = yes\n') + os.write(fd, 'rpc_daemon:epmd = fork\n') + os.write(fd, 'rpc_daemon:lsasd = fork\n') + os.close(fd) + + args = ["/usr/bin/net", "conf", "import", tmp_name] + + try: + ipautil.run(args) + finally: + os.remove(tmp_name) + + def __set_smb_ldap_password(self): + args = ["/usr/bin/smbpasswd", "-c", self.smb_conf, "-s", "-W" ] + + ipautil.run(args, stdin = self.smb_dn_pwd + "\n" + self.smb_dn_pwd + "\n" ) + + def __start(self): + try: + self.start() + except: + logging.critical("smbd service failed to start") + + def __stop(self): + self.backup_state("running", self.is_running()) + try: + self.stop() + except: + pass + + def __enable(self): + self.backup_state("enabled", self.is_enabled()) + # We do not let the system start IPA components on its own, + # Instead we reply on the IPA init script to start only enabled + # components as found in our LDAP configuration tree + self.ldap_enable('ADTRUST', self.fqdn, self.dm_password, self.suffix) + + def setup(self, fqdn, ip_address, realm_name, domain_name, netbios_name, + smbd_user="samba"): + self.fqdn =fqdn + self.ip_address = ip_address + self.realm_name = realm_name + self.domain_name = domain_name + self.netbios_name = netbios_name + self.smbd_user = smbd_user + self.suffix = ipautil.realm_to_suffix(self.realm_name) + + self.smb_conf = "/etc/samba/smb.conf" + + self.smb_dn = "uid=samba,cn=sysaccounts,cn=etc,%s" % self.suffix + self.smb_dn_pwd = ipautil.ipa_generate_password() + + + def create_instance(self): + + self.ldap_connect() + + self.step("stopping smbd", self.__stop) + self.step("create samba user", self.__create_samba_user) + self.step("create samba domain object", self.__create_samba_domain_object) + self.step("create samba config registry", self.__write_smb_registry) + self.step("writing samba config file", self.__write_smb_conf) + self.step("setting password for the samba user", self.__set_smb_ldap_password) + self.step("configuring smbd to start on boot", self.__enable) + self.step("starting smbd", self.__start) + + self.start_creation("Configuring smbd:") + + def uninstall(self): + if self.is_configured(): + self.print_msg("Unconfiguring %s" % self.service_name) + + running = self.restore_state("running") + enabled = self.restore_state("enabled") + + try: + self.stop() + except: + pass + + for f in [self.smb_conf]: + try: + self.fstore.restore_file(f) + except ValueError, error: + logging.debug(error) + pass + + if not enabled is None and not enabled: + self.chkconfig_off() + + if not running is None and running: + self.start() diff --git a/tests/test_ipaserver/install/test_smbinstance.py b/tests/test_ipaserver/install/test_smbinstance.py new file mode 100755 index 0000000000000000000000000000000000000000..ec015763ea2d4bfaaf7ff382fff4c6df6fd2e9b6 --- /dev/null +++ b/tests/test_ipaserver/install/test_smbinstance.py @@ -0,0 +1,59 @@ +# Authors: +# Sumit Bose <sb...@redhat.com> +# +# Copyright (C) 2011 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, either version 3 of the License, or +# (at your option) any later version. +# +# 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, see <http://www.gnu.org/licenses/>. +""" +Test `smbinstance` +""" + +import os +import nose + +from ipaserver.install import smbinstance + +class test_smbinstance: + """ + Test `smbinstance`. + """ + + def test_make_netbios_name(self): + s = smbinstance.make_netbios_name("ABCDEF") + assert s == 'ABCDEF' and isinstance(s, str) + s = smbinstance.make_netbios_name(U"ABCDEF") + assert s == 'ABCDEF' and isinstance(s, unicode) + s = smbinstance.make_netbios_name("abcdef") + assert s == 'ABCDEF' + s = smbinstance.make_netbios_name("abc.def") + assert s == 'ABC' + s = smbinstance.make_netbios_name("abcdefghijklmnopqr.def") + assert s == 'ABCDEFGHIJKLMNO' + s = smbinstance.make_netbios_name("A!$%B&/()C=?+*D") + assert s == 'ABCD' + s = smbinstance.make_netbios_name("!$%&/()=?+*") + assert not s + + def test_check_netbios_name(self): + assert smbinstance.check_netbios_name("ABCDEF") + assert not smbinstance.check_netbios_name("abcdef") + assert smbinstance.check_netbios_name("ABCDE12345ABCDE") + assert not smbinstance.check_netbios_name("ABCDE12345ABCDE1") + assert not smbinstance.check_netbios_name("") + + assert smbinstance.check_netbios_name(U"ABCDEF") + assert not smbinstance.check_netbios_name(U"abcdef") + assert smbinstance.check_netbios_name(U"ABCDE12345ABCDE") + assert not smbinstance.check_netbios_name(U"ABCDE12345ABCDE1") -- 1.7.6
_______________________________________________ Freeipa-devel mailing list Freeipa-devel@redhat.com https://www.redhat.com/mailman/listinfo/freeipa-devel