Как включить HTTPS на AWS EC2 с контейнером NGINX Docker?

У меня есть экземпляр EC2 на AWS, на котором работает Amazon Linux 2.

На нем я установил Git, docker и docker-compose. Сделав это, я клонировал свой репозиторий и запустил docker-compose up, чтобы запустить производственную среду. Я захожу в общедоступный DNS, и он работает.

Теперь я хочу включить HTTPS на сайте.

В моем проекте есть интерфейс, использующий React для работы на сервере Nginx-alpine. Бэкэнд — это сервер NodeJS.

Это мой файл nginx.conf:

server {
    listen       80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        try_files $uri /index.html;
    }

    location /api/ {
        proxy_pass http://${PROJECT_NAME}_backend:${NODE_PORT}/;
    }    

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

Вот мой файл docker-compose.yml:

version: "3.7"
services:
##############################
# Back-End Container
##############################
  backend: # Node-Express backend that acts as an API.
    container_name: ${PROJECT_NAME}_backend
    init: true
    build:
      context: ./backend/
      target: production
    restart: always
    environment:
      - NODE_PATH=${EXPRESS_NODE_PATH}
      - AWS_REGION=${AWS_REGION}
      - NODE_ENV=production
      - DOCKER_BUILDKIT=1
      - PORT=${NODE_PORT}
    networks:
      - client
##############################
# Front-End Container
##############################
  nginx:
    container_name: ${PROJECT_NAME}_frontend
    build:
      context: ./frontend/
      target: production
      args:
        - NODE_PATH=${REACT_NODE_PATH}
        - SASS_PATH=${SASS_PATH}
    restart: always
    environment:
      - PROJECT_NAME=${PROJECT_NAME}
      - NODE_PORT=${NODE_PORT}
      - DOCKER_BUILDKIT=1
    command: /bin/ash -c "envsubst '$$PROJECT_NAME $$NODE_PORT' < /etc/nginx/conf.d/nginx.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"
    expose:
      - "80"
    ports:
      - "80:80"
    depends_on:
      - backend
    networks:
      - client
##############################
# General Config
##############################
networks:
  client:

Я знаю, что для certbot есть образ Docker, но не знаю, как его использовать. Меня также беспокоит то, как я проксирую запросы на /api/ на сервер через http. Это тоже создаст мне проблемы?


Редактировать:

Попытка №1: Траефик

Я создал контейнер Traefik для маршрутизации всего трафика через HTTPS.

version: '2'

services:
  traefik:
    image: traefik
    restart: always
    ports:
      - 80:80
      - 443:443
    networks:
      - web
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /opt/traefik/traefik.toml:/traefik.toml
      - /opt/traefik/acme.json:/acme.json
    container_name: traefik

networks:
  web:
    external: true

Для файла toml я добавил следующее:

debug = false

logLevel = "ERROR"
defaultEntryPoints = ["https","http"]

[entryPoints]
  [entryPoints.http]
  address = ":80"
    [entryPoints.http.redirect]
    entryPoint = "https"
  [entryPoints.https]
  address = ":443"
  [entryPoints.https.tls]

[retry]

[docker]
endpoint = "unix:///var/run/docker.sock"
domain = "ec2-00-000-000-00.eu-west-1.compute.amazonaws.com"
watch = true
exposedByDefault = false

[acme]
storage = "acme.json"
entryPoint = "https"
onHostRule = true
[acme.httpChallenge]
entryPoint = "http"

Я добавил это в свой производственный файл docker-compose:

labels:
  - "traefik.docker.network=web"
  - "traefik.enable=true"
  - "traefik.basic.frontend.rule=Host:ec2-00-000-000-00.eu-west-1.compute.amazonaws.com"
  - "traefik.basic.port=80"
  - "traefik.basic.protocol=https"

Я запустил docker-compose up для контейнера Traefik, а затем запустил docker-compose up для своего производственного образа. Я получил следующую ошибку:

unable to obtain acme certificate

Я читаю документы Traefik и, по-видимому, есть способ настроить файл toml специально для Amazon ECS: https://docs.traefik.io/configuration/backends/ecs/

Я на правильном пути?

Развертывание модели машинного обучения с помощью Flask - Angular в Kubernetes
Развертывание модели машинного обучения с помощью Flask - Angular в Kubernetes
Kubernetes - это портативная, расширяемая платформа с открытым исходным кодом для управления контейнерными рабочими нагрузками и сервисами, которая...
Как создать PHP Image с нуля
Как создать PHP Image с нуля
Сегодня мы создадим PHP Image from Scratch для того, чтобы легко развернуть базовые PHP-приложения. Пожалуйста, имейте в виду, что это разработка для...
3
0
6 925
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Самый простой способ - настроить ALB и использовать его для HTTPS.

  1. Создать АЛБ
  2. Добавить прослушиватель 443 в ALB
  3. Создание сертификата с помощью AWS Certificate Manager
  4. Установите сертификат на сертификат по умолчанию для балансировщика нагрузки.
  5. Создать целевую группу
  6. Добавьте свой экземпляр EC2 в целевую группу
  7. Направьте ALB на целевую группу

Запросы будут обслуживаться с использованием ALB с https

Это хорошо сработало для меня. Я бы добавил, что вам нужно выбрать «Классический балансировщик нагрузки» (также помеченный как «предыдущее поколение») при создании балансировщика нагрузки в AWS, чтобы иметь возможность создать балансировщик нагрузки только с одним экземпляром EC2 за ним. Теперь AWS «рекомендует» вам выбрать «Балансировщик нагрузки приложений», для которого требуется как минимум 2 зоны доступности и, следовательно, 2 экземпляра EC2.

antun 08.06.2020 03:05

Это сработало и для меня, когда Application Load Balancer обслуживает кластер ECS, который (в настоящее время) имеет только один экземпляр EC2. Одна хитрость, на которую стоит обратить внимание / классный трюк: настройте Nginx на прослушивание только на порту 80, настройте прослушиватели балансировщика нагрузки (порты 80 и 443) так, чтобы они указывали на целевую группу, а затем на вашей целевой группе установите ТОЛЬКО цель для порта. 80 (так что НЕТ цели для 443). Это будет доставлять SSL-трафик в Nginx через порт 80, поэтому вам не нужно настраивать сертификат SSL в Nginx — он обрабатывается сертификатом AWS в балансировщике нагрузки.

TerryS 05.11.2020 16:53
Ответ принят как подходящий

Включение SSL выполняется с помощью руководства по Nginx и Let’s Encrypt с помощью Docker менее чем за 5 минут. Я столкнулся с некоторыми проблемами, следуя за ним, поэтому я попытаюсь прояснить некоторые вещи здесь.

Шаги включают добавление следующего к docker-compose.yml:

##############################
# Certbot Container
##############################
  certbot:
    image: certbot/certbot:latest
    volumes:
      - ./frontend/data/certbot/conf:/etc/letsencrypt
      - ./frontend/data/certbot/www:/var/www/certbot

Что касается раздела Контейнер Nginx файла docker-compose.yml, его следует изменить, чтобы включить те же тома, что и в Контейнер Certbot, а также добавить порты и предоставить конфигурации:

  service_name:
    container_name: container_name
    image: nginx:alpine
    command: /bin/ash -c "exec nginx -g 'daemon off;'"
    volumes:
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    expose:
      - "80"
      - "443"
    ports:
      - "80:80"
      - "443:443"
    networks:
      - default

Папка data может быть сохранена в любом другом месте, но убедитесь, что знаете, где она находится, и убедитесь, что ссылаетесь на нее правильно при повторном использовании позже. В этом примере я просто сохраняю его в том же каталоге, что и файл docker-compose.yml.

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

Во-первых, ваша конфигурация Nginx (default.conf) должна быть изменена для соответствия запросу проверки домена:

server {
    listen       80;
    server_name example.com www.example.com;
    server_tokens off;

    location / {
        return 301 https://$server_name$request_uri;
    }

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }
}

server {
     listen 443 ssl;
     server_name example.com www.example.com;
     server_tokens off;

     ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
     ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
     include /etc/letsencrypt/options-ssl-nginx.conf;
     ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

     location / {
         root   /usr/share/nginx/html;
         index  index.html index.htm;
         try_files $uri /index.html;
         proxy_set_header    Host                $http_host;
         proxy_set_header    X-Real-IP           $remote_addr;
         proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
     }
}

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

curl -L https://raw.githubusercontent.com/wmnnd/nginx-certbot/master/init-letsencrypt.sh > init-letsencrypt.sh && chmod +x init-letsencrypt.sh

После загрузки сценария в него необходимо внести следующие изменения:

#!/bin/bash

if ! [ -x "$(command -v docker-compose)" ]; then
  echo 'Error: docker-compose is not installed.' >&2
  exit 1
fi

-domains=(example.org www.example.org)
+domains=(example.com www.example.com)
rsa_key_size=4096
-data_path = "./data/certbot"
+data_path = "./data/certbot"
-email = "" # Adding a valid address is strongly recommended
+email = "[email protected]" # Adding a valid address is strongly recommended
staging=0 # Set to 1 when testing setup to avoid hitting request limits

if [ -d "$data_path" ]; then
  read -p "Existing data found for $domains. Continue and replace existing certificate? (y/N) " decision
  if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then
    exit
  fi
fi

if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then
  echo "### Downloading recommended TLS parameters ..."
  mkdir -p "$data_path/conf"
  curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf"
  curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem"
  echo
fi

echo "### Creating dummy certificate for $domains ..."
path = "/etc/letsencrypt/live/$domains"
mkdir -p "$data_path/conf/live/$domains"
-docker-compose run --rm --entrypoint "\
+docker-compose -f docker-compose.yml run --rm --entrypoint "\
  openssl req -x509 -nodes -newkey rsa:1024 -days 1\
    -keyout '$path/privkey.pem' \
    -out '$path/fullchain.pem' \
    -subj '/CN=localhost'" certbot
echo

echo "### Starting nginx ..."
-docker-compose up --force-recreate -d nginx
+docker-compose -f docker-compose.yml up --force-recreate -d service_name
echo

echo "### Deleting dummy certificate for $domains ..."
-docker-compose run --rm --entrypoint "\
+docker-compose -f docker-compose.yml run --rm --entrypoint "\
  rm -Rf /etc/letsencrypt/live/$domains && \
  rm -Rf /etc/letsencrypt/archive/$domains && \
  rm -Rf /etc/letsencrypt/renewal/$domains.conf" certbot
echo

echo "### Requesting Let's Encrypt certificate for $domains ..."
#Join $domains to -d args
domain_args = ""
for domain in "${domains[@]}"; do
  domain_args = "$domain_args -d $domain"
done

# Select appropriate email arg
case "$email" in
  "") email_arg = "--register-unsafely-without-email" ;;
  *) email_arg = "--email $email" ;;
esac

# Enable staging mode if needed
if [ $staging != "0" ]; then staging_arg = "--staging"; fi

-docker-compose run --rm --entrypoint "\
+docker-compose -f docker-compose.yml run --rm --entrypoint "\
  certbot certonly --webroot -w /var/www/certbot \
    $staging_arg \
    $email_arg \
    $domain_args \
    --rsa-key-size $rsa_key_size \
    --agree-tos \
    --force-renewal" certbot
echo

echo "### Reloading nginx ..."
-docker-compose exec nginx nginx -s reload
+docker-compose exec service_name nginx -s reload

Я позаботился о том, чтобы всегда включать флаг -f с командой docker-compose на тот случай, если кто-то не знает, что изменить, если у него есть файл с пользовательским именем docker-compose.yml. Я также установил имя службы как service_name, чтобы различать имя службы и команду Nginx, в отличие от руководства.

Примечание: Если вы не уверены в том, что установка работает, обязательно установите для промежуточной стадии значение 1, чтобы избежать превышения лимита запросов. Важно не забыть установить его обратно на 0 после завершения тестирования и повторить все шаги, начиная с внесения изменений в файл init-letsencrypt.sh. После того, как тестирование завершено и для промежуточной стадии установлено значение 0, важно остановить предыдущие запущенные контейнеры и удалить папку данных, чтобы обеспечить надлежащую начальную сертификацию:

$ docker-compose -f docker-compose.yml down && yes | docker system prune -a --volumes && sudo rm -rf ./data

Как только сертификаты будут готовы к инициализации, скрипт должен быть запущен с помощью sudo; очень важно использовать sudo, так как при запуске без него будут возникать проблемы с разрешениями внутри контейнеров.

$ sudo ./init-letsencrypt.sh

После выдачи сертификата возникает вопрос об автоматическом продлении сертификата; нужно сделать две вещи:

  • В Контейнер Nginx Nginx перезагружает вновь полученные сертификаты с помощью следующей поправки:
service_name:
...
- command: /bin/ash -c "exec nginx -g 'daemon off;'"
+ command: /bin/ash -c "while :; do sleep 6h & wait $${!}; nginx -s reload; done & exec nginx -g 'daemon off;'"
...
  • В раздел Контейнер Certbot необходимо добавить следующее, чтобы проверять, подлежит ли сертификат обновлению каждые двенадцать часов, как это рекомендовано Давайте зашифруем:
certbot:
...
+ entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew --webroot -w /var/www/certbot; sleep 12h & wait $${!}; done;'"

Перед запуском docker-compose -f docker-compose.yml up необходимо изменить право собственности на папку data на ec2-user; это сделано для того, чтобы избежать ошибок разрешений при запуске docker-compose -f docker-compose.yml up или при запуске в режиме sudo:

sudo chown ec2-user:ec2-user -R /path/to/data/

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

Если у вас возникли проблемы с контейнером Nginx из-за подстановки переменных, а $server_name и $request_uri не отображаются должным образом, вы можете обратиться к Эта проблема.

Привет @yaharga! Во втором блоке кода у вас есть container_name: container_name, просто интересно, будет ли это имя внешнего контейнера или это container_name чего-то другого.

elco45 26.02.2021 01:28

@ elco45 Это просто имя внешнего контейнера. Я использовал заполнитель для service_name и container_name, поскольку пытался не сбивать с толку.

yaserso 28.02.2021 12:03

обратите внимание, что этот подход не будет работать, если вы используете эластичный beanstalk, потому что каждый раз, когда новая версия приложения развертывается в Elastic Beanstalk, конфигурация обновляется, конфигурация Nginx/Apache перезаписывается.

Mikhail Mazurovskiy 05.12.2021 19:40

Другие вопросы по теме