2008-10-09

Настройка OpenVPN, VPN через http (https) прокси

В некоторых сетях единственным доступным сервисом является веб через прокси-сервер. Многие сервисы (ssh, почта, некоторые IM) не могут работать через прокси, но существуют способы пробросить vpn на какой-либо удаленных хост.

Итак, после проведения некоторого исследования, выбор пал на openvpn. И вот как я его настраивал:



1: установка OpenVPN


У меня было две машины: мой ноутбук (note) и домашний сервер vpn.mydomain.ru (который выполняет у меня функции интернет-шлюза, кешрующего прокси, тестового веб-сервера, файлохранилища, и многого чего другого).

Установка и на том и на другом прошла очень легко (строго говоря, он у меня уже был установлен "на всякий случай"):
emerge -av1 openvpn
Отмечу полезный USE-флаг: examples. Он включает установку примеров конфигурационных файлов, которыми можно воспользоваться как основой для написания своих.

2: конфигурация сервера


Первое что нужно сделать, это сгенерировать все необходимые ключи, это можно сделать с помощью скриптов, входящих в комплект openvpn:

cd /usr/share/openvpn/easy-rsa/

# инициализация
. ./vars
./clean-all

# эта команда создает Certificate Authority, выполняется интерактивно (т.е. вам нужно будет
#ответить на несколько вопросов). Можно оставить все по умолчанию, изменив лишь данные о
#географическом положении.
# Единственный вопрос, на который следует обратить внимание - Common Name. Не так важно, что вы
#туда введте, но он должен быть установлен.
./build-ca

# генерация пары ключей для сервера
./build-key-server server

# генерация пары ключей для клиента
./build-key note

# генерация Diffie Hellman:
./build-dh

После этого в поддиректории keys появятся все необходимые ключи.
Переместите их в какое-нибудь надежное и защищеное место.
Скопируйте файлы ca.crt и note.* на клиент.

Строго говоря, можно было сгенерировать пару ключей на клиенте и передать на сервер публичный ключ для подписи. Но поскольку у меня был защищеный канал (ssh) между клиентом и сервером, я сгенерировал все в одном месте.

Далее нужно написать конфигурационный файл для openvpn. У меня получилось так:
# биндимся к порту 443, т.к. это единственный порт, на который разрешено делать CONNECT на том прокси-сервере
#port 1194
port 443

# TCP or UDP server?
proto tcp
;proto udp

dev tun

# пути к файлам сертификатов
ca /etc/openvpn/keys/ca.crt
cert /etc/openvpn/keys/server.crt
key /etc/openvpn/keys/server.key  # This file should be kept secret
dh /etc/openvpn/keys/dh1024.pem

# тут важно выбрать такую подсеть, которая не пересекалась бы ни с одной другой используемой вами
server 192.168.120.0 255.255.255.0

ifconfig-pool-persist ipp.txt

# так можно указывать клиенту, какие сети нужно маршритизировать через этот vpn
;push "route 192.168.1.0 255.255.255.0"

# а так можно указать, что этот vpn следует сделать шлюзом по умолчанию (что нам и нужно)
push "redirect-gateway"



# Разрешаем клентам взаимодействовать друг с другом, по умолчанию отключено
client-to-client

# The keepalive directive causes ping-like
# messages to be sent back and forth over
# the link so that each side knows when
# the other side has gone down.
# Ping every 10 seconds, assume that remote
# peer is down if no ping received during
# a 120 second time period.
keepalive 10 120

# включаем компрессию
comp-lzo

# так можно указать максимально количество клиентов
;max-clients 100

# так можно заставить openvpn сбросить права
;user nobody
;group nobody

# The persist options will try to avoid
# accessing certain resources on restart
# that may no longer be accessible because
# of the privilege downgrade.
persist-key
persist-tun

# лог-фай
status openvpn-status.log

