Как обезопасить контейнерное приложение Node.js с помощью Nginx, Let’s Encrypt и Docker Compose

Вступление

Есть несколько способов повысить гибкость и безопасность вашего приложенияNode.js. Использованиеreverse proxy, напримерNginx, дает вам возможность загружать запросы балансировки, кэшировать статический контент и реализовыватьTransport Layer Security (TLS). Включение зашифрованного HTTPS на вашем сервере гарантирует, что связь с вашим приложением остается безопасной.

Реализация обратного прокси с TLS / SSL для контейнеров включает в себя другой набор процедур, нежели работа непосредственно в операционной системе хоста. Например, если вы получали сертификаты отLet’s Encrypt для приложения, работающего на сервере, вы должны установить необходимое программное обеспечение прямо на свой хост. Контейнеры позволяют использовать другой подход. ИспользуяDocker Compose, вы можете создавать контейнеры для вашего приложения, вашего веб-сервера иCertbot client, которые позволят вам получить ваши сертификаты. Следуя этим шагам, вы можете воспользоваться модульностью и переносимостью контейнерного рабочего процесса.

В этом руководстве вы развернете приложение Node.js с обратным прокси-сервером Nginx с помощью Docker Compose. Вы получите сертификаты TLS / SSL для домена, связанного с вашим приложением, и убедитесь, что оно получит высокий рейтинг безопасности отSSL Labs. Наконец, вы настроите заданиеcron для обновления сертификатов, чтобы ваш домен оставался безопасным.

Предпосылки

Чтобы следовать этому уроку, вам понадобится:

  • Сервер Ubuntu 18.04, пользователь без полномочий root с привилегиямиsudo и активный брандмауэр. Инструкции по их настройке см. ВInitial Server Setup guide.

  • Docker и Docker Compose установлены на вашем сервере. Чтобы получить инструкции по установке Docker, выполните шаги 1 и 2 изHow To Install and Use Docker on Ubuntu 18.04. Чтобы получить указания по установке Compose, следуйте шагу 1How To Install Docker Compose on Ubuntu 18.04.

  • Зарегистрированное доменное имя. В этом руководстве будет использоватьсяexample.com. Вы можете получить его бесплатно наFreenom или воспользоваться услугами регистратора доменов по вашему выбору.

  • Обе следующие записи DNS настроены для вашего сервера. Вы можете следить заthis introduction to DigitalOcean DNS, чтобы узнать, как добавить их в учетную запись DigitalOcean, если это то, что вы используете:

    • Запись A сexample.com, указывающая на общедоступный IP-адрес вашего сервера.

    • Запись A сwww.example.com, указывающая на общедоступный IP-адрес вашего сервера.

[[step-1 -—- cloning-and-testing-the-node-application]] == Шаг 1. Клонирование и тестирование приложения узла

В качестве первого шага мы клонируем хранилище с кодом приложения Node, который включает в себя Dockerfile, который мы будем использовать для создания образа приложения с помощью Compose. Сначала мы можем протестировать приложение, собрав и запустив его сdocker run command, без обратного прокси или SSL.

В домашнем каталоге пользователя без полномочий root клонируйтеnodejs-image-demo repository изDigitalOcean Community GitHub account. Этот репозиторий включает код из настройки, описанной вHow To Build a Node.js Application with Docker.

Клонируйте репозиторий в каталог с именемnode_project:

git clone https://github.com/do-community/nodejs-image-demo.git node_project

Перейдите в каталогnode_project:

cd  node_project

В этом каталоге находится файл Dockerfile, содержащий инструкции по созданию приложения Node с использованиемDocker node:10 image и содержимого текущего каталога проекта. Вы можете посмотреть содержимое Dockerfile, набрав:

cat Dockerfile
OutputFROM node:10-alpine

RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app

WORKDIR /home/node/app

COPY package*.json ./

USER node

RUN npm install

COPY --chown=node:node . .

EXPOSE 8080

CMD [ "node", "app.js" ]

