Przekierowanie połączenia na określony port przez szyfrowany tunel za pomocą OpenSSH
Kategoria: FAQ, etykiety: ssh, openssh, szyfrowany tunel, port forwarding, przekierowanie portów
Dodany: 2015-04-04 22:17
(zmodyfikowany: 2015-04-05 20:31)
Przez: uzytkownikubunt
Wyświetleń: 39332
Oprogramowanie użyte w tym poradniku Debian Jessie 8 amd64 OpenSSH_6.7p1 Debian-3
Czasem zdarza się, że aplikacje komunikują się przez sieć, jednak nie posiadają one możliwości dokonania bezpiecznego połączenia (m.in. szyfrowanego). Może być też tak, że nie jesteśmy pewni wykonania kodu odpowiedzialnego za przesyłanie i wolelibyśmy użyć czegoś bardziej sprawdzonego. Jednym z lepiej sprawdzonych programów jest OpenSSH. Za jego pomocą można bezpiecznie przesyłać dane.
W tym HOWTO przedstawię, jak połączyć za pomocą dwa zaufane komputery poprzez niezaufaną sieć. Od razu chcę zaznaczyć, że o ile konto do którego zalogowany przez OpenSSH użytkownik nie będzie kontem roota, to jest ono potencjalnie niebezpieczne, dlatego nie należy go przydzielać nikomu komu nie ufamy. Załóżmy więc, że w ten sposób łączymy tylko dwa własne komputery.
Jak to wygląda? Na pewno musimy mieć serwer i klient, łączące się poprzez sieć. Normalnie więc wyglądałoby to tak:
serwer <Niezaufana Sieć> klient
Można jednak skorzystać z OpenSSH i wtedy będzie wyglądało to tak:
serwer<loopback>zdalny OpenSSH(port 33773) <Niezaufana Sieć> lokalny OpenSSH<loopback>klient
Instancja sshd po stronie serwera będzie uruchomiona na koncie zwykłego użytkownika i logowanie do niej będzie udostępnione osobie, której ufamy na tyle by pokazać i pozwolić zmieniać nasz kalendarz i książkę adresową, ale nie na tyle by przekazać jej uprawnienia roota.
Instalujemy potrzebne pakiety:
apt-get install openssh-server openssh-client
Na początku tworzymy instancję OpenSSH po stronie serwera. Załóżmy, że serwer i klient nasłuchują tak, jak w przypadku poradnika o Radicale tj serwer aplikacji nasłuchuje na porcie 5232 tylko na interfejsie wewnętrznym. Stworzona instancja OpenSSH po stronie serwera nasłuchuje na porcie 33773 i pozwala łączyć się zaufanym osobom poprzez niezaufaną sieć do serwera Radicale. Na komputerze klienckim uruchomiony jest, oprócz normalnego klienta Kalendarza, klient OpenSSH. Klient OpenSSH pozwala łączyć się poprzez siebie do serwera OpenSSH i razem z nim współpracując tworzą bezpieczny tunel poprzez niezaufaną sieć. Klient Kalendarza łączy się z lokalnym klientem OpenSSH i nawet nie widzi, co dzieje się dalej.
Na początku tworzymy nowe konto użytkownika tunelSSH, który ma dostęp do urządzeń sieciowych (grupa netdev), a także niezbyt wysoki UID, by nie było go widać podczas logowania (to nie musi być w końcu komputer-serwer z prawdziwego zdarzenia, a tylko daemon świadczący jakąś usługę):
#useradd --create-home --home-dir=/home/tunelSSH/ --groups=netdev \
--comment "Konto użytkownika uruchamiającego tunel SSH" --shell="/bin/bash" \
-K UID_MIN=20 -K UID_MAX=499 -K GID_MIN=20 tunelSSH
Tworzymy teraz konfigurację dla serwera tunelującego w pliku /etc/default/tunelSSH.conf:
Protocol 2
AllowTcpForwarding yes
AuthenticationMethods publickey
AuthorizedKeysFile /kluczeSSH/tunelSSH/autoryzowaneKlucze.pub
X11Forwarding no
AllowStreamLocalForwarding no
AllowUsers tunelSSH
Compression yes
ChallengeResponseAuthentication no # wyłączenie PAM
ClientAliveCountMax 3
ClientAliveInterval 20
DebianBanner no
PermitRootLogin no
UsePAM no
PrintMotd no
AddressFamily inet
Ciphers aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com
KbdInteractiveAuthentication no
KerberosAuthentication no
ListenAddress 0.0.0.0:33773
MaxAuthTries 2
MaxSessions 3
MaxStartups 2
PasswordAuthentication no
PermitOpen 127.0.0.1:5232
PermitTunnel no
PermitUserEnvironment no
SyslogFacility AUTH
TCPKeepAlive no # masz opcje ClientAlive od tego
UsePrivilegeSeparation no # by uruchomić openssh jako zwykły użytkownik
#klucz musi być inny niż klucz OpenSSH uruchomionego na koncie roota!
HostKey /kluczeSSH/tunelSSH/kluczHostaEdLiczba
PidFile /var/run/tunelSSH/tunelSSH.pid
Tworzenie klucza dla instancji OpenSSH na serwerze.
Pamiętaj: to musi być inny klucz niż klucz dla OpenSSH uruchomionego na koncie roota!
#mkdir /kluczeSSH/tunelSSH/ -p
#chgrp tunelSSH /kluczeSSH/tunelSSH/
#chmod g+rw /kluczeSSH/tunelSSH/
#su tunelSSH
ssh-keygen -t ed25519 -f /kluczeSSH/tunelSSH/kluczHostaEdLiczba -N ''
ssh-keygen -t ed25519 -f /kluczeSSH/tunelSSH/autoryzowaneKlucze -N ''
exit
#chmod g-rw /kluczeSSH/tunelSSH/ -R
Kopiujemy plik (w bezpieczny sposób!) /kluczeSSH/tunelSSH/autoryzowaneKlucze na klienta do katalogu ~/.ssh/ do pliku id_ed25519.
Taki nasłuchujący połączeń OpenSSH można by uruchomić za pomocą
/usr/sbin/sshd -f /etc/default/tunelSSH.conf
Jednak oczywiście lepiej byłoby to opakować w skrypt startujący podczas bootowania. Stwórzmy więc taki.
Plik /etc/init.d/tunelSSH.sh
#! /bin/sh
### BEGIN INIT INFO
# Provides: tunelPoprzezSSH
# Required-Start: $local_fs $named $network $syslog
# Required-Stop: $local_fs $named $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Tunel SSH
# Description: Tunel SSH
### END INIT INFO
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Tunel SSH"
NAME=sshd
DAEMON=/usr/sbin/$NAME
DATADIR=/home/tunelSSH/
DAEMON_ARGS="-f /etc/default/tunelSSH.conf"
PIDDIR=/var/run/tunelSSH/
PIDFILE=/var/run/tunelSSH/tunelSSH.pid
SCRIPTNAME=/etc/init.d/tunelSSH.sh
USER=tunelSSH
GROUP=tunelSSH
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
if [ ! -d $PIDDIR ]
then
mkdir $PIDDIR
fi
chown $USER:$GROUP $PIDDIR
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test --chuid $USER > /dev/null || return 1
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --chuid $USER -- $DAEMON_ARGS > /dev/null || return 2
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME --chuid $USER
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON --chuid $USER
[ "$?" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
}
#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME --chuid $USER
return 0
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc -p $PIDFILE "$DAEMON" "$NAME" && exit 0 || exit $?
;;
reload)
log_daemon_msg "Reloading $DESC" "$NAME"
do_reload
log_end_msg $?
;;
restart)
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
echo "Usage: $SCRIPTNAME {status|start|stop|reload|restart}" >&2
exit 3
;;
esac
Teraz dodajemy daemon/usługę do systemu:
chmod +x /etc/init.d/tunelSSH.sh
insserv tunelSSH.sh
Zabezpieczanie za pomocą fail2ban
W Internecie dochodzi do ataków na nasłuchujące połączeń serwery SSH. Nie powinno dojść do sytuacji, w której atakujący metodą brute force dostanie się do naszego serwera poprzez SSH autoryzowane kluczami, ale z drugiej strony dlaczego nie dodać następnego zabezpieczenia?
apt-get install fail2ban
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Edytujemy /etc/fail2ban/jail.local, ustawiając banaction na
banaction = iptables-allports
A następnie przeładowujemy komendą:
/etc/init.d/fail2ban restart
Instancja OpenSSH na kliencie
Na serwerze nasłuchuje już OpenSSH na porcie 33773. Pozwoli on zalogować się do konta tunelSSH, a także połączyć się z lokalnym (wobec serwera) portem 5232 na interfejsie loopback. Trzeba jednak jakoś się z nim połączyć. Jak już piałem powyżej, kopiujemy plik (w bezpieczny sposób!) z serwera z katalogu /kluczeSSH/tunelSSH/autoryzowaneKlucze na klienta do katalogu ~/.ssh/ do pliku id_ed25519. Następnie wystarczy wykonać:
ssh -L 4500:[127.0.0.1]:5232 adresSerwera -p 33773 -l tunelSSH
Pod adresSerwera oczywiście należy podstawić adres IP serwera. Parametr -p określa port, na którym nasłuchuje OpenSSH na serwerze. Parametr -l określa do jakiego konta użytkownika ssh ma się zalogować.
Jeśli wykonaliśmy powyższe polecenie, wtedy możemy połączyć się na kliencie z portem 4500 na interfejsie loopback (adres ip 127.0.0.1). Pakiety wysłane na ten port zostaną przesłane do serwera OpenSSH, a następnie wewnątrz serwera przekierowane z OpenSSH na port 5232, na którym może nasłuchiwać serwer Radicale.