Запуск Traefik  как прокси с автоматическим получением сертификатов от Cloudflare

Запуск Traefik как прокси с автоматическим получением сертификатов от Cloudflare

На самом деле всё реально очень просто. И самое главное, после правильной настройки вообще не нужно будет заморачиваться на тему сертификатов для сайтов и сервисов.

Когда я настраивал свой новый домашний сервер, я решил все сервисы запускать строго в Docker-контейнерах и строить всю инфраструктуру именно таким образом. Поэтому остро встал вопрос web-сервера, который будет выступать в роли обратного прокси для всех сервисов. Сначала я планировал использовать nginx, с которым уже довольно давно работаю, к которому привык и который мне понятен. Но выбрал Traefik, потому что хотел попробовать что-то новое для себя. Да, nginx в теории быстрее и вообще хорош, но именно в моём случае это не так важно.

И к тому же Traefik больше всего подходит для инфраструктуры сервисов, которые запущены в Docker или вообще в инфраструктуре k8s. Он динамически может обнаруживать сервисы и делать всё необходимое, чтобы стали доступны снаружи быстро и безопасно. Удобство ещё и в том, что не нужно заморачиваться с сертификатами. Traefik возьмёт всё это на себя, если только попросить.

Итак, начнём всё настраивать.

[ Настройка домена в Cloudflare ]

Я предпочитаю настройку Let's Encrypt через DNS-вызов (dnsChallenge). Этот метод устраняет необходимость открывать порт 80 и значительно упрощает процесс.

Traefik поддерживает множество провайдеров DNS. Я выбрал Cloudflare для своих доменов. Этот сервис бесплатен и предлагает не только улучшение производительности, но и дополнительную безопасность даже на бесплатном тарифе. К тому же, Cloudflare без проблем сейчас работает в России.

Если у вас ещё нет аккаунта в Cloudflare, рекомендую его создать. После успешной авторизации и подтверждения адреса email вам откроется возможность добавлять сайты, то есть домены (да!). Процесс добавления довольно простой.

Процесс добавления домена прост:

  1. Зайдите в ваш дашборд Cloudflare и выберите "Add a site".
  2. Введите ваш домен и продолжайте.
  3. Cloudflare автоматически просканирует записи текущих DNS-серверов. При необходимости обновите или настройте их. Обратите внимание, это может повлиять на работу вашего сайта.
  4. Cloudflare попросит указать свои NS для домена. Это можно сделать в личном кабинете вашего регистратора.
Делать нечего, ждём!
Делать нечего, ждём!

После изменения NS домена подождите от нескольких часов до суток. После того как изменения вступят в силу, Cloudflare автоматически настроит всё необходимое и отправит вам письмо о успешном подключении домена.

[ Запуск Traefik через Docker Compose ]

Прежде всего в вашей системе должны быть установлены Docker Engine и Docker Compose. Как именно это сделать именно в вашей операционной системе, лучше уточнить на официальном сайте, так как различия даже для разных дистрибутивов Linux могут быть кардинальными.

Также необхимо будет создать внешнюю сеть в режиме bridge для Docker, через которую будут взаимодействовать ваши контейнеры, включая Traefik. Я предпочитаю именно вариант работы Traefik через внешнюю сеть вместо простого мониторинга контейнеров Docker на хосте. Это позволяет контролировать, какие именно сервисы и с какими параметрами будут доступны Traefik. Я выбрал название сети web, потому что привык к таком варианту. Вы можете выбрать любое другое название.

Bash
docker network create \
  --driver=bridge \
  --opt com.docker.network.bridge.name=web_bridge \
  web
docker network create \
  --driver=bridge \
  --opt com.docker.network.bridge.name=web_bridge \
  web

Сам файл docker-compose.yml для нашего экземпляра Traefik очень простой по структуре.

YAML
services:
  traefik:
    image: traefik:v2.11
    container_name: traefik
    ports:
      - "443:443"
    environment:
      - TZ=${TIMEZONE}
      - CF_API_EMAIL=${CF_API_EMAIL}
      - CF_API_TOKEN=${CF_API_TOKEN}
    volumes:
      # Чтобы выпущенные сертификаты не терялись при перезапуске.
      - ./ssl:/ssl
      # Путь к файлу конфигурации
      - ./traefik.toml:/etc/traefik/traefik.toml
      # Явное указание на сервис Docker с флагом read only.
      # Может быть специфичным для вашей системы.
      - /var/run/docker.sock:/var/run/docker.sock:ro
    # Эта секция необходима, если вы хотите использовать дашборд
    labels:
      - traefik.enable=true
      - traefik.http.routers.traefik.entrypoints=https
      - traefik.http.routers.traefik.rule=Host(`${DASHBOARD_DOMAIN}`)
      - traefik.http.routers.traefik.tls=true
      - traefik.http.routers.traefik.tls.certresolver=letsEncrypt
      - traefik.http.routers.traefik.service=api@internal
      - traefik.http.middlewares.traefik-auth.basicauth.users=${DASHBOARD_USER}
      - traefik.http.routers.traefik.middlewares=traefik-auth
    networks:
      - web
    restart: always

networks:
  web:
    external: true
services:
  traefik:
    image: traefik:v2.11
    container_name: traefik
    ports:
      - "443:443"
    environment:
      - TZ=${TIMEZONE}
      - CF_API_EMAIL=${CF_API_EMAIL}
      - CF_API_TOKEN=${CF_API_TOKEN}
    volumes:
      # Чтобы выпущенные сертификаты не терялись при перезапуске.
      - ./ssl:/ssl
      # Путь к файлу конфигурации
      - ./traefik.toml:/etc/traefik/traefik.toml
      # Явное указание на сервис Docker с флагом read only.
      # Может быть специфичным для вашей системы.
      - /var/run/docker.sock:/var/run/docker.sock:ro
    # Эта секция необходима, если вы хотите использовать дашборд
    labels:
      - traefik.enable=true
      - traefik.http.routers.traefik.entrypoints=https
      - traefik.http.routers.traefik.rule=Host(`${DASHBOARD_DOMAIN}`)
      - traefik.http.routers.traefik.tls=true
      - traefik.http.routers.traefik.tls.certresolver=letsEncrypt
      - traefik.http.routers.traefik.service=api@internal
      - traefik.http.middlewares.traefik-auth.basicauth.users=${DASHBOARD_USER}
      - traefik.http.routers.traefik.middlewares=traefik-auth
    networks:
      - web
    restart: always

networks:
  web:
    external: true

Обратите внимание, что секция environment содержит переменные окружения. Они автоматичеки будут браться при запуске контейнера из файла .env, который должен быть расположен в той же директории, что и docker-compose.yml. Вы можете также указать любой другой файл с полным или относительным путём через директиву env_file. В файле обязательно нужно указать значения. Контейнер то запуститься сможет, но толку от него будет немного.

Text
TIMEZONE=Europe/Moscow
DASHBOARD_DOMAIN=traefik.example.com
DASHBOARD_USER=<логин и пароль для basic auth>
[email protected]
CF_API_TOKEN=<ваш токен>
TIMEZONE=Europe/Moscow
DASHBOARD_DOMAIN=traefik.example.com
DASHBOARD_USER=<логин и пароль для basic auth>
[email protected]
CF_API_TOKEN=<ваш токен>

Если вы хотите пользоваться дашбордом Traefik, то выберите, по какому адресу он должен быть доступен. Чтобы он оказался скрыт от непрошенных гостей, необходимо указать пару из логина и хешированного пароля, которые можно получить через любой сервис генерации старого доброго htpwassd. Не самый безопасный способ авторизации, но простой и лёгкий.

CF_API_EMAIL – это электронная почта, которую вы указали в своём аккаунте на Cloudflare.

CF_API_TOKEN – токен для обращения к Cloudflare API. Получить его можно в разделе User API Tokens. Просто нажимаете "Create Token", выбираете там "Edit zone DNS", настраиваете все разрешения и указываете, к каким именно доменам относится получаемый токен. Также можно указать IP-адрес сервера, на котором будет работать ваш Traefik в качестве единственного разрешённого.

Все значения переменных указываются без кавычек.

И наконец, переходим к конфигурации Traefik с помощью файла traefik.toml. Он также должен быть расположен в одной директории с docker-compose.yml. Но вы можете указать свой путь к другому файлу в секции volumes.

TOML
[entryPoints]
  [entryPoints.https]
    address = ":443"

# Включаем дашборд, если хотим
[api]
  dashboard = true

# Помните, я говорил про отношения Docker и Traefik через сеть?
[providers.docker]
  exposedByDefault = false
  network = "web"

# Настраиваем именно провайдера "cloudflare" для получения сертификатов
[certificatesResolvers.letsEncrypt.acme]
  storage = "/ssl/acme.json"
  [certificatesResolvers.letsEncrypt.acme.dnsChallenge]
    provider = "cloudflare"
    delayBeforeCheck = 0
[entryPoints]
  [entryPoints.https]
    address = ":443"

# Включаем дашборд, если хотим
[api]
  dashboard = true

# Помните, я говорил про отношения Docker и Traefik через сеть?
[providers.docker]
  exposedByDefault = false
  network = "web"

# Настраиваем именно провайдера "cloudflare" для получения сертификатов
[certificatesResolvers.letsEncrypt.acme]
  storage = "/ssl/acme.json"
  [certificatesResolvers.letsEncrypt.acme.dnsChallenge]
    provider = "cloudflare"
    delayBeforeCheck = 0

Обратите внимание на то, как настроена секция [providers.docker]. Я не хочу, чтобы Traefik мониторил старты всех контейнеров на хосте. Вместого это я хочу, чтобы он следил только за теми сервисами, которые работают с ним в одной bridge-сети, и у которых установлен лейбл traefik.enable=true.

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

Bash
# Сначала запускаем не в режиме detach, 
# чтобы увидеть, если что-то пойдёт не так
docker-compose up

# Если всё хорошо, то останавливаем контейнер через Ctrl+C
# и удалить контейнер для надёжности
docker-compose down

# Если проблем нет, то можно уже запускать в режим detach
docker-compose up -d
# Сначала запускаем не в режиме detach, 
# чтобы увидеть, если что-то пойдёт не так
docker-compose up

# Если всё хорошо, то останавливаем контейнер через Ctrl+C
# и удалить контейнер для надёжности
docker-compose down

# Если проблем нет, то можно уже запускать в режим detach
docker-compose up -d

Всё работает, можно пользоваться.

[ Вариант настройки сервиса для работы через Traefik ]

Напоследок приведу пример простого docker-compose.yml, в котором запустим один сервис в режиме взамодействия с Traefik, а другой – в режиме взаимодействия только во внутренней сети.

YAML
networks:
    web:
        external: true
    servicename:
        external: false

services:
  # Сервис не должен общаться с Traefik, только со своими соседями
  service_1:
    build:
      context: ../
      dockerfile: Dockerfile
      target: service_1
    container_name: service_1
    restart: always
    labels:
      - traefik.enable=false
    networks:
      - servicename
    ports:
      - "9000:9000"

  # Сервис общается с Traefik.
  # Для этого указаны домен, резолвер сертификатов и порт, который должен
  # слушать Traefik.
  service_2:
    build:
      context: ../
      dockerfile: Dockerfile
      target: service_2
    container_name: service_2
    restart: always
    labels:
      - traefik.enable=true
      - traefik.http.routers.service_2.rule=Host(`service_2.example.com`)
      - traefik.http.routers.service_2.entrypoints=https
      - traefik.http.routers.service_2.tls=true
      - traefik.http.routers.service_2.tls.certresolver=letsEncrypt
      - traefik.http.services.service_2.loadbalancer.server.port=80
    networks:
      - web
      - servicename
    expose:
      - "80"
networks:
    web:
        external: true
    servicename:
        external: false

services:
  # Сервис не должен общаться с Traefik, только со своими соседями
  service_1:
    build:
      context: ../
      dockerfile: Dockerfile
      target: service_1
    container_name: service_1
    restart: always
    labels:
      - traefik.enable=false
    networks:
      - servicename
    ports:
      - "9000:9000"

  # Сервис общается с Traefik.
  # Для этого указаны домен, резолвер сертификатов и порт, который должен
  # слушать Traefik.
  service_2:
    build:
      context: ../
      dockerfile: Dockerfile
      target: service_2
    container_name: service_2
    restart: always
    labels:
      - traefik.enable=true
      - traefik.http.routers.service_2.rule=Host(`service_2.example.com`)
      - traefik.http.routers.service_2.entrypoints=https
      - traefik.http.routers.service_2.tls=true
      - traefik.http.routers.service_2.tls.certresolver=letsEncrypt
      - traefik.http.services.service_2.loadbalancer.server.port=80
    networks:
      - web
      - servicename
    expose:
      - "80"

Это именно пример для иллюстрации настроек секции labels. Второй сервис после запуска заявит о себе запущенному Traefik, сообщит о своём домене, получит для него сертификат и начнёт работать через него извне.

Надеюсь, что всё описанное вам поможет в ваших начиниях и движении вперёд!