Простая и быстрая настройка mesh VPN с помощью tinc

Зайчатки разума

Записная книжка айтишника

Простая и быстрая настройка mesh VPN с помощью tinc

2020-01-30 20:36:30 — Evgeniy Shumilov

  Периодически сталкиваюсь с какими-то программными продуктами, которые существуют уже много лет, удобны, легки, просто работают, но я о них по какой-то причине не слышал на протяжении всего времени их существования. На этой неделе открыл для себя tinc. Если кратко, то tinc - это vpn, который позволяет объединить несколько машин друг с другом, где каждая будет обмениваться при необходимости данными с каждой, т.е. не будет единой точки отказа. Чем прекрасен именно этот вариант - он требует минимальной настройки, конфигурационные файлы очень просты, создание ключа - тоже. Типичный сценарий для использования tinc - несколько географически удалённых филиалов одной сети. Поднимаем на двух машинах бридж, выдаём ему нужный локальный адрес, tinc поднимает tap или tun интерфейс, при создании/уничтожении интерфейса запускает скрипт, в который передаёт имя интерфейса, что позволяет добавить его в бридж или удалить из него. Всё просто работает, но есть один нюанс, о котором я расскажу ниже.


  Для начала поставим tinc и создадим в /etc/tinc директорию, соответствующую имени сети. Пусть в нашем случае это будет просто networkname:

sudo apt-get install tinc bridge-utils
cd /etc/tinc
sudo mkdir networkname
cd networkname

  Внутри этой директории нужно создать конфигурационный файл с именем tinc.conf и следующим содержимым:

Name = host1
Mode = switch
Interface = tap0
Port = 655

ConnectTo = host2

  Соответственно, host1 - это имя нашей машины, host2 - это машина, к которой мы будем подключаться. Строк ConnectTo может быть сколько угодно, таким образом, каждый из ваших узлов может соединяться со всеми остальными, на которых запущен tinc. В этом случае желательно настроить синхронизацию конфигурационных файлов между машинами, чтобы не переносить их каждый раз руками или, ещё лучше, накатывать чем-то вроде ansible. Вдаваться подробно в параметры не вижу смысла, вот здесь есть замечательная и понятная документация по параметрам конфигурационного файла. 

  Tinc работает по udp протоколу, поэтому необходимо на всех машинах с tinc открыть 655-й порт udp. На обоих машинах в директории /etc/tinc/networkname нужно создать директорию hosts, после чего создать пару ключей - публичный и приватный с помощью tincd:

tincd -n networkname -K

  Публичный ключ должен появиться в директории hosts в файле с именем, соответствующим значению поля Name в конфигурационном файле tinc.conf. Т.е. на host1 публичный ключ будет лежать в /etc/tinc/networkname/hosts/host1, а на host2 - в /etc/tinc/networkname/hosts/host2. Теперь на машине host1 мы должны создать файл /etc/tinc/networkname/hosts/host2 следующего содержания:

Address = 1.2.3.4
Cipher = aes-128-cbc
Digest = sha1
Compression = 0

-----BEGIN RSA PUBLIC KEY-----
SomeLongHashWasHere
-----END RSA PUBLIC KEY-----

  Где 1.2.3.4 - это белый IP адрес host2, а в конце файла после Compression = 0 нужно вставить публичный ключ с host2, т.е. содержимое файла /etc/tinc/networkname/hosts/host2. Симметричным образом на машине host2 нужно создать файл /etc/tinc/networkname/hosts/host1, после чего на обеих машинах можно запускать сервис, но тут как раз и появляется та тонкость, о которой я писал в самом начале.

  У меня было две машины - с Ubuntu 16.04 LTS и Ubuntu 18.04 LTS. На первой после запуска появился интерфейс tap0, а на второй - нет. Начал разбираться.

$ systemctl status tinc
● tinc.service - Tinc VPN
   Loaded: loaded (/lib/systemd/system/tinc.service; enabled; vendor preset: enabled)
   Active: active (exited) since Tue 2020-01-28 14:57:45 MSK; 4min 41s ago
  Process: 7920 ExecStart=/bin/true (code=exited, status=0/SUCCESS)
 Main PID: 7920 (code=exited, status=0/SUCCESS)

Jan 28 14:57:45 git systemd[1]: Starting Tinc VPN...
Jan 28 14:57:45 git systemd[1]: Started Tinc VPN.

  Запущен и тут же завершён с кодом возврата 0. Интересно, а что же внутри сервиса?

$ cat /lib/systemd/system/tinc.service
# This is a mostly empty service, but allows commands like stop, start, reload
# to propagate to all tinc@ service instances.

[Unit]
Description=Tinc VPN
After=network.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/true
ExecReload=/bin/true
WorkingDirectory=/etc/tinc

[Install]
WantedBy=multi-user.target

  Гениально! Внимание, запускаем VPN сервис // на самом деле нет! // ... Запустили! Наслаждайтесь! // на самом деле нет! //
Для сравнения на ubuntu 16.04 в статусе иное:

$ systemctl status tinc
● tinc.service - LSB: Start tinc daemons
   Loaded: loaded (/etc/init.d/tinc; bad; vendor preset: enabled)
   Active: active (running) since Tue 2020-01-28 14:55:06 MSK; 6min ago
     Docs: man:systemd-sysv-generator(8)
  Process: 31199 ExecStop=/etc/init.d/tinc stop (code=exited, status=0/SUCCESS)
  Process: 31215 ExecStart=/etc/init.d/tinc start (code=exited, status=0/SUCCESS)
    Tasks: 1
   Memory: 372.0K
      CPU: 32ms
   CGroup: /system.slice/tinc.service
           └─31224 /usr/sbin/tincd -n service

  Проверил sys-v-init скрипты запуска сервиса на обеих машинах - отличия минимальны. Решил убрать файл сервиса на Ubuntu 18.04, после чего systemd вынужден будет вместо него использовать старый скрипт sys-v-init, проверил - работает!

$ sudo mv /lib/systemd/system/tinc.service .
$ sudo systemctl daemon-reload
$ systemctl status tinc
● tinc.service - LSB: Start tinc daemons
   Loaded: loaded (/etc/init.d/tinc; generated)
   Active: active (exited) since Tue 2020-01-28 14:57:45 MSK; 11min ago
     Docs: man:systemd-sysv-generator(8)
 Main PID: 7920 (code=exited, status=0/SUCCESS)
    Tasks: 0 (limit: 4915)
   CGroup: /system.slice/tinc.service
$ sudo systemctl enable tinc
$ sudo systemctl restart tinc
$ systemctl status tinc
● tinc.service - LSB: Start tinc daemons
   Loaded: loaded (/etc/init.d/tinc; generated)
   Active: active (running) since Tue 2020-01-28 15:10:24 MSK; 6s ago
     Docs: man:systemd-sysv-generator(8)
  Process: 12683 ExecStop=/etc/init.d/tinc stop (code=exited, status=0/SUCCESS)
  Process: 12701 ExecStart=/etc/init.d/tinc start (code=exited, status=0/SUCCESS)
 Main PID: 7920 (code=exited, status=0/SUCCESS)
    Tasks: 1 (limit: 4915)
   CGroup: /system.slice/tinc.service
           └─12720 /usr/sbin/tincd -n service

Jan 28 15:10:24 git systemd[1]: Starting LSB: Start tinc daemons...
Jan 28 15:10:24 git tinc[12701]: Starting tinc daemons: service.
Jan 28 15:10:24 git systemd[1]: Started LSB: Start tinc daemons.
Jan 28 15:10:24 git tinc.service[12720]: tincd 1.0.33 starting, debug level 0
Jan 28 15:10:24 git tinc.service[12720]: /dev/net/tun is a Linux tun/tap device (tap mode)
Jan 28 15:10:24 git tinc.service[12720]: Ready

  После этого можно двигаться дальше. Итак, у нас на этот момент на обеих машинах должен быть интерфейс tap0. Далее возможны варианты - можно назначить на эти интерфейсы серые IP адреса и они будут видны друг-другу. Можно устроить роутинг через них. А можно создать два бриджа и добавить интерфейсы в бриджи, что даст возможность в те же бриджи потом добавить и физические интерфейсы, таким образом объединив удалённые подсети.

  При создании интерфейса tinc может запустить скрипт tinc-up, если найдёт таковой в директории с конфигурационным файлом сети и аналогичным образом будет запущен tinc-down при опускании интерфейса. Вот примеры моих скриптов.

tinc-up:

#!/bin/sh
cd `dirname "$0"`
[ -f ".env" ] && . ./.env

BR="$(basename `pwd`)"

if [ -z "$(brctl show | awk 'NR>1 && $1~/^'"$BR"'$/')" ]; then
    echo "Trying to bring up new bridge $BR"
    brctl addbr "$BR"
    [ -n "$ADDRESS" ] && echo "Setting address $ADDRESS to bridge $BR" && ifconfig "$BR" "$ADDRESS" netmask "$NETMASK" up
fi

ip link set $INTERFACE up
brctl addif "$BR" $INTERFACE

tinc-down:

#!/bin/sh
cd `dirname "$0"`
[ -f ".env" ] && . ./.env
BR="$(basename `pwd`)"

brctl delif "$BR" "$INTERFACE"
ip link set "$INTERFAСE" down

  Нужные мне параметры берутся из файла .env, в который я их буду добавлять по мере необходимости и усложнения.

.env:

ADDRESS=10.11.12.13
NETMASK=255.255.255.0
IFACE=tap0

  Скрипт в качестве имени сети берёт имя директории, в которой он запущен, проверяет наличие бриджа, если его нет, то создаёт его, назначает ему адрес сети и маску из файла .env и добавляет в него интерфейс tap0. Сетевой мост мы можем использовать и как стандартный интерфейс в том числе, что удобно. 
  На сегодня это всё.

P.S.: И не спрашивайте меня, почему на лого изображение боевого вертолёта. Я сам хотел бы это знать.

Теги: админское, networking

comments powered by Disqus