Chrooted SFTP User in Ubuntu

Kurzanleitung zur Einrichtung von Useraccounts, die nur sftp Zugang haben und in ihrem Homeverzeichnis eingesperrt sind. Die Anleitung orientiert sich stark an http://www.ericstockwell.com/?p=54. Wir verzichten auf Tools wie scponly, die meiner Meinung nach, ein unverschämtes UI haben.

Wir müssen zwei Dinge tun:

  1. Einen User anlegen und die richtigen Einstellungen vornehmen.
  2. Den SSH Daemon so umkonfigurieren, dass er den User in ein Chroot einsperrt.

Fall noch nicht geschehen, dann muss ssh installiert werden:

# apt-get install ssh

User anlegen

SSH ist sehr sensibel gegenüber Dateirechten, deshalb müssen wir sicherstellen, dass der Ordner in den wir den User einsperren wollen die richtigen Rechte gesetzt hat. Erstmal legen wir den User an, erzeugen sein Homeverzeichnis (das Verzeichnis in welches er eingesperrt werden soll), setzen das Passwort und dann stellen wir sicher, dass die Dateirechte richtig sind. Das Homeverzeichnis muss root gehören, und nur root hat Schreibrechte, die Gruppe ist im Prinzip egal, aber es macht Sinn die Gruppe des Users zu verwenden.

# useradd myuser
# mkdir /home/myuser
# usermod -d /home/myuser myuser
# passwd myuser
# chown root:myuser /home/myuser
# chmod 755 /home/myuser

Einer der wichtigsten Schritte ist es dem User die Loginshell wegzunehmen.

# usermod -s /bin/false myuser

/bin/false ist ein Kommando, das nichts tut und das auch noch unerfolgreich (siehe Manpage false(1)), d.h. wenn der User sich versucht einzuloggen, dann wird /bin/false ausgeführt, das schlägt fehl und der User wird sofort wieder rausgeworfen. Das sollte man vielleicht mal ausprobieren mit

$ su - myuser

Auch ein Loginversuch über ssh kann das Vertrauen erhöhen. Hier erfolgt der Rauswurf erst nachdem man versucht hat Befehle abzusetzen.

$ ssh myuser@host

SSHD einrichten

Beim SSHD müssen wir zwei Dinge abändern, erstens das Subsystem und zweitens müssen wir SSH sagen, dass wir myuser in sein Homeverzeichnis sperren wollen. Beides wird in der /etc/ssh/sshd_config eingestellt. Danach muss der sshd neu gestartet werden.

Als erstes ersetzen wir die Zeile die mit Subsystem beginnt durch

Subsystem sftp internal-sftp

Dann fügen wir am Ende der sshd_config folgendes ein:

Match User myuser
     ChrootDirectory /home/myuser
     ForceCommand internal-sftp

Und den sshd neustarten.

# /etc/init.d/ssh restart

Ab jetzt sollte es möglich sein, sich über sftp und nur über sftp als myuser zu verbinden. Und man sollte ausser einem leeren Verzeichnis nichts sehen und auch nicht in der Lage sein Dateien hochzuladen. Das sollten wir testen.

$ sftp myuser@host

Falls der Login nicht klappt, dann hilft ein Blick ins /var/log/auth.log Verzeichnis. Da könnte z.B. sowas drinstehen:

fatal: bad ownership or modes for chroot directory component “/home/”

Was in unserem Fall bedeuten würde, dass /home nicht root gehört.

Uploadverzeichnis einrichten

Ein Uploadverzeichnis ist eines mit Schreibrechten für den User.

$ mkdir /home/myuser/upload
$ chown myuser:myuser /home/myuser/upload
$ chmod 755 /home/myuser/upload

Weiteres


Bohrfutter der Bosch PSB 700-RE2 lässt sich nicht bewegen

Ich hab mir vor einiger Zeit eine Bosch PSB 700-RE2 Bohrmaschine gekauft. Nach dem auspacken musste ich feststellen, dass ich nicht in der Lage war einen Bohrer in das Bohrfutter zu bekommen. Die Anleitung hat mir auch nicht wirklich geholfen, weil da nur drinstand man solle an einer Stelle festhalten und an der anderen drehen. Nun genau das hab ich gemacht bis mit die Hände weh taten. Bewegt hat sich nichts.

Ich hab dann das gemacht, was ich in einer solchen Situation immer mache. Ich hab das Problem auf später verschoben. Und siehe da, bei der nächsten Inspektion der Maschine hab ich in der Nähe des Futters ein kleines Loch entdeckt. Da hab ich dann einen kleinen Bohrer durchgesteckt. So hat sich das Futter nicht mehr drehen können. Dann musste ich nur noch den Haltegriff als Helben missbrauchen und schon ließ sich das Futter lösen. Das Drecksding hatte sich bloß festgesetzt.


Tonspurtricks für mencoder, ffmpeg, mp3gain, lame u.ä.

Eine Audiospur zu einem AVI hinzufügen

ffmpeg -vcodec copy -acodec copy -i audio.mp3 -i input.avi output.avi -newaudio

Eine Audiospur aus einer VOB-Datei extrahieren

Dieser Befehl extrahiert die deutsche Tonspur und speichert sie als wav-Datei ab.

mplayer dvd://2 -dvd-device /path/to/dvd -alang de -vo null -ao pcm:fast

Alternativ kann man auch die Nummer der Tonspur angeben

mplayer dvd://2 -dvd-device /path/to/dvd -aid 128 -vo null -ao pcm:fast

Wav nach mp3 kodieren

Wenn man lame ohne irgendwelche Kodierungsparameter aufruft, dann wird ein 128kbps MP3 erstellt. Das sollte für 95% der Einsatzzwecke genau richtig sein.

lame input.wav output.mp3

Lautstärke normalisieren

Zum Normalisieren eignet sich imho mp3gain mit den Parametern -r -und -k ganz gut. -r ist super, wenn man mehrere Dateien hat und will dass die alle gleich laut sind (z.B. eine deutsche und eine englische Tonspur). Und -k verhindert nervige Klickgeräusche die entstehen, wenn einzelne Töne zu laut sind.

mp3gain -r -k *.mp3

Will man die Lautstärke schon mein Kodieren mit Mencoder normalisieren eignet sich der audio Filter volnorm=1.

mencoder dvd://2 -dvd-device /path/to/dvd \
  -alang de -af volnorm=1 -oac mp3lame -lameopts abr:br=128 \
  -ovc lavc -o out.avi

Have fun!


JavaScript Dateien effizient übertragen mit GZIP, PHP und Apache

Wenn man diese schönen JavaScript Bibliotheken, wie z.B. jQuery, nutzt, dann steht man schnell vor dem Problem, dass man ein Dutzend JavaScript Dateien laden muss, um die Seite korrekt angezeigt zu bekommen. Wenn man dann den Webserver falsch konfiguriert hat werden die Dateien alle unkomprimiert übertragen. Ausserdem muss der Browser für jede Datei den Webserver fragen. Und wenn man dann auch noch das Caching falsch konfiguriert hat, dann werden für jeden Seitenaufbau alle JS Dateien neu geholt.

Ob man sein System richtig Konfiguriert hat lässt sich sehr schön mit dem Firefox Plugin Firebug beobachten. Wie das geht bleibt dem geneigten Leser als Übung überlassen, denn mir geht es hier hauptsächlich um die Lösung dieses Problems.

Nach Monaten intensiver Recherchen und langen Versuchsreihen bin ich auf folgende Lösung gestoßen: Anstatt die JS Dateien direkt herunterzuladen, kopiere ich einfach den Inhalt von allen Dateien on-the-fly in eine Große. Diese wird dann hübsch komprimiert und mit den notwendigen HTTP Headern ausgestattet, damit das Caching auch funktioniert. Irgendwann hat sich folgendes PHP Script als optimal herauskristallisiert:

<?php 
// License: none (public domain) 
/*
 * Dieses Script concateniert mehrere Dateien und schickt sie gzip
 * komprimiert an den Browser. 
 * Ausserdem wird ein Etag gesetzt, der nötig ist, um das Cachen der 
 * Javascript Datei zu ermöglichen. Der Etag ändert sich jedesmal, wenn 
 * eine der Dateien geändert wird, oder eine hinzu-/weggenommen wird. 
 */ 
header('Content-Type: text/javascript');
header("Expires: ".gmdate("D, d M Y H:i:s", time() + 3600*24)." GMT");

/*
 * Hier die JavaScript Dateien angeben:
 */
