Сервисы Docker доступны только для локального хоста - PullRequest
0 голосов
/ 18 января 2019

Простой вариант использования: производственный сервер, работающий на Nginx (прослушивающий 0.0.0.0:80 и 443), отвечающий за чтение сертификатов SSL и, если он действителен, перенаправляет на «скрытую» службу в контейнере Docker.Эта служба запускает Gunicorn (так на веб-сайте Django) и прослушивает порт 8000. Это звучит стандартно, просто, проверено, почти слишком красиво, чтобы быть правдой ... но, конечно, это не работает, как хотелось бы.

Потому что Gunicorn, работающий в своем небольшом контейнере Docker, доступен через Интернет.Если вы перейдете на мое имя хоста, порт 8000, вы получите веб-сайт Gunicorn.Очевидно, это уродливо, но хуже всего то, что он полностью обходит Nginx и SSL-сертификаты.Так почему контейнер Docker будет доступен через Интернет?Я знаю, что какое-то время с Докером все было наоборот.Нам нужен правильный баланс!

При дальнейшей проверке проблемы: у меня есть брандмауэр, и он настроен так, чтобы быть чрезвычайно ограничивающим.Он допускает только порт 22 (для ssh, ожидающий переназначения), 80 и 443. Поэтому 8000 абсолютно не должен быть разрешен.Но ufw использует iptables, а Docker добавляет некоторые правила iptables для обхода конфигурации, если контейнер работает на этом порту.

Я пробовал много глупостей (это часть работы): в моем docker-compose.ymlфайл, указывающий порты для привязки, я пытался удалить их (конечно, если я это сделаю, nginx не сможет получить доступ к моей скрытой службе).Я попытался добавить IP-адрес, указанный для привязки (кажется, что он разрешен):

ports:
  - "127.0.0.1:8000:8000"

Это привело к странным результатам, так как Nginx не смог подключиться, но Gunicorn все еще был виден через Интернет.Так что я бы сказал, как раз наоборот, чего я хочу.Я попытался вручную изменить службу Docker для добавления флагов (не очень хорошо) и попытался добавить файл конфигурации в etc/docker/daemon.json (снова изменив настройку «ip» на «127.0.0.1»).У меня не хватает идей.Если у кого-то есть указатель ... Я бы не подумал, что это крайне редкое использование Docker, в конце концов.

Особенности: Я не запускаю контейнеры с docker run напрямую.У меня есть файл docker-compose.yml и я запускаю службы, используя docker stack, поэтому в рое (хотя у меня есть только одна машина одновременно).Может быть связано, хотя, опять же, я думаю, что нет.

  • Система: Debian 9.
  • Версия Docker: 18.09.
  • Версия Docker-Compose: 1.22
nginx (/etc/nginx/sites-available/example.com)
upstream gunicorn {
    server localhost:8000;
}

server {
    server_name example.com www.example.com;
    location / {
        proxy_pass http://gunicorn;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_redirect off;
    }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate ...;
    ssl_certificate_key ...;
    include ...;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = www.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 404; # managed by Certbot
}
docker-compose.yml
version: '3.7'

services:
  gunicorn:
    image: gunicorn:latest
    command: gunicorn mysite.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - ./:/usr/src/app/
    ports:
      - "8000:8000"
    depends_on:
      - db
    networks:
      - webnet
  db:
    image: postgres:10.5-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    networks:
      - webnet

networks:
  webnet:

volumes:
  postgres_data:

Примечание: изображение с оружием было создано заранее, но неттрюк, просто Python-3.7: тонкое изображение со всем набором для gunicorn и веб-сайта Django под mysite /.Он не предоставляет никаких портов (не то, чтобы я думал, что здесь есть какая-то разница).

1 Ответ

0 голосов
/ 21 января 2019

Хорошо, после некоторого копания вот что я нашел: docker обязательно создаст правила iptables для доступа к контейнерам вне сети.Просить Docker вообще не беспокоиться о iptables - это не очень хорошая стратегия, так как ему понадобится пересылать соединения из контейнеров во внешний мир.Поэтому в документации рекомендуется создать правило iptables в цепочке docker-user для предотвращения внешнего доступа к демону Docker.Это то, что я сделал.Конечно, данная команда (слегка измененная, чтобы полностью запретить внешний доступ) не сохранилась, поэтому мне пришлось создать сервис, чтобы добавить это правило.Вероятно, не лучший вариант, поэтому не стесняйтесь комментировать, если у вас есть лучший выбор, чтобы предложить.Вот правило iptables, которое я добавил:

iptables -I DOCKER-USER -i ext_if ! -s 127.0.0.1 -j DROP

Это правило запрещает внешний доступ к демону Docker, но все же позволяет подключаться к отдельным контейнерам.Это правило, похоже, ничего не решало для меня, так как это решение не было точным ответом на мой вопрос.Чтобы запретить доступ к моему контейнеру Docker, работающему на порту 8000, из Интернета, я добавил еще одно правило в тот же сценарий:

iptables -I DOCKER-USER -p tcp --destination-port 8000 -j DROP

Это правило несколько экстремально: оно полностью запрещает сетевой трафик на порту8000 по сети Docker.Хост (localhost) исключен из этого правила, так как предыдущее правило должно разрешать локальный трафик, независимо от того, что (если вы выполняете настройку iptables вручную, вам придется добавить это правило, это не дано, причина, по которой я перешелк более простым решениям, таким как ufw).Если вы посмотрите на правила брандмауэра с iptables -L, вы увидите, что цепочка DOCKER-USER вызывается перед любым правилом Docker.Вы можете найти другое правило, разрешающее трафик на тот же порт.Однако, поскольку мы запрещаем трафик в правиле с более высоким приоритетом, порт 8000 будет эффективно скрыт извне.

Это решение, хотя и кажется, что оно решает проблему, не совсем элегантно и интуитивно понятно.Опять же, я не могу не задаться вопросом, был ли я первым, кто использовал Docker для размещения своих «скрытых» сервисов, сохраняя при этом другой сервис впереди.Я думаю, что обычный подход заключается в том, чтобы использовать nginx в самом контейнере докера, но это создало для меня другие проблемы, которые я, честно говоря, решил превзойти в своих преимуществах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...