Ssh Portforwarding einschränken

Aus NOBAQ
Zur Navigation springenZur Suche springen

Normalerweise ist sshd dazu designed, dem User völligen Shellaccess zu gewähren. Nur spezielle Packages/Pakete erlauben z.B. chroot. Das gefällt mir gar nicht und deswegen ist das Argument, sftp/scp ersetze FTP vollkommen, für mich völlig unbrauchbar. Ausserdem fehlt jegliche Konfigurationsmöglichkeit auf Benutzerebene (ausser mittels public-keys).

In meinem Fall hätte ich gerne keine Einschränkungen für meinen eigenen Account, jedoch hätte ich gerne als Tunnel einen Account, der zu nichts anderem als zum Tunneln benutzt werden kann...


Für bestimmte Sachen wie z.B. FTP oder mySQL will ich keinen direkten Zugriff vom Internet aus Sicherheitsgründen gewähren. Allerdings sollen die Services ggf. Freunden/Bekannten zugänglich sein, denen ich nicht sofort einen Shellaccount geben will.

Aus diesem Grund habe ich einen Account "portfw" mit einem sehr einfachen Passwort eingerichtet.

root@nobaq:~ # grep portfw /etc/passwd
portfw:x:56651:65534:Dummy-User fuer Portforwarding:/tmp:/usr/local/sbin/portfwshell

Als Shell habe ich ein minimales C-Programm geschrieben, das nix kann, ausser "exit". Also sind keine Befehle auf dem Server ausführbar. Durch den einfachen Code reduziert sich die Wahrscheinlichkeit eines Bufferoverflows auf ein Minimum

root@nobaq:~ # cat /usr/local/src/portfwshell.c
#include
#include
#include

int main(void)
{
  char cmdbuf[11];
  while(1)
  {
    printf(”please enter ‘exit’ to quit> “);

    if(fgets(cmdbuf, 10, stdin))
    {
      if(!strncmp(cmdbuf, “exit”, 4))
      {
        return 0;
      }
    }
  }
  return 0;
}

Befehle können nun keine mehr ausgeführt werden, doch die Sache hat einen Schönheitsfehler: Es ist Portforwarding möglich, und zwar ins komplette interne Netzwerk! Nicht genau das, was ich haben will! Das soll sich doch nur auf ein paar Services beschränken. Und genau das ist der Teil, zu dem ich im Internet nichts gefunden habe! Mittels public-key Verfahren wäre das durch Optionen in authorized_keys möglich, jedoch will ich es ohne pubkey. Das Einloggen soll ja sehr einfach möglich sein, das größte Sicherheitsrisiko sehe ich im weiten Internet (u.a. durch automatisierte Scripts) und nicht durch einen eingeschränkten Zugang, der im Regelfall auf einen kleinen Personenkreis beschränkt ist.

Linux bietet ein Kernelmodul ipt_owner, mit Hilfe dessen über iptables Pakete aufgrund des Usernamens des erzeugenden Prozesses gefiltert werden können (logischerweise nur beim OUTPUT-chain).

Für den Test wird ein lokales netcat -p 1984 -l als niki aufgerufen, danach mit portfw eingestiegen und dabei ein Tunnel eingerichtet und versucht, mittels telnet auf den Port zu verbinden. Die (relevanten) Ausgaben:

root@nobaq:~ # ps -e fu
root 589 0.0 0.4 6612 2096 ? Ss 01:44 0:00 \_ sshd: portfw [priv]
root 20263 0.0 0.4 6612 2096 ? S 01:44 0:00 | \_ sshd: portfw [priv]
portfw 4451 0.0 0.4 6620 2176 ? S 01:44 0:00 | \_ sshd: portfw@pts/10
portfw 28381 0.0 0.0 1400 300 pts/10 Ss+ 01:44 0:00 | \_ -portfwshell
root@nobaq:~ # netstat -a –numeric-hosts –numeric-ports -p
Aktive Internetverbindungen (Server und stehende Verbindungen)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 30851/sshd
tcp 0 0 192.168.200.121:52753 192.168.200.121:1984 VERBUNDEN 4451/10
tcp 0 0 192.168.200.121:1984 192.168.200.121:52753 VERBUNDEN 23219/netcat
tcp 0 0 192.168.200.121:22 192.168.200.201:1579 VERBUNDEN 589/sshd: portfw [p

Die erste Zeile ist dabei der normale sshd, der auf neue Verbindungen wartet. Die letzte Zeile ist die ssh Verbindung vom ssh Server zum ssh-Client, die immer existiert. Die zweite Zeile ist das Socket, das ssh zum Ziel aufmacht. Wie man aus dem ps entnehmen kann, gehört dieses Socket dem Benutzer portfw, es kann also mittels ipt_owner gefiltert werden. Die dritte Zeile ist das Socket vom horchenden netcat, das logischerweise dem Benutzer niki gehört.

Daraus folgt, das eigentlich nur alles verworfen werden muss, dass vom lokalen Computer kommt (chain OUTPUT) und von einem Prozess, der portfw gehört, erstellt wurde. Nur spezielle Services sollen erlaubt werden (hier z.B. NUR die Verbindung auf 1984, lokaler Rechner)

iptables -F
iptables -A OUTPUT -m owner –uid-owner portfw -p tcp -d 192.168.200.121 –destination-port 1984 -j ACCEPT
iptables -A OUTPUT -m owner –uid-owner portfw -j DROP

Und siehe da, es funktioniert!!

Achtung aber wenn man sich viel Zeit ersparen will! Ich habe Stunden gebraucht, weil ich das Portfowarding nach www.nobaq.net:1984 eingerichtet habe. Erst die folgenden Zeilen in den Logdateien brachten mich auf einmal auf die Idee...

Apr 25 19:03:20 nobaq sshd[27037]: error: connect_to www.nobaq.net: unknown host (Temporary failure in name resolution)

das der ssh nun eine Namensauflösung machen will, aber Port 53 blockiere!

Da ich aber sowieso nur Dienste auf dem lokalen Rechner erlauben will, brauchr ich DNS nicht, und leite direkt auf 127.0.0.1 weiter. (Auflösung per /etc/hosts müsste normalerweise auch gehn)

Das ganze hab ich noch in ein schönes Script verpackt (sonst hat mein Server keine Firewall-Regeln, da dies die dezidierten Router pylon, ianus und elsa erledigen)

root@nobaq:~ # cat /etc/init.d/nobaq_firewall
#!/bin/bash
 
case$1in
	start)
		echo -n "Setting up nobaq firewall"
		#########################################################
		## SSH port forwarding ##
		#########################################################

		# IMAPs, mySQL, mldonkey
		ports="993 3306 4001"

		for port in $ports
		do
			iptables -A OUTPUT -m owner –uid-owner portfw -p tcp -d 127.0.0.1 \
				-destination-port $port -j ACCEPT
		done

		# alles andere verbieten
		iptables -A OUTPUT -m owner -uid-owner portfw -j DROP

		echo .
		;;
	stop)
		echo -n "Flushing iptables"
		iptables -F
		echo .
		;;
	*)
		echo "Usage: $0 {start|stop|}"
		exit 1
		;;
esac

exit 0

Und das ganze noch mit update-rc.d nobaq_firewall defaults 01 ins System eingebunden! Fertig!

Abschließend möchte ich noch sagen, dass ich jetzt sogar fürs Interne Netzwerk die ganzen Dienste verboten habe und nur mehr per SSH-portfw drauf zugreife.

Mittels plink (http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html) geht das unter Windows ganz einfach. Sogar mit Passwort übergeben (was für mich in diesem Fall kein Sicherheitsrisko darstellt)

niki@STYLISTIC:d $ cat portfw.cmd
@echo off

plink -batch -l portfw -pw secret -L 3306:127.0.0.1:3306 -N @www.nobaq.net

niki@STYLISTIC:d $

Natürlich gehts auch per putty (http://www.nobaq.net/~niki/portfw), wenn man sowieso eine ssh-Verbindung erstellt. Man beachte den Parameter "-N" mit dem die Shell erst gar nicht gestartet wird!

Interessant ist auch das Programm "PortForwarder", allerdings muss man dabei leider das Passwort eingeben!

Allerdings gibt es eine alte Version dafür für Pocket PCs - etwas, das ich schon lange gesucht habe!