Posts mit dem Label LDAP werden angezeigt. Alle Posts anzeigen
Posts mit dem Label LDAP werden angezeigt. Alle Posts anzeigen

Freitag, 18. Juli 2014

Linux: Active-Directory User-Login für Ubuntu/Debian Servern

Warum muss eigentlich für jedes Linux-System immer ein Passwort aus einem Passwort-Manager geholt werden, nur um eben einen Dienst neuzustarten oder die Logs zu durchforsten, wenn wir doch ein funktionierendes Active Directory im Einsatz haben?

Was muss also her: AD-Login für die Linux-Maschinen (Ubuntu/Debian)!
Da wir aber, wie auf den Windows Servern, nicht jedem den Zugriff und sudo (Kommandos als root ausführen!) erlauben wollen, müssen wir die Zugriffe einschränken, welches ich in diesem kurzen Tutorial ebenfalls tun werde.

Im Wesentlichen brauchen wir dafür die folgenden Pakete:
apt-get install winbind samba krb5-user libpam-krb5

Winbind übernimmt hier als Komponente von Samba im Zusammenspiel mit PAM & Kerberos die Hauptarbeit.

Kerberos

Als erstes werden die Kerberos Konfigurationen erledigt, dazu erstellen wir die /etc/krb5.conf in folgender Weise:

[libdefaults]
  default_realm = DOMAIN.TLD
  allow_weak_crypto = yes
  # passend zum AD:
  default_tkt_enctypes = arcfour-hmac-md5 des-cbc-crc des-cbc-md5
  default_tgs_enctypes = arcfour-hmac-md5 des-cbc-crc des-cbc-md5

[domain_realm]
  .domain.tld = DOMAIN.TLD

# wir nutzen nicht den PRÄ-2000 Namen!
[realms]
  DOMAIN.TLD = {
   kdc = dc1.fqdn
   kdc = dc2.fqdn
   admin_server = dc1.fqdn
  }
 
# wir nutzen nicht den PRÄ-2000 Namen, daher umschreiben, falls nötig
[appdefaults]
  pam = {
   mappings = DOMAIN_2000\\(.*) $1@DOMAIN.TLD
   forwardable = true
   validate = true
  }

  # Vorbereitung für Apache Kerberos Authentifizierung!
  httpd = {
   mappings = DOMAIN_2000\\(.*) $1@DOMAIN.TLD
   reverse_mappings = (.*)@DOMAIN\.TLD DOMAIN_2000\$1
  }

Zu ändern sind hier nur die Domänen-Namen, also der 2000er Name und die "moderne" Variante, sowie die Domänen-Controller, idealerweise als fqdn.

Jetzt testen wir den ersten Login mit einem AD-Admin und erzeugen ein Kerberos-Ticket mit
 kinit AD-Admin@DOMAIN.TLD

Und lassen uns das erzeugte Ticket anzeigen:
klist

Der Kerberos Part ist damit abgeschlossen.

Samba

Auch hier müssen wir die Konfiguration in /etc/samba/smb.conf ändern:
[global]
    security = ads
    realm = domain.tld
    workgroup = DOMAIN
    idmap uid = 70000-100000
    idmap gid = 70000-100000
    winbind enum users = yes
    winbind enum groups = yes
    winbind use default domain = yes
    winbind refresh tickets = yes
    template homedir = /home/%U
    template shell = /bin/bash
    client use spnego = yes
    client ntlmv2 auth = yes
    encrypt passwords = true
    restrict anonymous = 2
    domain master = no
    local master = no
    preferred master = no
    os level = 0
    socket address = 127.0.0.1
    load printers = no
    printing = bsd
    printcap name = /dev/null
    disable spoolss = yes

[domain-share]
 path = /foo/bar
 comment = Foo-Bar
 browseable = no
 read only = no
 force user = root
 force group = "DOMAIN+ad-group"
 force create mode = 0666
 force directory mode = 2777
 force directory security mode = 0777
 valid users = @"DOMAIN+ad-group"

Der "domain-share" ist nur ein Beispiel, wie man Samba-Freigaben mit AD Berechtigungen anlegen kann.
Wenn dieser genutzt werden soll, sollte die socket address auf die entsprechende IP-Adresse gesetzt werden oder einfach auskommentiert werden.
Um den Overhead zu reduzieren habe ich dem Samba hier verboten Master zu werden, sowie Druckfunktionen anzubieten.

Die Konfiguration des Samba ist abgeschlossen, wir müssen nun die smb und winbind (Wichtig!) Dienste neustarten
/etc/init.d/smbd restart && /etc/init.d/winbind restart

Fehlt nun noch der AD-Join:
net ads join -U AD-Admin

