Чиним резолвинг адресов в VPN-локалке (openconnect) для docker и systemd-resolved

    Для подключения к корпоративной сети у нас используется CiscoAnyConnect, работает хорошо, но не с докером. Как только докер пытается приподнять свою сеть, утилитка тут же отрубает VPN и переподключает. От этого докер себя плохо чувствует. Поэтому я решил использовать обычный линуксовый openconnect соместно с NetworkManager.

    systemd-resolved


    Поставил пакеты network-manager-openconnect network-manager-openconnect-gnome
    Настроил соединение и оно даже подключилось. Проблема первая, каждый раз когда я подключался снова, он забывал имя пользователя, что раздражает. Я нашел решение и создал
    баг. Решение простое, с консоли задаем свое имя в особом виде nmcli con mod prgcvp vpn.secrets 'form:main:username=yourName','save_passwords=yes'
    После чего оно будет запомнено. Да, галочку «запомнить пароль» я ставил и пароль он даже запомнил, но вот в тексте галочки про имя пользователя ничего не сказано, так что он честно его забыл :) Напомню, что настройки менеджера лежат в /etc/NetworkManager/system-connections/.

    И если параметры знаешь, то можно и руками отредактировать нужное соединение.

    Подключаться стало удобно и возникла вторая проблема, имена ресурсов в VPN сети не разрешаются в адреса DNS сервером. Сервер есть, все настройки на месте, но nslookup someserver.local выдает ошибку, а nslookup someserver.local somednsIP выдет правильный ответ. Странно, подумал я, как так, сервер есть, отвечает, а если его конкретно не указать, ошибка? Ответ оказался прост. Когда systemd-resolved пытается найти адрес сервера по имени, он выступает фасадом для других DNS серверов. Делается это так, ссылка /etc/resolv.conf может указывать на несколько мест:

    • /run/systemd/resolve/stub-resolv.conf это опция по умолчанию и этот файл будет содержать примерно такое
      nameserver 127.0.0.53
      options edns0 trust-ad
      search somedomain.local
    • /run/systemd/resolve/resolv.conf это можно использовать если функционал systemd-resolved чем то не устроил когда он прикидывается DNS сервером 127.0.0.53. В итоге это не пригодилось, так для информации пишу.

    Подробности тут.

    Саму ссылку /etc/resolv.conf вы можете сами настроить что бы смотрела в любое из мест.
    Так вот, дело в том, что openconnect при подключении к VPN получает таблицу route, DNS сервера, а так же search domain и этот домен от VPN сервера приходил неверный (так неправильно настроен у нас). От сервера приходило somedomain.local, а надо было просто local что бы somesrver.local был распознан.

    Когда systemd-resolved прикинулся локальным DNS сервером и через /etc/resolv.conf всех отправил к себе за разрешением имен, логика его работы такова. Для каждого коннекта, которые можно посмотреть командой nmcli connection show (это те коннекты, которые знает NetworkManager) systemd-resolved помнит DNS сервера, которые получил по DHCP. Это можно посмотреть командой:

    resolvectl dns
    Global:
    Link 6 (docker0):
    Link 5 (vpn0): 999.999.999.999 999.999.999.999
    Link 3 (wlp4s0): 192.168.3.8
    Link 2 (enp5s0f2):
    

    Когда в 127.0.0.53 приходит запрос на разрешение имени, systemd-resolved смотрит search domain у каждого из коннектов (эти домены он при подключении от DHCP получил тоже). Домены можно посмотреть командой:

    resolvectl domain
    Global:
    Link 6 (docker0):
    Link 5 (vpn0): somedomain.local
    Link 3 (wlp4s0): ~.
    Link 2 (enp5s0f2):
    

    Далее имя хоста проверяется на частичное совпадение с теми доменами, которые прикреплены к коннектам и самое длинное совпадение и определяет какой конкретно DNS сервер вызвать. Либо все идет в DNS сервер где search domain "~."

    От сервера компании мне приходил неверный search domain (somedomain.local) для VPN коннекта и потому когда я пытался разрешить адрес someserver.local, systemd-resolved их не мог найти, так как предполагал, что DNS сервера, полученные из этого соединения нужны что бы распознавать имена someserver.somedomain.local. Поправил я это подменив search domain в NetworkManager командой
    nmcli connection modify yourConnectionName ipv4.dns-search «local»
    Доменов может быть несколько через пробел. В итоге все заработало.
    Помимо этого я удалил пакет avahi-daemon, так как службы bonjur, которые обслуживает этот демон по умолчанию тоже резолвятся на домене local, а в нашей сетке именно это имя используется для локальной сети и будут конфликты.

    docker


    Теперь в хост системе работает резолвинг адресов, но при запуске докера резолвинг локальных адресов в VPN может не работать по прежнему. И тут есть несколько вариантов.

    Контейнер запущен с network_mode: host в таком случае будет использоваться для резольвинга то, что лежит в /run/systemd/resolve/resolv.conf и там у меня первый же днс сервер выбирается для резольва local и он для этого не подходит. Итог, не работает. Зато все сервисы хост машины видно из контейнера, что еще и неправильно.

    Контейнер запущен с network_mode: bridge с созданием отдельной сети. В таком случае сервисы хоста будет не видно, помимо этого будет использован все тот же не работающий у меня /run/systemd/resolve/resolv.conf

    Контейнер запущен без настроек сети и использует дефолтный bridge, созданный докером при инсталляции (docker0). В этом случае используется для резольва имен внутренний докеровский DNS, который судя по всему нормально взаимодействует с systemd-resolved и все резолвит как надо. В /etc/resolv.conf будет такое:

    bash-5.0# cat /etc/resolv.conf 
    search local
    nameserver 127.0.0.11
    options ndots:0
    

    Если надо показать сервисы хост машины для доступа из контейнера, то просто запускаем сервисы слушать на docker0 IP и получаем доступ.

    ifconfig|grep -n1 docker0
    27-
    28:docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
    29-        inet 192.168.32.1  netmask 255.255.240.0  broadcast 192.168.47.255
    

    Не забываем открыть порты, например у меня ufw зарезал 8080 и пришлось открывать.
    Было бы отлично если бы docker просто использовал systemd-resolved dns stub как в последнем описанном варианте всегда. Но к сожалению это не так. В версии systemd-resolved 248, которая только вышла и в дистрах ее нет в документации есть параметр DNSStubListenerExtra, который может задать адрес где слушать для stub. Подробности тут.

    В итоге можно будет указать адрес где слушать не захардкоженный 127.0.0.53, а доступный изнутри докера и все будет работать, но пока нет.

    Есть другое решение, когда контейнер пойдет на порт 53 мы будем перебрасывать его запросы в стаб systemd с помощью чего то вроде
    socat UDP-LISTEN:53,fork,reuseaddr,bind=yourInterfaceIP UDP:127.0.0.53:53
    Соответственно, этот DNS можно указать докеру при старте и весь функционал systemd будет работать. Но не стал это использовать.

    В итоге мы получаем работающий VPN на хосте, который резолвит имена в локальной сети, работающий докер, который может резолвить эти адреса как родные.

    Похожие публикации

    Средняя зарплата в IT

    120 000 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 9 250 анкет, за 1-ое пол. 2021 года Узнать свою зарплату
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

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

      +1

      Тема поднята важная, но в тексте сумбур и колхоз.


      1. Многие шаги не автоматизированы, не руками же чинить при каждом подключении?
      2. Если DHCP сломан, это надо править на сервере, а не бороться локально.
      3. mDNS (Bonjour & co) и .local могут работать вместе, если правильно настроен /etc/nsswitch.conf.
      4. В итоге контейнеры в отдельных сетях так и не могут разрешать домены в .local. Насколько я знаю, решается либо /etc/hosts (с очевидными недостатками), либо через socat, как описано (но у меня в нем регулярно виснут соединения, приходится перезапускать), либо через --add-host/extra_hosts для контейнер (не лучше /etc/hosts).
        0
        2й пункт поддерживаю.
        По 3ему пункту можете поделиться знаниями?
          0

          https://github.com/lathiat/nss-mdns


          nss-mdns will check SOA before every request to resolve .local names, meaning that neither nss-mdns nor Avahi need to be disabled to allow .local queries to be served from unicast DNS.
            +1
            Я бы хотел описание, что, где, как, зачем, для чего. Но уже сам разобрался(
            Вот старая статья, которая, к сожалению, не рассказывает, почему надо такие настройки делать.
            Вот здесь описание работы библиотек/и для nsswitch.
            Вот здесь описание файла настроек nsswitch.
            В кратце, строка в конфиге должна выглядеть так:
            hosts: files mdns4_minimal dns mdns
            Почему так:
            1. Мы проверяем сначала файлы hosts.
            2. Потом проверяем адреса 169.254.0.0/16 и домены X.local и Y.local. (только второй уровень днс).
            3. Потом делаем поиск в днс серверах.
            4. И только после этого проверяем все адреса ip стеков 4 и 6, а так же все домены перечисленные в файле /etc/mdns.allow

        –2
        1. А зачем руками чинить при каждом подключении? Один раз это настраивается. Нечего там автоматизировать, настройки соединения менеджер помнит.
        2. Кому из админов это надо, рисковать менять поисковый домен. Фиг знает, что на него завязано у клиентов. Проще самому починить.
        3. Так как это мне ненужно, проще удалить и не настраивать.
        4. Решение описано, но мне не нужно. Даже два. Сокат рвет соединение, сделай багу, поступи правильно! :)

        В общем претензии непонятны :)


        О каком сумбуре речь вообще неясно, можно команды прям по очереди выполнить и все причины описаны. Это уж вообще странное заявление :) Все это похоже на высосанный из пальца комент :)

          0
          1. Поверю вам, не особо знаю NetworkManager.
          2. Админам деньги платят, чтобы настроили все правильно. По-вашему, лучше каждому клиенту приседать?
          3. Тогда должна быть оговорка. Иначе получается инструкция, которая ломает смежную функциональность.
          4. Достаточно оказалось открыть man socat и прочитать про -T :)

          команды прям по очереди выполнить и все причины описаны

          Претензия как раз к тому, что вы останавливаетесь на удобном вам решении, но не всегда упоминаете его проблемы (вовсе необязательная поломка mDNS, итоговое решение для Docker не связано с VPN).


          P. S. Минусы не от меня.

            –1

            Ну тут да, это инструкция по решению конкретной проблемы, а не ман, соглашусь, удобно для меня.


            Минусов два, а в закладках заметка у семерых. Ценители минусов могут расслабиться ;)

              +1
              Есть поговорка: «миллионы мух не могут ошибаться». Давайте на техническом ресурсе апеллировать не косвенными фактами.

              P. S. Минусы не от меня.
                –1

                Это вообще о чем? :) Давайте лучше вопрос или апдейт к написанному в статье напишите.

                  +1
                  Всё есть в первом комментарии, не вижу необходимости разводить флейм.
                    –1

                    Вот и ок.

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое