В этой статье я расскажу, как можно организовать доступ к множеству Docker-контейнеров через VNC с использованием noVNC, websockify и SSL/TLS для шифрования и защиты соединений. Добавим защиту паролем для каждого контейнера, и включим шифрование трафика.
Проблема
В моем проекте было несколько Docker-контейнеров с графическими приложениями, к которым приходилось подключаться удаленно через VNC. До тех пор пока контейнеров было несколько штук и они создавались вручную было не сложно выделить им отдельные порты для экспозиции наружу из контейнеров и прописать их в VNC клиенте. Но с развитием проекта, контейнеры пришлось создавать динамически и в разных количествах, что сделало неудобным доступ к ним по разным портам и началась путаница с уже созданными в VNC клиенте подключениями. Захотелось подобрать более удобный вариант для подключений и вот что получилось.
Изначально задача казалась простой: запустить несколько контейнеров с VNC-серверами и обращаться к ним через прокси, например Nginx или Traefik. Однако проблему подключения ко всем контейнерам через один интерфейс с возможностью маршрутизации запросов на конкретные контейнеры по URL с применением Nginx или Traefik решить не удалось. Раньше много раз встречал упоминания/описания noVNC, но никогда не прибегал к использованию, оказалось, что в моем случае — вполне подходящее решение.
Решение: noVNC и websockify
Сформулирую задачу:
-
Имеем n контейнеров каждый с VNC сервером для доступа, который слушает порт 5900.
-
Хочется не заморачиваться с выделением каждому контейнеру отдельного внешнего порта, а вместо этого обращаться к каждому из них по отдельному URL.
Для решения проблемы используем noVNC — веб-клиент для работы с VNC через браузер, и websockify, который будет маршрутизировать запросы к различным VNC-серверам внутри Docker-контейнеров. Это позволит получить доступ к каждому контейнеру через уникальный URL и защищать соединения с помощью SSL/TLS. Сам сервер noVNC также будет в контейнере.
Далее в статье пример настройки noVNC, который каждый может адаптировать для себя.
Шаг 1: Настройка Docker Compose
Начнём с настройки Docker Compose, которая развернёт несколько контейнеров с VNC-серверами (container1, container2, container3) и предоставит к ним доступ через веб-интерфейс noVNC с маршрутизацией запросов через websockify (контейнер novnc).
Пример Docker Compose файла:
services: novnc: image: theasp/novnc # noVNC образ ports: - "${NOVNC_HTTP_PORT}:8080" # Порт для веб-интерфейса noVNC - "${NOVNC_HTTPS_PORT}:8443" # Порт для HTTPS с SSL/TLS volumes: - ./vnc-key.pem:/etc/ssl/private/vnc-key.pem:ro # Приватный ключ (только для чтения) - ./vnc-cert.pem:/etc/ssl/certs/vnc-cert.pem:ro # Сертификат environment: - VNC_PASSWORD=${VNC_PASSWORD} command: > sh -c 'mkdir -p /etc/websockify && echo "pushkin: container1:5900" > /etc/websockify/tokens && echo "dostoevsky: container2:5900" >> /etc/websockify/tokens && echo "chekhov: container3:5900" >> /etc/websockify/tokens && /usr/bin/websockify --web /usr/share/novnc --cert=/etc/ssl/certs/vnc-cert.pem --key=/etc/ssl/private/vnc-key.pem 0.0.0.0:8443 --token-plugin TokenFile --token-source /etc/websockify/tokens' networks: - test-net container1: image: dorowu/ubuntu-desktop-lxde-vnc # Образ для контейнера 1 с VNC-сервером expose: - "5900" environment: - VNC_PASSWORD=${VNC_PASSWORD} # Использование пароля для доступа к VNC networks: - test-net container_name: container1 container2: image: dorowu/ubuntu-desktop-lxde-vnc # Образ для контейнера 2 с VNC-сервером expose: - "5900" environment: - VNC_PASSWORD=${VNC_PASSWORD} networks: - test-net container_name: container2 container3: image: dorowu/ubuntu-desktop-lxde-vnc # Образ для контейнера 3 с VNC-сервером expose: - "5900" environment: - VNC_PASSWORD=${VNC_PASSWORD} networks: - test-net container_name: container3 networks: test-net:
Описание:
-
noVNC: Запускается контейнер с веб-интерфейсом noVNC, слушающий на порту
NOVNC_HTTP_PORT
для HTTP и на портуNOVNC_HTTPS_PORT
для HTTPS. В файлы/etc/ssl/private/vnc-key.pem
и/etc/ssl/certs/vnc-cert.pem
монтируются приватный ключ и сертификат для SSL/TLS шифрования. -
VNC-контейнеры: Контейнеры с VNC-серверами, которые запускаются на порту
5900
. Пароль для каждого VNC-сервера передаётся через переменные окружения. -
Маршрутизация через websockify: Запросы направляются на нужные контейнеры через websockify с использованием токенов.
Обратите внимание на команду, которая формирует список токенов и сохраняет их в файл для настройки websockify. В примере это сделано для простоты, в реальном проекте можно сохранить токены другими способами. Токены определяют то куда будет перенаправлен web запрос с использованием websockify.
command: > sh -c 'mkdir -p /etc/websockify && echo "pushkin: container1:5900" > /etc/websockify/tokens && echo "dostoevsky: container2:5900" >> /etc/websockify/tokens && echo "chekhov: container3:5900" >> /etc/websockify/tokens && /usr/bin/websockify --web /usr/share/novnc --cert=/etc/ssl/certs/vnc-cert.pem --key=/etc/ssl/private/vnc-key.pem 0.0.0.0:8443 --token-plugin TokenFile --token-source /etc/websockify/tokens'
Шаг 2: Защита паролем
Для защиты VNC-серверов паролем используем переменную окружения VNC_PASSWORD
. Пароль можно хранить в .env
файле или через Docker Secrets для более безопасного использования.
Использование .env файла:
Создайте файл .env
в той же директории, где находится docker-compose.yml
, и добавьте в него переменные с паролями и портами для доступов к noNVC:
VNC_PASSWORD=your_secure_password NOVNC_HTTP_PORT=8080 NOVNC_HTTPS_PORT=443 # Порт по умолчанию для https
В файле Docker Compose мы используем переменную для настройки пароля VNC-сервера:
environment: - VNC_PASSWORD=${VNC_PASSWORD}
Шаг 3: SSL/TLS для шифрования
Чтобы защитить трафик VNC, мы добавляем SSL/TLS сертификаты в контейнер noVNC, который будет работать через HTTPS.
Генерация сертификатов
Вы можете сгенерировать самоподписанный сертификат с помощью OpenSSL:
openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ -keyout vnc-key.pem -out vnc-cert.pem \ -subj "/C=US/ST=My Lovely Town/L=My Lovely Town/O=Example Corp/OU=IT Department/CN=example.com"
Монтирование сертификатов в контейнер
Сгенерированные сертификаты монтируются в контейнер noVNC с флагом только для чтения, чтобы предотвратить их изменение:
volumes: - ./vnc-key.pem:/etc/ssl/private/vnc-key.pem:ro - ./vnc-cert.pem:/etc/ssl/certs/vnc-cert.pem:ro
Шаг 4: Подключение к контейнерам
После запуска контейнеров с помощью команды:
docker compose up -d
Вы можете подключаться к контейнерам через браузер по HTTPS с указанием или без указания порта, если наружу открыт порт по умолчанию 443 для htps:
https://my_host/vnc.vnc.html?path=websockify?token=...
вместо точек укажите токен нужного контейнера, чтобы подключиться к нему. В нашем примере:
pushkin = container1 dostoevsky = container2 chekhov = container3
Подключение с самоподписанным сертификатом немного напряжет браузер и он засомневается, что стоит подключаться дальше. Можно не опасаться, и разрешить ему это сделать. В окне откроется такая картинка:
Введем пароль и получим доступ к vnc указанного в параметре token контейнера.
Для удобства небольшой скрипт который выполнит генерацию сертификатов и поможет в управлении Docker Compose:
#!/bin/bash CERT_KEY="vnc-key.pem" CERT_CRT="vnc-cert.pem" generate_certificate() { echo "Генерация SSL-сертификата..." openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ -keyout "${CERT_KEY}" -out "${CERT_CRT}" \ -subj "/C=US/ST=My Lovely Town/L=My Lovely Town/O=Example Corp/OU=IT Department/CN=example.com" echo "Сертификат сгенерирован." } start_compose() { echo "Запуск Docker Compose..." docker-compose up -d } stop_compose() { echo "Остановка Docker Compose..." docker-compose down } restart_compose() { echo "Перезапуск Docker Compose..." docker-compose down docker-compose up -d } echo "1. Генерация сертификата" echo "2. Запуск Docker Compose" echo "3. Остановка Docker Compose" echo "4. Перезапуск Docker Compose" read -p "Выберите действие (1-4): " choice case $choice in 1) generate_certificate ;; 2) start_compose ;; 3) stop_compose ;; 4) restart_compose ;; *) echo "Неверный выбор." ;; esac
Оценка безопасности
Буквально пара слов про безопасность, в моем проекте высоких требований не предъявлялось, потому что все контейнеры были в закрытой сети. И доступ к ним осуществлялся через ssh соединение с форвардом портов или vpn. Кроме того, требования в самом проекте к безопасности были не очень высокие. При необходимости иметь хорошо защищенный доступ стоит лучше изучить такой вариант подключения. Но несколько общих рекомендаций могу привести:
Защита паролей
-
Docker Secrets: Рассмотрите использование Docker Secrets в боевых средах для безопасного хранения паролей.
SSL/TLS
-
Самоподписанные сертификаты можно использовать для тестирования, но для боевых сред, все же, рекомендуется использовать сертификаты от проверенных центров сертификации (например, Let’s Encrypt).
Ограничение доступа
-
Ограничьте порты и сети: Убедитесь, что доступ к VNC-серверам и WebSocket портам открыт только для доверенных пользователей или через VPN.
Заключение
С помощью noVNC, websockify, и Docker получилось удобно организовать доступ к множеству контейнеров через VNC с защитой паролями и SSL/TLS шифрованием.
Мне этот подход помог в организации доступных через единый интерфейс подключений.
ссылка на оригинал статьи https://habr.com/ru/articles/847670/
Добавить комментарий