[Freeipa-devel] [PATCH] 1 Add ipa-adtrust-install utility
Sumit Bose
sbose at redhat.com
Tue Aug 30 14:40:28 UTC 2011
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 <sbose at 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 at redhat.com
> https://www.redhat.com/mailman/listinfo/freeipa-devel
-------------- next part --------------
From 7929cb97ad2f7df9506ee97dbfd1213f4273c66c Mon Sep 17 00:00:00 2001
From: sbose <sbose at 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 <sbose at redhat.com>
+# Based on ipa-server-install by Karl MacMillan <kmacmillan at 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 <sbose at 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 <sbose at 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 <sbose at 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
More information about the Freeipa-devel
mailing list