Kleine und große Linux AHAs
Beiträge getaggt mit aufzeichnen
Simpler Zugang zum Netz, oder nicht? CaptivePortal in einfach
23. Feb
Viele kennen es aus Hotels, Bahnhöfen, Cafes oder öffentlichen Plätzen. Es gibt ein offenes WLAN zum Internet und wenn man sich damit verbindet landet man zuerst einmal auf einer speziellen Seite. Auf dieser muss man sich dann irgendwie registrieren oder halt bezahlen. Aber wie funktioniert sowas und macht das vielleicht auch Sinn für ein Unternehmen intern?
Warum kann das unternehmensintern Sinn machen?
Also hier geht es nicht um Provider oder Firmen die das ganze öffentlich anbieten. Es gibt beispielsweise die Anforderung in Unternehmen die Produktionsstraßen besitzen, Großraumhallen, Fertigungshallen oder ähnliches. Oft gibt es dort viele Geräte verschiedener Hersteller die gewartet werden (nur ein Beispiel). Es kommen also externe Dienstleister um Updates einzuspielen oder Support zu leisten. Diese müssen mittlerweile, um vernünftig arbeiten zu können, Kontakt mit Ihrer Firma haben. Das kann über 3G geschehen, ist allerdings schneller über WLAN. Für solche Dienstleister macht ein WLAN Zugang also Sinn. Das auch für einen “normalen” Besprechungs- oder Schulungsraum schon Sinn machen.
Nun hat man ein Problem. Das WLAN offen zu lassen ist quasi grob fahrlässig. Das WLAN schützen macht es umständlich – je nach Anzahl der verschiedenen Benutzer. Dann haben wir da noch den Aspekt des Loggings. Prinzipiell muss man die Verbindungsdaten mitloggen, da man je nach dem in die Beweispflicht genommen werden kann. Um zu loggen muss ich auch wissen wer zu welchem Gerät gehört, da mir ansonsten die Information nichts bringt. Das alles kann ich mit der Anmeldung quasi erledigen. Ich weiß wer zu welchem Gerät gehört und kann auch mitloggen. Bevor der Nutzer sich über das WLAN mit dem Internet verbindet, muss er einer entsprechenden Nutzungserklärung zustimmen (wegen logging etc.).
Wie kann sich so was mit Linux darstellen?
Diese Portale sind gar nicht so kompliziert wie man denkt. Um so etwas unter Linux zu realisieren kann man sich fertiger Software bedienen oder auch einfach das ganze selbst – relativ schnell – programmieren. Ein Projekt mit einer “CaptivePortal” Funktion ist beispielsweise pfsense. Die meisten CaptivePortal Funktionen befinden sich im Bundle mit Firewalls und bieten weit mehr als eine einfach Authentifizierung für einen Netzzugang.
Um so etwas mit Linux Mitteln darstellen zu können habe ich folgende Komponenten genutzt:
- einen Apache Webserver
- MySQL Datenbank
- iptables
- DHCP Server (Standardkonfig mit Netz 192.168.199.0/24)
Das ganze funktioniert nach folgendem Ablauf:
- Gerät verbindet sich mit dem Netz (dabei ist unerheblich ob per WLAN oder per Kabel)
- das Gerät bekommt eine IP per DHCP
- per Default werden alle Verbindungen geblockt, Verbindungen zu Port 80 (egal wohin) werden auf den Webserver des Gateways umgeleitet, bis das Device registriert ist
Zur Registrierung benötigt man einen Schlüssel (5 Stellen, Alphanumerisch). Diesen kann man sich an eine interne Mailaddresse schicken lassen. Also können nur interne Accounts quasi den Zugriff genehmigen. Wenn man einen Schlüssel besitzt, dann kann man sich über das Portal registrieren (Name, Firma und Schlüssel werden abgefragt). Mit der Registrierung – richtiger Schlüssel vorausgesetzt – wird der Zugang für einen Tag freigegeben.
Genauer??
Der Client bekommt eine IP Adresse vom DHCP Server. Alle Zugriffe ins (bspw.) Internet werden erst einmal geblockt, außer Zugriffen auf Port 80 – diese werden umgeleitet auf den Webserver des Gatewyas – das CaptivePortal erscheint. Hat man noch keinen Schlüssel zum registrieren, so muss dieser angefordert werden. Man trägt eine eMail Adresse ein und sendet die Anfrage. Im Hintergrund wird per Perl Script geprüft ob die Empfängeradresse ok ist (bspw. interne Domain oder in einer Liste) und ein Schlüssel generiert. Dieser Schlüssel inkl. Mailadresse wird in der Datenbank hinterlegt und per Mail an den Empfänger geschickt.
Mit dem Schlüssel registriert sich der Client am Portal (inkl. Name, Firma). Mit dem Klicken des Buttons “registrieren” wird der Schlüssel geprüft und deaktiviert. MAC Adresse, Name und Firma des Anforderers werden in die Datenbank geschrieben. Danach wird ein iptables Befehl abgesetzt, der dem Client mit der betreffenden MAC Adresse den Zugriff zum Netz gewährt. Ebenfalls wird in der Datenbank ein Zeitstempel gespeichert, dadurch ist es möglich über ein iptables-update den Zugriff generell nur für eine gewisse Zeit freizugeben und dann automatisch wieder zu sperren. In der Datenbank gibt es ein Flag um zu definieren, ob dieser Eintrag überhaupt abläuft oder nicht (expires yes/no). Damit kann man also auch das ganze für interne Mitarbeiter statisch freigeben, damit diese sich nicht immer wieder neu registrieren müssen.
Die Komponenten
(Die Scripte liegen bei mir unter /opt/captivePortal – der Pfad ist in manchen Scripten zu finden)
iptables
iptables ist ja quasi auf jedem Linux System vorhanden und muss auch nicht groß konfiguriert werden.
Script 1 (Initiales iptables Script / iptables.sh):
#!/bin/bash # Initial Iptables Script to enable captivePortal # # Ronny Becker, 02.2012 IPTABLES=/sbin/iptables # the interface to authenticate PORTAL_INT=eth2 # the interface where traffic goes through OUTPUT_INT=eth0 # PortalIP (for captivePortal website) PORTAL_IP=192.168.199.201 # clear all rules iptables -F iptables -X iptables -t nat -F iptables -t nat -X iptables -t mangle -F iptables -t mangle -X iptables -P INPUT ACCEPT iptables -P FORWARD ACCEPT iptables -P OUTPUT ACCEPT # Create captivePortal chain $IPTABLES -N captivePortal -t mangle # All traffic goes to this chain $IPTABLES -t mangle -A PREROUTING -j captivePortal ###### captivePortal CHAIN ########## # get allowed MACs out of the database (maybe persistent or in time) /opt/captivePortal/iptablesFromDB.pl # DNS is allowed for all $IPTABLES -t mangle -A captivePortal -i $PORTAL_INT -p udp --dport 53 -j RETURN # Mark packets that are not allowed till here $IPTABLES -t mangle -A captivePortal -i $PORTAL_INT -j MARK --set-mark 99 # redirect port 80 to captivePortal $IPTABLES -t nat -A PREROUTING -m mark --mark 99 -i $PORTAL_INT -p tcp --dport 80 -j DNAT --to-destination $PORTAL_IP # drop all marked with 99 $IPTABLES -t filter -A FORWARD -m mark --mark 99 -j DROP # masquerading (if needed) iptables -t nat -A POSTROUTING -o $OUTPUT_INT -s 192.168.199.0/24 -j MASQUERADE
Das iptables.sh Script muss bei jedem Systemstart ausgeführt werden. Des Weiteren muss ip_forward (/etc/sysctl.conf) gesetzt sein, damit der Server routen kann.
Script 2 (Perl Script zum Auslesen der aktuellen Berechtigungen aus der Datenbank / iptablesFromDB.pl):
#!/usr/bin/perl
# captivePortal
# Script to get registered MACs out of DB
use DBI;
# get Config from central conf file
BEGIN { require "/opt/captivePortal/captivePortal.conf" };
# database connection
$connectionInfo="DBI:mysql:database=$db;$host:$port";
$dbi = DBI->connect($connectionInfo,$userid,$passwd);
# select datasets
$sql="select mac from registered where expires='no' or ((UNIX_TIMESTAMP()-timestamp) < $tte)";
$sth = $dbi->prepare($sql);
$sth->execute() or die("Cannot get MACs");
$sth->bind_columns(undef, \$db_mac);
while ( $sth->fetch() ) {
# and run iptables
`/sbin/iptables -t mangle -A captivePortal -m mac --mac-source "$db_mac" -j RETURN`;
}
$sth->finish();
$dbi->disconnect();
Config (Konfig für die Perl Scripte (s. Script 2 / captivePortal.conf):
# captivePortal - central config # this conf file is used in perl scripts use vars qw( $tte $db $host $port $userid $passwd $iptables_if ); # Time To Expire for dynamic subscriptions $tte="86400"; # Database Information $db="captivePortal"; $host="localhost"; $port="3306"; $userid="portal"; $passwd="captive"; # portal interface $iptables_if="eth2";
Script 3 (Updatescript für iptables um abgelaufene Registrierungen zu löschen / iptables_update.sh):
#!/bin/bash # captivePortal # Update iptables rules to delete expired registrations IPTABLES=/sbin/iptables # the interface to authenticate PORTAL_INT=eth2 # clear the chain $IPTABLES -t mangle -F captivePortal # run the rules out of the db /opt/captivePortal/iptablesFromDB.pl # add default rules (see iptables.sh) $IPTABLES -t mangle -A captivePortal -i $PORTAL_INT -p udp --dport 53 -j RETURN $IPTABLES -t mangle -A captivePortal -i $PORTAL_INT -j MARK --set-mark 99
Das Update Script sollte per cron alle Xmin / Xstunden ausgeführt werden, damit abgelaufene Registrierungen den Zugriff verlieren. Wenn man den cronjob nicht gerade jede Minute laufen lässt, ist damit natürlich nicht gewährleistet dass eine Registrierung genau so lange gültig ist, wie es in der Konfiguration steht. In meinem Fall läuft das Script jede Stunde – das ist für meine Zwecke ausreichend.
MySQL
Die Datenbank besteht aus zwei Tabellen -> captivePortal.sql
Apache
Wir benötigen lediglich ein CGI Script. Auf dieses muss per Default umgeleitet werden sobald der Server eine Anfrage bekommt. Das kann man auf verschiedenste Weisen machen – also nicht schimpfen
Redirect
Damit das ganze mit iPhone und Android funktioniert, habe ich folgende Apache config am Start:
<VirtualHost 192.168.199.201:80>
ServerAdmin webmaster@localhost
Servername captivePortal
DocumentRoot /var/www
Redirect /index.html http://192.168.199.201/cgi-bin/captivePortal.pl
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
Options +ExecCGI Indexes -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^/$ http://192.168.199.201/cgi-bin/captivePortal.pl [L,R]
</IfModule>
ErrorLog ${APACHE_LOG_DIR}/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Dem entsprechend liegt das CGI (captivePortal.pl) unter /usr/lib/cgi-bin.
Das CGI
#!/usr/bin/perl
# captivePortal
#
# This is the cgi for the simple captivePortal
#
# Do not forget to change
# line 51: valid domain to send regkey to
# line 58: sender adress
#
#
#
# Ronny Becker, 02.2012
use CGI qw(:all);
use DBI;
# get Config from central conf file
BEGIN { require "/opt/captivePortal/captivePortal.conf" };
# connect to DB
$connectionInfo="DBI:mysql:database=$db;$host:$port";
$dbi = DBI->connect($connectionInfo,$userid,$passwd);
# params
$thisscript=$ENV{'SCRIPT_NAME'};
# cgi params
$para_req_sendto=param('req_sendto');
$para_req_submit=param('req_submit');
$para_reg_company=param('reg_company');
$para_reg_name=param('reg_name');
$para_reg_key=param('reg_key');
$para_reg_submit=param('reg_submit');
# print some headers
print "Content-type: text/html\n\n";
print '
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>WLAN Gastzugang</title>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
</head>
<body style="font-family: courier,courier-new">
<h2>WLAN Gastzugang</h2><br>';
# request for key
if ( $para_req_submit ) {
# address valid?
if ( $para_req_sendto ) {
if ( $para_req_sendto =~ /.*\@mydomain\.de/i || $para_req_sendto =~ /.*\@mydomain2\.de/ ) {
# generate a key
my @chars=('a'..'z','0'..'9');
foreach (1..5) {
$secret.=$chars[rand @chars];
}
# send key to recipient
$result=`export EMAIL="gastzugang\@mydomain.de"; echo "Der Registrierungsschluessel fuer Ihren Gast lautet: $secret" | mail -s "WLAN Gastzugang" $para_req_sendto`;
print "$result";
# Insert Key into DB
$sql="insert into regkeys(regkey,sendto,datetime) values('$secret','$para_req_sendto',NOW())";
$sth = $dbi->prepare($sql);
$sth->execute() or die("Fehler beim schreiben in die Datenbank");
$sth->finish();
print "<div style='background-color: green; color: white'><p><b>Registrierungsschluessel wurde zugeschickt</b></p></div>";
}
}
}
# request for registration
if ( $para_reg_submit ) {
# name given?
if ( length($para_reg_name) <= 5 || length($para_reg_key) != 5 ) {
print "<div style='background-color: red; color: white'><p><b>Sie muessen einen Namen und den Registrierungsschluessel angeben.</b></p></div>";
} else {
# Check DB for regkey
$sql="select sendto from regkeys where regkey='$para_reg_key' and isActive='1'";
$sth = $dbi->prepare($sql);
$sth->execute() or die("DB: Cannot run select");
$sth->bind_columns(undef, \$db_sendto);
$sth->fetch();
$sth->finish();
if ( $db_sendto ) {
# disable key
$sql="update regkeys set isActive='0' where regkey='$para_reg_key'";
$sth = $dbi->prepare($sql);
$sth->execute() or die("DB: Cannot update");
$sth->finish();
# get MAC
open (ARP,"</proc/net/arp");
while (<ARP>) {
$line = $_;
$line =~ s/ */;/g;
($arp_ip,undef,undef,$arp_mac,undef,undef) = split(";",$line);
if ( $arp_ip eq $ENV{'REMOTE_ADDR'} ) {
$mac=$arp_mac;
}
}
close ARP;
# write data into db
$sql="insert into registered(name,guestof,mac,timestamp,expires) values('$para_reg_name, $para_reg_company','$db_sendto','$mac',UNIX_TIMESTAMP(),'yes')";
$sth = $dbi->prepare($sql);
$sth->execute() or die("DB: Cannot insert");
$sth->finish();
$output=`sudo /sbin/iptables -I captivePortal 1 -t mangle -m mac --mac-source $mac -j RETURN`;
print "<div style='background-color: green; color: white'><p><b>erfolgreich registriert</b><br>$output</p></div>";
} else {
print "<div style='background-color: red; color: white'><p><b>Registrierungsschluessel nicht gefunden</b></p></div>";
}
}
}
# disconnect from DB
$dbi->disconnect();
# print form etc.
print '
<form method=get action='.$thisscript.'>
<b>Registrierungsschluessel anfragen</b><br>
senden an: <input type=text name=req_sendto size=40> <input type=submit name=req_submit value=zusenden></form>
<hr><br>
<form method=get action='.$thisscript.'>
<b>Registrieren</b><br>
<b><u>Achtung! Registrieren Sie sich mit dem Geraet, mit dem Sie Zugriff benoetigen</b></u><br>
<table>
<tr><td>Name:</td><td><input type=text name=reg_name size=30></td></tr>
<tr><td>Firma:</td><td><input type=text name=reg_company size=30></td></tr>
<tr><td>Registrierungsschluessel:</td><td><input type=text name=reg_key size=10></td></tr>
<tr><td colspan=2 align=center><font color=red><b><u>Nutzungsbedingungen:</u><br>
Mit klicken auf den Button "registrieren" stimmen Sie zu,dass die ihre Verbindungsdaten (Zeitpunkt und Dauer einer Verbindung, Addressierungsdaten) mitprotokolliert werden.
Dies dient lediglich zur Fehleranalyse und zur Sicherheitsueberpruefung.
Es werden keine Daten an dritte weitergegeben oder weiterverarbeitet.<br><br><br>
Sie haben (per Default) ab dem Zeitpunkt der registrierung Zugriff fuer 24 Stunden.
<input type=submit name=reg_submit value=registrieren></td></tr>
</table>
</form>
</body>
</html>';
Das Webinterface ist recht einfach gehalten damit es auch mit mobilen Endgeräten keine Probleme gibt. Das ganze ist natürlich nach belieben anzupassen oder zu ändern
Ich hoffe ich hab nix vergessen – falls doch einfach fragen.
Unleserliches debuggen, oder: Fehlersuche in verschlüsselten Protokollen
03. Mai
Immer mehr Datenverkehr wird verschlüsselt. Einerseits natürlich richtig und gut. Andererseits ein Problem. Früher konnte man mit Hilfe von Tools wie Wireshark (Ethereal) oder tcpdump relativ schnell Probleme (z.B.) in der Kommunikation zwischen Programm und Server herausfinden. Einfach den Netzwerkverkehr mitschneiden, ansehen / auswerten und es zeigten sich Fehler die man im Programm möglicherweise nicht zu Gesicht bekommen hat.
Mittlerweile sind die Pakete weitestgehend verschlüsselt und man kann nicht mehr einfach so “mit schreiben” – naja, man kann natürlich; aber lesen kann man es nicht. Also fällt ein mitschreiben – einfach mal so – flach.
Eine andere Möglichkeit die Kommunikation zwischen Programmen zu debuggen ist, sich einfach manuell zu verbinden und die Kommandos direkt einzugeben um zu prüfen ob der Server richtig reagiert. Dazu nahm man früher telnet … Auch telnet ist kein Kandidat mehr für verschlüsselte Protokolle.
Verbindung zu einem SSL geschützten Dienst
Eine Möglichkeit sich auf einen solchen Dienst zu verbinden bietet der Befehl
openssl s_client -connect <Zielrechner>:<Port>
Mit diesem Befehl kann man sich beispielsweise direkt auf einen per SSL geschützten IMAP Server (Port 993) verbinden und manuell Kommandos absetzen. Das ganze über eine SSL geschützte Verbindung – die nicht mitgeschnitten werden kann
Ganz wie früher mit telnet *freu*
Piwik Selbstversuch: Sehr schön – und diese Farben …
22. Feb
Durch einen Planetenartikel bin ich auf Piwik aufmerksam geworden (ja ok, Denny hatte mir schon vorher mal davon erzählt).
Da ich selbst einen Blog schreibe (Ohhh, echt?) interessiere ich mich natürlich schon dafür, wer sich so auf meiner Seite tummelt. Das ist rein fürs Ego, da sich auf meiner Seite ja keine Werbung befindet
Bisher hatte ich ein Plugin für WordPress genutzt – StatPress – welches mir einige Informationen lieferte, jedoch lange nicht so hübsch wie Piwik. Des Weiteren liefert Piwik auch einige Details mehr über den Browser bzw. das Surfverhalten.
Also gesagt getan Piwik auf meinem Webspace installiert. Dann ein WordPress Plugin (WP Piwik) dazu, welches mir das Editieren von Header / Footer Seiten abnimmt und mich lediglich nach der URL und einem authkey für die Piwik Installation fragt. Alles prima – es funktioniert und ständig trudeln neue Datensätze ein.
Warum nochmal?
Warum ich das ganze nochmal hier schreibe? Ich sehe das ganze ein wenig mehr aus Firmensicht. Es gibt kommerzielle Anbieter für diese Dienste, die teilweise mehr oder weniger genau das können, was man mit Piwik bewerkstelligen kann. Diese verlangen jedoch Geld dafür – naja sagen wir mal häufig.
Einer der bekannten Anbieter ist Google mit Analytics. Dieses Produkt ist sehr umfangreich und gut – man liefert jedoch Google auch einen Haufen an Daten die man vielleicht nicht unbedingt weitergeben möchte, bzw. müsste man an sich den Benutzer fragen ob diese Daten bei Google landen dürfen. Es ist klar, dass auch ich “meine Benutzer” nicht gefragt habe ob Ihre Daten in Piwik landen dürfen, jedoch werden die Daten bei mir anonymisiert und nicht weitergegeben. Wie das bei Google oder anderen aussieht …
Eine der Funktionen die für eine Firma interessant sein kann sind die “Ziele” in Piwik. In anderen Systemen kann das ganze auch “Kampagne” genannt werden. Mit dieser Funktion ist es möglich beispielsweise eine URL zu definieren, die genau “getrackt” werden soll. Ein Beispiel: Man erstellt einen Newsletter und möchte wissen, wie viele Leute diesen öffnen, bzw. einen speziellen Link anklicken. Das kann man erreichen in dem man ein Bild innerhalb des Newsletters hinterlegt und dieses Bild als Ziel in Piwik einrichtet. Damit kann genau verfolgt werden wie oft dieses Bild aufgerufen wurde. Das ganze funktioniert auch mit Downloads (Dateien) und mit quasi jeder URL einer Seite.
Das ganze natürlich nur neben den bereits bekannten Funktionen wie Anzahl Zugriffe (heruntergebrochen in diversen Levels), Browsertyp, Betriebssysteme, installierte Plugins, Bildschirmauflösung, Besucher nach Land, Besucher nach Datum / Zeit …….
Erweiterbar
Da Piwik mit Plugins arbeitet kann man mit Sicherheit auch eigene Erweiterungen entwickeln. Das hab ich jedoch nicht ausprobiert.
Was mir da eher in den Sinn kommt ist, dass man durch die MySQL Datenbank natürlich auf alle Daten zugreifen kann. Damit kann man – gerade wenn es um Unternehmen geht – eigene Reports nach ganz eigenen Vorstellungen erstellen.
Compiz: “Ein Stück vom Bildschirm”-Shot
22. Okt
Heute hab ich eine durchaus interessante Funktion in Compiz gefunden. Ich hatte erst vor kurzem wieder “das große” Effekt-Paket (Visuelle Effekte -> Extra) aktiviert. Damit funktioniert dann der Standard Shortcut zum Screenshot erstellen nicht mehr – das war mir bereits von vorherigen Versuchen bekannt.
Nun habe ich heute einen Screenshot benötigt und wollte prüfen wieso der Shortcut Alt+Druck nicht mehr funktioniert. Dabei bin ich über den ccsm (Compiz Einstellungs Manager) auf ein Modul “Bildschirmfoto” gestoßen.
Sobald dieses Modul aktiviert ist, kann man mit – per Default – gehaltener Super Taste einen Bereich auf dem Desktop mit der Maus markieren. Dieser wird dann als Screenshot1.png auf dem Desktop abgelegt. Da man meistens nur einen Teil des Bildschirms benötigt, finde ich diese Funktion wirklich nützlich. Die Tastenkombination kann natürlich innerhalb des Moduls geändert werden.
(x)clip (x)clip hurra: Mediathek Download v2
20. Mai
Viele Kommentare gab es zu “Mediathek Download” (s. Artikel) – auch mit vielen Tipps und Hinweisen. Vielen Dank dafür!!
Einen Verbesserungsvorschlag habe ich nun umgesetzt. Aicke hat vorgeschlagen den Weg für das Einfügen der .asx URL über xclip abzukürzen. Naja, da hat er auch Recht. Warum sollte man die URL erst kopieren und dann einfügen, wenn das eigentlich auch das Script übernehmen kann?!
Die neue Version hat also quasi 3 Modi:
- das Script fragt über einen Dialog nach der URL
- das Script wird über die Kommandozeile mit Parameter die URL gestartet (Debug)
- oder und neu: erst die URL in die Zwischenablage kopieren und dann das Script starten
Sobald sich in der Zwischenablage ein String mit der Endung .asx befindet, startet der Download ohne weitere Interaktion.
#!/bin/bash
#
# Download Script fuer .asx URLs (embedded .wmv)
#
# Download aus der ZDF Mediathek:
# - HTML Version der Mediathek aufrufen
# - Beitrag auswaehlen
# - Windows Media Player Link (DSL 1000 / DSL 2000) kopieren
# - Script aufrufen ... Download startet automatisch
# oder
# - HTML Version der Mediathek aufrufen
# - Beitrag auswaehlen
# - Windows Media Player Link (DSL 1000 / DSL 2000) kopieren
# - den Link als Parameter dem Script uebergeben (debug)
# oder (ohne xclip)
# - HTML Version der Mediathek aufrufen
# - Beitrag auswaehlen
# - Windows Media Player Link (DSL 1000 / DSL 2000) kopieren
# - Script aufrufen und Link einfügen
# - Download starten
#
# Prerequisites:
# - mencoder
# - curl
# - zenitiy
# - .asx URL (linked to a .wmv Stream)
# - xclip (optional)
#
# Ronny Becker, 01.2010
#
# Update: xclip integriert
# Ronny Becker, 05.2010
# grab URL
which xclip >/dev/null 2>&1 && CLIPBOARD="`xclip -o`"
if [[ $CLIPBOARD =~ \.asx ]]
then
URL=$CLIPBOARD
fi
if [ ! $1 ] && [ ! $URL ]; then
URL=`zenity --entry --text "Bitte die URL (.asx) eingeben:" --title "Mediathek Downloader" --width=400`
else
if [ ! $URL ]
then
URL=$1
fi
fi
# check if a URL was entered
if [ ${#URL} -eq 0 ]; then
zenity --error --title "Mediathek Downloader" --text="Keine gueltige URL ($URL)"
exit
fi
# get mms:... out of .asx
URL=`curl -s ${URL} | grep -o "mms:.*\.wmv"`
# get filename from the origin; without .wmv
OUTFILE=`basename $URL | sed 's/\.wmv//'`
# download and save; show progressbar
# if you need debugging, please comment out the progressbar function
# beginning at "| awk ..."; then you get all to console output
if [ $1 ]; then
mencoder -ofps 25 -oac lavc -ovc lavc -of mpeg -mpegopts format=dvd -vf harddup\
-srate 48000 -af lavcresample=48000,volnorm=1\
-lavcopts vcodec=mpeg2video:vrc_buf_size=1835:vrc_maxrate=15360:vbitrate=15360:keyint=15:acodec=ac3:abitrate=192\
-o "${OUTFILE}.mpg"\
$URL
else
mencoder -ofps 25 -oac lavc -ovc lavc -of mpeg -mpegopts format=dvd -vf harddup\
-srate 48000 -af lavcresample=48000,volnorm=1\
-lavcopts vcodec=mpeg2video:vrc_buf_size=1835:vrc_maxrate=15360:vbitrate=15360:keyint=15:acodec=ac3:abitrate=192\
-o "${OUTFILE}.mpg"\
$URL 2>&1 | awk -vRS="\r" '$1 ~ /Pos/ {gsub(/Pos:/," ");gsub(/%\)/," ");gsub(/ \(/," ");print $3"\n#Position
\t"$1"\\nFrame
\t"$2"\\nPercentage
\t"$3"%\\nFrame Rate
\t"$4"\\nTime Remaining
\t"$6; fflush();}' | zenity --progress --auto-kill --auto-close
fi
sftp Server Teil II – “Jail made easy”
08. Feb
Im ersten Teil haben wir das aktuelle openssh Paket für RHEL gebaut und sollten das auch mittlerweile installiert haben. Die Konfiguration des SSH-Servers hat sich nur wenig geändert. Es gibt eigentlich auch nur wenig neue Stellen die hier für uns interessant sind. Aber wir beginnen am Anfang:
Gruppe für sftp
Die Steuerung ob ein Benutzer in einer “gechrooteten” Umgebung landet wird über eine Gruppe gesteuert. Diese könnte man beispielsweise “sftpchroot” nennen. Diese Gruppe wird also angelegt:
groupadd sftpchroot
Benutzer anlegen
Als nächstes muss der Benutzeraccount angelegt werden, der sich auch in der Gruppe sftpchroot befindet:
useradd testuser -G sftpchroot
(Passwort setzen oder disablen, je nach dem ob sich per Key oder Passwort eingeloggt werden soll. Eine Shell benötigt der User auch nicht unbedingt.)
Benutzerhome anpassen
Das Benutzerhome (/home/testuser) muss von den Rechten her angepasst werden, damit das “chrooten” funktioniert. Gleichzeitig legen wir ein Verzeichnis (exchange) an, in welches der Benutzer über sftp schreiben darf.
chown root:sftpchroot /home/testuser mkdir /home/testuser/exchange chown testuser:testuser /home/testuser/exchange
Möchte man die Authentifizierung per Key machen, so muss folgendes ebenfalls eingerichtet werden:
mkdir /home/testuser/.ssh chown testuser:testuser /home/testuser/.ssh
Der Key kommt dann in die authorized_keys; die Rechte werden auf 0600 gesetzt und fertig.
Logging
Wir möchten genau sehen, welche Befehle auf dem SFTP Server abgesetzt werden. Daher muss auch das Logging aufgesetzt werden. Dazu ist es nötig, dass in jedem Home ein dev Verzeichnis angelegt wird (-> mkdir /home/testuser/dev). Dort wird dann vom dem Syslog Server des Vertrauens eine Pipe angelegt und damit ist das Logging im System.
Wir verwenden den syslog-ng (syslog-ng). Die Konfiguration dafür sieht wie folgt aus:
source s_sftpchroot {
# Please add each chrooted environment here
# These logs stay on the local machine; not sended to the syslog-server
unix-stream ("/home/testuser/dev/log");
};
destination d_sftplog { file("/var/log/sftp_chrooted.log"); };
log { source(s_sftpchroot); destination(d_sftplog); };
Jede Chroot Umgebung muss hier im Abschnitt “source” eingetragen werden. Damit fließen alle sftp logs in eine Datei. Alternativ kann natürlich auch für jeden Benutzer ein eigenes Log geschrieben werden. Welche Konfigteile dazu angepasst werden müssen sollte ersichtlich sein
Openssh Chroot
Nun zu dem früher schwierigsten, heute jedoch einfachsten Teil. Das Openssh Chroot. Ich zeige einfach die Konfiguration – it`s that easy !
Subsystem sftp internal-sftp Match group sftpchroot X11Forwarding no AllowTcpForwarding no ForceCommand internal-sftp -l VERBOSE ChrootDirectory /home/%u
Mit dieser Konfiguation am Ende der sshd_config (/etc/ssh) bekommt jeder Benutzer, der sich in der Gruppe sftpchroot befindet, besondere Optionen. Unter anderem wird lediglich der interne sftp-server (internal-sftp) zugelassen, der mit dem Parameter “-l VERBOSE” sämtliche Befehle des Benutzers loggt. Fertig.
Aufzeichnen von Administratortätigkeiten II
05. Okt
“timing_shorter.sh”
Cooler Name für etwas ganz einfaches. Man wird schnell feststellen, dass das abspielen einer Aufnahme schnell “langweilig” wird, da alle Pausen in voller Länge aufgezeichnet wurden. Wenn also jemand 30 Minuten lang nichts macht, dann sind diese 30 Minuten 1-1 in der Aufzeichnung.
Um dieses Problem zu umgehen läuft vor dem Cleanup Script der “timing_shorter”. Dieses Script erkennt alle Zeilen innerhalb der .timing Datei die > 3 Sekunden sind; diese werden auf 3 Sekunden gekürzt. Damit sind die Pausen maximal 3 Sekunden lang.
#!/bin/bash
# This reduces the time for typescript
# pauses (when user gives no input and
# system gives no output) to max 3 secs
# by changing the .timing file.
#
# No changes to the typescript !
#
# Ronny Becker, 09.2009
#set -x
for TIMINGFILE in `find /adminmon/timing/ -mmin +300`
do
TIMINGLINE=0
cat $TIMINGFILE | while read LINE
do
(( TIMINGLINE++ ))
if [ ${LINE%\.*} -gt 3 ]; then
sed -i "${TIMINGLINE}s/^.*\./3\./" $TIMINGFILE
fi
done
done
Das Script sollte vor dem Cleanup laufen.
Das Cleanup Script
Wir möchten die Dateien die während einer Aufzeichnung entstehen verwaltbar machen. Dazu sollen die zusammengehörigen Dateien gepackt werden – die Originale werden dann gelöscht. Womit das Problem des Zugriffs ( auf die .timing Dateien ) auch spätetstens gelöst ist.
#!/bin/bash
ADMINMON_PATH="/adminmon"
for SCRIPTFILE in `find ${ADMINMON_PATH}/output/ -mmin +300`
do
MAINNAME=`basename $SCRIPTFILE`
MAINNAME=${MAINNAME%\.*}
echo $SCRIPTFILE
echo $MAINNAME
if [ -e ${ADMINMON_PATH}/timing/${MAINNAME}.timing ]; then
tar -czf ${ADMINMON_PATH}/pairing/${MAINNAME}.tgz ${ADMINMON_PATH}/timing/${MAINNAME}.timing $SCRIPTFILE
if [ $? != 0 ]; then
echo "Fehler bei tar $MAINNAME"
continue
else
rm -f ${ADMINMON_PATH}/timing/${MAINNAME}.timing $SCRIPTFILE
fi
else
tar -czf ${ADMINMON_PATH}/pairing/${MAINNAME}_notiming.tgz $SCRIPTFILE
if [ $? != 0 ]; then
echo "Fehler bei tar $MAINNAME"
else
rm -f $SCRIPTFILE
fi
fi
done
Das Script einfach per cron alle X ausführen und gut.
Aufzeichnen von Administratortätigkeiten
28. Sep
Nahezu jeder Admin ist heute mit dem Problem der “Kontrollen” und “Nachweispflicht” konfrontiert. Dabei sind Changes / Changemanagement der Anfang und das komplette “Monitoring der Administratortätigkeiten” die logische Konsequenz. Dazu werden beispielsweise auf einem Terminalserver alle Sitzungen per Video aufgezeichnet. Was ich nun suchte war eine Möglichkeit die kompletten Bildschirmausgaben einer SSH Sitzung aufzuzeichnen. Damit könnten dann sämtliche Befehle … recht einfach nachvollzogen werden, bzw. sogar zur Fehlerfindung genutzt werden wenn der Admin Unsinn gemacht hat und selbst nachvollziehen möchte was er eigentlich gemacht hat.
Ob das ganze sinnvoll oder nicht ist, das soll an dieser Stelle nicht diskutiert werden. Es geht darum eine Lösung für diese Anforderung zu finden.
Ich habe lange nach einer Lösung gesucht. Die gibt es auch. Es sind die Boxen zweier Hersteller, die den kompletten ssh Traffic, teilweise sogar inklusive X11 und auch anderer Protokolle aufzeichnen. Schaut man sich jedoch die Preise für eine solche Lösung an, dann überlegt man sich doch zweimal, ob man wirklich so viel Geld dafür ausgeben möchte / muss. Eines vorweg: Die Boxen wurden von mir nicht getestet, da das ganze preislich völlig uninteressant war.
Also wurde Dr. Google bemüht und auch diverse Kollegen wurden gefragt – leider nirgendwo eine wirkliche Lösung.
Naja, wie man es so aus der Community gewöhnt ist wird sich dann eben selbst hingesetzt und versucht etwas zu “bauen”. Ganz neu erfinden musste ich das ganze nicht. Es gab – wie so schön unter Linux – schon diverse Möglichkeiten und Tools. Dabei ist eines das zentrale und wichtigste: script. Mit Hilfe von script ist es möglich z.B. Befehlsfolgen aufzuzeichnen inkl. der Ausgabe. Wobei die Ausgabe ebenfalls alls Steuerzeichen für die Ausgabe enthällt. Im Endeffekt entsteht ein “Video” der Ausgabe, welches man beispielsweise zum Support schicken kann.
Das Programm um die Aufnahmen zu erstellen und abzuspielen war also gefunden. Als nächstes musste eine Lösung gefunden werden um die Aufzeichung automatisch und auch sicher zu starten. Es bringt nur relativ wenig wenn ich von einem Benutzer erwarte einen Befehl zu starten wenn er sich anmeldet, bzw. wäre es auch ein wenig albern das ganze als bsp. SOX konformes Monitoring zu deklarieren, wenn ich einfach den Prozess beenden kann und dem entsprechend dann machen kann was ich möchte ohne dass das ganze aufgezeichnet wird. Dazu wurden diverse Schritte nötig die im folgenden Howto enthalten sind.
Prerequisites
- jumphost (Server, über den alle ssh Zugriffe auf Server gestartet werden; dieser Host sollte der einzige sein, der Zugriff auf die Server hat (iptables, …))
- script (normalerweise in den linux-utils Paketen enthalten)
- das wars schon
Technische anpassungen
Anpassen der /etc/bashrc
Um den Benutzern nach der Umstellung weiterhin zu erlauben eigene alias Definitionen, bzw. Funktionen zu setzen, wird in der /etc/bashrc eine Kleinigkeit hinzugefügt:
# additional aliases to be set by the user
if [ -e /home/$USER/.bashrc_user ]; then
. /home/$USER/.bashrc_user
fi
Dadurch kann jeder Benutzer sich in der Datei ~/.bashrc_user seine eigenen Definitionen hinterlegen.
Anpassen der ~/.bashrc
Meldet ein Benutzer sich am Jumphost an, so muss das Programm “script” automatisch gestartet werden. Dies wird über die ~/.bashrc realisiert. Diese sieht aus wie folgt:
# jumphost configuration
#
# If you want to set own alias definitions, please
# create a file .bashrc_user in your homedirectory !!
#
script -c "/bin/bash --rcfile /etc/bashrc" -f -q -t 2>/adminmon/timing/`date +%Y%m%d_%H%M%S`-$$-$USER.timing /adminmon/output/`date +%Y%m%d_%H%M%S`-$$-$USER.script
wait
exit
Verzeichnisse
Die Daten (.script, .timing) werden in eigenen Verzeichnissen gesammelt. .script in einem Verzeichnis, welches nur von root gelesen und beschrieben werden darf, und eines für die .timing Dateien auf die der Benutzer kompletten Zugriff hat. Eigentlich könnte man sagen, dass doch beide Dateien in einem Verzeichnis liegen könnten, auf die der Benutzer selbst keinen Zugriff hat – richtig. Leider funktioniert das nicht mit script. script schreibt die Bildschirmausgaben per Parameter in eine Datei – also mit den Rechten des Benutzerkontextes unter dem script gestartet wird. Die Timing Informationen werden jedoch als Ausgabe auf STDERR von script ausgegeben und umgelenkt (s. Befehl in der .bashrc) – die .timing Datei wird also mit den Rechten des Benutzers geschrieben. script wird während der Konfiguration (s.u.) die setuid root Rechte bekommen. Dadurch wird das Programm zwar vom Benutzer gestartet, allerdings mit root Rechten. Das gilt wie eben beschrieben für die Ausgabedatei der Bildschirmdaten, jedoch nicht für die Umleitung der Timing Informationen. Daher die Verzeichnisse mit den verschiedenen Rechten.
[root@machine adm32]# ls -la /adminmon/
total 20
drwxrwxrwx 4 root root 4096 Sep 25 11:05 .
drwxr-xr-x 25 root root 4096 Sep 25 11:05 ..
drwx------ 2 root root 4096 Sep 25 14:22 output
drwxrwxrwx 2 root root 4096 Sep 25 14:22 timing
script setuid root
Aus oben genanntem Grund muss /usr/bin/script das setuid root Bit bekommen.
[root@machine adm32]# chmod u+s /usr/bin/script
Überwachungsscript
Das Überwachungsscript ist für die Überwachung der angemeldeten Benutzer verantwortlich. Überwachung in dem Sinne, als dass jeder Benutzer einen laufenden script Prozess besitzen muss – ansonsten wird er automatisch abgemeldet.
#!/bin/bash
# Security Script to check if all users are
# monitored by script to get typescripts of
# their doing.
#
# If a user runs a session without this, the
# user session will be terminated.
#
# Ronny Becker, 09.2009
#set -x
NOT="root bin daemon adm shutdown halt mail uucp operator ftp nobody dbus avahi mailnull smmsp nscd ntp vcsa rpc rpcuser nfsnobody sshd pcap haldaemon rpm xfs gdm"
# do a dumb loop
while [ 0 == 0 ]; do
# get processlist
PSDATA=`ps a -o user,tname,pid,command | sed 's/ *//'`
# users online ?
# to get unique users, set username = username(p)tty;
for ACTUSER in `who | awk '{print $1$2}'`; do
# if the user is in NOT list - continue
if [[ $NOT =~ ${ACTUSER%pts*} ]]; then
continue
else
# get proc information
DATA=`echo "$PSDATA" | grep "pts${ACTUSER#*pts}"`
if [[ $DATA =~ script\ -c\ /bin/bash\ --rcfile\ /etc/bashrc\ -f ]]; then
# delete /dev/null if you want to have more logging
echo "$ACTUSER: Proc found" > /dev/null
else
echo "$DATA" | while read PROCDATA; do
if [[ $PROCDATA =~ \-bash ]]; then
PID2KILL=`echo "$PROCDATA" | awk '{print $2}'`
# check if proc ist running > 10 secs
TIMEDIFF=$((`date +%s` - `stat --format=%Z /proc/${PID2KILL}`))
if [ $TIMEDIFF -lt 5 ]; then logger -t script_security -p local0.notice "User: ${ACTUSER%pts*} Msg: lt 5 secs without"; continue; fi
echo "Diese Session wird nun geschlossen, da die Protokollierung nicht mehr sichergestellt ist." | write ${ACTUSER%pts*}
sleep 3
kill -9 $PID2KILL
logger -t script_security -p local0.warning "User: $ACTUSER Msg: User killed PID: $PID2KILL"
fi
done
# delete /dev/null if you want to have more logging
echo "$ACTUSER: No proc found" > /dev/null
fi
fi
done
sleep 5;
done
Das Script überprüft alle 5 Sekunden den Status der Benutzer und loggt eventuelle “Zwischenfälle” per syslog. Zusätzlich müssen Benutzer mind. 5 Sekunden lang im System angemeldet sein, bevor die Session geschlossen wird. Das kommt daher, dass ein Benutzer den script Befehl erst während der Anmeldung startet und ansonsten die Session beendet werden könnte bevor er wirklich angemeldet ist.
Dieses Script muss quasi immer im Hintergrund laufen (als root). Als Daemon überwacht es dann jede Session.
Hilfe von cfengine
Bei einigen der “tasks” kann cfengine gute Dienste leisten. z.B. wenn es darum geht die ~/.bashrc eines jeden Benutzers zu prüfen oder die Verzeichnissrechte unterhalb von /adminmon. Desweiteren kann cfengine prüfen ob das Überwachungsskript läuft.
Abspielen einer Aufnahme
Aufnahmen werden mit Hilfe von “scriptreplay” abgespielt.
scriptreplay 20090925_142228-8287-adm31.timing 20090925_142228-8287-adm31.script
Um eine Aufnahme während der Wiedergabe anzuhalten, genügt der Shortcut STRG+S (Pause); zum Fortfahren STRG-Q.
Noch offen
- Cleanup Script erstellen (die zusammengehörigen .script / .timing Dateien sollten in ein .tar gepackt werden)
- Script erstellen um größere Wartezeiten in denen nichts passiert zu minimieren. Dazu werden einfach alle Werte (z.B.) > 5 innerhalb der .timing Datei auf 5 gesetzt.