$files = array(
    "jquery-1.4.2.min.js",
    "ui.core.js",
    "ui.dialog.js",
    "ui.draggable.js",
    "ui.resizable.js",
    "jquery.textarearesizer.compressed.js"
);

$md5 = '';
foreach ($files as $file)
{
    $md5 .= md5_file($file);
}

$etag = md5($md5);
header("Etag: $etag");

if ( isset($_SERVER['HTTP_IF_NONE_MATCH'])
  && $_SERVER['HTTP_IF_NONE_MATCH'] == $etag)
{
    header('HTTP/1.1 304 Not Modified');
}
else
{
    if ( isset($_SERVER['HTTP_ACCEPT_ENCODING'])
      && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], "gzip") !== false )
    {
        ob_start("ob_gzhandler");
    }
    else
    {
        ob_start();
    }

    foreach ($files as $file)
    {
        echo file_get_contents($file);
    }
}
?>

Vorgehensweise:

Lege eine Datei ‘js.php‘ in dem Ordner mit den JavaScript-Dateien an. Kopiere obigen Code in die Datei. Ändere das $files Array so, dass deine JavaScript-Dateien da drin stehen. Füge folgenden Script-Tag in deinen HTML-Code ein:

<script src="/path/js.php" type="text/javascript"></script>

Das ganze funktioniert natürlich auch mit CSS-Dateien, wobei man dann den Content-Type auf text/css ändern muss.


OpenBSD 4.8 als DSL-Router (NAT) einrichten

OpenBSD Logo
OpenBSD verfügt über alles was nötig ist, um schnell einen DSL-Router aufzusetzen. Alles was man benötigt ist ein Rechner mit zwei Netzwerkkarten und eine standard OpenBSD Installation.
Diese Anleitung wurde zwar für OpenBSD 4.6 geschrieben, sollte aber auf OpenBSD 4.7 und OpenBSD 4.8 genauso funktionieren.

Installation

Als erstes sollte man das Betriebssystem installieren. Dazu läd man sich von http://www.openbsd.org/ das neueste ISO-Image oder kauft die neuesten Installations CDs. In der FAQ sollte alles nötige was man sonst noch wissen muss drinstehen: http://www.openbsd.org/faq/de/faq4.html.

Netzwerk vorbereiten

Das DSL-Modem wird an eine Netzwerkkarte angeschlossen und das restliche Netzwerk an die andere. Es sollten Lämpchen leuchten die die Verbindung signalisieren. Im folgenden werden wir annehmen, dass das DSL-Modem an dem Interface rl1 hängt und das lokale Netz in rl0.

Internet Verbindung einrichten

In der Datei /etc/hostname.pppoe0 werden die Verbindungsdetails von der Internetverbindung eingetragen. Bei der Telekom muss man folgendes eintragen:

inet 0.0.0.0 255.255.255.255 NONE \
   pppoedev rl1 authproto pap \
   authname '00012345678901234567890#0001@t-online.de' authkey 'xxxx' up
dest 0.0.0.1
!/sbin/route add default 0.0.0.1

Der authname ist diese lange Nummer die sich aus Anschlusskennung, T-Online-Nummer, einer Raute (aka. Sharp, Teppich, Doppelkreuz), Mitbenutzernummer (meist 0001) und dem Suffix @t-online.de zusammensetzt.

Ausserdem sollte man dafür sorgen, dass das rl1 interface beim booten aktiviert wird. Das geht mit:

echo "up" >> /etc/hostname.rl1

Das Interface für das lokale Netz muss ganz normal konfiguriert werden. Z.B. in dem man in die /etc/hostname.rl0 folgendes reinschreibt

inet 10.0.0.1 255.255.255.0 NONE
up

