Поиск по сайту:

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


Введение

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

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

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

Предпосылки

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

  • Сервер Ubuntu 18.04, пользователь без полномочий root с привилегиями sudo и активный брандмауэр. Чтобы узнать, как их настроить, ознакомьтесь с этим руководством по начальной настройке сервера.
  • Docker и Docker Compose установлены на вашем сервере. Чтобы получить инструкции по установке Docker, выполните шаги 1 и 2 раздела «Как установить Docker Compose в Ubuntu 18.04».
  • Зарегистрированное доменное имя. В этом руководстве будет использоваться your_domain. Вы можете получить его бесплатно на Freenom или воспользоваться услугами регистратора домена по вашему выбору.
  • Обе следующие записи DNS настроены для вашего сервера. Вы можете следовать этому введению в DNS DigitalOcean, чтобы узнать, как добавить их в учетную запись DigitalOcean, если это то, что вы используете:
    • Запись A с your_domain, указывающая на общедоступный IP-адрес вашего сервера.
    • Запись A с www.your_domain, указывающая на общедоступный IP-адрес вашего сервера.

    После того, как вы все настроили, вы готовы начать первый шаг.

    Шаг 1 — Клонирование и тестирование приложения узла

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

    В домашнем каталоге вашего пользователя без полномочий root клонируйте How To Build a Node.js Application with Docker.

    Клонируйте репозиторий в каталог. В этом примере в качестве имени каталога используется node_project. Не стесняйтесь называть этот каталог по своему вкусу:

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

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

    1. cd node_project

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

    1. cat Dockerfile
    Output
    FROM 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. Они также используют преимущества кэширования и многоуровневого изображения Docker, отделяя копию package.json и package-lock.json, содержащих перечисленные зависимости проекта, от копии файла остальной код приложения. Наконец, в инструкциях указано, что контейнер будет запускаться от имени пользователя узла без полномочий root с соответствующими разрешениями, установленными для кода приложения и каталогов node_modules.

    Для получения дополнительной информации об этом Dockerfile и рекомендациях по образу Node, пожалуйста, изучите полное обсуждение в шаге 3 Как создать приложение Node.js с помощью Docker.

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

    1. docker build -t node-demo .

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

    1. docker images

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

    Output
    REPOSITORY 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 по привязке портов.
    • -d: запускает контейнер в фоновом режиме.
    • --name: позволяет дать контейнеру запоминающееся имя.

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

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

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

    1. docker ps

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

    Output
    CONTAINER 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://ваш_домен. Не забудьте заменить your_domain своим собственным доменным именем. Ваше приложение отобразит следующую целевую страницу:

    Теперь, когда вы протестировали приложение, вы можете остановить контейнер и удалить изображения. Используйте docker ps, чтобы получить свой ID КОНТЕЙНЕРА:

    1. docker ps
    Output
    CONTAINER 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. Обязательно замените указанный здесь ИДЕНТИФИКАТОР КОНТЕЙНЕРА своим собственным ИДЕНТИФИКАТОРОМ КОНТЕЙНЕРА:

    1. docker stop 4133b72391da

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

    1. docker system prune -a

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

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

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

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

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

    1. mkdir nginx-conf

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

    1. nano nginx-conf/nginx.conf

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

    server {
            listen 80;
            listen [::]:80;
    
            root /var/www/html;
            index index.html index.htm index.nginx-debian.html;
    
            server_name your_domain www.your_domain;
    
            location / {
                    proxy_pass http://nodejs:8080;
            }
    
            location ~ /.well-known/acme-challenge {
                    allow all;
                    root /var/www/html;
            }
    }
    

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

    Закончив редактирование, сохраните и закройте файл. Если вы использовали nano, вы можете сделать это, нажав CTRL + X, затем Y и ENTER. Чтобы узнать больше о сервере Nginx и алгоритмах блокировки местоположения, обратитесь к этой статье «Понимание алгоритмов выбора сервера Nginx и блока местоположения».

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

    Шаг 3 — Создание файла Docker Compose

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

    Создайте и откройте файл в текущем каталоге:

    1. nano docker-compose.yml

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

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

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

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

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

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

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

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

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

    ...
     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 загрузить последнюю версию How 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. Обязательно замените информацию о домене и электронной почте своим собственным доменным именем и контактным адресом электронной почты:

    ...
      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 sammy@your_domain --agree-tos --no-eff-email --staging -d your_domain  -d www.your_domain 
    

    Это определение сообщает Compose, что необходимо извлечь образ certbot/certbot из 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.
    • --no-eff-email: Это сообщает Certbot, что вы не хотите делиться своей электронной почтой с Electronic Frontier Foundation (EFF). Не стесняйтесь опустить это, если хотите.
    • --staging: указывает Certbot, что вы хотели бы использовать тестовую среду Let’s Encrypt для получения тестовых сертификатов. Использование этого параметра позволяет протестировать параметры конфигурации и избежать возможных ограничений запросов домена. Дополнительные сведения об этих ограничениях см. в документации по ограничениям скорости Let’s Encrypt.
    • -d: это позволяет вам указать доменные имена, которые вы хотели бы применить к вашему запросу. В данном случае вы включили ваш_домен и www.ваш_домен. Обязательно замените их своими собственными доменами.

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

    ...
    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 принимает параметры, аналогичные описанным в разделе Как создать приложение Node.js с помощью Docker.

    Ниже приведен полный файл 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 sammy@your_domain --agree-tos --no-eff-email --staging -d your_domain  -d www.your_domain 
    
    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-сертификатов и учетных данных

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

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

    1. docker-compose up -d

    Ваш вывод подтвердит, что ваши сервисы были созданы:

    Output
    Creating nodejs ... done Creating webserver ... done Creating certbot ... done

    Используйте docker-compose ps, чтобы проверить статус ваших служб:

    1. docker-compose ps

    Если все прошло успешно, ваши службы nodejs и веб-сервер будут 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, обязательно проверьте журналы службы с помощью команды docker-compose logs. Например, если вы хотите проверить журнал Certbot, вы должны запустить:

    1. docker-compose logs certbot

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

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

    Как только ваш запрос будет выполнен успешно, ваш вывод покажет следующее:

    Output
    total 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 your_domain

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

    Откройте файл docker-compose.yml:

    1. nano docker-compose.yml

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

    ...
      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 sammy@your_domain --agree-tos --no-eff-email --force-renewal -d your_domain -d www.your_domain
    ...
    

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

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

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

    Output
    Recreating certbot ... done Attaching to certbot certbot | Account registered. certbot | Renewing an existing certificate for your_domain and www.your_domain certbot | certbot | Successfully received certificate. certbot | Certificate is saved at: /etc/letsencrypt/live/your_domain/fullchain.pem certbot | Key is saved at: /etc/letsencrypt/live/your_domain phd.com/privkey.pem certbot | This certificate expires on 2022-11-03. certbot | These files will be updated when the certificate renews. certbot | NEXT STEPS: certbot | - The certificate will need to be renewed before it expires. Cert bot can automatically renew the certificate in the background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal-setu p for instructions. certbot | Saving debug log to /var/log/letsencrypt/letsencrypt.log certbot | certbot | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - certbot | If you like Certbot, please consider supporting our work by: certbot | * Donating to ISRG / Let's Encrypt: https://letsencrypt.org/do nate certbot | * Donating to EFF: https://eff.org/donate-le certbot | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - certbot exited with code 0

    Имея сертификаты, вы можете перейти к изменению конфигурации Nginx, чтобы включить SSL.

    Шаг 5 — Изменение конфигурации веб-сервера и определения службы

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

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

    1. docker-compose stop webserver

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

    1. mkdir dhparam

    Сгенерируйте ключ с помощью команды openssl:

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

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

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

    1. rm nginx-conf/nginx.conf

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

    1. nano nginx-conf/nginx.conf

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

    
    server {
            listen 80;
            listen [::]:80;
            server_name your_domain www.your_domain;
    
            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 your_domain www.your_domain;
    
            server_tokens off;
    
            ssl_certificate /etc/letsencrypt/live/your_domain/fullchain.pem;
            ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem;
    
            ssl_buffer_size 8k;
    
            ssl_dhparam /etc/ssl/certs/dhparam-2048.pem;
    
            ssl_protocols TLSv1.2;
            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. Он также включает директиву перезаписи, которая направляет HTTP-запросы к корневому каталогу на HTTPS.

    Блок сервера HTTPS включает ssl и http2. Чтобы узнать больше о том, как HTTP/2 повторяет протоколы HTTP, и о преимуществах, которые он может иметь для производительности веб-сайта, ознакомьтесь с введением в TLS-рукопожатие, которое может ускорить процесс аутентификации.

    Блок также указывает ваши учетные данные SSL и Diffie-Hellman и расположение ключей.

    Наконец, вы переместили в этот блок информацию о пропуске прокси, включая блок местоположения с функцией «предварительной загрузки».

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

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

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

    1. nano docker-compose.yml

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

    ...
     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. Не забудьте заменить каталоги sammy и node_project на ваши:

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

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

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

    Воссоздайте службу webserver:

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

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

    1. 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://ваш_домен, заменив ваш_домен своим собственным доменным именем:

    Значок замка должен появиться в индикаторе безопасности вашего браузера. При желании вы можете перейти на тестовую целевую страницу сервера Security Headers. Включенные параметры конфигурации должны принести вашему сайту рейтинг A в тесте сервера SSL Labs. Чтобы получить оценку A в тесте сервера Security Headers, вам нужно раскомментировать заголовок Strict Transport Security (HSTS) в вашем файле nginx-conf/nginx.conf:

    …
    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
            }
    …
    

    Опять же, включите эту опцию только в том случае, если вы понимаете последствия и оценили ее функциональность «предварительной загрузки».

    Шаг 6 — Обновление сертификатов

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

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

    1. nano ssl_renew.sh

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

    #!/bin/bash
    
    COMPOSE="/usr/local/bin/docker-compose --ansi never"
    DOCKER="/usr/bin/docker"
    
    cd /home/sammy/node_project/
    $COMPOSE run certbot renew --dry-run && $COMPOSE kill -s SIGHUP webserver
    $DOCKER system prune -af
    

    Этот сценарий сначала присваивает двоичный файл docker-compose переменной с именем COMPOSE и указывает параметр --no-ansi, который будет запускать docker-compose команды без управляющих символов ANSI. Затем он делает то же самое с двоичным файлом docker. Наконец, он переходит в каталог ~/node_project и выполняет следующие команды docker-compose:

    • docker-compose run. Это запустит контейнер certbot и переопределит команду, указанную в certbot. определение услуги. Вместо использования подкоманды certonly используйте подкоманду renew, которая обновит сертификаты, срок действия которых истекает. Также включен параметр --dry-run для проверки скрипта.
    • Сигнал
    • SIGHUP контейнеру webserver для перезагрузки конфигурации Nginx.

    Затем он запускает docker system prune, чтобы удалить все неиспользуемые контейнеры и образы.

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

    1. chmod +x ssl_renew.sh

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

    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, чтобы убедиться, что запрос на продление выполнен успешно:

    1. 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/your_domain/fullchain.pem (success) ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates above have not been saved.) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Killing webserver ... done
    Output
    … Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/your_domain/fullchain.pem (success) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Saving debug log to /var/log/letsencrypt/letsencrypt.log Killing webserver ... Killing webserver ... done Deleted Containers: 00cad94050985261e5b377de43e314b30ad0a6a724189753a9a23ec76488fd78 Total reclaimed space: 824.5kB

    Выйдите, введя CTRL + C в своем терминале.

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

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

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

    #!/bin/bash
    
    COMPOSE="/usr/local/bin/docker-compose --no-ansi"
    DOCKER="/usr/bin/docker"
    
    cd /home/sammy/node_project/
    $COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver
    $DOCKER system prune -af
    

    Ваше задание cron гарантирует, что ваши сертификаты Let’s Encrypt не истечет, обновляя их, когда они соответствуют требованиям. Вы также можете настроить ротацию журналов с помощью утилиты Logrotate для ротации и сжатия файлов журналов.

    Заключение

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

    Если вы хотите узнать больше о подключаемых модулях Let’s Encrypt, ознакомьтесь с нашими статьями об использовании автономного подключаемого модуля.

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

    • Как установить Docker Compose в Ubuntu 18.04.
    • Как настроить среду тестирования непрерывной интеграции с помощью Docker и Docker Compose в Ubuntu 16.04.
    • Как настроить Laravel, Nginx и MySQL с помощью Docker Compose.

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