OpenVPN-сервер с авторизацией по логину и паролю

()

Достаточно часто возникают ситуации когда нужно использовать OpenVPN, но по какой-то причине нет возможности генерировать отдельный сертификат на каждого пользователя. В этом случае хорошим решением будет использование авторизации по логину и паролю.

Рассмотрим пример подобного решения. Для начала возьмём недорогой VPS с FreeBSD 11.1 и в первую очередь установим необходимое ПО. Нам понадобится собственно OpenVPN, пакет denyhosts, чтобы хоть как-то "защитить" SSH от брутфорс-атак и Perl, на котором мы напишем скрипт аутентификации:

pkg install openvpn denyhosts perl5

Приступаем к настройке. Создадим директорию для хранения конфигурации и скопируем скрипты генерации ключей:

mkdir -p /usr/local/etc/openvpn
cp -av /usr/local/share/easy-rsa /usr/local/etc/openvpn/easy-rsa

Перейдём в директорию со скриптами генерации ключей:

/usr/local/etc/openvpn/easy-rsa

Отредактируем файл vars, чтобы он принял примерно такой вид:

if [ -z "$EASYRSA_CALLER" ]; then
        echo "You appear to be sourcing an Easy-RSA 'vars' file." >&2
        echo "This is no longer necessary and is disallowed. See the section called" >&2
        echo "'How to use this file' near the top comments for more details." >&2
        return 1
fi
set_var EASYRSA "$PWD"
set_var EASYRSA_PKI             "$EASYRSA/../keys"
set_var EASYRSA_REQ_COUNTRY     "US"
set_var EASYRSA_REQ_PROVINCE    "New Jersey"
set_var EASYRSA_REQ_CITY        "New Jersey"
set_var EASYRSA_REQ_ORG         "Best Company"
set_var EASYRSA_REQ_EMAIL       "your@email.com"
set_var EASYRSA_REQ_OU          "IT"

Генерируем ключи:

./easyrsa.real init-pki
./easyrsa.real build-ca nopass
./easyrsa.real gen-dh
./easyrsa.real build-server-full main nopass

Далее нам нужно написать скрипт аутентификации. Скрипт будет максимально простой: он будет брать из переменных окружения логин и пароль и в случае успеха завершаться с кодом 0, а в случае неудачи - кодом 1. собственно скрипт:

#!/usr/bin/env perl

use strict;
use warnings;
use diagnostics;

#use Data::Dumper;
#print Dumper(\%ENV);

# Хэш с данным пользователей
# Ключ - логины
# Значения - пароли
my %auth_data = (
    'vasya' => 'meg4P4ss54',
    'petya' => 'S3cretP4ss',
);

my $user = $ENV{username} or die("Bad login!\n");
my $pass = $ENV{password} or die("Bad password!\n");

exit 1 unless defined($auth_data{$user});
exit 1 unless $auth_data{$user} eq $pass;

exit 0;

Его надо сохранить как "/usr/local/etc/openvpn/checkpass.pl". Затем создадим файл конфигурации сервера OpenVPN ("/usr/local/etc/openvpn/main.conf"):

mode server
daemon
tls-server

# Подсеть, используемая в виртуальной сети между сервером и клиентами
server 172.16.251.128 255.255.255.224

# Порт сервера
port 443

# Протокол. Если использовать tcp и порт 443, то трафик будет похож на https ;)
# Но с udp трафик бегает чуть быстрее
proto udp
dev tun0
#tun-mtu 1480

ca "/usr/local/etc/openvpn/keys/ca.crt"
cert "/usr/local/etc/openvpn/keys/issued/main.crt"
key "/usr/local/etc/openvpn/keys/private/main.key"
dh "/usr/local/etc/openvpn/keys/dh.pem"

# Явно укажем используемы шифр
cipher "AES-256-CBC"

# Если мы хотим кому-то из пользователей персональные настройки
# То их надо размещать в этой директории
client-config-dir /usr/local/etc/openvpn/ccd

# Явно говорим что клиент должен весь свой трафик завернуть в тоннель
push "redirect-gateway def1"

# DNS, рекоммендуемые клиенту
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option WINS 8.8.8.8"

keepalive 10 120
persist-key
persist-tun

# В качестве бонуса будем сжимать трафика:)
comp-lzo

script-security 3

# Клиентский сертификат не нужен
verify-client-cert none

# Логин пользователя будет использоваться вместо имени клиентского сертификата
# Например при поиске настроек в client-config-dir
username-as-common-name

# Путь к скрипту аутентификации
auth-user-pass-verify "/usr/local/etc/openvpn/checkpass.pl" via-env

# Логи лишними не бывают:)
verb 3
log-append /var/log/openvpn.log

Добавляем в /etc/rc.conf строки:

openvpn_enable="YES"
openvpn_if="tun"
openvpn_configfile="/usr/local/etc/openvpn/main.conf"
openvpn_dir="/usr/local/etc/openvpn"

И запускаем сервер:

/usr/local/etc/rc.d/openvpn start

Теперь нам нужно включить пересылку пакетов между интерфейсами сервера. Добавляем в /etc/rc.conf строку:

gateway_enable="YES"

Чтобы включить пересылку пакетов не перезагружая сервер выполним команду:

sysctl net.inet.ip.forwarding=1

Приступаем к настройке файрволла. Мы будем использовать pf, как самый современный и простой в настройке. Создадим файл конфигурации "/etc/pf.rules" следующего содержания:

set limit { states 20000, frags 5000 }
set timeout { adaptive.start 6000, adaptive.end 12000 }
set skip on { lo0 }

# Внешний интерфейс сервера (см. вывод ifconfig)
if_ext = "vtnet0"

# Интерфейс, используемый openvpn
if_int = "tun0"

# Подсеть, используемая OpenVPN
net_int = "172.16.251.128/27"

set block-policy drop
set state-policy if-bound

scrub in

# NAT для внутренней сети
nat pass on $if_ext from $net_int -> ($if_ext) static-port

# Разрешаем всё на loopback-интерфейсе
pass quick on lo0 all

# Разрешаем исходящий трафик
pass out quick on $if_ext inet proto tcp from ($if_ext) to any flags S/SA keep state
pass out quick on $if_ext inet proto { udp, icmp } from ($if_ext) to any keep state

# Разрешаем доступ к серверу по SSH
pass in quick on $if_ext inet proto tcp from any to ($if_ext) port 22 flags S/SA keep state

# Разрешаем доступ к OpenVPN
pass in quick on $if_ext inet proto udp from any to ($if_ext) port 443

# Разрешаем пинговать наш сервер:)
pass in quick on $if_ext inet proto icmp from any to ($if_ext)

# Разрешаем все обращения к нашему серверу от клиентов OpenVPN
pass in quick on $if_int from $net_int to any keep state

# Блокируем весь остальной трафик
block drop all

Закончив создание файла конфигурации допишем в /etc/rc.conf строки:

pf_enable="YES"
pf_rules="/etc/pf.rules"

И запускаем файрволл командой:

/etc/rc.d/pf start

Остаётся настроить denyhosts и настройка сервера будет завершена. Создаём файл "/usr/local/etc/denyhosts.conf" следующего содержания:

SECURE_LOG = /var/log/auth.log
HOSTS_DENY = /etc/hosts.allow
PURGE_DENY=6h
BLOCK_SERVICE = ALL
DENY_THRESHOLD_INVALID = 5
DENY_THRESHOLD_VALID = 10
DENY_THRESHOLD_ROOT = 2
DENY_THRESHOLD_RESTRICTED = 1
WORK_DIR = /usr/local/share/denyhosts/data
SUSPICIOUS_LOGIN_REPORT_ALLOWED_HOSTS=YES
HOSTNAME_LOOKUP=NO
LOCK_FILE = /var/run/denyhosts.pid
DAEMON_LOG = /var/log/denyhosts
DAEMON_SLEEP = 30s
DAEMON_PURGE = 1h

Дописываем в /etc/rc.conf строку:

denyhosts_enable="YES"

запускаем denyhosts:

/usr/local/etc/rc.d/denyhosts start

На этом настройка сервера закончена. Переходим к настройке клиента. Тут всё совсем просто: создаём директорию для файлов конфигурации клиента и помещаем в неё файл ca.crt с сервера (на сервере полный путь к нему: "/usr/local/etc/openvpn/keys/ca.crt". Затем там же создаём файл client.ovpn и записываем в него:

comp-lzo
nobind
dev tun

# Протокол
proto udp

# IP и порт на сервере
remote IP-вашего-сервера 443

script-security 2
persist-key
persist-tun
client
resolv-retry infinite

ca ca.crt

cipher "AES-256-CBC"
verb 3

# Запрашивать у пользователя логин и пароль
auth-user-pass

Теперь можно запустить openvpn-клиент с этим файлом конфигурации, ввести логин и пароль и спокойно пользоваться сервисом.

Если же необходимо сохранить логин и пароль то нужно в директории с конфигурацией клиента создать файл pswd.dat, содержащий две строки:

лоигн_пользователя
пароль_пользователя

А в файле client.ovpn заменить строку auth-user-pass на:

auth-user-pass pswd.dat

Однако надо помнить что поскольку пароль хранится в открытом виде то он может быть похищен вредоносной программой, или третьим лицом, имеющим доступ к компьютеру.

Что дальше? Например можно доработать скрипт аутентификации чтобы он хранил не пароли, а их хэши, добавить к этим хэшам соль, перенести хранение этих данных в БД и написать веб-интерфейс для управления логинами и паролями. Но это мы оставим читателю;)

На этом всё. Приятной и безопасной вам работы!

Ключевые слова: openvpn, freebsd, tun, auth-user-pass, авторизация.

Комментарии:

Новый комментарий



© 2006-2017 Вадим Калинников aka MooSE