Jetzt noch schnell einen DNS-Server in die /etc/resolv.conf eingetragen (im Idealfall nimmst du den DNS deines Providers, alternativ einen aus der Liste http://www.stanar.de/)

echo "nameserver 123.4.5.6" >> /etc/resolv.conf

und schon kann man prüfen, ob man ‘drin’ ist. Dazu bringen wir zuerst das Interface rl1 up und dann pppoe0.

ifconfig rl1 up
ifconfig pppoe0 up

Nach einigen Sekunden sollte der Befehl ifconfig pppoe0 einen Erfolg vermelden. Den Erfolg erkennt man daran, dass man eine IP bekommen hat.  Der Befehl ping example.com sollte jetzt erfolgreich sein.

NAT-Regeln

Damit das Routing funktioniert muss man den PacketFilter (OpenBSDs Firewall)  konfigurieren.  Eine sehr simple, aber ausreichende Filterregel Datei sieht so aus (meine liegt in /root/pf/nat.conf):

# network information
int_if = "rl0"
ext_if = "pppoe0"
int_net = "10.0.0.0/24"

# well known hosts
marla = "10.0.0.222"
dmz_host = "10.0.0.254"

# ports that should be blocked. Hier kannst du auch die Portnummern angeben. Weitere
# Namen findest du in /etc/services
to_block = "{ smtp, auth, ntp, netbios-ns, netbios-dgm, netbios-ssn, imap, nfsd, mysql }"

# nat/rdr
nat on $ext_if from $int_if:network to any -> ($ext_if)

rdr on $ext_if proto {tcp, udp} from any to any port 33881:33891 -> $marla
rdr on $ext_if proto {tcp, udp} from any to any port 22 -> $marla

# Entmilitarisierte Zone (dmz):
#rdr on $ext_if from !$int_net -> $dmz_host

block in quick on $ext_if proto {tcp, udp} from any to any port $to_block
pass out keep state

Als erstes definieren wir einige Makros. Z.B. welches das interne und welches das externe Netz ist, oder welche IPs im internen Netz verwendet werden. Dann kommen einige Makros zu bekannten Rechnern. Wir haben hier zwei Rechner marla und einen DMZ Host. Anschließend defineren wir noch ein Makro das Ports enthält die geblockt werden sollen.
Nun beginnt der interessante Teil. Die Zeile die mit nat beginnt ist die welche für das Routing (genauer die Network Address Translation) sorgt. Im Grunde besagt die Regel: Alles was beim internen Interface reinkommt wird beim externen Interface wieder rausgeschickt. Eigentlich ist diese eine Zeile die einzige die man wirklich braucht.

Danach habe ich noch ein paar Beispiele angefügt. Z.B. wie man eine Portumleitung macht. Die erste Zeile die mit rdr (also redirect) anfängt besagt: Wenn auf dem externen Interface ein TCP oder UDP Packet ankommt dessen Zielport im Bereich von 33881 bis 33891 liegt, dann leite das Packet an den Rechner 10.0.0.222 aus dem internen Netz weiter. Auf diese Weise könnte Marla auf einem dieser Ports einen Server bereitstellen, der von Internet aus erreichbar wäre.

Dann habe ich noch ein Beispiel für einen DMZ Host, also einen Rechner, an den alle einkommenden Packete weitergeleitet werden mit denen der Router sonst nichts anzufangen weiss.

Die Zeile die mit block beginnt dient dazu einzelne Ports auf dem Router dicht zu machen. Das kann nötig sein, falls man auf dem Router noch Server laufen hat, die nur im lokalen Netz benutzt werden sollen. Oder um den DMZ Host vor einigen bestimmten Packeten zu schützen.

Damit dem Packetfilter auch erlaubt ist Packete weiterzuleiten müssen wir in der /etc/sysctl.conf die Variable net.inet.ip.forwarding auf 1 setzen. Um nicht auf den nächsten Neustart warten zu müssen setzen wir die Variable auch gleich noch für das laufende System

sysctl net.inet.ip.forwarding=1

Jetzt noch den Packefilter einschalten.

pfctl -e -f /root/pf/nat.conf

Diese Zeile sollte man auch in die Datei /etc/rc.local eintragen, damit die Firewall gleich beim booten mitgestartet wird.

Done

Der Theorie nach sollten die Rechner aus dem internen Netz jetzt ‘drin’ sein.

Wie gehts weiter?

DHCP Server einrichten.

Tipps

Ich hab bei mir noch zusätzlich einen Cronjob, der morgens einmal kurz die Verbindung trennt und sofort wieder aufbaut. Damit stelle ich sicher, dass mich die 24h Zwangstrennung nicht mitten in der Arbeit erwischt. Folgendes Skript wird einmal täglich ausgeführt:

#!/bin/sh
ifconfig pppoe0 down
sleep 10
ifconfig pppoe0 up

Kommentare und Anregungen sind wie immer herzlich willkommen.


OpenBSD 4.8: DHCP-Server einrichten


Um einen OpenBSD-Rechner als DHCP-Server zu nutzen braucht man nicht viel, denn die benötigte Software ist in der Standardinstallation bereits vorinstalliert. Man muss den Server nur noch einschalten und die Konfiguration leicht anpassen.

Diese Anleitung wurde zwar ursprünglich für OpenBSD 4.6 geschrieben, sollte aber auch auf OpenBSD 4.7 bzw. OpenBSD 4.8 funktionieren.

Um den DHCP-Server auf dem Interface rl0 zu aktivieren trägt man folgende Zeile in die /etc/rc.conf.local ein (falls die Datei nicht existiert sollte man sie vorher anlegen)

dhcpd_flags="rl0"

Anschließend passt man die /etc/dhcpd.conf seinen eigenen Wünschen an. Meine sieht so aus:

#       $OpenBSD: dhcpd.conf,v 1.2 2008/10/03 11:41:21 sthen Exp $
#
# DHCP server options.
# See dhcpd.conf(5) and dhcpd(8) for more information.
#
# Network:              10.0.0.0/255.255.255.0
# Domain name:          my.domain
# Name servers:         194.25.2.129 and 194.25.2.130
# Default router:       10.0.0.1
# Addresses:            10.0.0.100 - 10.0.0.199
#
option  domain-name "my.domain";
option  domain-name-servers 194.25.2.129, 194.25.2.130;
subnet 10.0.0.0 netmask 255.255.255.0 {
 option routers 10.0.0.1;

 range 10.0.0.100 10.0.0.199;

 host static-client {
   hardware ethernet 00:50:04:0c:11:22;
   fixed-address 10.0.0.222;
 }
}

Das heisst, mein Router hat die IP 10.0.0.1, ich benutze zwei Telekom DNS-Server und vergebe IP Adressen aus dem Bereich 10.0.0.100 bis 10.0.0.199. Ausserdem habe ich einem Rechner eine ‘statische’ IP verpasst, nämlich dem dessen Netzwerkkarte die MAC-Adresse 00:50:04:0c:81:71 hat. (Die MAC-Adresse bekommt man mit Hilfe von apr -a heraus. Eventuell muss man vorher den Rechner anpingen, damit die Adresse auch im ARP-Speicher ist.)

Anschließend würde ich einen Rechnerneustart empfehlen, so können wir gleich feststellen, ob nach einem Stromausfall alles wieder funktionieren wird.

Einen guten Einstieg rund um das Thema Netzwerk in OpenBSD bietet die FAQ: http://www.openbsd.org/faq/de/faq6.html


Ohne Passwort per SSH einloggen (Private-Public-Key-Verfahren)

ssh_logo

Ich habe lange Zeit gedacht, dass Private-Public-Key Authentifizierung bei SSH aufwendig wäre. Das lag z.B. daran, dass ich oft Beispiele gesehen habe die vorraussetzten, dass man bei jedem Login den Parameter -i mit anzugeben. Man liest auch häufig Beispiele die den ssh-agent benötigen. Aber es geht auch einfacher.

  1. Auf dem Rechner von dem aus du dich einloggen möchtest führst du ssh-keygen aus und drückst dreimal Enter. Dann wird an der Standardstelle (meist ~/.ssh) mit dem Standardnamen (meist id_rsa oder id_dsa) dein Private-Public-Key Paar angelegt, welche nicht durch ein Password geschützt sind.
  2. Du fügst den Inhalt des Public-Key (meist id_rsa.pub) ans Ende der Datei ~/.ssh/authorized_keys auf dem Rechner auf dem du dich einloggen möchstest hinzu. Das ganze natürlich in dem Homeverzeichnis des Users unter dem du dich anmelden willst.
  3. Probiere aus obs funzt mit ssh <hostname> bzw. ssh -l <user> <hostname>. Beim erstenmal darf er noch fragen, ob der Public-Key des Remoterechners zur Liste der bekannten Keys hinzugefügt werden soll, aber beim zweiten Versuch sollte es ohne jede Nachfrage klappen.

Falls das wider Erwarten nicht funktioniert, dann kann ich leider auch nicht weiterhelfen, weil ich nämlich auch kein Experte bin.


Follow

Bekomme jeden neuen Artikel in deinen Posteingang.