Запуск сервера происходит командой openvpn /etc/openvpn/server.conf.
Перед запуском надо подгрузить модуль tun.

3: Конфигурация клиента


Вот конфигурационный файл:
client

dev tun

# указываем протокол и прокси
proto tcp
http-proxy proxy 3128
;http-proxy-retry # retry on connection failures

# указываем адрес и порт удаленного сервера
remote vpn.mydomain.ru 443

resolv-retry infinite
nobind

# Downgrade privileges after initialization (non-Windows only)
;user nobody
;group nobody

persist-key
persist-tun

# пути к сертификатам и ключам
ca /etc/openvpn/keys/ca.crt
cert /etc/openvpn/keys/note.crt
key /etc/openvpn/keys/note.key

# разрешаем компрессию
comp-lzo

После подгрузки модуля tun и запуска клиента, можно убедиться что интерфейс поднимается, и сервер пингуется.
Внимание: после поднятия vpn, openvpn перепишет шлюз по умолчнию, так что нужно заранее добавить статические маршруты (если таковых нету) к прокси-серверу и местной локальным ресурсам.

Однако после поднятия vpn, внешние ресурсы все еще не будут работать. А все потому что нужно выполнить шаг

4: настройка NAT


Во-первых, разрешаем серверу маршрутизировать пакеты. Для этого пишем в /etc/sysctl следующее:
net.ipv4.ip_forward = 1
net.ipv4.ip_dynaddr = 1
После чего выполняем sysctl -a.

Далее нужно средствами iptables настроить правила маскарадинга. Для этого удобно написать sh-скриптик, который в дальнейшем будет удобно пополнять и редактировать. У меня он имеет примерно такой вид (сокращено):
#!/bin/sh

# маски используемых сетей
IP_LAN="192.168.1.1/24" # моя домашняя сеть
IP_PROVIDER="10.0.0.1/8" # локальная сеть провайдера

# интерфейсы
IF_EXT="eth1" # сюда подключен Ethernet от провайдера
IF_LAN="eth0" # сюда подключена локальная сеть
IF_INET="ppp0" # сюда "подключен" интернет
IF_VPN="tun0" # это интерфейс нашего vpn

IPT="iptables"

# cleaning:
$IPT --flush
$IPT -t nat --flush
$IPT -t mangle --flush
$IPT -X

# тут по желанию и степени паранои
$IPT -P INPUT ACCEPT
$IPT -P OUTPUT ACCEPT
$IPT -P FORWARD ACCEPT

# allow loopback
$IPT -A INPUT -i lo -j ACCEPT
$IPT -A OUTPUT -o lo -j ACCEPT

###
# local host:

# allow outgoing connections:
$IPT -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPT -A OUTPUT -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

# разрешаем подключения к vpn
$IPT -A INPUT -p tcp --dport 443 -j ACCEPT

# разрешаем NAT из локальной сети и VPN:
$IPT -t nat -A POSTROUTING -o $IF_EXT -j MASQUERADE
$IPT -t nat -A POSTROUTING -o $IF_INET -j MASQUERADE

$IPT -A FORWARD -i $IF_LAN -o $IF_EXT -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
$IPT -A FORWARD -i $IF_EXT -o $IF_LAN -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPT -A FORWARD -i $IF_LAN -o $IF_INET -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
$IPT -A FORWARD -i $IF_INET -o $IF_LAN -m state --state ESTABLISHED,RELATED -j ACCEPT

$IPT -A FORWARD -i $IF_VPN -o $IF_EXT -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
$IPT -A FORWARD -i $IF_EXT -o $IF_VPN -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPT -A FORWARD -i $IF_VPN -o $IF_INET -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
$IPT -A FORWARD -i $IF_INET -o $IF_VPN -m state --state ESTABLISHED,RELATED -j ACCEPT

После выполнения скрипта достаточно сделать /etc/init.d/iptables save и добавить iptables в runlevel: rc-update add iptables default