Эти инструкции создают образ узла, копируя код проекта из текущего каталога в контейнер и устанавливая зависимости с помощьюnpm install. Они также используют Dockercaching and image layering, отделяя копиюpackage.json иpackage-lock.json, содержащую перечисленные зависимости проекта, от копии остальной части кода приложения. Наконец, в инструкциях указано, что контейнер будет запускаться от имени пользователяnode без полномочий root с соответствующими разрешениями, установленными для кода приложения и каталоговnode_modules.

Для получения дополнительной информации об этом Dockerfile и передовых методах создания образов узлов см. Полное обсуждение вStep 3 of How To Build a Node.js Application with Docker.

Чтобы протестировать приложение без SSL, вы можете создать и пометить изображение, используяdocker build и флаг-t. Мы назовем изображениеnode-demo, но вы можете назвать его как-нибудь иначе:

docker build -t node-demo .

После завершения процесса сборки вы можете перечислить свои изображения с помощьюdocker images:

docker images

Вы увидите следующий вывод, подтверждающий сборку образа приложения:

OutputREPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
node-demo           latest              23961524051d        7 seconds ago       73MB
node                10-alpine           8a752d5af4ce        3 weeks ago         70.7MB

Затем создайте контейнер сdocker run. Мы включим три флага с этой командой:

  • -p: публикует порт на контейнере и сопоставляет его с портом на нашем хосте. Мы будем использовать порт80 на хосте, но вы можете изменить его при необходимости, если у вас есть другой процесс, работающий на этом порту. Для получения дополнительной информации о том, как это работает, см. Это обсуждение в документации Docker поport binding.

  • -d: запускает контейнер в фоновом режиме.

  • --name: позволяет дать контейнеру запоминающееся имя.

Выполните следующую команду, чтобы построить контейнер:

docker run --name node-demo -p 80:8080 -d node-demo

Осмотрите свои запущенные контейнеры с помощьюdocker ps:

docker ps

Вы увидите вывод, подтверждающий, что ваш контейнер приложения работает:

OutputCONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
4133b72391da        node-demo           "node app.js"       17 seconds ago      Up 16 seconds       0.0.0.0:80->8080/tcp   node-demo

Теперь вы можете посетить свой домен, чтобы проверить настройку:http://example.com. Не забудьте заменитьexample.com своим собственным доменным именем. Ваше приложение отобразит следующую целевую страницу:

Application Landing Page

Теперь, когда вы протестировали приложение, вы можете остановить контейнер и удалить изображения. Снова используйтеdocker ps, чтобы получитьCONTAINER ID:

docker ps
OutputCONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
4133b72391da        node-demo           "node app.js"       17 seconds ago      Up 16 seconds       0.0.0.0:80->8080/tcp   node-demo

Остановите контейнер с помощьюdocker stop. Не забудьте заменить перечисленные здесьCONTAINER ID на свое собственное приложениеCONTAINER ID:

docker stop 4133b72391da

Теперь вы можете удалить остановленный контейнер и все изображения, включая неиспользуемые и оборванные изображения, с помощьюdocker system prune и флага-a:

docker system prune -a

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

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

[[шаг-2 -—- определение-конфигурации-веб-сервера]] == Шаг 2 - Определение конфигурации веб-сервера

С нашим приложением Dockerfile мы можем создать файл конфигурации для запуска нашего контейнера Nginx. Мы начнем с минимальной конфигурации, которая будет включать наше доменное имя,document root, информацию о прокси и блок местоположения для направления запросов Certbot в каталог.well-known, где он поместит временный файл для проверки этого DNS для нашего домена разрешается на наш сервер.

Сначала создайте каталог в текущем каталоге проекта для файла конфигурации:

mkdir nginx-conf

Откройте файл с помощьюnano или вашего любимого редактора:

nano nginx-conf/nginx.conf

Добавьте следующий блок сервера для прокси-запросов пользователей к контейнеру приложения Node и для направления запросов Certbot в каталог.well-known. Обязательно заменитеexample.com на свое собственное доменное имя:

~/node_project/nginx-conf/nginx.conf

