Poor man's HTTP port knocking with authentication
19 stycznia 2015 blog security ssh apache lightdm cgi
Port knocking z uwierzytelnianiem.
Poor man's HTTP port knocking with authentication
19 stycznia 2015 blog security ssh apache lightdm cgi
Port knocking z uwierzytelnianiem.
Port knocking jest jedn膮 z metod zabezpiecze艅. Dzia艂a to w ten spos贸b, 偶e klient otwiera po艂膮czenia do serwera na wskazane porty w 艣cis艂ej kolejno艣ci. Po przechwyceniu takiej sekwencji, serwer otwiera jaki艣 inny port, dzi臋ki czemu jest mo偶liwe po艂膮czenie np. SSH.
Zaprezentowany tu pomys艂 jest uproszczeniem wspomnianej idei. Klient otwiera stron臋 i po uwierzytelnieniu, czyli podaniu nazwy u偶ytkownika i has艂a, serwer otwiera port (lub porty) dla klienta. Najwi臋kszymi zaletami s膮: dodatkowe sprawdzenie uprawnie艅 (bo klient musi si臋 zalogowa膰) oraz uniezale偶nienie si臋 od dodatkowego oprogramowania u偶ywanego do standardowej techniki port knocking.
Trzeba pami臋ta膰, 偶e uwierzytelnianie podstawowe (HTTP Basic) jest bardzo 艂atwe do pods艂uchania i u偶ycia przez innych, dlatego zalecane jest w艂膮czenie szyfrowania (TLS).
Klient wchodzi na stron臋 allow.php
, wpisuje nazw臋 u偶ytkownika i has艂o. Jego adres IP jest pobierany i zapisywany do katalogu good-ip
.
Dzi臋ki skryptowi dirwatch.sh
uruchomionemu jako root (tylko on mo偶e wykonywa膰 iptables
) plik utworzony przez allow.php
zostaje "zauwa偶ony".
Zostaje uruchomiony skrypt allow.sh
z jednym parametrem - nazw膮 pliku, w kt贸rym zosta艂 zapisany adres IP. Adres IP zostaje wczytany i regu艂a dopuszczaj膮ca IP klienta jest dodawana do 艂a艅cucha regu艂 GOOD
.
Regu艂y iptables
s膮 konfigurowane dzi臋ki /etc/network/if-pre-up.d/firewall
, kt贸ry wczytuje regu艂y firewalla przy nawi膮zaniu po艂膮czenia sieciowego z plik贸w /etc/ip6tables.firewall.rules
oraz /etc/iptables.firewall.rules
.
Dodatkowo dzi臋ki crontab
skrypt monitoruj膮cy dirwatch.sh
jest uruchamiany przy starcie systemu.
Aby przetestowa膰 dzia艂anie port knocking wystarczy uruchomi膰 (pami臋taj膮c o zamianie nazw katalog贸w na w艂a艣ciwe):
/root/dirwatch.sh /var/www/html/portknocking/good-ip/ /root/allow.sh
Po wej艣ciu na stron臋 allow.php
w konsoli powinny wy艣wietli膰 si臋 informacje o pr贸bie dodania adresu klienta do 艂a艅cucha regu艂. Mo偶na te偶 skorzysta膰 z programu curl
:
curl -u user:password https://devsite.pl/portknocking/allow.php
G艂贸wna cz臋艣膰 konfiguracji. Skrypt przyjmuje adres IP jako parametr, sprawdza czy ju偶 wcze艣niej nie by艂 dodany do 艂a艅cucha GOOD
regu艂 iptables
i go dodaje.
#! /usr/bin/env bash
REGEXP_IP='([0-9]{1,3}[\.]){3}[0-9]{1,3}'
listGood() {
iptables -nL GOOD | \
awk '{print($4);}' | \
grep -E "$REGEXP_IP" | \
sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n
}
help() {
cat <<EOF
Allowed IPs manager.
Parameters
-l, --list list good ips
-r, --reset reset firewall
-h, --help show this help
EOF
}
if [[ "$1" == '-h' || "$1" == '--help' ]]; then
help
exit 0
fi
if [ "$EUID" -ne 0 ]; then
echo "Please run as root."
echo "Run `basename $0` --help for help."
exit 3
fi
if [[ "$1" == '-l' || "$1" == '--list' ]]; then
listGood
exit 0
elif [[ "$1" == '-r' || "$1" == '--reset' ]]; then
/etc/network/if-pre-up.d/firewall
RESULT=$?
exit $RESULT
elif [[ "$1" != "" ]]; then
echo "Checking $1 on `date`"
if [ -f "$1" ]; then
echo "It appears to be a file"
IP=`head -n 1 "$1" | grep -E "$REGEXP_IP"`
else
IP=`echo "$1" | grep -E "$REGEXP_IP"`
fi
if [[ "$IP" != "" ]]; then
echo "$IP appears to be an IP"
found=`listGood | grep -F "$IP"`
if [[ "$found" == "" ]]; then
echo "Addig $IP to good-list"
iptables -A GOOD -s "$IP" -j ACCEPT
RESULT=$?
exit $RESULT
else
echo "$IP is already good-listed"
exit 0
fi
else
echo "$1 is not an IP"
echo "Run `basename $0` --help for help."
exit 2
fi
else
echo "Run `basename $0` --help for help."
exit 1
fi
Skrypt ten jest odpowiedzialny za monitorowanie katalogu (przy u偶yciu inotifywait
). Je艣li plik zostanie zamkni臋ty po zapisaniu close_write uruchamiany jest skrypt podany jako parametr.
Aktualna wersja skryptu znajduje si臋 w repozytorium.
#! /usr/bin/env bash
help() {
cat <<EOF
Watch given dir for created files using inotify-tools.
Usage:
`basename $0` directory commands and parameters
The script executes [commands and parameters] [created file]
for every created file and DELETES the file.
EOF
}
fail() {
echo $*
echo "Run `basename $0` --help for help."
exit 1
}
if [[ "$1" == '-h' || "$1" == '--help' ]]; then
help
exit 0
fi
if [[ "$1" == "" ]]; then
fail "This script needs at least two parameters."
fi
pushd "$1" >/dev/null || fail "Cannot cd into the directory $1"
DIR="`pwd`/"
popd >/dev/null
shift
if [ ! -d "$DIR" ]; then
fail "Directory $DIR does not exist."
fi
if [[ "$1" == "" ]]; then
fail "This script needs at least two parameters."
fi
inotifywait -m -e close_write -r "$DIR" | \
while read l; do \
l=${l/*CLOSE /}; \
[ -f "${DIR}${l}" ] && \
$* "${DIR}${l}" && rm -f "${DIR}${l}" || \
echo "Given command returned non-zero for ${DIR}${l}."
done
W Ubuntu jest to plik, kt贸ry jest uruchamiany przy nawi膮zaniu po艂膮czenia sieciowego. Musi by膰 wykonywalny.
#! /usr/bin/env sh
/sbin/iptables-restore < /etc/iptables.firewall.rules
/sbin/ip6tables-restore < /etc/ip6tables.firewall.rules
Jest to przyk艂adowa konfiguracja. Tworzony jest 艂a艅cuch regu艂 GOOD
, do kt贸rego dodawane s膮 numery IP autoryzowanych klient贸w.
Dla ka偶dego portu (w przyk艂adzie 22
), kt贸ry ma by膰 chroniony, s膮 dodane dwie linie:
-A INPUT -p tcp --dport 22 -j GOOD
-A INPUT -p tcp --dport 22 -j DROP
Pierwsza z nich powoduje przeskoczenie filtrowania do 艂a艅cucha GOOD
, a druga zabrania dost臋pu do portu.
*filter
-A INPUT -i lo -j ACCEPT
-A INPUT -d 127.0.0.0/8 -j REJECT
-N GOOD
-A OUTPUT -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT
-A INPUT -p tcp --dport 22 -j GOOD
-A INPUT -p tcp --dport 22 -j DROP
-A INPUT -p icmp --icmp-type echo-request -j ACCEPT
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
-A INPUT -j DROP
-A FORWARD -j DROP
COMMIT
Regu艂y dla IPv6. Je艣li w systemie IPv6 jest wy艂膮czony, nie jest konieczne ich dodawanie.
-P INPUT DROP
-P FORWARD DROP
-P OUTPUT ACCEPT
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A OUTPUT -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT
-A OUTPUT -p ipv6-icmp -j ACCEPT
COMMIT
Pierwszy wpis resetuje otwarte porty codziennie, o pi膮tej rano. Druga linia uruchamia skrypt monitoruj膮cy katalog good-ip.
0 5 * * * /etc/network/if-pre-up.d/firewall
@reboot dtach -n /tmp/dtach.allow.ip /root/dirwatch.sh /var/www/html/portknocking/good-ip/ /root/allow.sh &
Nale偶y utworzy膰 katalog good-ip i umo偶liwi膰 zapisywanie do niego innym u偶ytkownikom (np. chmod 777 good-ip
), poniewa偶 domy艣lnie Apache jest uruchomiany jako u偶ytkownik apache.
<?php
@set_time_limit(0);
@error_reporting(E_ALL ^ E_NOTICE);
header("Expires: Tue, 03 Jul 2001 06:00:00 GMT");
// header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
if($_SERVER['HTTP_CLIENT_IP']) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} else if($_SERVER['HTTP_X_FORWARDED_FOR']) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else if($_SERVER['HTTP_VIA']) {
$ip = $_SERVER['HTTP_VIA'];
} else if($_SERVER['REMOTE_ADDR']) {
$ip = $_SERVER['REMOTE_ADDR'];
} else {
$ip = "?";
}
echo $ip;
$f = "good-ip/" . $ip . ".ip";
$fh = fopen($f, 'w') or die("can't open file");
fwrite($fh, $ip);
fclose($fh);
?>
Utworzenie pliku z has艂em:
htpasswd -c /etc/htpasswd username
Konfiguracja aplikacji (w Ubuntu /etc/apache2/sites-enabled/domena.conf
):
Alias /portknocking /var/www/html/portknocking
<Directory "/var/www/html/portknocking">
AuthUserFile /etc/htpasswd
AuthType Basic
AuthName "devsite.pl http port knocking authentication"
Require valid-user
</Directory>