10-02-04 09:56
Kako prebaciti korisnike iz LDAP imenika u (obrisan) /etc/passw
piše ŽELJA BOROŠ
Problem:
Podaci o korisničkim računima su obrisani. Backup traka je zakazala, odnosno sadržava nešto starije podatke. Velika većina korisničkih direktorija se i dalje nalazi na disku (ili su vraćeni s trake), obrisani su "samo" korisnički passwordi i grupe.
Što treba učiniti:
Vratiti korisnicima mogućnost prijave na sustav, čitanja pošte te spajanja na CMU. Pri tom je nužno očuvati korisničke UID-ove kako ne bi došlo do problema s pravima pristupa na /home direktorije i problema s korisničkim webovima u ~/public_html. Uključiti i postaviti kvote korisnicima.
Rješenje:
Kako je postojala recentna kopija dumpa LDAP baze (LDIF datoteka), bilo je moguće vratiti korisnike i njihove zaporke na sustav pisanjem skripte u perlu. Skripta je potrebna jer bi ručno dodavanja korisnika bilo nepraktično. Perl je odabran zbog fleksibilnosti i brzine.
Naknadno uočeni problemi:
Nisu svi korisnici na sustavu uneseni u LDAP, dok neki korisnici koji postoje u LDAP-u ne postoje na sustavu (nemaju /home direktorij). Prvi slučaj se može riješiti uvidom u "papirnatu" evidenciju i ručnim dodavanjem imena i prezimena (nakon rekreiranja /etc/passwd datoteke) naredbom 'usermod'.
Drugi slučaj se može riješiti skriptom, uz napomenu da takvi korisnici nužno dobivaju drugi UID (jer nemaju /home direktorij pa prema tome ni sačuvan UID). U ovom slučaju očuvanje UID-a ni nije prioritet, jer je sasvim svejedno koji će UID korisnik dobiti.
Postupak:
Iz LDIF se datoteke perl skriptom 'users_rescue.pl' izvlače podaci o korisnicima (ime, prezime, uid). Kako bi ova skripta pravilno radila, potrebno je kreirati dvije datoteke: 'uids.txt' i 'usernames.base64' (imena su proizvoljna, ali treba izmijeniti source perl skripte u slučaju promjene).
U prvoj datoteci ("uids.txt") se nalaze dva stupca: u prvom korisnički uid, u drugom username. U drugoj datoteci ("usernames.base64") se nalaze "izgrepani" samo potrebni podaci iz LDIF datoteke (kako bi perl kod bio što jednostavniji). Datoteke je najlakše kreirati naredbama:
ls -l /home | awk '{print $3, $9}' > uids.txt
egrep '(cn:|uid:)' < organization.ldif > usernames.base64
Nakon toga samo treba pokrenuti skriptu 'users_rescue.pl' koja će generirati
ispis u /etc/passwd formatu sa svim korisnicima koji imaju uid na sustavu a
ujedno se nalaze u LDAP-u. Skripta brine o imenima korisnika koji sadržavaju
dijakritičke znakove, te ih konvertira u oblik bez "kvačica".
Izlaz naredbe 'users_rescue.pl' preporučljivo je preusmjeriti u privremenu datoteku te ručno "zalijepiti" na /etc/passwd, pazeći da se ne preklapaju uid brojevi postojećih (sustavskih) i novih (korisničkih) računa.
Iako je pripremljena i skripta 'pass_rescue.pl' koja ima zadatak kreirati izlaz u formatu /etc/shadow datoteke, ona nije upotrijebljena.
Razlog je slaba podrška za SHA (Secure Hash Algorithm) način "hashiranja"
zaporki kao načina autentificiranja na Linuxu (a nikakva na Solarisu). Iz tog razloga (kao i što bržeg rješenja problema), išlo se na direktnu autentifikaciju preko PAM LDAP modula. Ovo je zapravo prednost, jer korisnik sad ima samo jednu zaporku za sve: e-mail, CMU, shell pristup itd.
Kako Linux ima složeniju strukturu PAM-a (svaki servis ima svoju datoteku, za razliku od Solarisa koji rabi samo jednu, /etc/pam.conf), kreirana je skripta 'pam-fix.sh' koja automatski pravi nužne promjene u PAM konfiguracijskim datotekama. Naravno, predviđeno je kreiranje pričuvnih
kopija datoteka u /etc/pam.d direktoriju (imaju dodatak .orig).
U datoteku /etc/nsswitch.conf još treba dodati (izmijeniti) redak
passwd: compat
u
passwd: ldap compat
Korisnici se u LDAP dodaju preko standardne 'ldapmodify' naredbe (postoji
"kuharica" na webu: http://cmung.cmu.carnet.hr).
Vjerojatno se navedeno moglo i elegantnije izvesti, ali bitan ograničavajući
čimbenik je bilo vrijeme, kako bi korisnici što prije mogli početi rabiti e-mail i CMU sustav, kao i ažurirati vlastite web stranice.
NAPOMENA: Prije svih operacija potrebno je napraviti backup svih datoteka
koje se mijenjaju: /etc/passwd, /etc/shadow itd. Skripte ne moraju nužno odmah proraditi kod vas, jer su pisane za specifične potrebe. No, u slučaju potrebe, s lakoćom se mogu modificirati jer su pisane u perlu.
DISCLAIMER: Sve navedene skripte i upute koristite na vlastitu odgovornost!
------------
# pam-fix.sh
# Author: zelja@zelja.com Wed, 4 Feb 2004 22:43:15 +0100
# Script adjusts PAM files so that every service tries to authenticate
# against LDAP first. Failing that, it tries previous configuration.
# Script makes backup files, but make your own backup first.
# Use at your own risk!
set -e
cd /etc/pam.d
perl -pi.orig -e 's/^(.*)(required)(.*pam_unix.*so.*)$/#$1$2$3\n$1sufficient$3\n$1required\tpam_ldap.so\n/g;' $1
----------
# users_rescue.pl
#!/usr/bin/perl
# Author: zelja@zelja.com Wed, 4 Feb 2004 22:43:15 +0100
# Script recreates /etc/passwd from LDIF file, preserving UIDs found on system
# Create $base64 file with:
# egrep '(cn:|uid:)' < organization.ldif > usernames.base64
# Create $uids file with:
# ls -l /home | awk '{print $3, $9}' > uids.txt
# DO backup before running this script.
# Use at your own risk!
# Numerical value of 'users' group on linux. Modify according to your system.
$defgid = "100";
$uids = "uids.txt";
$base64 = "usernames.base64";
$pisi = 0;
%UIDS = ();
%USERNAMES = ();
# read uids in field UIDS
open(UIDS, "<$uids") or die "No \'$uids\' file!";
while (<UIDS>) {
chomp ($_);
($uid, $username) = split;
$UIDS{$username} = $uid;
$USERNAMES{$uid} = $username;
}
close UIDS;
undef $uid, $username;
# read uids and names from ldif
open (B64, "<$base64") or die "No \'$base64\' file!";
while (<B64>) {
chomp ($_);
($what, $value) = split(/: /);
$name = $value;
next unless ($what eq "uid" || $what =~ "cn:");
# if $what looks like 'cn::' don't decode
if ($what eq "cn::") {
$decoded = $name;
}
elsif ($what eq "cn:")
{
use MIME::Base64;
$decoded = decode_base64($name);
$decoded =~ s/�/c/g; #meko ch
$decoded =~ s/�/c/g; #ch
$decoded =~ s/�/dj/g; #dj
$decoded =~ s/�/C/g; #meko CH
$decoded =~ s/�/C/g; #CH
$decoded =~ s/Ĺ /S/g; #SH
$decoded =~ s/Ĺž/z/g; #zh
$decoded =~ s/ĹĄ/s/g; #sh
$decoded =~ s/�/Dj/g; #DJ
$decoded =~ s/Ĺ˝/Z/g; #ZH
$decoded =~ s/ĂŚ/c/g; #meko ch 2
$decoded =~ s/Ăź/ue/g; #u s prijeglasom
}
# read only users found in 'uids.txt' file
if (($what eq "uid") && ($UIDS{$value} ne "")) {
$line = "$value:x:";
$line .= "$UIDS{$value}\:";
$line .= "$defgid:";
$line .= "$decoded\:";
$line .= "/home/$name:/bin/bash";
print "$line\n";
undef $line;
}
}
exit 0;
------------
# pass_rescue.pl
#!/usr/bin/perl
# Author: zelja@zelja.com Wed, 4 Feb 2004 22:43:15 +0100
# Script tries to recreate /etc/shadow from LDIF file
# Create $base64 file with:
# egrep '(cn:|uid:)' < organization.ldif > usernames.base64
# DO backup before running this script.
# Use at your own risk! This script is not tested in production!
use MIME::Base64;
$ldif = "organization.ldif";
$base64 = "usernames.base64";
%USERS = ();
%PASS = ();
open(USERS, "<$ldif") or die "No \'$ldif\' file!";
while (<USERS>) {
if (/uid:/) {
chomp ($_);
($dummy, $user) = split;
$USERS{$user} = decode_base64($pass);
# 12400 = number of days between epoch and last pass change
print "$user:$USERS{$user}:12400::::::\n";
} elsif (/userPassword:/) {
($dummy, $pass) = split;
}
}
exit 0;
# THE REST IS JUST AN EXAMPLE, MODIFY IF NEEDED (AND REMOVE exit 0;
open (B64, "<$base64");
while (<B64>) {
chomp ($_);
($prvi, $ime) = split(/: /);
if ($prvi eq "uid") {
$username = $ime;
undef $ime;
$line = "$username:x:";
if ($UIDS{$username} eq "") {
$line .= "NOUID\:";
} else {
$line .= "$UIDS{$username}\:";
}
$line .= "100:";
next;
}
if ($prvi ne "cn:") { $decoded = $ime; goto OUT; }
use MIME::Base64;
$decoded = decode_base64($ime);
$decoded =~ s/�/c/g; #meko ch
$decoded =~ s/�/c/g; #ch
$decoded =~ s/�/dj/g; #dj
$decoded =~ s/�/C/g; #meko CH
$decoded =~ s/�/C/g; #CH
$decoded =~ s/Ĺ /S/g; #SH
$decoded =~ s/Ĺž/z/g; #zh
$decoded =~ s/ĹĄ/s/g; #sh
$decoded =~ s/�/Dj/g; #DJ
$decoded =~ s/Ĺ˝/Z/g; #ZH
$decoded =~ s/ĂŚ/c/g; #meko ch 2
$decoded =~ s/Ăź/ue/g; #u s prijeglasom
OUT:
#print "uid=$uid, $UIDOVI{$username}, $UIDS{$username}, $username\n";
$line .= "$decoded\:";
$line .= "/home/$username:/bin/bash";
print "$line\n";
}
|