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

Mittwoch, 20. August 2014

Nachtrag für Ubuntu 14.04 Trusty zu Active-Directory User-Login für Ubuntu/Debian Servern

Nachdem ich gestern das erste System von 12.04 auf 14.04 gehoben habe, musste ich feststellen, dass der AD-Login (wie hier beschrieben: http://oskibbe.blogspot.de/2014/07/linux-active-directory-user-login-fur.html) nicht mehr funktionierte.


Der Server war weiterhin in der Domäne und die Winbind Tools wbinfo -u / wbinfo -g lieferten weiterhin die korrekten User/Gruppen zurück.

Im Auth-Log fand ich allerdings einen Eintrag:

Aug 18 16:07:38 host login[1738]: pam_listfile(login:auth): Refused user oliver.skibbe for service login
Aug 18 16:07:40 host login[1738]: pam_unix(login:auth): check pass; user unknown
Aug 18 16:07:40 host login[1738]: pam_unix(login:auth): authentication failure; logname=LOGIN uid=0 euid=0 tty=/dev/tty1 ruser= rhost=
Aug 18 16:07:40 host login[1738]: pam_winbind(login:auth): getting password (0x00000388)
Aug 18 16:07:40 host login[1738]: pam_winbind(login:auth): pam_get_item returned a password
Aug 18 16:07:42 host login[1738]: FAILED LOGIN (1) on '/dev/tty1' FOR 'UNKNOWN', Authentication failure

Ausgehend von diesem Eintrag und der Info, dass die User weiterhin abrufbar waren, konnte ich das Problem auf nsswitch eingrenzen und zwar gibt es seit Ubuntu 14.04 Trusty für diesen Part zwei zusätzliche Pakete Namens: libnss-winbind und libpam-winbind benötigt werden:

ii  libnss-winbind:amd64                2:4.1.6+dfsg-1ubuntu2.14.04.3 amd64        Samba nameservice integration plugins
ii  libpam-winbind:amd64                2:4.1.6+dfsg-1ubuntu2.14.04.3       amd64        Windows domain authentication integration plugin


Nach der Installation dieses Paketes funktionierte dann der AD-Login sofort wieder, schade das Winbind keine explizite Abhängigkeit für dieses Paket hat.

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.



Dienstag, 1. Juli 2014

Wie funktioniert eigentlich der Linux IO-Stack oder wie kommen wir Performance Problemen auf die Schliche

Bei Performance Problemen ist es immer nett zu wissen, an welchen Stellen man suchen sollte.


Dazu bin ich im Thomas-Krenn Wiki auf eine nette Grafik zum Thema IO-Stack unter Linux gestoßen:

Linux I/O Stack Diagram

Schön und gut, wir wissen nun wie der IO-Stack arbeitet, aber wie kommen wir nun tatsächlich unserem Performance-Problem auf die Schliche? Dazu gucken wir uns das Diagramm von Brendan Gregg - Linux Performance Analysis & Tools an.

Dieses Diagramm zeigt recht anschaulich mit welchen Tools man welchen Problem auf den Grund gehen kann:



Links:

Mittwoch, 11. Juni 2014

Nützliche Dinge rund um die Bash und den Prompt

Da wir Admins generell faul sind, suchen wir Wege um unsere Arbeit effizienter zu gestalten.

Unter Linux gibt es da mit der Bash einige schöne Möglichkeiten, von denen ich einige mal vorstellen möchte:

(Alle Beispiele gehören in die $HOME/.bash_alias, damit sie auch beim Login aktiv sind)

Farbige Manpages (kann natürlich auch in die /etc/environments eingetragen werden)

export LESS_TERMCAP_mb=$'\E[01;31m'
export LESS_TERMCAP_md=$'\E[01;31m'
export LESS_TERMCAP_me=$'\E[0m'
export LESS_TERMCAP_se=$'\E[0m'
export LESS_TERMCAP_so=$'\E[01;44;33m'
export LESS_TERMCAP_ue=$'\E[0m'
export LESS_TERMCAP_us=$'\E[01;32m'

Textdateien

alias t="tail -f -n200" # t /var/log/apache2/error.log -> tail wird im fortlaufenden Modus gestartet
alias nocomment='grep -Ev '\''^(#|$)'\''' # nocomment /etc/apache2/apache2.conf -> Ausgabe der Zieldatei, allerdings ohne Kommentare
alias lvim="vim -c \"normal '0\"" # erneut die zuletzt geöffnete Datei im vim öffnen

 

 Nützliche "Tools"

alias mkdir="mkdir -pv" # erstellung von rekursiven Ordnern und Anzeige der zuerstellenden Ordner
alias lsmount="mount|column -t" # formatierte Ausgabe von mount
alias path='echo -e ${PATH//:/\\n}' # formatierte Ausgabe des Path

# System
alias meminfo='free -m -l -t' # erweiterte RAM Ausgabe
alias psmem='ps auxf | sort -nr -k 4' # sortierte Ausgabe aller Prozesse nach RAM Auslastung
alias pscpu='ps auxf | sort -nr -k 3' # sortierte Ausgabe aller Prozesse nach CPU Auslastung
alias j="jobs -l" # Ausgabe der aktiven Jobs (Strg+Z, fg)
alias h="history" # Kurzform für Ausgabe der History

# Datum
alias nowtime='date +"%T"' # formatierte Ausgabe der Uhrzeit
alias nowdate='date +"%d-%m-%Y"' # formatierte Ausgabe des Datums

# Netzwerk
alias ports='netstat -tulanp' # Anzeige aller offenen Ports

# Web
alias header='curl -I' # Abruf des Headers von Argument Webseite 
alias headerc='curl -I --compress' # same, allerdings mit Komprimierung
alias apache2test='/usr/sbin/apachectl -t && /usr/sbin/apachectl -t -D DUMP_VHOSTS' # Apache2 Configtest 

function cdl(){ cd "$@"; ls -al;} # Bei der Nutzung von cdl, anstelle von cd, wird nun der Inhalt des Zielordners angezeigt

## Zeigt die aktuelle Verzeichnisstruktur an, sehr huebsch
function tree(){
        pwd
        ls -R | grep ":$" | \
        sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/ /' -e 's/-/|/'
}

# Für die Apt Nutzer unter uns
alias upd="apt-get update" # refresh der sourcen
alias del="apt-get remove " # Entfernen (kein purge!) von Paketen
alias get="apt-get install " # Installation von Paketen
alias upg="apt-get upgrade" # Aktualisierung des Systems 
alias searchpkg="apt-cache search " # Paketsuche
alias update="apt-get update && apt-get upgrade" # Refresh der Sourcen und Aktualisierung der Pakete kombiniert



Wenn man diese Funktionen / Aliases und Variablen in der .bash_alias verankert hat, erspart man sich eine Menge Zeit und kann diese Zeit in die Weiterentwicklung der System-Landschaft stecken.

Tipp: Natürlich gibt es da noch mehr Möglichkeiten,  einfach mal sich selber über die Schulter schauen und überlegen ob man sein Nutzverhalten nicht mit eigenen Aliases/Funktionen noch optimieren kann


Bei Fragen bitte einfach kurz melden :-)

Donnerstag, 3. April 2014

Netzwerk-Debugging mit und unter Linux

Nachdem der letzte Blog-Eintrag schon etwas her ist, möchte ich dieses Mal etwas über (einfaches) Netzwerk-Debugging mit und unter Linux schreiben.

Die typischen Kommandos ping, netstat lasse ich dabei mal außen vor, sondern gehe auf "etwas" speziellere Kommandos bzw. Alternativen ein.

Prüfung auf aktive Hosts/Erreichbarkeit

Ich nutze dafür fping:
Vorteile: Ip-Ranges (Range oder CIDR Notation!), Statistiken und parallele Verarbeitung!
Nachteile: muss meist nachinstalliert werden

1. Single Host
$ fping -s 172.16.1.91

172.16.1.91 is alive

       1 targets
       1 alive
       0 unreachable
       0 unknown addresses

       0 timeouts (waiting for response)
       1 ICMP Echos sent
       1 ICMP Echo Replies received
       0 other ICMP received

 0.98 ms (min round trip time)
 0.98 ms (avg round trip time)
 0.98 ms (max round trip time)
        0.011 sec (elapsed real time)


2. mit IP-Range und Statisktik
$ fping -s -g 172.16.1.90 172.16.1.100 -r 1
172.16.1.91 is alive
172.16.1.92 is alive
172.16.1.93 is alive
172.16.1.94 is alive
172.16.1.95 is alive
172.16.1.97 is alive
172.16.1.90 is unreachable
172.16.1.96 is unreachable
172.16.1.98 is unreachable
172.16.1.99 is unreachable
172.16.1.100 is unreachable
 
      11 targets
       6 alive
       5 unreachable
       0 unknown addresses
 
      10 timeouts (waiting for response)
      16 ICMP Echos sent
       6 ICMP Echo Replies received
       0 other ICMP received
 
 0.38 ms (min round trip time)
 0.75 ms (avg round trip time)
 1.45 ms (max round trip time)
        2.808 sec (elapsed real time)

Verbindungen/Sessions anzeigen

Die meisten werden wahrscheinlich netstat nutzen, ich nehme dafür "ss".
"ss" ist schneller und bietet mehr Möglichkeiten in den Abfragen.


Alle Verbindungen anzeigen
$ ss -a | less
 
State      Recv-Q Send-Q      Local Address:Port          Peer Address:Port
LISTEN     0      0                       *:6080                     *:*
LISTEN     0      0             172.16.0.250:5667                     *:*
LISTEN     0      0                       *:5668                     *:*
LISTEN     0      0                       *:11301                    *:*
LISTEN     0      0               127.0.0.1:smux                     *:*
LISTEN     0      0                       *:mysql                    *:*
LISTEN     0      0                       *:sunrpc                   *:*
LISTEN     0      0                       *:http                     *:*
LISTEN     0      0                       *:ssh                      *:*
LISTEN     0      0                       *:smtp                     *:*
LISTEN     0      0                       *:iscsi                    *:*
TIME-WAIT  0      0             172.16.0.250:http           172.16.0.1:14653
TIME-WAIT  0      0             172.16.0.250:http           172.16.0.14:58166
TIME-WAIT  0      0             172.16.0.250:http           172.16.0.14:58167
TIME-WAIT  0      0             172.16.0.250:http           172.16.0.14:58164
TIME-WAIT  0      0             172.16.0.250:http           172.16.0.1:14654
TIME-WAIT  0      0             172.16.0.250:http           172.16.0.14:58165
<snip>

Lauschende Ports

Alle offenen Ports auflisten
$ ss -l | less
 
State      Recv-Q Send-Q      Local Address:Port          Peer Address:Port
LISTEN     0      50              127.0.0.1:netbios-ssn              *:*
LISTEN     0      128                     *:59532                    *:*
LISTEN     0      128                    :::sunrpc                  :::*
LISTEN     0      128                     *:sunrpc                   *:*
LISTEN     0      128                     *:http                     *:*
LISTEN     0      128                    :::38386                   :::*
LISTEN     0      128                    :::ssh                     :::*
LISTEN     0      128                     *:ssh                      *:*
LISTEN     0      128             127.0.0.1:6010                     *:*
LISTEN     0      128                   ::1:6010                    :::*
LISTEN     0      50              127.0.0.1:microsoft-ds             *:*
LISTEN     0      50              127.0.0.1:mysql                    *:*
Nur TCP auflisten
$ ss -lt | less
 
State      Recv-Q Send-Q      Local Address:Port          Peer Address:Port
LISTEN     0      50              127.0.0.1:netbios-ssn              *:*
LISTEN     0      128                     *:59532                    *:*
LISTEN     0      128                    :::sunrpc                  :::*
LISTEN     0      128                     *:sunrpc                   *:*
LISTEN     0      128                     *:http                     *:*
LISTEN     0      128                    :::38386                   :::*
LISTEN     0      128                    :::ssh                     :::*
LISTEN     0      128                     *:ssh                      *:*
LISTEN     0      128             127.0.0.1:6010                     *:*
LISTEN     0      128                   ::1:6010                    :::*
LISTEN     0      50              127.0.0.1:microsoft-ds             *:*
LISTEN     0      50              127.0.0.1:mysql                    *:*
Nur UDP auflisten
$ ss -lu | less
 
State      Recv-Q Send-Q      Local Address:Port          Peer Address:Port
UNCONN     0      0                       *:bootpc                   *:*
UNCONN     0      0                       *:sunrpc                   *:*
UNCONN     0      0              172.16.0.75:ntp                      *:*
UNCONN     0      0               127.0.0.1:ntp                      *:*
UNCONN     0      0                       *:ntp                      *:*
UNCONN     0      0            172.16.255.255:netbios-ns               *:*
UNCONN     0      0              172.16.0.75:netbios-ns               *:*
UNCONN     0      0                       *:netbios-ns               *:*
UNCONN     0      0            172.16.255.255:netbios-dgm              *:*
UNCONN     0      0              172.16.0.75:netbios-dgm              *:*
UNCONN     0      0                       *:netbios-dgm              *:*
UNCONN     0      0                       *:37222                    *:*
UNCONN     0      0               127.0.0.1:746                      *:*
UNCONN     0      0                       *:858                      *:*
UNCONN     0      0                      :::sunrpc                  :::*
UNCONN     0      0                     ::1:ntp                     :::*
UNCONN     0      0        fe80::250:56ff:feb2:6063:ntp             :::*
UNCONN     0      0                      :::ntp                     :::*
UNCONN     0      0                      :::858                     :::*
UNCONN     0      0                      :::38139                   :::*
Prozess-IDs zu den jeweiligen Diensten mit anzeigen
ss -lp

Anzeige mit Filterung

Die Filter können im iproute(2)-doc nachgeschlagen werden.

Nach Port:
Alle SSH Verbindungen:
$ ss -t src :22
 
State      Recv-Q Send-Q      Local Address:Port          Peer Address:Port
ESTAB      0      0              172.16.0.75:ssh           172.16.0.12:62708
Alle HTTP oder HTTPS Verbindungen
$ ss -t '( src :80 or src: 443 )'
 
State      Recv-Q Send-Q        Local Address:Port          Peer Address:Port
ESTAB      0      0               172.16.0.250:80             172.16.0.239:59538
Nach Adresse in CIDR Notation, nur TCP (-t!)
$ ss -t  dst 172.16.0.0/24
oder single Host:
$ ss -t dst 172.16.0.123
oder mit Host und Port
$ ss -t dst 172.16.0.123:80
Aktive (state established) TCP-Sessions
$ ss -t state established | less
 
Recv-Q Send-Q           Local Address:Port               Peer Address:Port
0      0                   172.16.0.75:58739                 172.16.0.2:microsoft-ds
0      0                   172.16.0.75:51709                 172.16.0.3:microsoft-ds
0      0                   172.16.0.75:39253                 172.16.0.3:1025
0      0                   172.16.0.75:ssh                 172.16.0.123:62708
Mögliche weitere States:
  • established
  • syn-sent
  • syn-recv
  • fin-wait-1
  • fin-wait-2
  • time-wait
  • closed
  • close-wait
  • last-ack
  • closing
  • all - Alle..
  • connected - Alle verbundenen Sessions
  • synchronized - Alle verbundenen Sessions, ohne syn-sent
  • bucket - minisockets, z.B. time-wait und syn-recv.
  • big - „Normale“ sockets

Statistiken

$ ss -s
 
Total: 364 (kernel 449)
TCP:   1272 (estab 19, closed 1240, orphaned 1, synrecv 0, timewait 1238/0), ports 825
 
Transport Total     IP        IPv6
*         449       -         -
RAW       0         0         0
UDP       11        11        0
TCP       32        32        0
INET      43        43        0
FRAG      0         0         0

Verbindungen mitschneiden

Bei Netzwerk/Verständnis-Problemen können Verbindungen mitgeschnitten werden, dazu wird TCPdump genutzt. Die geschriebene Datei kann dann mit WireShark verarbeitet/gelesen werden.
Format:
$ tcpdump -n -i $INTERFACE -s$MAXIMALEPAKETGRÖßE -w $AUSGABEDATEI $FILTER
 
-s = maximale Paketgröße, 0 => 65535 Bytes (empfehlenswert)
-n = keine DNS Auflösung
-i = Interface
-w = Ausgabedatei

Filter

Alles was einen bestimmten Host betrifft (IP oder DNS-Name)
$ tcpdump -ni lanbond0 -w ~/http_mitschnitt.pcap -s0 host 172.16.0.5
Alles was HTTP betrifft, ohne SSH Verbindungen
$ tcpdump -ni lanbond0 -w ~/http_mitschnitt.pcap -s0  port not ssh and port http

Beispiel

Welche Daten werden bei einem Request mittels HTTP und Host 172.16.0.5 übertragen:
$ tcpdump -ni lanbond0 -w ~/http_mitschnitt.pcap -s0 host 172.16.0.5 and port http

Montag, 28. Oktober 2013

Windows Print Server und die Druckerwarteschlange

Ein typisches Problem in der Windows Printserver Welt scheinen fehlerhafte (=hängende) Druckeraufträge zu sein.
Wenn ein Druckauftrag fehlerhaft ist, blockiert er diesen Drucker, bis dieser Druckauftrag entfernt wurde.

Damit blockierte Drucker nicht durch die BenutzerInnen gemeldet werden müssen (immer unschön und wenig professionell), habe ich einen Check in VBSkript für Nagios/NSclient geschrieben, welcher alle registrierten Drucker mittels WMI auf eben diese Aufträge prüft:

' MSDN: http://msdn.microsoft.com/en-us/library/aa394370%28v=vs.85%29.aspx
' Required Variables
' Author: Oliver Skibbe oliskibbe (at) gmail.com
' Date: 2013-10-30
Const PROGNAME = "check_print_spooler"
Const VERSION = "1.1.0"

' Nagios helper functions
Include "C:\Programme\NSClient++\scripts\lib\NagiosPlugins.vbs"

' Arguments
strComputer = WScript.Arguments.Item(0)

' Defaults
return_code = 0
return_msg = "Everythings fine"

' automatically kill job?
killjob = true


' Create WMI object
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

' Create the NagiosPlugin object
Set np = New NagiosPlugin

' Fetch all jobs with status error
Set Result = objWMIService.ExecQuery("Select * From Win32_PrintJob Where Status = 'Error'")

For Each instance In Result

 ' automatic job kill
 If killjob = True Then
  instance.Delete_
 Else
  ' JobId is attached to "caption, description and name" thus we want to split and accessible with printerName(1)
  printerName = Split(instance.Caption,",")
  ' if job should not be automatically killed, print critical and printer name
  failedPrinterStr = failedPrinterStr & " " & Chr(34) & printerName(0) & Chr(34)
  return_code = 2
 End If
next

If return_code > 0 Then
 return_msg = "Job Errors on printer " & failedPrinterStr
End If

' Nice Exit with msg and exitcode
np.nagios_exit return_msg, return_code


Sub Include( cNameScript )
    Set oFS = CreateObject("Scripting.FileSystemObject")  
    Set oFile = oFS.OpenTextFile( cNameScript )
    ExecuteGlobal oFile.ReadAll()
    oFile.Close
End Sub

Dieses Skript sollte über einen Windows-Server z.B. über NRPE (mit NSCLIENT++) aufgerufen werden und nimmt als Argument den Computernamen an.

Dazu nimmt man folgende Einstellungen im NSClient vor (gilt für Version 0.38):
; Script to check external scripts and/or internal aliases.
CheckExternalScripts.dll
 
[NRPE]
;# NRPE PORT NUMBER
;  This is the port the NRPEListener.dll will listen to.
port=5666

;# COMMAND TIMEOUT
;  This specifies the maximum number of seconds that the NRPE daemon will allow plug-ins to finish executing before killing them off.
command_timeout=60

;# COMMAND ARGUMENT PROCESSING
;  This option determines whether or not the NRPE daemon will allow clients to specify arguments to commands that are executed.
allow_arguments=1

[External Scripts]
check_print_spooler=cscript.exe //T:30 //NoLogo scripts\check_print_spooler.vbs $ARG1$

Der aufmerksame Leser wird festgestellt haben, dass dem Spooler ein Argument übergeben werden kann, dieses nimmt den Namen des Servers an, das heißt man kann von einem Server aus viele andere Server angesprochen werden können, dazu muss dann allerdings der NSClient++ Dienst mit einem entsprechenden User gestartet werden, für den rein lokalen Aufruf ("." oder "localhost") reicht das Systemkonto.

Im Nagios muss dann folgendes in die commands.cfg eingetragen werden:

Entweder die Verteilung des Service über einen Host oder eine Hostgruppe (Argument wäre der "Hop"-Server):
define command {
        command_name    check_windows_print_spooler
        command_line    $USER1$/check_nrpe -H $ARG1$ -c check_print_spooler -a $HOSTADDRESS$
}

Oder als rein lokale Variante:
define command {
        command_name    check_windows_print_spooler
        command_line    $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_print_spooler -a "."
}


Folgende Dinge werde ich wohl noch implementieren:
  • Flag für automatisches Löschen der Jobs
  • E-Mail an Druckjobersteller, dass der Job abgebrochen wurde
  • Umbau auf PHP mit Nutzung von "wmic", d.h. Hop-Server wird überflüssig 

* UPDATE:  Damit das Skript besser skaliert, habe ich den Code etwas umgebaut und ein Flag eingebaut, welches den fehlerhaften Job automatisch löscht.

Download: https://dl.dropboxusercontent.com/u/9482545/check_print_spooler.vbs

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.

Mittwoch, 24. Juli 2013

Wie löscht man sicher, effizient und automatisiert Daten

Bei meiner neuen Stelle schnappt man auch viele Dinge nebenbei auf, die verbessert werden wollen.

Ein Beispiel ist das sichere Löschen von Notebooks / PCs / Servern zum Leasing-Ende, damit auf keinen Fall sensible Daten das Unternehmen verlassen und später bei Ebay landen können.

Bisher wurden die Geräte händisch mit einer DBAN (Darik's Boot and Nuke) CD einzeln gestartet und anschließend mit manueller Bestätigung gelöscht.
Als Löschmethode kam dabei DoD 5220.22-M Full zum Einsatz

Kurzer Exkurs zu DBAN:
DBAN unterstützt natürlich noch mehr Methoden:
  • Quick Erase: 1x mit "0" überschreiben, eigentlich nur bei Virenbefall empfehlenswert
  • RCMP TSSIT OPS-II: 8x mit Zufallswerten überschreiben, geht allerdings
  • DoD Short (5220.22-M): Schritt 1 (mit "0" überschreiben), 2 (mit "1" überschreiben) und 7 (mit "Zufalls"werten überschreiben) des "vollen" DoD  5220.22-M
  • DoD Full (5220.22-M): 7x überschreiben
  • Gutmann: 35x Durchläufe (http://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html), wobei der Erfinder mittlerweile selbst gesagt, dass diese Variante über das Ziel hinaus schießt. 
  • PRNG: Pseudo Random Number Generator, wie der Name sagt, wird die Festplatte mit "Zufalls"-zahlen (bei heutigen Rechenwerke gibt es keine echte Zufälligkeit) überschrieben. Die Anzahl der Durchläufe kann angegeben werden.
Meine Empfehlung geht bei weniger sensiblen Daten zu DoD Short / PRNG mit 2-3 Durchläufen und bei sensibleren Daten zu DoD Full / PRNG 4-6 Durchläufe.

Je nach Auswahl der Löschmethode und Größe der Festplatten kann das Löschen der Geräte lange dauern, aber was tut man nicht alles für das sichere Löschen der Daten.


Um die Löschmethode effizienter durchzuführen, habe ich die Idee gehabt, einfach einen PXE-Server mit automatischem Löschen aufzusetzen, dieser soll komplett von der restlichen Netzwerk-Infrastruktur getrennt sein, theoretisch wäre es natürlich möglich den PXE-Server auf bestimmte MAC-Adressen einzuschränken, das führt aber in meinem Fall nicht ans Ziel.


Dazu wird im ersten Schritt ein Ubuntu 12.04 Server installiert, es reicht dabei eine minimale Installation, z.B. auf Oracle VirtualBox oder auf kleiner, energieeffizienter Hardware wie z.B. Raspberry Pi.

Nach erfolgter Installation loggt man sich via SSH oder Lokal auf dem System ein und installiert die nötigen Dienste via apt-get.

# paketquellen erneuern
apt-get update

# Installation tftpd-hpa, isc-dhcp-server, syslinux, wget (optional, wenn dban via winscp
# übertragen wird)
apt-get install tftpd-hpa isc-dhcp-server syslinux wget

# stoppen des dhcp-server Dienstes 
service isc-dhcp-server stop

# stoppen des tftpd-hpa Dienstes
service tftpd-hpa stop


Da nun die Dienste soweit installiert sind, geht es an die Konfiguration eben dieser, dazu werden die entsprechenden Konfiguration mit dem Lieblingseditor geöffnet und bearbeitet.

vim /etc/dhcp/dhcpd.conf (absolute minimal Konfiguration):
default-lease-time 600;
max-lease-time 7200;
authoritative;
log-facility local7;
# Achtung: idealerweise wird ein Netz genommen, welches noch nicht vorhanden ist
# Die Netzmaske muss entsprechend der maximal möglichen PXE-Clients gewählt werden,
# bei mir reicht ein /24 vollkommen aus
subnet 192.168.220.0 netmask 255.255.255.0 {
       range 192.168.220.5 192.168.220.240;
       # hier kommt die eigene IP, also des PXE-Servers
       next-server 192.168.220.1;
       # pre boot loader, kommen wir später zu 
       filename "/pxelinux.0";
}

Die Konfiguration des tftpd-hpa ist, Stand Juli 2013, für unsere Zwecke ausreichend und kann dementsprechend im Standard gelassen werden, ggf. kann die Listen-Address (/etc/default/tftpd-hpa) noch auf

Nun beginnen  die Vorbereitungen für das DBAN PXE-Image, dazu wird das Image von www.dban.org via wget oder lokal heruntergeladen und auf den Server, z.B. unter /usr/src, abgelegt und gemountet.

# Download DBAN
wget -O /usr/src/dban-2.2.27.iso "http://downloads.sourceforge.net/project/dban/dban/dban-2.2.7/dban-2.2.7_i586.iso?r=http%3A%2F%2Fwww.dban.org%2Fdownload&ts=1374667513&use_mirror=surfnet"
# Mount der CD
mount -o loop /usr/src/dban-2.2.27.iso /mnt
# Kopieren der dban Dateien in das TFTP Verzeichnis
cp -r /mnt/* /var/lib/tftpboot

Anschließend brauchen wir noch ein Pre-Boot Environment, dazu bedienen wir uns bei syslinux:
# Kopieren 
cp /usr/lib/syslinux/pxelinux.0 /var/lib/tftpboot
# preboot cfg
mkdir /var/lib/tftpboot/pxelinux.cfg
# einfügen der Konfiguration
echo "DEFAULT autonuke"

LABEL autodban
 KERNEL dban.bzi
 APPEND nuke="dwipe --autonuke --method dod522022m" silent
" > /var/lib/tftpboot/pxelinux.cfg/default

Damit haben wir nun alle nötigen Schritte vorbereitet.

Da ich relativ paranoid bin, habe ich das automatische Starten des DHCP-Servers deaktiviert, dadurch wird man gezwungen den DHCP-Server manuell und dadurch bewusst zu starten.
Wichtig: seit Upstart muss man dieses allerdings an zwei Stellen tun!
# deaktiviere init.d dhcp-server beim boot
update-rc -f isc-dhcp-server remove
# deaktivere upstart dhcp-server beim boot
echo "manual" > /etc/init/isc-dhcp-server.override
# starte tftpd-hpa beim boot
update-rc.d tftpd-hpa defaults

Damit der DHCP-Server gestartet werden kann, muss noch die IP-Konfiguration angepasst werden, dazu wird die Datei /etc/network/interfaces mit dem Lieblingseditor geöffnet und eine statische IP eingetragen werden:
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
        address 192.168.220.1
        netmask 255.255.255.0

Nach einem Reboot haben wir nun alle nötigen Arbeiten erledigt und können das automatische Löschen durch Starten des DHCP-Servers beginnen.

Aber Achtung, verbindet das System niemals, mit gestartetem DHCP-Server, mit dem lokalen LAN, denn der neue DHCP-Server könnte schneller als die anderen DHCP-Server antworten und das Telefon würde anschließend wohl nicht mehr still stehen ;-)

Bei Fragen bitte einfach melden!

Montag, 15. Juli 2013

Wie überwache ich Oracle Datenbanken auf Schwellenwerte

Da wir auf Oracle für unsere Applikationen einsetzen, sollen diese natürlich auch überwacht werden.

Das geht relativ einfach mit SQL-Querys, benötigt wird dazu der Oracle Instant Client:
Da ich ein Fan von Automatisierung bin, habe ich für die Installation unter Ubuntu 12.04 (Debian sollte ebenfalls funktionieren, für CentOS/Oracle/Rehadt muss nur das Umwandeln der RPM Pakete auskommentiert werden) ein Skript erstellt.

Das Skript nutzt in der aktuellen Version eine Art Deploymentserver um die RPM Pakete abzuholen, mit geringem Aufwand kann man aber die Aufrufe von wget auch gegen ein cp o.ä. austauschen.
Vor der Nutzung müssen noch die entsprechenden Variablen angepasst werden:
  • Optional: proxy server
  • IP des Deployment Servers
Zusätzlich gibt es noch Abfragen ob der Instant Client mit PHP (interessant für Abfragen mittels PHP-Skripte) oder ohne und in x64 oder x86 installiert werden soll.

Hier das Skript als Download: prepare_ora_inst.sh


#!/bin/bash
# Author: oliver.skibbe (at) gmail.com
# Purpose: install oracle instant client 
# Date: 2013-05-21 15:00

# Variables
SRC=/usr/src
# deployment server, oracle instant client rpms should be there
deploymentserver="deploymentip/dns"
# http / https
http_type="http"
# basic auth
auth="user:password"
# proxy server for pecl/apt
proxy=""

# oracle versions
major=11.2
minor=0.3.0-1
# ora files
files=('basic' 'devel' 'sqlplus')

echo "####################################################"
echo -e "#### Oracle Instant Client Version $major.$minor ####"
echo "####################################################"

echo "Do you want to install 64-bit or 32-bit oracle instant client? 1/2"
read answer

case "$answer" in 
    1)
        arch=x86_64
        shortarch=64
    ;;
    2)
        arch=i386
        shortarch=
    ;;
    *)
        echo "Wrong answer..try again"
        exit 1
esac

echo "Do you want to install php extension? y/N"
read phpanswer
case "$phpanswer" in
        y*|Y*)
        echo "Installing php extension"
        php=true
        ;;
        *)
        echo "Not installing php extension"
        php=false
esac

if [ "x$proxy" = "x" ]; then
    echo "Adding proxy to apt.conf"
    if [ -d /etc/apt/apt.conf.d/ ] ; then
        echo "Acquire::http::Proxy \"$proxy\";" >> /etc/apt/apt.conf.d/99proxy.conf
    else 
        echo "Acquire::http::Proxy \"$proxy\";" >> /etc/apt/apt.conf
fi

# refresh sources
apt-get update

# install rpm to deb converter
apt-get install alien libaio1

for file in ${files[@]}
do
    filename=oracle-instantclient$major-$file-$major.$minor.$arch.rpm
    wget -c --no-proxy -O $SRC/$filename $http_type://$auth@$deploymentserver/deployment/global/oracle/$filename
    if [ $? != 0 ] ; then
        echo "Downloading $filename from $http_type://$deploymentserver/deployment/global/oracle/ failed!"
        exit 1
    fi
    alien -i $SRC/$filename
done

# libs
echo "/usr/lib/oracle/$major/client$shortarch/lib" > /etc/ld.so.conf.d/oracle.conf
ldconfig
ORACLE_HOME=/usr/lib/oracle/$major/client$shortarch

# log directory
mkdir /usr/include/oracle/$major/client$shortarch/log

# includes
ln -s /usr/include/oracle/$major/client$shortarch $ORACLE_HOME/include

# path variables
cat << EOT > /etc/profile.d/oracle.sh 
export ORACLE_HOME=$ORACLE_HOME
export PATH=\$PATH:\$ORACLE_HOME/bin
EOT
chmod +x /etc/profile.d/oracle.sh

if [ $php = true ] ; then
    echo "php oracle stuff"
    export ORACLE_HOME=$ORACLE_HOME
    if [ "x$proxy" = "x" ]; then
        pear config-set http_proxy $proxy
    fi
    pecl install oci8
    echo "extension=oci8.so" > /etc/php5/apache2/conf.d/oci8.ini

    service apache2 reload
fi

exit 0


Nach dem Ausführen des Skripts sollte die Installation des Oracle Instant Clients erfolgreich erledigt sein, anschließend kümmern wir uns nun um den Abfrageteil:

Hier das Beispiel-Skript als Download:  check_oracle_abfrage.php (Username, Password, IP/DNS, DBName und die Abfrage müssen noch angepasst werden!)

#!/usr/bin/php
<?php
// oracle database
$oracleuser = "user";
$oraclepass = "pass";
$instance = "ip/dbname";

$warn = $argv[1];
$crit = $argv[2];

// connect oracle database
$conn = oci_connect($oracleuser,$oraclepass,$instance);

$query = "select count(*) from table where export_date is null";
$abfrage = oci_parse($conn,$query);
oci_execute($abfrage);
$row = oci_fetch_array ($abfrage, OCI_BOTH);

$count = $row[0];

if ( $count >= $crit ) {
        $exitCode = 2;
        $exitString = "CRITICAL";
} else if ( $count >= $warn ) {
        $exitCode = 1;
        $exitString = "WARNING";
} else {
        $exitCode = 0;
        $exitString = "OK";
}

$perfData = "auftraege=$count;$warn;$crit";

// return values
echo $exitString . " {$count} Auftraege|" . $perfData;
exit($exitCode);

?>


Das Skript muss nun noch ausführbar gemacht (chmod +x) oder direkt via php aufgerufen werden.

 Anschließend solltet ihr ein funktionierendes Oracle Abfragesystem haben.


Bei Fragen bitte einfach melden.