Как ограничить доступ к сервисам на разных портах по IP и разрешить по домену? Nginx, Linux, Docker

Вопрос или проблема

На моем сервере (с единственным IP-адресом) работают несколько сервисов на разных портах. Nginx также работает на портах 80 и 443 для обслуживания страниц по запросам к доменам. Все сервисы и nginx запускаются через docker-compose. Как мне отклонить все внешние запросы к IP-адресу (например, к 56.56.56.56:3000 или к 56.56.56.56), но в то же время разрешить все запросы к доменным именам, используя nginx, iptables, файрвол и другие инструменты разработки? Все работает на сервере Ubuntu 22.04.

nginx.conf

http {
    limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
    limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=5r/s;

    server {
        listen 80 default_server;
        server_name my.domain www.my.domain;

        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl;
        server_name my.domain;

        ssl_certificate /etc/nginx/ssl/my.domain/fullchain.pem;
        ssl_certificate_key /etc/nginx/ssl/my.domain/privkey.pem;

        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;

        client_body_timeout 10s;
        client_header_timeout 10s;
        keepalive_timeout 10s;
        send_timeout 10s;

        location / {
            proxy_pass http://56.56.56.56:3002; # адрес для примера
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            limit_conn conn_limit_per_ip 10;
            limit_req zone=req_limit_per_ip burst=10 nodelay;
        }
    }
    ...
}

docker-compose.yml

...
  site:
    build:
      context: ...
      dockerfile: Dockerfile
    restart: unless-stopped
    ports:
      - "3002:3002"
    logging:
      driver: "json-file"
      options:
        max-size: "1m"
        max-file: "1"

  nginx:
    image: nginx:latest
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/ssl/:/etc/nginx/ssl/

  grafana:
    image: grafana/grafana:latest
    ports:
      - '3000:3000'
    volumes:
      - grafana-data:/var/lib/grafana
...

Заранее спасибо!

Важно

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

  • Если я запускаю все службы локально на машине (например, app.run("127.0.0.1", port=3000)), nginx этого не обнаруживает, что означает, что proxy_pass http://127.0.0.1:3000 не сработает. Я подумывал запустить все службы локально, чтобы они не могли быть доступны напрямую извне, но nginx все равно мог бы к ним обращаться, так как он работает в том же контейнере, но только через запросы к доменам. Однако по какой-то причине я получаю ошибку “соединение отклонено”, когда nginx пытается запросить 127.0.0.1.

Ответ или решение

Для ограничения доступа к услугам, работающим на различных портах по IP-адресу, при этом разрешая доступ по доменным именам, вы можете использовать Nginx в качестве обратного прокси-сервера, а также настроить firewall (iptables или UFW) на вашем сервере Ubuntu 22.04. Рассмотрим шаги, которые вам необходимо предпринять для достижения этой цели.

1. Настройка Nginx

Ваш текущий конфигурационный файл nginx.conf уже имеет базовую настройку для обслуживания доменного имени. Давайте уточним несколько моментов:

  • Убедитесь, что в конфигурации Nginx правильные блоки server настроены для обработки всех необходимых доменных имен и маршрутов, ссылок на внутренние сервисы.
server {
    listen 80 default_server;
    server_name my.domain www.my.domain;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name my.domain;

    ssl_certificate /etc/nginx/ssl/my.domain/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/my.domain/privkey.pem;

    location / {
        proxy_pass http://site:3002;  # Используйте имя сервиса Docker
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    ...
}

Замените http://56.56.56.56:3002 на имя сервиса Docker, например http://site:3002.

2. Настройка Docker Compose

Ваш docker-compose.yml файл также надо настраивать для создания сети между сервисами. Docker создаёт виртуальную сеть для взаимодействия контейнеров, что позволяет использовать имена сервисов для связи.

version: '3.8'
services:
  site:
    # Ваши другие настройки
    ports:
      - "3002:3002"

  nginx:
    image: nginx:latest
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/ssl/:/etc/nginx/ssl/
    depends_on:
      - site

  grafana:
    image: grafana/grafana:latest
    expose:    # Используйте 'expose' вместо 'ports', если доступ из вне не требуется
      - '3000'
    ...

3. Настройка Firewall (iptables или UFW)

Теперь перейдем к ограничению внешнего доступа через firewall. Если вы используете UFW, выполните следующую команду:

sudo ufw allow 'Nginx Full' # Разрешить Nginx
sudo ufw deny 3000          # Заблокировать прямой доступ к порту 3000
sudo ufw deny 3002          # Заблокировать прямой доступ к порту 3002

Если вы решите использовать iptables, ваша конфигурация может выглядеть следующим образом:

# Разрешить входящие соединения на порты 80 и 443
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Заблокировать доступ ко всем другим портам
iptables -A INPUT -p tcp --dport 3000 -j DROP
iptables -A INPUT -p tcp --dport 3002 -j DROP

# Разрешить трафик с localhost
iptables -A INPUT -i lo -j ACCEPT

4. Дополнительные рекомендации

  • Убедитесь, что все службы, запускаемые в Docker-контейнерах, слушают на внутреннем IP (например 0.0.0.0 в пределах контейнера), а не на 127.0.0.1.
  • Используйте команду docker-compose up для запуска контейнеров и убедитесь, что они работают корректно.
  • Протестируйте доступ к вашим сервисам, отправляя запросы через Nginx, используя доменное имя в браузере или через команду curl.

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

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

Капча загружается...