Mercurial > hg > savane-forge
changeset 120:8d4b08714c90
Import existing uid/gid; document LDAP setup; no-downtime LDAP repopulation script
author | Sylvain Beucler <beuc@beuc.net> |
---|---|
date | Tue, 04 Aug 2009 01:14:34 +0200 |
parents | a34e97e27050 |
children | 4de601f0d69f |
files | doc/LDAP sbin/sv-fetch-passwd-ids sbin/sv-populate-ldap src/savane/backend/auth_ldif_export.py src/savane/svmain/models.py |
diffstat | 5 files changed, 202 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/LDAP +++ b/doc/LDAP @@ -1,4 +1,102 @@ +Django +====== + Backend: http://code.djangoproject.com/ticket/11526 Synchro: http://www.djangosnippets.org/snippets/893/ + +It looks like a truly synchronized LDAP Django auth backend is +difficult. In a first step we'll just export the Django userbase to +LDAP (instead of the other way around). + +In addition, Django's "sha1$" passwords are ridiculously incompatible +with SSHA passwords used by LDAP (among others). A solution is to +implement a custom Django auth backend with support for SSHA +passwords. + + +OpenLDAP +======== + +# - domain: savannah.gnu.org +# - organisation: (whatever) +# - Allow LDAPv2 protocol: no +# - HDB + +cat <<EOF | debconf-set-selections +slapd slapd/no_configuration boolean false +slapd slapd/domain string savannah.gnu.org +slapd shared/organization string GNU +slapd slapd/password2 password admin +slapd slapd/password1 password admin +EOF + +apt-get --assume-yes install slapd +#dpkg-reconfigure slapd + +# Test: +#ldapsearch -b 'dc=savannah,dc=gnu,dc=org' -D 'cn=admin,dc=savannah,dc=gnu,dc=org' -w admin -x + +# Alternatively: minimal config: +cat <<EOF > /etc/ldap/slapd.conf: +pidfile /var/run/slapd/slapd.pid +modulepath /usr/lib/ldap +moduleload back_bdb +include /etc/ldap/schema/core.schema +sizelimit unlimited + +index uid,uidNumber,gidNumber,memberUid,shadowExpire eq + +# DB n1 +database bdb +directory /var/lib/ldap +suffix "dc=savannah,dc=gnu,dc=org" +rootdn "cn=admin,dc=savannah,dc=gnu,dc=org" +rootpw admin + +access to attrs=userPassword,shadowLastChange + by dn="cn=admin,dc=gnu,dc=org" write + by anonymous auth + by self write + by * none +EOF + +(in all case add the indexes) + +Unix auth +========= + +# Enable user lookup with libnss-ldap. For additional passwords +# support you'll need libpam-ldap but we don't need it for Savane, +# since we're using SSH keys instead of passwords. + +cat <<EOF | debconf-set-selections +libnss-ldap shared/ldapns/ldap-server string ldap://127.0.0.1/ +libnss-ldap shared/ldapns/base-dn string dc=savannah,dc=gnu,dc=org +libnss-ldap libnss-ldap/rootbinddn string cn=admin,dc=savannah,dc=gnu,dc=org +libnss-ldap libnss-ldap/rootbindpw password admin +libnss-ldap shared/ldapns/ldap_version select 3 +EOF + +apt-get --assume-yes install libnss-ldap +cat <<EOF >> /etc/libnss-ldap.conf +nss_base_passwd ou=users,dc=savannah,dc=gnu,dc=org +nss_base_shadow ou=users,dc=savannah,dc=gnu,dc=org +nss_base_group ou=groups,dc=savannah,dc=gnu,dc=org +EOF + +sed -i -e 's/^\(passwd:.*$\)/\1 ldap/' \ + -e 's/^\(group:.*$\)/\1 ldap/' \ + -e 's/^\(shadow:.*$\)/\1 ldap/' \ + /etc/nsswitch.conf + +# Fetching a user's groups is sadly pretty inefficient (e.g. try 'id +# yourusername'). To compensate, you can install the Name Service +# Caching Daemon: +apt-get --assume-yes install nscd + +# To avoid user listings to be too long, you can either limit the +# number of result entries in slapd (sizelimit), or filter out some +# users, e.g. with nss_base_passwd +# ou=users,dc=savannah,dc=gnu,dc=org?sub?!(shadowExpire=10)
new file mode 100644 --- /dev/null +++ b/sbin/sv-fetch-passwd-ids @@ -0,0 +1,56 @@ +#!/bin/bash +# Fetch user and group IDs for import in the Savane database +# Copyright (C) 2009 Sylvain Beucler +# +# This file is part of Savane. +# +# Savane is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Savane 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This is meant to be called before disabling the old-school +# replication crons and switching to LDAP authentication. + +( +echo "CREATE TEMPORARY TABLE temp_table (username varchar(30), uidNumber int, gidNumber int);" +echo "INSERT INTO temp_table VALUES " +# ('admin', 65535), ('loic', 65536); +cat /etc/passwd | while IFS=: read username pass uid gid rest; do + if [ $uid -gt 1000 ]; then + echo -n "('$username', $uid, $gid),"; + fi; +done | sed 's/,$//' +echo ";" +echo "UPDATE auth_user, svmain_extendeduser, temp_table + SET svmain_extendeduser.uidNumber = temp_table.uidNumber, + svmain_extendeduser.gidNumber = temp_table.gidNumber + WHERE auth_user.id = svmain_extendeduser.user_ptr_id + AND auth_user.username = temp_table.username;" +) > t.sql +echo "DROP TABLE temp_table;" >> t.sql + +( +echo "CREATE TEMPORARY TABLE temp_table (name varchar(30), gidNumber int);" +echo "INSERT INTO temp_table VALUES " +# ('mifluz', 1004), ('figure', 1006); +cat /etc/group | while IFS=: read name pass gid rest; do + if [ $gid -gt 1000 ]; then + echo -n "('$name', $gid),"; + fi; +done | sed 's/,$//' +echo ";" +echo "UPDATE auth_group, svmain_extendedgroup, temp_table + SET svmain_extendedgroup.gidNumber = temp_table.gidNumber + WHERE auth_group.id = svmain_extendedgroup.group_ptr_id + AND auth_group.name = temp_table.name;" +) >> t.sql +echo "DROP TABLE temp_table;" >> t.sql
--- a/sbin/sv-populate-ldap +++ b/sbin/sv-populate-ldap @@ -1,7 +1,49 @@ #!/bin/bash +# Populate slapd from Savane's LDIF export, with minimum downtime +# Copyright (C) 2009 Sylvain Beucler +# +# This file is part of Savane. +# +# Savane is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Savane 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Tested on Debian >= Lenny +. /etc/default/slapd + +# Create the new database in a different directory to reduce the slapd +# downtime - better if /tmp is on the same filesystem than +# /var/lib/ldap +tmpdir=$(mktemp -d -p /var/lib/ldap) +conf=$(mktemp) +sed 's,^directory\s.*,directory "'$tmpdir'",' /etc/ldap/slapd.conf > $conf + +# Rebuild the LDAP database from scratch. '-q' disables integrity +# checks and is nearly 30x faster. Still needs ~30s just for bare +# slapadd - and more for the Python string substitutions and other +# computings (the MySQL query itself requires only 1-2s). +./sv auth_ldif_export | slapadd -f $conf -q +rm -f $conf + +# Efficiently switch slapd database /etc/init.d/slapd stop rm -f /var/lib/ldap/* -# '-q' disables integrity checks and is nearly 30x faster -./sv auth_ldif_export | slapadd -q -chown -R openldap: /var/lib/ldap/* +mv $tmpdir/* /var/lib/ldap/ +chown -R $SLAPD_USER:$SLAPD_GROUP /var/lib/ldap/* +rmdir $tmpdir /etc/init.d/slapd start + +# Invalidate nscd cache if used +if [ which nscd > /dev/null ]; then + nscd --invalidate passwd + nscd --invalidate group +fi
--- a/src/savane/backend/auth_ldif_export.py +++ b/src/savane/backend/auth_ldif_export.py @@ -28,6 +28,7 @@ # - min gid: 1000 # - default group: cn=svusers / gid=1000 # - loginShell: /usr/local/bin/sv_membersh +# - homedir: 2-level /home/u/us/username import sys import codecs @@ -210,7 +211,7 @@ # Dump groups group_saves = [] -svmain_models.ExtendedGroup.query_active_groups_raw(conn, ('group_id', 'name', 'gidNumber')) +svmain_models.ExtendedGroup.query_active_groups_raw(conn, ('group_ptr_id', 'name', 'gidNumber')) res = conn.store_result() #for group in svmain_models.ExtendedGroup.objects.only('name'): for row in res.fetch_row(maxrows=0):