server {
        listen 80;
        listen [::]:80;

        root /var/www/html;
        index index.html index.htm index.nginx-debian.html;

        server_name example.com www.example.com;

        location / {
                proxy_pass http://nodejs:8080;
        }

        location ~ /.well-known/acme-challenge {
                allow all;
                root /var/www/html;
        }
}

Этот блок сервера позволит нам запустить контейнер Nginx в качестве обратного прокси-сервера, который будет передавать запросы нашему контейнеру приложений Node. Это также позволит нам использовать Certbotwebroot plugin для получения сертификатов для нашего домена. Этот плагин зависит отHTTP-01 validation method, который использует HTTP-запрос, чтобы доказать, что Certbot может получить доступ к ресурсам с сервера, который отвечает на заданное доменное имя.

Как только вы закончите редактирование, сохраните и закройте файл. Чтобы узнать больше о сервере Nginx и алгоритмах блокировки местоположения, обратитесь к этой статье оUnderstanding Nginx Server and Location Block Selection Algorithms.

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

[[step-3 -—- created-the-docker-compose-file]] == Шаг 3. Создание файла Docker Compose

Файлdocker-compose.yml будет определять наши службы, включая приложение Node и веб-сервер. Он будет указывать такие детали, как именованные тома, которые будут иметь решающее значение для совместного использования учетных данных SSL между контейнерами, а также информации о сети и порте. Это также позволит нам указать конкретные команды, которые будут выполняться при создании наших контейнеров. Этот файл является центральным ресурсом, который определит, как наши сервисы будут работать вместе.

Откройте файл в вашем текущем каталоге:

nano docker-compose.yml

Сначала определим службу приложения:

~/node_project/docker-compose.yml

version: '3'

services:
  nodejs:
    build:
      context: .
      dockerfile: Dockerfile
    image: nodejs
    container_name: nodejs
    restart: unless-stopped

Определение службыnodejs включает следующее:

  • build: определяет параметры конфигурации, включаяcontext иdockerfile, которые будут применяться, когда Compose строит образ приложения. Если вы хотите использовать существующий образ из реестра, напримерDocker Hub, вы можете вместо этого использоватьimage instruction с информацией о вашем имени пользователя, репозитории и теге изображения.

  • context: определяет контекст сборки для сборки образа приложения. В данном случае это текущий каталог проекта.

  • dockerfile: указывает Dockerfile, который Compose будет использовать для сборки - Dockerfile, который вы просматривали вStep 1.

  • image,container_name: они применяют имена к изображению и контейнеру.

  • restart: определяет политику перезапуска. По умолчанию этоno, но мы настроили перезапуск контейнера, если он не остановлен.

Обратите внимание, что мы не включаем bind mounts с этим сервисом, поскольку наша установка ориентирована на развертывание, а не на разработку. Для получения дополнительной информации см. Документацию Docker поbind mounts иvolumes.

Чтобы обеспечить связь между приложением и контейнерами веб-сервера, мы также добавим мостовую сеть под названиемapp-network под определением перезапуска:

~/node_project/docker-compose.yml

services:
  nodejs:
...
    networks:
      - app-network

Определяемая пользователем мостовая сеть, подобная этой, обеспечивает связь между контейнерами на одном хосте демона Docker. Это оптимизирует трафик и обмен данными в вашем приложении, поскольку оно открывает все порты между контейнерами в одной и той же мостовой сети, не открывая порты для внешнего мира. Таким образом, вы можете выборочно открывать только те порты, которые необходимы для предоставления ваших услуг веб-интерфейса.

Затем определите службуwebserver:

~/node_project/docker-compose.yml

...
 webserver:
    image: nginx:mainline-alpine
    container_name: webserver
    restart: unless-stopped
    ports:
      - "80:80"
    volumes:
      - web-root:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
    depends_on:
      - nodejs
    networks:
      - app-network

Некоторые параметры, которые мы определили для службыnodejs, остались прежними, но мы также внесли следующие изменения:

  • image: это указывает Compose на получение последнихAlpine-basedNginx image из Docker Hub. Для получения дополнительной информации об изображенияхalpine см. Шаг 3How To Build a Node.js Application with Docker.

  • ports: открывает порт80 для включения параметров конфигурации, которые мы определили в нашей конфигурации Nginx.

Мы также указали следующие именованные тома и привязываемые монтирования:

  • web-root:/var/www/html: это добавит статические ресурсы нашего сайта, скопированные на том с именемweb-root, в каталог/var/www/html в контейнере.

  • ./nginx-conf:/etc/nginx/conf.d: это свяжет монтируемый каталог конфигурации Nginx на хосте с соответствующим каталогом в контейнере, гарантируя, что любые изменения, которые мы вносим в файлы на хосте, будут отражены в контейнере.

  • certbot-etc:/etc/letsencrypt: это подключит соответствующие сертификаты и ключи Let's Encrypt для нашего домена в соответствующий каталог в контейнере.

  • certbot-var:/var/lib/letsencrypt: монтирует рабочий каталог Let’s Encrypt по умолчанию в соответствующий каталог в контейнере.

Затем добавьте параметры конфигурации для контейнераcertbot. Обязательно замените информацию о домене и электронной почте своим доменным именем и контактным адресом электронной почты:

~/node_project/docker-compose.yml

...
  certbot:
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
      - web-root:/var/www/html
    depends_on:
      - webserver
    command: certonly --webroot --webroot-path=/var/www/html --email [email protected] --agree-tos --no-eff-email --staging -d example.com  -d www.example.com

Это определение сообщает Compose, что нужно извлечьcertbot/certbot image из Docker Hub. Он также использует именованные тома для совместного использования ресурсов с контейнером Nginx, включая сертификаты домена и ключ вcertbot-etc, рабочий каталог Let’s Encrypt вcertbot-var и код приложения вweb-root.

Опять же, мы использовалиdepends_on, чтобы указать, что контейнерcertbot должен запускаться после запуска службыwebserver.

Мы также включили параметрcommand, который указывает команду, которая будет запускаться при запуске контейнера. Он включает подкомандуcertonly со следующими параметрами:

  • --webroot: это указывает Certbot использовать подключаемый модуль webroot для размещения файлов в папке webroot для аутентификации.

  • --webroot-path: указывает путь к корневому веб-каталогу.

  • --email: Ваш предпочтительный адрес электронной почты для регистрации и восстановления.

  • --agree-tos: это означает, что вы согласны сACME’s Subscriber Agreement.

  • --no-eff-email: это сообщает Certbot, что вы не хотите делиться своей электронной почтой сElectronic Frontier Foundation (EFF). Не стесняйтесь опустить это, если вы предпочитаете.

  • --staging: это сообщает Certbot, что вы хотите использовать промежуточную среду Let’s Encrypt для получения тестовых сертификатов. Использование этого параметра позволяет протестировать параметры конфигурации и избежать возможных ограничений на количество запросов домена. Для получения дополнительной информации об этих ограничениях см. Let's Encrypt’srate limits documentation.

  • -d: позволяет указать имена доменов, которые вы хотите применить к вашему запросу. В этом случае мы включилиexample.com иwww.example.com. Обязательно замените их собственными настройками домена.

На последнем этапе добавьте определения тома и сети. Обязательно замените здесь имя пользователя своим собственным пользователем без полномочий root:

~/node_project/docker-compose.yml

...
volumes:
  certbot-etc:
  certbot-var:
  web-root:
    driver: local
    driver_opts:
      type: none
      device: /home/sammy/node_project/views/
      o: bind

networks:
  app-network:
    driver: bridge

Наши именованные тома включают в себя наш сертификат Certbot и тома рабочего каталога, а также том для статических ресурсов нашего сайта,web-root. В большинстве случаев драйвером по умолчанию для томов Docker является драйверlocal, который в Linux принимает параметры, аналогичныеmount command. Благодаря этому мы можем указать список параметров драйвера с помощьюdriver_opts, которые монтируют каталогviews на хосте, который содержит статические ресурсы нашего приложения, на том во время выполнения. Содержимое каталога затем может быть разделено между контейнерами. Для получения дополнительных сведений о содержимом каталогаviews см.Step 2 of How To Build a Node.js Application with Docker.

После завершения файлdocker-compose.yml будет выглядеть так:

~/node_project/docker-compose.yml

version: '3'

services:
  nodejs:
    build:
      context: .
      dockerfile: Dockerfile
    image: nodejs
    container_name: nodejs
    restart: unless-stopped
    networks:
      - app-network

  webserver:
    image: nginx:mainline-alpine
    container_name: webserver
    restart: unless-stopped
    ports:
      - "80:80"
    volumes:
      - web-root:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
    depends_on:
      - nodejs
    networks:
      - app-network

  certbot:
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
      - web-root:/var/www/html
    depends_on:
      - webserver
    command: certonly --webroot --webroot-path=/var/www/html --email [email protected] --agree-tos --no-eff-email --staging -d example.com  -d www.example.com

volumes:
  certbot-etc:
  certbot-var:
  web-root:
    driver: local
    driver_opts:
      type: none
      device: /home/sammy/node_project/views/
      o: bind

networks:
  app-network:
    driver: bridge

После определения сервисов вы готовы запускать контейнеры и тестировать запросы на сертификаты.

[[шаг-4 -—- получение-ssl-сертификатов и учетных данных]] == Шаг 4 - Получение сертификатов и учетных данных SSL

Мы можем запустить наши контейнеры с помощьюdocker-compose up, который будет создавать и запускать наши контейнеры и службы в указанном нами порядке. Если наши запросы к домену будут успешными, мы увидим правильный статус выхода в наших выходных данных и правильные сертификаты, смонтированные в папке/etc/letsencrypt/live в контейнереwebserver.

Создайте службы сdocker-compose up и флагом-d, которые будут запускать контейнерыnodejs иwebserver в фоновом режиме:

docker-compose up -d

Вы увидите вывод, подтверждающий, что ваши услуги были созданы:

OutputCreating nodejs ... done
Creating webserver ... done
Creating certbot   ... done

Используяdocker-compose ps, проверьте статус ваших сервисов:

docker-compose ps

Если все прошло успешно, ваши службыnodejs иwebserver должны бытьUp, а контейнерcertbot будет завершен с сообщением о состоянии0:

Output  Name                 Command               State          Ports
------------------------------------------------------------------------
certbot     certbot certonly --webroot ...   Exit 0
nodejs      node app.js                      Up       8080/tcp
webserver   nginx -g daemon off;             Up       0.0.0.0:80->80/tcp

Если вы видите что-либо, кромеUp в столбцеState для службnodejs иwebserver, или статус выхода, отличный от0 дляcertbot) s, обязательно проверьте журналы службы с помощью командыdocker-compose logs:

docker-compose logs service_name

Теперь вы можете проверить, что ваши учетные данные были подключены к контейнеруwebserver с помощьюdocker-compose exec:

docker-compose exec webserver ls -la /etc/letsencrypt/live

Если ваш запрос был успешным, вы увидите вывод:

Outputtotal 16
drwx------ 3 root root 4096 Dec 23 16:48 .
drwxr-xr-x 9 root root 4096 Dec 23 16:48 ..
-rw-r--r-- 1 root root  740 Dec 23 16:48 README
drwxr-xr-x 2 root root 4096 Dec 23 16:48 example.com

Теперь, когда вы знаете, что ваш запрос будет успешным, вы можете отредактировать определение службыcertbot, чтобы удалить флаг--staging.

Открытьdocker-compose.yml:

nano docker-compose.yml

Найдите раздел файла с определением службыcertbot и замените флаг--staging в опцииcommand на флаг--force-renewal, который сообщит Certbot, что вы хотите запросить новый сертификат с теми же доменами, что и существующий сертификат. Определение службыcertbot теперь должно выглядеть так:

~/node_project/docker-compose.yml

...
  certbot:
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
      - web-root:/var/www/html
    depends_on:
      - webserver
    command: certonly --webroot --webroot-path=/var/www/html --email [email protected] --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com
...

Теперь вы можете запуститьdocker-compose up, чтобы воссоздать контейнерcertbot и соответствующие ему тома. Мы также включим параметр--no-deps, чтобы сообщить Compose, что он может пропустить запуск службыwebserver, поскольку она уже запущена:

docker-compose up --force-recreate --no-deps certbot

Вы увидите вывод, указывающий, что ваш запрос сертификата был успешным:

Outputcertbot      | IMPORTANT NOTES:
certbot      |  - Congratulations! Your certificate and chain have been saved at:
certbot      |    /etc/letsencrypt/live/example.com/fullchain.pem
certbot      |    Your key file has been saved at:
certbot      |    /etc/letsencrypt/live/example.com/privkey.pem
certbot      |    Your cert will expire on 2019-03-26. To obtain a new or tweaked
certbot      |    version of this certificate in the future, simply run certbot
certbot      |    again. To non-interactively renew *all* of your certificates, run
certbot      |    "certbot renew"
certbot      |  - Your account credentials have been saved in your Certbot
certbot      |    configuration directory at /etc/letsencrypt. You should make a
certbot      |    secure backup of this folder now. This configuration directory will
certbot      |    also contain certificates and private keys obtained by Certbot so
certbot      |    making regular backups of this folder is ideal.
certbot      |  - If you like Certbot, please consider supporting our work by:
certbot      |
certbot      |    Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
certbot      |    Donating to EFF:                    https://eff.org/donate-le
certbot      |
certbot exited with code 0

С вашими сертификатами вы можете перейти к изменению конфигурации Nginx для включения SSL.

[[step-5 -—- modifying-the-web-server-configuration-and-service-definition]] == Шаг 5. Изменение конфигурации веб-сервера и определения службы

Включение SSL в нашей конфигурации Nginx будет включать добавление перенаправления HTTP к HTTPS и указание нашего SSL-сертификата и расположения ключей. Это также потребует указания нашей группы Диффи-Хеллмана, которую мы будем использовать дляPerfect Forward Secrecy.

Поскольку вы собираетесь воссоздать сервисwebserver, чтобы включить эти дополнения, вы можете остановить его сейчас:

docker-compose stop webserver

Затем создайте каталог в вашем текущем каталоге проекта для вашего ключа Диффи-Хеллмана:

mkdir dhparam

Создайте свой ключ с помощьюopenssl command:

sudo openssl dhparam -out /home/sammy/node_project/dhparam/dhparam-2048.pem 2048

Генерация ключа займет несколько минут.

Чтобы добавить соответствующую информацию Diffie-Hellman и SSL в вашу конфигурацию Nginx, сначала удалите созданный ранее файл конфигурации Nginx:

rm nginx-conf/nginx.conf

Откройте другую версию файла:

nano nginx-conf/nginx.conf

Добавьте следующий код в файл, чтобы перенаправить HTTP на HTTPS и добавить учетные данные SSL, протоколы и заголовки безопасности. Не забудьте заменитьexample.com своим собственным доменом:

~/node_project/nginx-conf/nginx.conf

server {
        listen 80;
        listen [::]:80;
        server_name example.com www.example.com;

        location ~ /.well-known/acme-challenge {
          allow all;
          root /var/www/html;
        }

        location / {
                rewrite ^ https://$host$request_uri? permanent;
        }
}

server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        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;

        ssl_buffer_size 8k;

        ssl_dhparam /etc/ssl/certs/dhparam-2048.pem;

        ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
        ssl_prefer_server_ciphers on;

        ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;

        ssl_ecdh_curve secp384r1;
        ssl_session_tickets off;

        ssl_stapling on;
        ssl_stapling_verify on;
        resolver 8.8.8.8;

        location / {
                try_files $uri @nodejs;
        }

        location @nodejs {
                proxy_pass http://nodejs:8080;
                add_header X-Frame-Options "SAMEORIGIN" always;
                add_header X-XSS-Protection "1; mode=block" always;
                add_header X-Content-Type-Options "nosniff" always;
                add_header Referrer-Policy "no-referrer-when-downgrade" always;
                add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
                # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
                # enable strict transport security only if you understand the implications
        }

        root /var/www/html;
        index index.html index.htm index.nginx-debian.html;
}

Блок HTTP-сервера указывает корневой веб-сайт для запросов обновления Certbot в каталог.well-known/acme-challenge. Он также включаетrewrite directive, который направляет HTTP-запросы в корневой каталог на HTTPS.

Блок сервера HTTPS включаетssl иhttp2. Чтобы узнать больше о том, как HTTP / 2 выполняет итерацию по протоколам HTTP, и о преимуществах, которые он может иметь для производительности веб-сайта, см. Введение вHow To Set Up Nginx with HTTP/2 Support on Ubuntu 18.04. Этот блок также включает в себя ряд параметров, обеспечивающих использование самых современных SSL-протоколов и шифров и включение сшивания OSCP. Сшивание OSCP позволяет вам предлагать ответ с отметкой времени от вашегоcertificate authority в течение начальногоTLS handshake, что может ускорить процесс аутентификации.

В блоке также указываются ваши учетные данные SSL и Diffie-Hellman и ключевые местоположения.

Наконец, мы переместили информацию о проходе прокси в этот блок, включая блок местоположения с директивойtry_files, указывающий запросы на наш контейнер приложения Node.js с псевдонимом, и блок местоположения для этого псевдонима, который включает заголовки безопасности. это позволит нам получать рейтингиA на таких сайтах, как серверные тестовые сайтыSSL Labs иSecurity Headers. Эти заголовки включаютX-Frame-Options,X-Content-Type-Options,Referrer Policy,Content-Security-Policy иX-XSS-Protection. ЗаголовокHTTP Strict Transport Security (HSTS) закомментирован - включите это, только если вы понимаете последствия и оценили его“preload” functionality.

Как только вы закончите редактирование, сохраните и закройте файл.

Перед воссозданием службыwebserver вам нужно будет добавить несколько вещей в определение службы в вашем файлеdocker-compose.yml, включая соответствующую информацию о портах для HTTPS и определение тома Диффи-Хеллмана.

Откройте файл:

nano docker-compose.yml

В определении службыwebserver добавьте следующее сопоставление портов и именованный томdhparam:

~/node_project/docker-compose.yml

...
 webserver:
    image: nginx:latest
    container_name: webserver
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - web-root:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
      - dhparam:/etc/ssl/certs
    depends_on:
      - nodejs
    networks:
      - app-network

Затем добавьте томdhparam к вашим определениямvolumes:

~/node_project/docker-compose.yml

...
volumes:
  ...
  dhparam:
    driver: local
    driver_opts:
      type: none
      device: /home/sammy/node_project/dhparam/
      o: bind

Аналогично томуweb-root, томdhparam будет монтировать ключ Диффи-Хеллмана, хранящийся на хосте, в контейнерwebserver.

Сохраните и закройте файл, когда вы закончите редактирование.

Воссоздайте сервисwebserver:

docker-compose up -d --force-recreate --no-deps webserver

Проверьте свои услуги с помощьюdocker-compose ps:

docker-compose ps

Вы должны увидеть вывод, указывающий, что ваши службыnodejs иwebserver работают:

Output  Name                 Command               State                     Ports
----------------------------------------------------------------------------------------------
certbot     certbot certonly --webroot ...   Exit 0
nodejs      node app.js                      Up       8080/tcp
webserver   nginx -g daemon off;             Up       0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp

Наконец, вы можете посетить свой домен, чтобы убедиться, что все работает как положено. Перейдите в браузере кhttps://example.com, не забудьте заменитьexample.com своим собственным доменным именем. Вы увидите следующую целевую страницу:

Application Landing Page

Вы также должны увидеть значок замка в индикаторе безопасности вашего браузера. Если хотите, вы можете перейти кSSL Labs Server Test landing page илиSecurity Headers server test landing page. Включенные нами параметры конфигурации должны обеспечить вашему сайту рейтингA по обоим параметрам.

[[шаг-6 -—- возобновление-сертификатов]] == Шаг 6 - Продление сертификатов

Сертификаты Let Encrypt действительны в течение 90 дней, поэтому вам нужно настроить автоматический процесс обновления, чтобы они не истекли. Один из способов сделать это - создать задание с помощью утилиты планированияcron. В этом случае мы запланируем заданиеcron с помощью сценария, который обновит наши сертификаты и перезагрузит нашу конфигурацию Nginx.

Откройте скрипт с именемssl_renew.sh в каталоге вашего проекта:

nano ssl_renew.sh

Добавьте следующий код в скрипт, чтобы обновить сертификаты и перезагрузить конфигурацию веб-сервера:

~/node_project/ssl_renew.sh

#!/bin/bash

/usr/local/bin/docker-compose -f /home/sammy/node_project/docker-compose.yml run certbot renew --dry-run \
&& /usr/local/bin/docker-compose -f /home/sammy/node_project/docker-compose.yml kill -s SIGHUP webserver

Помимо указания местоположения нашего двоичного файлаdocker-compose, мы также указываем местоположение нашего файлаdocker-compose.yml для выполнения командdocker-compose. В этом случае мы используемdocker-compose run для запуска контейнераcertbot и переопределенияcommand, предоставленного в нашем определении службы, другим: подкомандойrenew, которая обновляет сертификаты. срок действия близок к истечению. Мы включили здесь параметр--dry-run, чтобы протестировать наш скрипт.

Затем сценарий используетdocker-compose kill для отправкиSIGHUP signal в контейнерwebserver для перезагрузки конфигурации Nginx. Дополнительные сведения об использовании этого процесса для перезагрузки конфигурации Nginx см. Вthis Docker blog post on deploying the official Nginx image with Docker.

Закройте файл, когда вы закончите редактирование. Сделайте его исполняемым:

chmod +x ssl_renew.sh

Затем откройте файлrootcrontab, чтобы запустить сценарий обновления с указанным интервалом:

sudo crontab -e

Если вы впервые редактируете этот файл, вам будет предложено выбрать редактор:

кронтаб

no crontab for root - using an empty one
Select an editor.  To change later, run 'select-editor'.
  1. /bin/ed
  2. /bin/nano        <---- easiest
  3. /usr/bin/vim.basic
  4. /usr/bin/vim.tiny
Choose 1-4 [2]:
...

В нижней части файла добавьте следующую строку:

кронтаб

...
*/5 * * * * /home/sammy/node_project/ssl_renew.sh >> /var/log/cron.log 2>&1

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

Через пять минут проверьтеcron.log, чтобы узнать, был ли запрос на обновление успешно выполнен:

tail -f /var/log/cron.log

Вы должны увидеть вывод, подтверждающий успешное обновление:

Output- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates below have not been saved.)

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/example.com/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Killing webserver ... done

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

кронтаб

...
0 12 * * * /home/sammy/node_project/ssl_renew.sh >> /var/log/cron.log 2>&1

Вы также можете удалить опцию--dry-run из вашего скриптаssl_renew.sh:

~/node_project/ssl_renew.sh

#!/bin/bash

/usr/local/bin/docker-compose -f /home/sammy/node_project/docker-compose.yml run certbot renew \
&& /usr/local/bin/docker-compose -f /home/sammy/node_project/docker-compose.yml kill -s SIGHUP webserver

Ваша работаcron гарантирует, что ваши сертификаты Let’s Encrypt не истекут, обновляя их, когда они соответствуют требованиям. Вы также можетеset up log rotation with the Logrotate utility вращать и сжимать файлы журнала.

Заключение

Вы использовали контейнеры для настройки и запуска приложения Node с обратным прокси-сервером Nginx. Вы также защитили сертификаты SSL для домена своего приложения и настроили заданиеcron для обновления этих сертификатов при необходимости.

Если вы хотите узнать больше о плагинах Let's Encrypt, ознакомьтесь с нашими статьями об использованииNginx plugin илиstandalone plugin.

Вы также можете узнать больше о Docker Compose, обратившись к следующим ресурсам:

Compose documentation также является отличным ресурсом для получения дополнительных сведений о многоконтейнерных приложениях.

Related