Как организовать безопасный доступ к Docker-контейнерам через VNC с использованием noVNC, websockify и SSL/TLS

от автора

В этой статье я расскажу, как можно организовать доступ к множеству Docker-контейнеров через VNC с использованием noVNC, websockify и SSL/TLS для шифрования и защиты соединений. Добавим защиту паролем для каждого контейнера, и включим шифрование трафика.

Проблема

В моем проекте было несколько Docker-контейнеров с графическими приложениями, к которым приходилось подключаться удаленно через VNC. До тех пор пока контейнеров было несколько штук и они создавались вручную было не сложно выделить им отдельные порты для экспозиции наружу из контейнеров и прописать их в VNC клиенте. Но с развитием проекта, контейнеры пришлось создавать динамически и в разных количествах, что сделало неудобным доступ к ним по разным портам и началась путаница с уже созданными в VNC клиенте подключениями. Захотелось подобрать более удобный вариант для подключений и вот что получилось.

Изначально задача казалась простой: запустить несколько контейнеров с VNC-серверами и обращаться к ним через прокси, например Nginx или Traefik. Однако проблему подключения ко всем контейнерам через один интерфейс с возможностью маршрутизации запросов на конкретные контейнеры по URL с применением Nginx или Traefik решить не удалось. Раньше много раз встречал упоминания/описания noVNC, но никогда не прибегал к использованию, оказалось, что в моем случае — вполне подходящее решение.

Решение: noVNC и websockify

Сформулирую задачу:

  1. Имеем n контейнеров каждый с VNC сервером для доступа, который слушает порт 5900.

  2. Хочется не заморачиваться с выделением каждому контейнеру отдельного внешнего порта, а вместо этого обращаться к каждому из них по отдельному 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: 

Описание:

  1. noVNC: Запускается контейнер с веб-интерфейсом noVNC, слушающий на порту NOVNC_HTTP_PORT для HTTP и на порту NOVNC_HTTPS_PORT для HTTPS. В файлы /etc/ssl/private/vnc-key.pem и /etc/ssl/certs/vnc-cert.pem монтируются приватный ключ и сертификат для SSL/TLS шифрования.

  2. VNC-контейнеры: Контейнеры с VNC-серверами, которые запускаются на порту 5900. Пароль для каждого VNC-сервера передаётся через переменные окружения.

  3. Маршрутизация через 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/


Комментарии

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

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