Сложете VPN клиент в контейнер и получавате едно именно пространство с тунел вътре в него, а не хост, който насочва целия си трафик през VPN. Това са отделни проекти и именно третирането им като един е начинът контейнерите случайно да станат привилегировани. Това ръководство покрива само първото: един прегледан образ, един защитен профил и клиентски процес, ограничен в собственото си мрежово именно пространство.

Започнете от артефакт, който някой вече е одобрил. Стъпките по-долу предполагат образ, който съдържа изпълнимия файл openvpn и пълен клиентски профил със сертификат и ключов материал, вече разрешени в монтирането. Те не изграждат софтуер, не издават удостоверения и не покриват среди без root и родни Windows контейнери, всяка от които променя отговора.

Какво получава контейнерът и какво не

Дръжте профила и всичко, към което сочи, в локална директория с ограничени права, монтирана само за четене. Относителните пътища в профила трябва да се разрешават в рамките на това монтиране; това е подробност, която хората пропускат, когато сертификатът стои една директория над профила и контейнерът никога не го вижда.

services:
  vpn:
    image: ${VPN_CLIENT_IMAGE:?set the reviewed image reference}
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun:/dev/net/tun
    volumes:
      - ./vpn:/vpn:ro
    working_dir: /vpn
    entrypoint: ["openvpn"]
    command: ["--config", "/vpn/client.ovpn"]

Този файл дава три неща и не иска нищо друго. Препратката към образа трябва да е закрепена с дайджест, ако политиката ви за внедряване го изисква, а профилът трябва да е прегледаният, а не копие, което някой е редактирал на място на хоста.

Само за четене тук означава, че контейнерът не може да променя файловете ви на хоста; не означава, че не може да прочете частния ключ, който сте монтирали. Това е целият смисъл на монтирането и затова правата на файловете, собствеността и начинът, по който тайната е попаднала на хоста, все още имат значение. Никога не подавайте парола в командния ред и не я вграждайте в слой на образа, защото и двете остават на места, които не можете да изчистите по-късно.

Възможности, entrypoint и капанът между тях

Нужни са две настройки и те вършат несвързани работи. NET_ADMIN позволява манипулиране на интерфейси и маршрути в именното пространство на контейнера. Записът за устройство /dev/net/tun позволява на процеса изобщо да отвори тунелното устройство. Предоставянето на едното без другото дава ясен отказ, което е удобно: липсващото TUN устройство се проваля при отваряне, а липсващата възможност се проваля, когато клиентът се опита да конфигурира интерфейса.

Нито едно от тях не оправдава privileged: true. Привилегирован контейнер получава всички възможности плюс достъп до устройства и това е много по-голямо предоставяне, отколкото тази натовареност изисква. справката за привилегии и възможности на средата за изпълнение описва докъде стига това предоставяне. Ако диаграма или публикация в блог го изискват, първо проверете дали изискването не е било всъщност липсващ път до устройство или липсваща възможност.

Отменянето на entrypoint е частта, която хапе. Директното извикване на openvpn е правилно за обикновен образ и погрешно за всеки продуктов образ, чийто стартов скрипт подготвя правила на защитна стена, файлове на резолвера или удостоверения преди стартиране на демона. Отменянето на този скрипт пропуска подготовката и ви оставя да отстранявате грешки в тунел, който е тръгнал в наполовина конфигурирано именно пространство.

Стартирайте клиента на преден план и го оставете да бъде основният процес на контейнера. Процес, който демонизира, излиза веднага, контейнерът се рестартира в цикъл, а дневниците, които ви трябват, са разпръснати из рестартирания, които изглеждат еднакво.

Проверявайте отвътре в именното пространство, а не от хоста

Вдигнете стека, потвърдете в дневника, че клиентът е достигнал свързано състояние, и след това прегледайте именното пространство, което всъщност държи тунела.

docker compose up -d
docker compose logs --tail 40 vpn
docker compose exec vpn ip -br addr
docker compose exec vpn ip route

Списъкът с адреси трябва да показва тунелен интерфейс с адрес от пула, а таблицата с маршрути — изпратените дестинации, сочещи към него. Ако интерфейсът липсва, клиентът никога не е стигнал до създаването му. Ако съществува, но никой маршрут не го използва, мястото за търсене са push или опциите за маршрути.

След това тествайте дестинацията, която ви интересува, от именното пространство, което трябва да използва тунела, чрез приложението, а не само с ping. Разрешаването на имена е отделен провал, който си струва да се тества изрично: изпратен DNS адрес не обновява резолвера на контейнера сам по себе си, а самостоятелен клиент, който игнорира push, ще разрешава имена чрез каквото образът е конфигурирал.

Прекъснатото състояние е част от договора

Поведението при повторно свързване се нуждае от собствена проверка, защото политиката за рестартиране доказва, че процесът се рестартира, и не казва нищо за това какво прави трафикът междувременно. Спрете тунела, опитайте предвидената дестинация и наблюдавайте дали връзката се отказва, засича или тихо успява по обичайния маршрут на контейнера. Последният резултат е този, който има значение, и той е невидим в здрав дневник.

Ако натовареността никога не бива да използва обичайния път, проектирайте това изрично с правила на защитна стена и сигнал за готовност в именното пространство. Kill switch е политика, която пишете и тествате, а не свойство, което наследявате от пускането на клиент в контейнер.

Накрая, помнете границата: тези маршрути живеят в именното пространство на контейнера и не заменят маршрутната таблица на хоста. Правилата за препращане, транслация и защитна стена на Docker остават част от външния път, а публикуваният порт не е VPN интерфейс.

Подобни статии