Ob der AD-Join wirklich erfolgreich war, können wir mit dem Tool wbinfo gut ermitteln:
wbinfo -u | wc -l

wbinfo -u liefert alle User aus dem AD zurück und mit wc -l zählen wir das Ergebnis.
Alternativ geht auch wbinfo -g (Gruppen).
Achtung: es kann eine gewisse Zeit dauern bis wbinfo korrekte Ergebnisse anzeigt!

Damit wir die gefundenen User auch als User im System und damit auch für die Anmeldung nutzen können, müssen wir am "internen" DNS ebenfalls etwas ändern und zwar in der /etc/nsswitch.conf:
Im Wesentlichen ergänzen wir die Zeilen user und group, so dass diese dann folgendermaßen aussehen:
passwd:         compat winbind
group:          compat winbind

Anschließend können wir noch einmal die Dienste neustarten:
/etc/init.d/smbd restart && /etc/init.d/winbind restart

Und einen erneuten Test durchführen, dieses Mal rufen wir die virtuelle passwd inkl. Domänen-User ab:
getent passwd

Hier sollten nun alle Domänen-User im normalen Passwd Format auftauchen:
username:*:71776:70000:Surname, givenName:/home/username:/bin/bash

Die Domänen-User können wir gut an den IDs über 70000 erkennen.

Damit haben wir die Samba Konfiguration abgeschlossen.

PAM

Dem Plugable Authentication Mechanism, kurz PAM, muss nun noch die Nutzung von Winbind beigebracht werden, dazu wird die Datei /etc/pam.d/common-account um folgende Zeile erweitert:
account [success=1 new_authtok_reqd=done default=ignore] pam_winbind.so 

Weiterhin müssen wir Datei /etc/pam.d/common-auth erweitern, so dass sie wie folgt aussieht:
## restricted access
auth required pam_listfile.so onerr=fail item=group sense=allow file=/etc/login.group.allowed

auth [success=2 default=ignore] pam_unix.so nullok_secure
auth [success=1 default=ignore] pam_winbind.so krb5_auth krb5_ccache_type=FILE cached_login try_first_pass
# here's the fallback if no module succeeds
auth requisite   pam_deny.so
# prime the stack with a positive return value if there isn't one already;
# this avoids us returning an error just because nothing sets a success code
# since the modules above will each just jump around
auth required   pam_permit.so

Wir erinnern uns, wir wollen den Zugriff für bestimmte Nutzer Gruppen einschränken, dazu nutzen wir das PAM-Modul pam_listfile.so und tragen dort zeilen-basiert unsere erlaubten Gruppen (ohne Prefix!) ein.

Damit wir auf jedem System auch ein entsprechendes Home Verzeichnis bekommen, benötigen wir das Modul pam_mkhomedir.so, welches wir in die Datei /etc/pam.d/common-session einfügen:
session required   pam_mkhomedir.so umask=0022 skel=/etc/skel

Abschluss

Damit wir die Änderungen testen können, machen wir einen SSH auf uns selbst:
ssh ad-user@hostname

Wenn das erfolgreich ist, fügen wir noch Samba und Winbind zum Autostart hinzu:
update-rc.d smb defaults && update-rc.d winbind defaults

Das wars!

Nachtrag: RHEL/CentOS/Oracle Linux

Das ganze funktioniert natürlich auch unter RHEL basierten Systen, dafür nutzt man die Pakete:
nscd pam_krb5 samba-common samba

Und ergänzt die Datei /etc/pam.d/system-auth-ac um folgende Zeilen:
auth        sufficient    pam_winbind.so use_first_pass
account     [default=bad success=ok user_unknown=ignore] pam_winbind.so
session     optional      pam_winbind.so mkhomedir

Ansonsten variieren natürlich die Befehle etwas.


Bei Fragen bitte einfach melden.



Montag, 28. Oktober 2013

Passive Sicherheit im AD mit PHP

Ab und an kommt es mal vor, dass ein User im AD erhöhte Rechte oder zusätzliche Gruppen bekommen muss.
Zum Beispiel könnte dieser erhöhte Rechte im Internet (AD-basierte Proxygruppen) oder für eine Applikation (Argh!) benötigen.

Was passiert aber nun, wenn diese Rechte in Vergessenheit geraten und dadurch Schaden (z.B. Data-Loss, Malware, ...) entsteht? Die Frage ist natürlich reichlich polemisch, allerdings muss man sich die Frage stellen, wer dafür die Verantwortung trägt? Natürlich die IT.

Und genau die kann dafür sorgen, dass solche Dinge NICHT in Vergessenheit geraten, indem man sich automatisch in periodischen Abständen ein Aufstellung der wichtigsten Gruppen zuschicken lässt.

Genau dieses erfüllt das Skript, welches ich, aus den oben erwähnten Gefahren heraus, geschrieben habe:


#!/usr/bin/php -q
<?php
/*
 Read dom admins from AD
 Author: Oliver Skibbe oliskibbe (at) gmail.com
 Date: 2013-10-28
*/

function parseLdapDn($dn)
{
        $dn = addcslashes( $dn, "<>" );
        $result = ldap_explode_dn( $dn, 0 );
        //translate hex code into ascii again
        foreach( $result as $key => $value )
                $result[$key] = preg_replace("/\\\([0-9A-Fa-f]{2})/e", "''.chr(hexdec('\\1')).''", $value);
        unset($result["count"]);
        return $result;
}

function sendMail ( $bodyStuff ) {

        global $debug, $dryrun, $to, $replyto, $cc, $from;

        // mail header
        $header = "MIME-Version: 1.0\r\n";
        $header .= "Content-type: text/html; charset=utf-8\r\n";
        $header .= "From: " . $from . "\r\n";
        $header .= "Reply-To: " . $replyto . "\r\n";
  if ( $cc ) {
   $header .= "Cc: " . $cc . "\r\n";
  }
        $header .= "X-Mailer: PHP ". phpversion();
  
        $subject = "Mitglieder der Gruppe {$bodyStuff["name"]}";
        // erfüllt seinen Zweck..
  $body = "<html>
<br>
Mitglieder der Gruppe {$bodyStuff["name"]}
<br><br>
<table border=\"1\">
<tr><th align=\"left\"><b>Username</b></th><th align=\"left\"><b>OU</b></th></tr>\n\n";

        unset($bodyStuff["name"]);

        foreach ( $bodyStuff as $members) {
                foreach ( $members as $member => $foo ) {
                        $body .= "<tr><td nowrap>{$member}</td><td>{$foo}</td></tr>\n";
                }
        }
        $body .= "\n</table>
</html>";
        if ( $debug  || $dryrun ) {
                echo "Subject " . $subject . PHP_EOL;
                echo "Body: " . $body . PHP_EOL;
                echo "Header: " . $header . PHP_EOL;
        }
        if ( ! $dryrun ) {
                $result = mail( $to, $subject, $body, $header);
        } else {
                $result = true;
        }

        return $result;
}

// Variables

// debug prints
$debug = false;

// do not send mail, just print
$dryrun = false;

// receiver
$to = "oliskibbe@gmail.com";
$replyto = "ticketsystem@example.tld";
$from = "absender@example.tld";

// optional
$cc = "chef@example.tld";

// ad
$host = "ADHOST";
$user = "ADUSER";
$pass = "ADPASS";

// erlaube mehrere Gruppen z.B. Domänen-Admin und Proxy Gruppen ohne Beschränkung
$filter = "(&(|(name=Domänen-Admins)(name=FreiesInternet))(objectcategory=group))";
$dn = "dc=example,dc=local";
// nur Name und Mitglieder
$justthese = array('name', 'member');

// mit AD verbinden
$link = ldap_connect($host) or die ("Keine Verbindung zu {$host} möglich!" . PHP_EOL);

// AD Protokoll Einstellungen setzen
ldap_set_option($link, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($link, LDAP_OPT_REFERRALS, 0);

// Am AD anmelden
$bind = ldap_bind($link, $user, $pass) or die ("Keine Anmeldung am AD mit User {$user} an Host {$host} möglich!");

$searchresult = ldap_search ($link, $dn, $filter, $justthese );

$result = ldap_get_entries($link, $searchresult);

ldap_close($link);

// helper

foreach ( $result as $groupArray ) {
        $members = array();

        if (is_array ($groupArray) ) {

                $members["name"] = $groupArray["name"][0];

                // remove count stuff
                unset($groupArray["member"]["count"]);

                foreach ( $groupArray["member"] as $member ) {
                        $dnResult = parseLdapDn($member);

                        $name = str_replace("CN=","",$dnResult[0]);
                        unset($dnResult[0]);

                        $members[$members["name"]][$name] = implode(",", $dnResult);

                        unset($member);
                        unset($dnResult);
                }
                $result = sendMail($members);
                if ( ! $result && $debug ) {
                        echo "Sending mail failed!";
                }
        }
}

Dieses Skript fragt 1-n Gruppen über den LDAP Filter ab und verschickt wunderschöne E-Mails  mit einer tabellarischen Aufstellung der entsprechenden User und in welcher OU sich diese befinden.

Download

Anschließend trägt man dieses Skript in die crontab ein:
0 01 * * 1     /usr/bin/php -q /usr/local/bin/check_ad_admins.php

Nun wird man jede Woche am Montag zum Arbeitsbeginn eine E-Mail mit den aktuellen Gruppenmitgliedern haben, bei sehr großen Firmen sollte man vielleicht noch einen Zwischenschritt einlegen und entweder eine kleine Datenbank oder ein Textfile mit Altwerten füllen um die neu hinzugekommen zu highlighten, aber in meinem Fall war das nicht nötig.


Bei Fragen wie immer einfach melden.

Donnerstag, 22. August 2013

Outlook Signaturen und wie bekämpft man die Anarchie 2/2

Wie bereits im letzten Blogpost als mögliches, bzw. typisches Problem erwähnt, müssen für eine saubere Signatur vollständige Daten im AD enthalten sein.

Dazu kann man entweder auf die Mitarbeiter vertrauen, dass diese sich immer melden, wenn sich etwas ändert oder man beschäft je nach Unternehmensgröße einfach eine Vollzeitstelle damit, welche den ganzen Tag durchs Haus läuft und alle Mitarbeiter "inventarisiert"...natürlich nur ein Spaß.. ;-)

Die, meiner Meinung nach, sinnvollste Lösung ist die kontrollierte Selbstpflege durch die BenutzerInnen selbst, dazu stellt man ihnen ein Tool bereit welches die Daten abfragt und speichert.

Vorteile:
  • weniger Hotlineanrufe
  • BenutzerInnen möchten die Daten selbst ändern, damit z.B. ihre Signatur aktuell ist

Ich habe mich dabei für eine PHP Web-Applikation entschieden, da diese unabhängig vom Computer und vom Server ist, da es PHP für ebenso für Windows gibt.

Im Anwendungsfall habe ich mich für einen netten, kleinen Ubuntu 12.04 LTS Server mit LAP Stack (ja, der heißt normalerweise LAMP, allerdings benötige ich keinen MySQL-Server.. ;-)) entschieden, da ich eine gewissen Open-Source Affinität habe, Lizenzkosten sparen möchte und sehr gute Erfahrung damit gemacht habe.
Dieser authentifiziert die Benutzer mittels Kerberos über den Apache2 am AD (voraussichtlich folgt dazu ein kleines Tutorial im nächsten Post), anschließend übernimmt PHP den Benutzernamen über die Server Variable REMOTE_USER.

Da die Code-Menge dem Leserahmen sprengen würde, gibt es dieses Mal nur einen Direkt-Download als Zip-Archiv: https://dl.dropboxusercontent.com/u/9482545/webapp_signatur.zip

Das Zip-Archiv muss auf das Zielsystem in einen, für den Webserver lesbaren Pfad gepackt werden (je nach Distribution: /var/www, /srv/htdocs, ...).

Features:
  • 1-N Standorte können konfiguriert / vorbereitet werden
  • Schreiben auf den jeweilig konfigurierten, lokalen DC (bei mehreren Standorten, damit nach Neuanmeldung das Signatur Generierungsskript die aktuellen Daten bekommt)
  • Generierung der Adressdaten über die Auswahl des Standorts, auch bei mehreren Standorten pro Stadt
  • Generierung der Fax-/Telefonnummer über die Auswahl des Standorts, es wird nur die Durchwahl eingegeben => einheitliche Formatierung
  • Handynummer wird über Auswahl der Vorwahl+Rufnummer generiert => einheitliche Formatierung
  • Unterstützung für Unterfirmen, abhängig vom Standort (z.B. Wayne Enterprises Hannover, Wayne Business Berlin) => unterschiedliche Einträge in die Firmenfelder
  • Unterstützung von Geschäftsbereichen/Abteilungen und Job-Titeln

Folgende Schritte müssen erfüllt werden, damit das ganze lauffähig wird:
  • Geringe PHP Kenntnisse
  • Fortgeschrittene Linux Kenntnisse (für Kerberos Authentifizierung )
  • Installation Apache2 mit PHP (+ LDAP Module) und Authentifizierung (Zugriff auf REMOTE_USER) z.B. Kerberos oder mod_sspi (nur für Win32 und Apache2 2.0/2.2)
  • Anpassung der Konfiguration in config.inc.php nach eigenem Ermeßen.
  • Anpassung der index.php für CI-Anpassungen

Einen Schönheitsfehler gibt es allerdings noch, die Übertragung wird per Ajax durchgeführt und der Return vom Speichern zurück zur Index.php funktioniert noch nicht perfekt.

Da die Daten im AD nun im Laufe der Zeit aktualisiert werden, kann man aus diesen z.B. genauso leicht eine Telefonliste genieren.


Bei Fragen bitte wie immer einfach melden. ;-)