Совсем недавно на мой сервер с git репозиторием началась атака по подбору паролей к gitlab и ключей к ssh. Намерения злоумышлеников понятны — вытащить исходный код проприетарного приложения хранящегося в git.
Мне не совсем понятны попытки подбора ssh-ключей, т.к. проблематично подобрать RSA-ключ (это займет десятки лет), но я всё же сделал некоторые ограничения для того что бы не так сильно «загаживались» логи.
Кому интересно как защитить gitolite и gitlab (работает за nginx) от подбора паролей — добро пожаловать под кат.
Защита ssh.
Многим извесно что сам по себе sshd в Linux не умеет ограничивать количество соединений. Для нас это не явилось проблемой, мы ограничили их на уровне фаервола.
В стандартной поставке iptables под CentOS есть модуль hashlimit. Им мы и воспользуемся. Я написал следующие правила для iptables:
iptables -N ssh_input
iptables -A ssh_input
-m hashlimit
--hashlimit 5/m
--hashlimit-burst 5
--hashlimit-mode srcip,dstport
--hashlimit-name ssh
--hashlimit-htable-expire 3600000
-j ACCEPT
iptables -A ssh_input -p tcp -j REJECT --reject-with tcp-reset
iptables -A INPUT -m state -m tcp -p tcp --dport 22 --state NEW -j ssh_input
Что же мы сделали? Сперва мы добавили цепочку ssh_input. Потом добавляем правило которое ограничивает количество соединений до 5 в минуту (--hashlimit 5/m --hashlimit-burst 5). В нем же указываем параметры по которым стоит группировать соединения (--hashlimit-mode srcip,dstport). После добавляем правило которое запрещает доступ (-j REJECT). И добавляем цепочку в input с условиями что соединения должно быть новым и приходить на порт 22.
Как работают эти правила? Все пакеты с флагом нового соединения на порт 22 отправляются на обработку в цепочку ssh_input. Там при выполнении условия что количество таких коннектов с данного ip не превышает 5 в минуту происходит пропускание пакета (-j ACCEPT). Если условия не выполняется переходим к следующему правилу: -j REJECT.
Теперь наш злоумышленик может годами (десятками, сотнями лет ?) подбирать ssh ключ. И «загаживания» лога будет поменьше.
Защита gitlab
Так же злоумышленники пытаются подобрать пароль к web-интерфейсу gitlab. В качестве Front-end мы используем Nginx. Он там довольно старой версии 0.8.55 и обновлять его сейчас не времени и желания.
В первую очередь мы добавляем basic авторизацию и ограничиваем количество соединений в минуту (чтобы не так просто было подобрать этот пароль). Проблема ограничения в нашем случае такова, что загрузка web страницы вызывает еще около 15 обращений к серверу за статикой. Это заставит нас разрешить более 15 соединений в секунду. Это нас не устравиает т.к. имея 15 соединений в секунду на каждый ip злоумышленик сможет подобрать пароль и мы делаем следующий «финт ушами»:
Логин и пароль basic-авторизации у нас общий для всех пользователей и служит лиш помехой для подбора пароля от самого web-приложения. Раз так, то мы можем делать следующую проверку:
if ($http_authorization != "Basic secretdsddsaadsdsasad=="){
return 403;
break;
}
для всех url отличающихся от /. На самом / мы делаем basic авторизацию и ограничение на 5 попыток в минуту для 1 ip:
limit_req_zone $binary_remote_addr zone=one:10m rate=5r/m;
...
server {
....
location = / {
auth_basic "Top secret";
auth_basic_user_file /etc/nginx/conf.d/ssl/.htpasswd;
limit_req zone=one burst=5 nodelay;
.....
}
....
}
А теперь давайте представим что наш basic пароль всё же подобрали или укарали. Защитим так же и форму входа в приложение:
limit_req_zone $binary_remote_addr zone=two:10m rate=5r/m;
...
server {
...
location = /users/sign_in {
if ($http_authorization != "Basic secretdsddsaadsdsasad=="){ //что бы даже не пытались без успешной basic авторизации
return 403;
break;
}
limit_req zone=two burst=5 nodelay;
....
}
....
}
И наконец основной локейшин для прочих адресов:
server {
...
location / {
if ($http_authorization != "Basic secretdsddsaadsdsasad=="){ //что бы даже не пытались без успешной basic авторизации
return 403;
break;
}
...
}
}
Таким образом при запросе без basic-авторизации на любой url кроме корня мы получаем 403. Basic-авторизация возможна только на корне и ограничена на 5 запросов в минуту. Даже если подберут basic-авторизацию, форма авторизации в web приложении ограничена на 5 запросов в минуту. Я выделил в разные зоны ограничения на попытки входа в basic и web-приложение для того чтобы ошибки ввода разных парлей в разных местах не накапливались и реальным пользователям не выдавалось «Сервис недоступен».
Автор: piromanlynx