Автоматическое получение конфигов Openvpn для бесплатного VPN от vpnbook.com

Преамбула

Есть относительно неплохой бесплатный сервис VPN vpnbook.com, мы про него когда-то давно писали, а я одно время даже активно пользовался, пока меня не задолбали некоторые ограничения, и я не перешел на лучший и более надежный в плане безопасности Riseup, но в качестве резерва, почему бы не иметь и его? Заодно и знакомый попросил его настроить.
В общем, пошел я на сайт, а там все изменилось, появились и периодически добавляются новые сервера, старые подохли, в общем контора активно цветет и пахнет.

Так вот, на случай появления новых серверов, придется идти на сайт, скачивать конфиги, при необходимости добавлять свои параметры, и т.д. А зачем это каждый раз делать руками, если можно автоматизировать?

Описание задачи

1. Вытащить с сайта список конфигов
2. Скачать архивы конфигов на локальный компьютер и распаковать их
3. Автоматически добавить в конфиг дополнительные параметры, например изменить уровень script-security и добавить пользовательские скрипты, выполняющиеся при соединении и разрыве соединения с сервером, и т.д. Подробности тут или тут
4. Конфиги для каждого сервера поставляются в нескольких вариантах, для соединения по разным портам, например TCP 443, TCP 80, UDP 25000, UDP 53. Сервера различаются по странам. Соответственно, надо предусмотреть возможность выбора всех или определенных конфигов.

Дополнительные программы

Нам понадобится какой-нибудь парсер HTML. Я использую xidel, о котором недавно писал

Подготовительные действия

1. Добавляем в начало скрипта необходимые переменные:

VPNBOOKPAGE="https://www.vpnbook.com/"
WORKDIR="/tmp/vpnbook/"
UNPDIR="/tmp/vpnbook/unpack/"
HTMLFILE="vpnhtml.html"

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

2. Предусматриваем возможность удалить каталог с распакованными конфигами (если пользователь запустит скрипт с ключом ):

if [[ "$1" == "-c" ]]; then
    echo "Cleaning  unpack directory..."
    rm -rf "$UNPDIR"
fi

3. Удаляем ранее сохраненный HTML-файл (иначе новые wget будет сохранять под именами vpnhtml.html.1, vpnhtml.html.2 и т.д.) и создаем рабочий каталог и каталог для распаковки архивов. Путь будет создан со всеми подкаталогами, если он ранее не существовал. Если каталог уже существовал, то никакой ошибки не будет.

rm "$WORKDIR$HTMLFILE"
mkdir -p "$WORKDIR"
mkdir -p "$UNPDIR"


Получение конфигов

1. Сохраняем на диск главную страницу сайта:

echo "Get vpnbook page..."
wget -P $WORKDIR --default-page=$HTMLFILE --header="Content-type: text/html" $VPNBOOKPAGE

2. … и проверяем, загрузилась ли она:

if [[ ! -f "$WORKDIR$HTMLFILE" ]];then
    echo "Page not downloaded!"
    exit
fi

3. Получаем ссылки на архивы с конфигами. Ссылка на архив выглядит как обычная HTML ссылка a href внутри элемента <li>.:

<li><strong><span class="red">Server:</span></strong> <a href="/free-openvpn-account/VPNBook.com-OpenVPN-PL226.zip">Download PL226 Server OpenVPN Certificate Bundle</a></li>

Вообще вытаскивать что-либо из HTML регулярками плохо и некультурно, поэтому воспользуемся парсером xidel.
Вытаскиваем все ссылки, точнее, их атрибуты href, и фильтруем необходимые (содержащие ZIP-архивы) с помощью grep:

xidel -s --extract "//a/@href" "$WORKDIR$HTMLFILE" |grep ".zip"

4. Полученные данные обрабатываем в цикле:
4.1. Скачиваем архив
4.2. Распаковываем содержимое в $UNPDIR.
4.3. Удаляем архив.

Цикл целиком:

for ZIPURL in $(xidel -s --extract "//a/@href" "$WORKDIR$HTMLFILE" |grep ".zip"); do
    ZIPNAME=`basename $ZIPURL`
    echo "Download $ZIPNAME..."
    wget -P $WORKDIR "$VPNBOOKPAGE/$ZIPURL" >/dev/null 2>/dev/null
    if [ -f "$WORKDIR$ZIPNAME" ];then
	echo "Unpacking $ZIPNAME..."
	unzip -o "$WORKDIR$ZIPNAME" -d "$UNPDIR" >/dev/null 2>/dev/null
	rm "$WORKDIR$ZIPNAME"
    fi
done

5. Минус конфигурационных файлов Vpnbook — концы строк в файле .ovpn CR+LF, т.е. формата Windows. Ищем все распакованные файлы (*.ovpn) в директории из переменной $UNPDIR, и заменяем в каждом файле концы строк Windows на концы строк Linux (LF). Делаем это в цикле sed‘ом:

echo -n "Replacing Windows line ends"
for FLE in $(find $UNPDIR -maxdepth 1 -iname "*.ovpn"); do
    echo -n "."
    sed -i 's/\r$//' "$FLE"
done
echo

Готово, конфиги получены.

Добавление пользовательских параметров в конфиги Openvpn

Как я уже говорил, многие пользователи правят конфиги, добавляя необходимые им параметры, например, параметр daemon, позволяющий запускаться Openvpn в фоновом режиме, или параметры script-security, up <scriptfile>, down <scriptfile>, позволяющие Openvpn выполнять пользовательские скрипты при установке и разрыве соединения. Каждый раз при обновлении серверов неудобно редактировать/добавлять вручную эти параметры, особенно, если для любого сервера данного VPN они одинаковы. Будем автоматизировать.
Некоторые уточнения:
— если параметр (например auth-user-pass) уже есть в конфиге, то его необходимо отредактировать
— если параметра в конфиге нет, добавить.
— добавлять новые параметры будем в начало конфигурационного файла, т.к. в конце у файлов конфигурации для vpnbook хранятся сертификаты в BASE64.

1. Для хранения пользовательских параметров заведем массив ADD_COMMANDS и наполним его содержимым. Для примера я хочу запускать пользовательские скрипты при установке/разрыве соединения, а также автоматически вводить имя пользователя и пароль (все равно они для всех одинаковые, сервис без регистрации). Заведу в начале скрипта массив:

ADD_COMMANDS[0]="script-security 2"
ADD_COMMANDS[1]="up '~/openvpn/vpnbook/vb-routes up'"
ADD_COMMANDS[2]="down '~/openvpn/vpnbook/vb-routes down'"
ADD_COMMANDS[3]="auth-user-pass ~/openvpn/vpnbook/vpnbook.auth"

2. Для работы с параметрами заведем функцию add_commands(), которая будет принимать единственный параметр — полный путь к редактируемому файлу.

3. В функции получим количество элементов массива в переменную $CTR и заведем счетчик цикла:

CTR="${#ADD_COMMANDS[*]}"; I="0"

4. Организуем цикл:

until [ "$CTR" -eq "$I" ]; do
	[Тут будет код :)]
done

5. В цикле:
5.1. Вытаскиваем в переменную $CMND_ пользовательскую команду из текущего элемента массива:

CMND_="${ADD_COMMANDS[I]}"

5.2. Вытаскиваем первое слово из команды (script-security или auth-user-pass, например). Его будем искать, и, соответственно, принимать решение, заменять существующую опцию в конфиге или добавлять новую:

CMND_ST=`echo "$CMND_" |awk '{print $1}'`

5.3. Считаем количество строк, содержащих эту опцию в конфиге, с помощью grep:

CMND_C=`grep -c "$CMND_ST" "$1"`

5.4. Если таких строк нет, то добавляем, если уже есть — то редактируем:

if [ "$CMND_C" -eq 0 ];then #not found, add

else #found, replace

fi

5.4.1. Добавление строки:

sed -i -e "1 s%^%$CMND_\n%" "$1"

5.4.2. Замена строки, начинающейся с ключевого слова из переменной $CMND_ST на пользовательскую опцию (в переменной $CMND_):

sed -i "s%^ *$CMND_ST.*%$CMND_%" "$1"

5.5. Увеличиваем счетчик цикла на единицу:

let "I+=1"

Функция целиком:

add_commands() #$1-output file
{
    CTR="${#ADD_COMMANDS[*]}"; I="0"
    until [ "$CTR" -eq "$I" ]; do
	
	CMND_="${ADD_COMMANDS[I]}"
	CMND_ST=`echo "$CMND_" |awk '{print $1}'`
	CMND_C=`grep -c "$CMND_ST" "$1"`
	
	if [ "$CMND_C" -eq 0 ];then #not found, add
	    sed -i -e "1 s%^%$CMND_\n%" "$1"
	else #found, replace
	    sed -i "s%^ *$CMND_ST.*%$CMND_%" "$1"
	fi
	
	let "I+=1"
    done
}

В самом скрипте добавляем цикл обработки скачанных и распакованных конфигов, также выбирая их командой find:

echo -n "Add commands"
for FLE in $(find $UNPDIR -maxdepth 1 -iname "*.ovpn"); do
    echo -n "."
    add_commands "$FLE"
done
echo

Выбор нужных конфигов

Так, а теперь пользователю нужно отобрать определенные конфиги. Имя файла у vpnbook стандартизировано и имеет такой вид:

vpnbook-странаNсервера-порт.ovpn, например, vpnbook-ca198-tcp80.ovpn.

Т.е. выбор нужных файлов легко произвести по маске.

1. Заведем переменные для хранения выходной директории и маски файлов. Например, выберем конфиги для 80-го порта:

OUTDIR="~/openvpn/vpnbook/"
OUTMASK="*tcp80.ovpn"

2. Проверяем не пуста ли переменная $OUTDIR, далее с помощью find выбираем нужные файлы по маске, и в цикле for копируем их в нужный каталог:

#copyng to outdir
if [ -n "$OUTDIR" ]; then
    echo "Copy to outdir:"
    for FLE in $(find $UNPDIR -maxdepth 1 -iname $OUTMASK); do
	if [ -n "$FLE" ]; then
	    BN=`basename $FLE`
	    echo "$BN"
	    cp "$FLE" "$OUTDIR$BN"
	fi
    done
fi

Скрипт целиком

На PasteBin
На GitHub

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *