One place for hosting & domains

      Encrypt

      Cómo proteger una aplicación de Node.js en contenedor con Nginx, Lets Encrypt y Docker Compose


      Introducción

      Existen varias formas de mejorar la flexibilidad y la seguridad de su aplicación de Node.js. Utilzar un proxy inverso como Nginx le permite cargar solicitudes de equilibrio, almacenar en caché contenido estático e implementar seguridad en la capa de transporte (TLS). Habilitar HTTPS cifrado en su servidor garantiza que la comunicación hacia y desde su aplicación permanezca protegida.

      La implementación de un proxy inverso con TLS/SSL en contenedores implica un conjunto de procedimientos diferente del que se emplea trabajar directamente en un sistema operativo host. Por ejemplo, si obtiene certificados de ​​​Let’s Encrypt​​​ ​para una aplicación que se ejecuta en un servidor, instalaría el software requerido directamente en su host. Los contenedores le permiten adoptar un enfoque diferente. Utilizando Docker Compose, puede crear contenedores para su aplicación, su servidor web y el cliente de Certbot que le permitirá obtener sus certificados. Siguiendo estos pasos, puede aprovechar la modularidad y portabilidad de un flujo de trabajo en contenedor.

      A través de este tutorial, implementará una aplicación Node.js con un proxy inverso de Nginx utilizando Docker Compose. Obtendrá certificados TLS/SSL para el dominio asociado con su aplicación y garantizará que reciba una alta calificación de seguridad de SSL Labs. Por último, configurará una tarea cron para renovar sus certificados de modo que su dominio permanezca seguro.

      Requisitos previos

      Para seguir este tutorial, necesitará lo siguiente:

      • Un servidor de Ubuntu 18.04, un usuario no root con privilegios sudo y un firewall activo. Para obtener información sobre cómo configurarlos, consulte esta guía de configuración inicial de servidores.
      • Docker y Docker Compose instalados en su servidor. Para obtener orientación sobre la instalación de Docker, siga los pasos 1 y 2 de Cómo instalar y usar Docker en Ubuntu 18.04. Para acceder a orientación relacionada con la instalación de Compose, siga el paso 1 de Cómo instalar Docker Compose en Ubuntu 18.04.
      • Un nombre de dominio registrado. En este tutorial, se utilizará** example.com** en todo momento. Puede obtener un ejemplar gratis en Freenom o utilizar el registrador de dominios que desee.
      • Los dos registros DNS que se indican a continuación se han configurado para su servidor. Puede seguir esta introducción al DNS de DigitalOcean para obtener información sobre cómo agregarlos a una cuenta de DigitalOcean, si usa una:

        • Un registro A con example.com​​​ orientado a la dirección IP pública de su servidor.
        • Un registro A con example.com​​​ ​​orientado a la dirección IP pública de su servidor.

      Paso 1: Clonar y probar la aplicación de Node

      Como primer paso, clonaremos el repositorio con el código de aplicación de Node, que incluye el Dockerfile que utilizaremos para crear nuestra imagen de aplicación con Compose. Primero, podemos probar la aplicación compilándola y ejecutándola con el comando docker run, sin un proxy inverso o SSL.

      En el directorio principal de su usuario no root, clone el repositorio nodejs-image-demo de la cuenta de GitHub de la comunidad de DigitalOcean. Este repositorio incluye el código de la configuración descrita en Cómo crear una aplicación de Node.js con Docker.

      Clone el repositorio en un directorio llamado node_project:

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

      Pase al directorio node_project:

      En este directorio, hay un Dockerfile que contiene instrucciones para crear una aplicación de Node usando la imagen Docker node:10 y el contenido de su directorio de proyecto actual. Puede consultar el contenido de Dockerfile escribiendo lo siguiente:

      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" ]

      Estas instrucciones crean una imagen de Node copiando el código del proyecto del directorio actual al contenedor e instalando dependencias con npm install. También aprovechan el almacenamiento en caché y la disposición en capas de imágenes de Docker al separar la copia de package.json y package-lock.json, que contienen las dependencias listadas del proyecto, de la copia del resto del código de aplicación. Por último, las instrucciones especifican que el contenedor se ejecutará como usuario del node no root con los permisos apropiados establecidos en el código de aplicación y los directorios de node_modules.

      Para obtener más información sobre prácticas recomendadas de Dockerfile y Node, consulte el análisis completo en el paso 3 de Cómo crear una aplicación Node.js con Docker.

      Para probar la aplicación sin SSL, puede crear y etiquetar la imagen usando docker build y el indicador -t. Daremos a la imagen el nombre node-demo, pero puede elegir cualquier otro nombre:

      • docker build -t node-demo .

      Una vez que el proceso de compilación esté completo, podrá enumerar sus imágenes con docker images:

      Visualizará el siguiente resultado, que confirma la compilación de la imagen de la aplicación:

      Output

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

      A continuación, cree el contenedor con docker run. Incluiremos tres marcas con este comando:

      • -p: edita el puerto en el contenedor y lo asigna a un puerto en nuestro host. Usaremos el puerto 80 en el host, pero puede modificarlo como considere necesario si tiene otro proceso en ejecución en ese puerto. Para obtener más información sobre cómo funciona, consulte esta discusión en la documentación de Docker sobre enlaces de puerto.
      • -d: ejecuta el contenedor en segundo plano.
      • --name: permite darle al contenedor un nombre fácil de recordar.

      Ejecute el siguiente comando para compilar el contenedor:

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

      Inspeccione sus contenedores en ejecución con docker ps:

      Verá un resultado que confirmará que el contenedor de su aplicación se encuentra en ejecución:

      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

      Ahora podrá visitar su dominio para probar su configuración: http://example.com. Recuerde sustituir example.com por su propio nombre de dominio. En su aplicación se mostrará la siguiente página de inicio:

      Página de destino de la aplicación

      Ahora que probó la aplicación, puede detener el contenedor y quitar las imágenes. Utilice docker ps de nuevo para obtener su CONTAINER ID:

      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

      Detenga el contenedor con docker stop. Asegúrese de sustituir el CONTAINER ID que se enumera aquí por su propio CONTAINER ID de la aplicación:

      Ahora podrá quitar el contenedor detenido y todas las imágenes, incluidas las que no se utilicen y las pendientes, con docker system prune y el indicador -a:

      Escriba y cuando se le indique, en la salida, para confirmar que desea eliminar el contenedor detenido y las imágenes. Tenga en cuenta que esto también eliminará la memoria caché de su compilación.

      Una vez que se pruebe la imagen de su aplicación, puede continuar creando el resto de su configuración con Docker Compose.

      Paso 2: Definir la configuración del servidor web

      Una vez implementada nuestra aplicación Dockerfile, podemos crear un archivo de configuración para ejecutar nuestro contenedor de Nginx. Comenzaremos con una configuración mínima que incluirá nuestro nombre de dominio, root de documentos, información de proxy y un bloque de ubicación para dirigir las solicitudes de Certbot al directorio .well-known, donde creará un archivo temporal para validar que el DNS de nuestro dominio se resuelve en nuestro servidor.

      Primero, cree un directorio en el directorio de proyecto actual para el archivo de configuración:

      Abra el archivo con nano o su editor favorito:

      • nano nginx-conf/nginx.conf

      Añada el siguiente bloque de servidor a las solicitudes de los usuarios de proxy en su contenedor de aplicación de Node, y para dirigir solicitudes de Certbot al directorio .well-known. Asegúrese de sustituir example.com por su propio nombre de dominio:

      ~/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;
              }
      }
      

      Este bloque de servidor nos permitirá iniciar el contenedor de Nginx como proxy inverso, lo cual transmitirá solicitudes a nuestro contenedor de aplicación de Node. También nos permitirá usar el complemento webroot de Certbot para obtener certificados para nuestro dominio. Este complemento depende del método de validación HTTP-01, que utiliza una solicitud HTTP para probar que Certbot puede acceder a los recursos de un servidor que responda a un nombre de dominio determinado.

      Una vez que haya concluido de editar, guarde y cierre el archivo. Para obtener más información sobre los algoritmos bloques de servidor y ubicación de Nginx, consulte el artículo Información sobre algoritmos de selección de bloques de servidores y ubicación de Nginx.

      Una vez configurados los detalles del servidor web, podremos crear nuestro archivo docker-compose.yml que nos permitirá crear nuestros servicios de aplicación y el contenedor de Certbot que utilizaremos para obtener nuestros certificados.

      Paso 3: Crear el archivo de Docker Compose

      El archivo docker-compose.yml definirá nuestros servicios, incluidos la aplicación y el servidor web de Node. Especificará detalles, como volúmenes nombrados, que serán esenciales para compartir credenciales SSL entre contenedores, así como información de redes y puertos. También nos permitirá especificar comandos puntuales que se ejecutarán cuando se creen nuestros contenedores. Este archivo es el recurso central que definirá la manera en que nuestros servicios funcionarán juntos.

      Abra el archivo en su directorio actual:

      Primero, defina el servicio de aplicación:

      ~/node_project/docker-compose.yml

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

      La definición de servicio nodejs incluye lo siguiente:

      • build: define las opciones de configuración, incluido el context y dockerfile, que se aplicarán cuando Compose cree la imagen de la aplicación. Si desea utilizar una imagen existente de un registro como Docker Hub, podría utilizar la instrucción de imagen como alternativa, con información sobre su nombre de usuario, repositorio y etiqueta de imagen.
      • context: define el contexto de compilación para la compilación de la imagen de la aplicación. En este caso, es el directorio de proyectos actual.
      • dockerfile: especifica el Dockerfile que Compose usará para la compilación, el Dockerfile que examinó en el paso 1.
      • image y container_name: aplican nombres a la imagen y al contenedor.
      • restart: define la política de reinicio. El valor predeterminado es no, pero configuramos el contenedor para reiniciarse a menos que se detenga.

      Tenga en cuenta que no incluiremos los montajes “bind” con este servicio, ya que nuestra configuración no se centra en el desarrollo sino en la implementación. Para obtener más información, consulte la documentación de Docker sobre montajes bind y volúmenes.

      Para habilitar la comunicación entre los contenedores de la aplicación y del servidor web, también añadiremos una red de puente llamada app-network debajo de la definición de reinicio:

      ~/node_project/docker-compose.yml

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

      Una red de puente definida por el usuario como esta permite la comunicación entre contenedores en el mismo host de demonio de Docker. Esto agiliza el tráfico y la comunicación dentro de su aplicación, ya que abre todos los puertos entre contenedores en la misma red de puente y, al mismo tiempo, no expone ningún puerto al mundo exterior. Por lo tanto, puede ser selectivo a la hora de abrir solo los puertos que necesita para exponer sus servicios de frontend.

      A continuación, defina el servicio 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
      

      Algunos de los ajustes que definimos para el servicio nodejs siguen siendo los mismos, pero también realizamos los siguientes cambios:

      También especificamos los siguientes montajes “bind” y volúmenes con nombre:

      • web-root:/var/www/html: agregará los activos estáticos de nuestro sitio, copiados a un volumen llamado web-root, al directorio /var/www/html del contenedor.
      • ./nginx-conf:/etc/nginx/conf.d: vinculará mediante montaje “bind” el directorio de configuración de Nginx en el host con el directorio pertinente en el contenedor, lo cual garantizará que cualquier cambio que realicemos en los archivos del host se reflejarán en el contenedor.
      • certbot-etc:/etc/letsencrypt: montará los certificados y las claves pertinentes de Let’s Encrypt para nuestro dominio en el directorio apropiado del contenedor.
      • certbot-var:/var/lib/letsencrypt: monta el directorio de trabajo predeterminado de Let’s Encrypt en el directorio correspondiente del contenedor.

      A continuación, agregue las opciones de configuración para el contenedor de certbot. Asegúrese de sustituir la información de dominio y correo electrónico por su propio nombre de dominio y su correo electrónico de contacto:

      ~/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 sammy@example.com --agree-tos --no-eff-email --staging -d example.com  -d www.example.com
      

      Esta definición indica a Compose que obtenga la imagen de certbot/certbot de Docker Hub. También utiliza volúmenes con nombre para compartir recursos con el contenedor de Nginx, incluidos los certificados de dominio y la clave en certbot-etc, el directorio de trabajo de Let’s Encrypt en certbot-var y el código de aplicación en web-root.

      Una vez más, usamos depends_on para especificar que el contenedor de certbot debe iniciarse una vez que el servicio webserver esté en ejecución.

      También incluimos una opción de command que especifica el comando que se ejecutará cuando se inicie el contenedor. Incluye el subcomando certonly con las siguientes opciones:

      • --webroot: indica a Cerbot que utilice el complemento webroot para colocar archivos en la carpeta webroot para la autenticación.
      • --webroot-path: especifica la ruta del directorio webroot.
      • --email: su correo electrónico preferido para el registro y la recuperación.
      • --agree-tos: especifica que usted acepta el Acuerdo de suscripción de ACME.
      • --no-eff-email: indica a Certbot que no usted desea compartir su correo electrónico con la Electronic Frontier Foundation (EFF). Puede omitir esto si lo prefiere.
      • --staging: indica a Certbot que usted desea utilizar el entorno de configuración de Let’s Encrypt para obtener certificados de prueba. Utilizar esta opción le permite probar sus opciones de configuración y evitar posibles límites vinculados a solicitudes de dominio. Para obtener más información sobre estos límites, consulte la documentación sobre los límites de las tasas de Let’s Encrypt.
      • -d: le permite especificar los nombres de dominio que desea aplicar a su solicitud. En este caso, incluimos example.com y www.example.com. Asegúrese de sustituirlos por sus propias preferencias de dominio.

      Como paso final, agregue las definiciones de volumen y red. Asegúrese de sustituir aquí el nombre de usuario por su propio usuario no 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
      

      Nuestros volúmenes con nombre incluyen nuestros volúmenes de certificados de Certbot y directorios de trabajo, y el volumen de los activos estáticos de nuestro sitio, web-root. En la mayoría de los casos, el controlador predeterminado de volúmenes de Docker es el controlador local, que en Linux acepta opciones similares al comando mount. Gracias a esto, podemos especificar una lista de opciones de controladores con driver_opts que montan el directorio views en el host, el cual contiene los activos estáticos de nuestra aplicación, en el volumen en tiempo de ejecución. El contenido del directorio puede, entonces, compartirse entre contenedores. Para obtener más información sobre el contenido del directorio de views, consulte el paso 2 de Cómo crear una aplicación de Node.js con Docker.

      El archivo docker-compose.yml tendrá este aspecto al terminar:

      ~/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 sammy@example.com --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  
      

      Una vez configuradas las definiciones de servicio, estará listo para iniciar los contenedores y probar las solicitudes de su certificado.

      Paso 4: Obtener certificados y credenciales SSL

      Podemos iniciar nuestros contenedores con docker-compose up, que creará y ejecutará nuestros contenedores y servicios en el orden que especificamos. Si las solicitudes de nuestros dominios tienen éxito, veremos el estado de salida correcto en nuestro resultado y los certificados correctos montados en la carpeta /etc/letsencrypt/live del contenedor webserver.

      Cree los servicios con docker-compose up y el indicador -d, que ejecutarán los contenedores nodejs y webserver en segundo plano:

      Verá un resultado que confirmará la creación de sus servicios:

      Output

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

      Mediante docker-compose ps, compruebe el estado de sus servicios:

      Si todo se realizó correctamente, el estado de sus servicios de nodejs y webserver debería ser Up y el contenedor de certbot se habrá cerrado con un mensaje de estado de 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

      Si ve algo diferente de Up en la columna State para los servicios nodejs y webserver, o un estado de salida distinto de 0 para el contenedor de certbot, asegúrese de verificar los registros de servicio con el comando docker-compose logs:

      • docker-compose logs service_name

      Ahora podrá verificar que sus credenciales se hayan montado en el contenedor webserver con docker-compose exec:

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

      Si su solicitud fue correcta, verá un resultado como este:

      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 example.com

      Ahora que sabe que su solicitud será correcta, puede editar la definición de servicio de certbot para eliminar el marcador --staging.

      Abra docker-compose.yml:

      Encuentre la sección del archivo con la definición de servicio de certbot y sustituya el indicador --staging en la opción command por el indicador --force-renewal, el cual indicará a Certbot que usted desea solicitar un nuevo certificado con los mismos dominios que un certificado existente. Ahora, la definición de servicio de certbot debería tener este aspecto:

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

      Podrá ejecutar docker-compose up para recrear el contenedor de certbot y sus volúmenes pertinentes. También incluiremos la opción --no-deps para indicar a Compose que puede omitir el inicio del servicio webserver, dado que ya está en ejecución:

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

      Verá un resultado que indica que su solicitud de certificado fue exitosa:

      Output

      certbot | 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

      Una vez implementados sus certificados, podrá modificar su configuración de Nginx para incluir SSL.

      Paso 5: Modificar la configuración del servidor web y la definición del servicio

      Habilitar SSL en nuestra configuración de Nginx implicará agregar un redireccionamiento de HTTP a HTTPS y especificar las ubicaciones de nuestros certificados y nuestras claves SSL. También implicará especificar nuestro grupo Diffie-Hellman, que utilizaremos para confidencialidad directa perfecta.

      Debido a que va a recrear el servicio webserver para incluir estas adiciones, puede detenerlo ahora:

      • docker-compose stop webserver

      A continuación, cree un directorio en el directorio de su proyecto actual para su clave Diffie-Hellman:

      Genere su clave con el comando openssl:

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

      Tomará algunos momentos generar la clave.

      Para agregar la información pertinente de Diffie-Hellman y SSL a su configuración de Nginx, primero elimine el archivo de configuración de Nginx que creó anteriomente:

      Abra otra versión del archivo:

      • nano nginx-conf/nginx.conf

      Añada el siguiente código al archivo para redireccionar HTTP a HTTP y agregar credenciales, protocolos y encabezados de seguridad SSL. Recuerde sustituir example.com por su propio dominio:

      ~/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;
      }
      

      El bloque de servidor HTTP especifica el webroot para solicitudes de renovación de Certbot del directorio .well-known/acme-challenge. También incluye una directiva de reescritura que dirige las solicitudes HTTP al directorio root hacia HTTPS.

      El bloque de servidor HTTPS habilita ssl y http2. Para obtener más información sobre la iteración de HTTP/2 en protocolos HTTP y los beneficios que puede tener para el rendimiento del sitio web, consulte la introducción a Cómo configurar Nginx con soporte HTTP/2 en Ubuntu 18.04. Este bloque también incluye varias opciones para garantizar que usted utilice los protocolos y los cifrados SSL más actualizados y que el engrapado OSCP esté activado. El grapado OCSP le permite ofrecer una respuesta con registro de tiempo de su autoridad de certificación durante el protocolo de enlace TLS inicial, lo que puede acelerar el proceso de autenticación.

      El bloque también especifica sus credenciales y ubicaciones de claves SSL y Diffie-Hellman.

      Por último, movimos la información de pase de proxy a este bloque, incluido un bloque de ubicación con una directiva try_files, que dirige solicitudes a nuestro contenedor de aplicación de Node.js con alias y un bloque de ubicación para ese alias, que incluye encabezados de seguridad que nos permitirán obtener calificaciones de A en aspectos como los laboratorios SSL y sitios de prueba de servidores de encabezados de seguridad. Entre estos encabezado se incluyen X-frame-Options, X-Content-Type-Options, Referer Policy, Content-Security-Policy y X-XSS-Protection. El encabezado HTTP de Strict Transport Security (HSTS) no se incluye: habilite esto solo si comprende las implicaciones y evaluó su funcionalidad “preload”.

      Una vez que haya finalice la edición, guarde y cierre el archivo.

      Antes de recrear el servicio webserver, deberá realizar algunas adiciones a la definición de servicio de su archivo docker-compose.yml, incluida la información de puerto pertinente para HTTPS y una definición de volumen de Diffie-Hellman.

      Abra el archivo:

      En la definición del servicio webserver, agregue la siguiente asignación de puerto y el volumen llamado 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
      

      A continuación, agregue el volumen dhparam a sus definiciones de volúmenes:

      ~/node_project/docker-compose.yml

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

      De manera similar al volumen web-root, el volumen dhparam monta en el contenedor webserver la clave Diffie-Hellman almacenada en el host.

      Guarde y cierre el archivo cuando haya terminado de editar.

      Recree el servicio webserver:

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

      Compruebe sus servicios con docker-compose ps:

      Debería ver un resultado que indique que sus nodejs y sus servicios webserver se encuentran en ejecución:

      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

      Por último, puede visitar su dominio para asegurarse de que todo funcione como se espera. Visite con su navegador https://example.com y asegúrese de sustituir example.com por su propio nombre de dominio. Visualizará la siguiente página de destino:

      Página de destino de la aplicación

      También debería ver el icono del candado en el indicador de seguridad de su navegador. Si lo desea, puede visitar la página de destino de SSL Labs Server Test o la página de inicio de prueba de servidores de encabezados de seguridad. Las opciones de configuración que incluimos deberían hacer que la calificación de su sitio sea *A *en ambos casos.

      Paso 6: Renovar certificados

      Los certificados de Let’s Encrypt son válidos durante 90 días, por lo que le convendrá configurar un proceso de renovación automática para asegurarse de que no caduquen. Una forma de hacerlo es crear un trabajo con la utilidad de programación de cron. En este caso, programaremos una tarea cron utilizando una secuencia de comandos que renovará nuestros certificados y volverá a cargar nuestra configuración de Nginx.

      Abra una secuencia de comandos llamada ssl_renew.sh en el directorio de su proyecto:

      Agregue el siguiente código a la secuencia de comandos para renovar sus certificados y volver a cargar la configuración de su servidor web:

      ~/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
      

      Además de especificar la ubicación de nuestro binario docker-compose, también especificamos la ubicación de nuestro archivo docker-compose.yml para poder ejecutar comandos docker-compose. En este caso, usamos docker-compose run para iniciar un contenedor certbot y anular el command proporcionado en nuestra definición de servicio con otro: el subcomando ​​​renew​​​​​, que renovará certificados que caducarán pronto. Aquí incluimos la opción --dry-run para probar nuestra secuencia de comandos.

      La secuencia de comandos utiliza docker-compose kill para enviar una señal de SIGHUP al contenedor webserver a fin de volver a cargar la configuración de Nginx. Si desea obtener más información sobre el uso de este proceso para volver a cargar su configuración de Nginx, consulte este post del blog de Docker sobre la implementación de la imagen oficial de Nginx con Docker.

      Cierre el archivo cuando finalice la edición. Haga que sea ejecutable:

      A continuación, abra su archivo root crontab para ejecutar la secuencia de comandos de renovación en un intervalo especificado:

      Si es la primera vez que edita este archivo, se le solicitará elegir un editor:

      crontab

      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]:
      ...
      

      Al final del archivo, añada la siguiente línea:

      crontab

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

      Esto fijará un intervalo de tarea de cinco minutos, de modo que puede probar si su solicitud de renovación ha funcionado como estaba previsto. También creamos un archivo de registro, cron.log, para registrar el resultado pertinente de la tarea.

      Después de cinco minutos, revise ​​​​​cron.log​​​​​​ para comprobar si la solicitud de renovación se realizó con éxito o no:

      • tail -f /var/log/cron.log

      Debería ver un resultado que confirme el éxito de la renovación:

      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

      Ahora podrá modificar el archivo de crontab para establecer un intervalo diario. Para ejecutar la secuencia de comandos cada día al mediodía, por ejemplo, modificaría la última línea del archivo de modo que tenga el siguiente aspecto:

      crontab

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

      También le convendrá eliminar la opción --dry-run de su secuencia de comandos 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
      

      Su tarea cron controlará que sus certificados de Let´s Encrypt no caduquen al renovarlos cuando reúnan las condiciones. También puede configurar la rotación de registros con la utilidad Logrotate para rotar y comprimir sus archivos de registro.

      Conclusión

      Usó contenedores para configurar y ejecutar una aplicación de Node con un proxy inverso de Nginx. También protegió certificados SSL para el dominio de su aplicación y configuró una tarea cron para renovar estos certificados cuando sea necesario.

      Si le interesa obtener más información sobre los complementos de Let´s Encrypt, consulte nuestros artículos sobre el uso del complemento de Nginx o el complemento independiente.

      También puede obtener más información sobre Docker Compose consultando los siguientes recursos:

      La documentación de Compose es también un excelente recurso para aprender más sobre aplicaciones en varios contenedores.



      Source link

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


      Введение

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

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

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

      Предварительные требования

      Для данного обучающего модуля вам потребуется следующее:

      • Сервер Ubuntu 18.04, пользователь без прав root с привилегиями sudo и активный брандмауэр. Дополнительную информацию о настройке этих параметров см. в руководстве по первоначальной настройке сервера.
      • Docker и Docker Compose, установленные на вашем сервере. Инструкции по установке Docker см. в шагах 1 и 2 руководства Установка и использование Docker в Ubuntu 18.04. Дополнительную информацию по установке Compose см. в шаге 1 руководства Установка Docker Compose в Ubuntu 18.04.
      • Зарегистрированное доменное имя. В этом обучающем руководстве мы будем использовать example.com. Вы можете получить бесплатный домен на Freenom или зарегистрировать доменное имя по вашему выбору.
      • На вашем сервере должны быть настроены обе нижеследующие записи DNS. Вы можете воспользоваться введением в работу с DigitalOcean DNS, чтобы получить подробную информацию о добавлении доменов в учетную запись DigitalOcean, если вы используете этот способ:

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

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

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

      В корневой директории пользователя без прав root клонируйте репозиторий nodejs-image-demo из учетной записи DigitalOcean на GitHub. Этот репозиторий содержит код настройки, описанной в руководстве Сборка приложения Node.js с помощью Docker.

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

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

      Перейдите в директорию node_project:

      В этой директории имеется файл Dockerfile, содержащий инструкции по сборке приложения Node с помощью образа Docker node:10 и содержимое директории вашего текущего проекта. Вы можете посмотреть содержимое файла 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" ]

      Эти инструкции позволят собрать образ Node, скопировав код проекта из текущей директории в контейнер и установив зависимости с помощью npm install. Также они помогут воспользоваться преимуществами кэширования и слоев образа Docker, отделив копию package.json и package-lock.json​​​, содержащую список зависимостей проекта, от копии остального кода. Наконец, инструкции указывают, что контейнер будет запускаться в качестве пользователя node без прав root с соответствующими разрешениями, заданными для кода приложения и директорий node_modules.

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

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

      • docker build -t node-demo .

      После завершения процесса сборки вы можете вывести список образов с помощью команды 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: позволяет присвоить контейнеру запоминающееся имя.

      Запустите следующую команду для сборки контейнера:

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

      Просмотрите запущенные контейнеры с помощью команды 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://example.com. Обязательно замените example.com​​ на ваше доменное имя. Ваше приложение отобразит следующую начальную страницу:

      Начальная страница приложения

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

      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. Обязательно замените указанный здесь CONTAINER ID на CONTAINER ID вашего приложения:

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

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

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

      Шаг 2 — Настройка конфигурации веб-сервера

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

      Во-первых, создайте директорию в текущей директории проекта для файла конфигурации:

      Откройте файл с помощью 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. Также это позволит нам использовать плагин webroot для Certbot, чтобы получить сертификаты для нашего домена. Работа плагина основана на методе валидации HTTP-01, который использует запрос HTTP, чтобы доказать, что Certbot может получить доступ к ресурсам с сервера, который отвечает на заданное доменное имя.

      После завершения редактирования сохраните и закройте файл. Чтобы узнать больше о сервере Nginx и алгоритмах блока расположения, ознакомьтесь со статьей Знакомство с сервером Nginx и алгоритмами выбора блоков расположения.

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

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

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

      Откройте файл в текущей директории:

      Определите службу приложения:

      ~/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 с информацией об имени пользователя, репозитория и теге образа.
      • context: определение контекста сборки для сборки образа приложения. В нашем случае это текущая директория проекта.
      • dockerfile: данный параметр указывает файл Dockerfile, который Compose будет использоваться для сборки, т. е. файл Dockerfile, который мы рассматривали на шаге 1.
      • image, container_name: эти параметры присваивают имена для образа и контейнера.
      • restart: данный параметр определяет политику перезапуска. По умолчанию установлено значение no, но мы задали значение, согласно которому контейнер будет перезапускаться, пока не будет закрыт.

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

      Чтобы активировать коммуникацию между контейнерами приложения и веб-сервера, мы также добавим мостовую сеть 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 на необходимость извлечения последнего образа Nginx на базе Alpine из Docker Hub. Дополнительную информацию о образах alpine см. в шаге 3 руководства Сборка приложения Node.js с помощью 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 sammy@example.com --agree-tos --no-eff-email --staging -d example.com  -d www.example.com
      

      Это определение попросит 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: данный элемент указывает путь директории webroot.
      • --email: предпочитаемый адрес электронной почты для регистрации и восстановления.
      • --agree-tos: данный элемент указывает, что вы принимаете Соглашение о подписке ACME.
      • --no-eff-email: данный элемент указывает Certbot, что вы не хотите делиться адресом электронной почты с Electronic Frontier Foundation​​​ (EFF). Вы можете пропустить этот элемент.
      • --staging: данный элемент говорит Certbot, что вы хотите использовать промежуточную среду Let’s Encrypt для получения тестовых сертификатов. При использовании этого параметра вы можете протестировать параметры конфигурации и избежать возможных пределов для запросов домена. Дополнительную информацию об этих предельных значениях см. в документации по ограничениям скорости Let’s Encrypt.
      • -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. Благодаря этому мы можем указать список параметров для драйверов с помощью команды driver_opts, которая монтирует директорию views на хосте, содержащем статические активы нашего приложения, на том во время исполнения. Содержимое директории затем может передаваться между контейнерами. Дополнительную информацию о содержании директории views см. в шаге 2 руководства Сборка приложения Node.js с помощью 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 sammy@example.com --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

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

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

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

      Output

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

      С помощью 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, проверьте журналы службы с помощью команды docker-compose logs:

      • docker-compose logs service_name

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

      • 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 example.com

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

      Откройте 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 sammy@example.com --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

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

      Output

      certbot | 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.

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

      Активация SSL в нашей конфигурации Nginx будет подразумевать добавление перенаправления HTTP на HTTPS и указание расположения сертификата и ключей SSL. Также нам нужно будет указать нашу группу Diffie-Hellman, которую мы будем использовать для Совершенной прямой секретности.

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

      • docker-compose stop webserver

      Далее создайте директорию в текущей директории проекта для вашего ключа Diffie-Hellman:

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

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

      Процесс генерации ключа может занять несколько минут.

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

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

      • 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 указывает webroot для запросов обновления Certbot в директории .well-known/acme-challenge. Также он содержит директиву перезаписи, которая перенаправляет запросы HTTP в корневую директорию HTTPS.

      Блок сервера HTTPS активирует ssl и http2. Дополнительную информацию о том, как выполняется итерация HTTP/2 в протоколах HTTP и какие преимущества это может дать для повышения производительности веб-сайта, см. во вводной части руководства по настройке Nginx с поддержкой HTTP/2 в Ubuntu 18.04. Этот блок также включает ряд параметров, гарантирующих, что вы используете самые актуальные протоколы и шифры SSL, а также то, что расширение OCSP stapling будет активировано. Расширение OCSP stapling позволяет вам обеспечивать ответ с привязкой по времени от издателя сертификата во время первоначального TLS-рукопожатия, что позволяет ускорить процесс аутентификации.

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

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

      После завершения редактирования сохраните и закройте файл.

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

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

      В определении службы 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 в определения ваших томов:

      ~/node_project/docker-compose.yml

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

      Как и в случае с томом web-root, том dhparam будет монтировать ключ Diffie-Hellman, хранящийся на хосте, в контейнер webserver.

      Сохраните и закройте файл после завершения редактирования.

      Повторно создайте службу webserver:

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

      Проверьте ваши службы с помощью команды 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 на имя вашего домена. Вы увидите следующую начальную страницу:

      Начальная страница приложения

      Также вы должны увидеть значок замка в индикаторе безопасности браузера. При желании вы можете перейти к начальной странице теста сервера SSL Labs или начальной странице теста сервера Security Headers. Параметры конфигурации, которые мы использовали, должны обеспечить получение оценки А для вашего сайта в обоих тестах.

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

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

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

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

      ~/node_project/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 --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 и переопределяет параметр command, указанный в определении службы certbot. Вместо использования субкоманды certonly мы используем здесь субкоманду renew, которая будет обновлять сертификаты, срок действия которых близок к окончанию. Мы включили параметр --dry-run, чтобы протестировать наш скрипт.
      • docker-compose kill: данный параметр отправляет сигнал SIGHUP контейнеру webserver для перезагрузки конфигурации Nginx. Дополнительную информацию об использовании этого процесса для перезагрузки конфигурации Nginx см. в этой публикации блога Docker, посвященной развертыванию официального образа Nginx с помощью Docker.

      После этого он выполняет команду docker system prune для удаления всех неиспользуемых контейнеров и образов.

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

      Далее откройте root файл crontab для запуска скрипта обновления с заданным интервалом:

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

      crontab

      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]:
      ...
      

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

      crontab

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

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

      Через 5 минут проверьте 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 для настройки ежедневного интервала. Чтобы запускать скрипт каждый день в полдень, например, вы должны изменить последнюю строку файла, которая должна выглядеть следующим образом:

      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
      
      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, ознакомьтесь со статьями, посвященными использованию плагина Nginx или автономного плагина.

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

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



      Source link

      How To Secure a Containerized Node.js Application with Nginx, Let’s Encrypt, and Docker Compose


      Introdução

      Existem várias maneiras de melhorar a flexibilidade e segurança do seu aplicativo Node.js. O uso de um proxy reverso como o Nginx oferece a você a capacidade de carregar solicitações de balanceamento de carga, conteúdo de cache estático e de* implementar a Segurança em Camada*s de Transporte (TLS). Ao habilitar o HTTPS criptografado no seu servidor, garante-se que a comunicação para o seu aplicativo e vinda dele permaneça segura.

      A implementação de um proxy reverso com TLS/SSL em contêineres envolve um conjunto diferente de procedimentos do trabalho em um sistema operacional de host. Por exemplo, se estivesse obtendo certificados do Let’s Encrypt para um aplicativo em execução em um servidor, deveria instalar o software necessário diretamente no seu host. Os contêineres permitem que você utilize uma abordagem diferente. Ao usar o Docker Compose, é possível criar contêineres para o seu aplicativo, seu servidor Web e o cliente Certbot que permite você de obter seus certificados. Ao seguir estes passos, você pode aproveitar a modularidade e a portabilidade de um fluxo de trabalho em contêiner.

      Neste tutorial, será implantado um aplicativo Node.js com um proxy reverso Nginx usando o Docker Compose. Você receberá certificados TLS/SSL para o domínio associados ao seu aplicativo e garantirá que ele receba uma classificação de segurança elevada do SSL Labs. Por fim, será configurado um trabalho cron para renovar seus certificados para que seu domínio permaneça seguro.

      Pré-requisitos

      Para seguir este tutorial, será necessário:

      • Um servidor Ubuntu 18.04, um usuário não raiz com privilégios sudo e um firewall ativo. Para saber como configurar isso, consulte este guia de configuração inicial do servidor.
      • O Docker e o Docker Compose instalados no seu servidor. Como orientação na instalação do Docker, siga os Passos 1 e 2 de Como instalar e usar o Docker no Ubuntu 18.04. Como orientação na instalação do Compose, siga o Passo 1 de Como instalar o Docker Compose no Ubuntu 18.04.
      • Um nome de domínio registrado. Este tutorial usará o example.com do início ao fim. Você pode obter um de graça no Freenom, ou usar o registrador de domínios da sua escolha.
      • Ambos os registros de DNS a seguir serão configurados para o seu servidor. Você pode seguir esta introdução para o DNS da DigitalOcean para mais detalhes sobre como adicioná-los a uma conta DigitalOcean, caso seja o que está usando:

        • Um registro A com example.com apontando para o endereço IP público do seu servidor.
        • Um registro A com example.com apontando para o endereço IP público do seu servidor.

      Passo 1 — Clonando e testando o aplicativo Node

      Como primeiro passo, clonaremos o repositório com o código do aplicativo Node, que inclui o Dockerfile que usaremos na construção da nossa imagem de aplicativo com o Compose. Podemos testar primeiro o aplicativo a partir de sua construção e execução com o comando docker run, sem um proxy reverso ou SSL.

      No diretório home do seu usuário não raiz, clone o repositório nodejs-image-demo da conta GitHub da Comunidade DigitalOcean. Este repositório inclui o código da configuração descrito em Como construir um aplicativo Node.js com o Docker.

      Clone o repositório em um diretório chamado node_project:

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

      Vá para o diretório node_project:

      Neste diretório, há um Dockerfile que contém instruções para a construção de um aplicativo Node usando a imagem do Docker node:10 e o conteúdo do seu diretório de projeto atual. Você pode olhar o conteúdo do Dockerfile digitando:

      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" ]

      Essas instruções constroem uma imagem do Node através da cópia do código do projeto do diretório atual para o contêiner e instalação de dependências com o npm install. Elas também se aproveitam do salvamento em cache e disposição em camadas da imagem. Isso é feito pela separação da cópia do package.json e package-lock.json, que contém as dependências listadas do projeto, da cópia do resto do código do aplicativo. Por fim, as instruções especificam que o contêiner será executado como o usuário node não raiz com as permissões apropriadas definidas no código do aplicativo e no diretório node_modules.

      Para obter mais informações sobre este Dockerfile e práticas recomendadas da imagem do Node, consulte a discussão completa no Passo 3 de Como construir um aplicativo Node.js com o Docker.

      Para testar o aplicativo sem o SSL, construa e identifique a imagem usando o docker build e a flag -t. Vamos nomear a imagem node-demo, mas você pode dar a ela o nome que quiser:

      • docker build -t node-demo .

      Assim que o processo de construção for concluído, você pode listar suas imagens com o docker images:

      Você verá o seguinte resultado, confirmando a compilação da imagem do aplicativo:

      Output

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

      Em seguida, crie o contêiner com o docker run. Vamos incluir três flags com este comando:

      • -p: publica a porta no contêiner e a mapeia para uma porta no nosso host. Usaremos a porta 80 no host, mas sinta-se a vontade para escolher outra se necessário, caso tenha outro processo em execução naquela porta. Para obter mais informações sobre como isso funciona, veja esta discussão nos documentos do Docker sobre associação de portas.
      • -d: executa o contêiner em segundo plano.
      • --name: permite-nos dar ao contêiner um nome memorável.

      Execute o comando a seguir para criar o contêiner:

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

      Verifique seus contêineres em execução com o docker ps:

      Você verá o resultado que confirma que o seu contêiner do aplicativo está funcionando:

      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

      Agora, você pode visitar seu domínio para testar sua configuração: http://example.com. Lembre-se de substituir o example.com pelo seu próprio nome de domínio. Seu aplicativo exibirá a seguinte página de destino:

      Application Landing Page

      Agora que você testou o aplicativo, pare o contêiner e remova as imagens. Use o docker ps novamente para obter seu CONTAINER ID:

      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

      Pare o contêiner com o docker stop. Certifique-se de substituir o CONTAINER ID listado aqui pelo CONTAINER ID do seu aplicativo:

      Agora, é possível remover o contêiner parado e todas as imagens, incluindo imagens não utilizadas e penduradas, com o docker system prune e a flag -a:

      Digite y quando solicitado na saída para confirmar que você gostaria de remover o contêiner e imagens parados. Fique ciente de que isso também removerá seu cache de construção.

      Com sua imagem de aplicativo testada, siga em frente para a construção do resto da sua configuração com o Docker Compose.

      Passo 2 — Definindo as configurações do servidor Web

      Com nosso aplicativo Dockerfile funcionando, podemos criar um arquivo de configuração para executar nosso contêiner Nginx. Começaremos com uma configuração mínima que incluirá nosso nome de domínio, root do documento, informações de proxy e um bloco de localização para dirigir os pedidos do Certbot ao diretório .well-known. Lá, ele colocará um arquivo temporário para validar que o DNS para nosso domínio resolva para nosso servidor.

      Primeiramente, crie um diretório no diretório atual do projeto para o arquivo de configuração:

      Abra o arquivo com o nano ou com o seu editor favorito:

      • nano nginx-conf/nginx.conf

      Adicione o seguinte bloco de servidor para servir como proxy para os pedidos de usuário para o contêiner do seu aplicativo Node e redirecionar os pedidos do Certbot ao diretório .well-known. Certifique-se de substituir o example.com pelo seu próprio nome de domínio:

      ~/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;
              }
      }
      

      Este bloco de servidor nos permitirá iniciar o contêiner Nginx como um proxy reverso, que passará pedidos para nosso contêiner do aplicativo Node. Ele também nos permitirá usar o plug-in webroot do Certbot para obter certificados para nosso domínio. Este plug-in depende do método de validação HTTP-01, que usa um pedido HTTP para provar que o Certbot pode acessar recursos de um servidor que responde a um dado nome de domínio.

      Assim que terminar a edição, salve e feche o arquivo. Para aprender mais sobre o servidor Nginx e os algoritmos de blocos de localização, consulte este artigo Entendendo o servidor Nginx e os algoritmos de seleção de blocos de localização.

      Com os detalhes de configuração do servidor Web funcionando, podemos seguir em frente para a criação do nosso arquivo docker-compose.yml, que nos permitirá criar nossos serviços de aplicativo e o contêiner do Certbot que usaremos para obter nossos certificados.

      Passo 3 — Criando o arquivo do Docker Compose

      O arquivo docker-compose.yml definirá nossos serviços, incluindo o aplicativo Node e o servidor Web. Ele especificará detalhes como volumes nomeados, que serão críticos para compartilhar credenciais SSL entre contêineres, além de informações de rede e portas. Ele também nos permitirá especificar comandos específicos para serem executados quando nossos contêineres forem criados. Este arquivo é o recurso central que definirá como nossos serviços funcionarão em conjunto.

      Abra o arquivo no seu diretório atual:

      Primeiro, defina o serviço de aplicativo:

      ~/node_project/docker-compose.yml

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

      A definição de serviço nodejs inclui o seguinte:

      • build: define as opções de configuração, incluindo o context e dockerfile, que serão aplicadas quando o Compose construir a imagem do aplicativo. Se quisesse usar uma imagem existente de um registro como o Docker Hub, poderia usar como alternativa a instrução image, com informações sobre seu nome de usuário, repositório e tag da imagem.
      • context: define o contexto de compilação para a compilação de imagem do aplicativo. Neste caso, é o diretório atual do projeto.
      • dockerfile: especifica o Dockerfile que o Compose usará para a compilação — o Dockerfile que você olhou no Passo 1.
      • image, container_name: aplicam nomes à imagem e contêiner.
      • restart: define a política de reinício. A padrão é no, mas definimos o contêiner para reiniciar a menos que ele seja interrompido.

      Note que não estamos incluindo bind mounts com este serviço, uma vez que nossa configuração está focada na implantação e não no desenvolvimento. Para obter mais informações, consulte a documentação do Docker sobre bind mounts e volumes.

      Para habilitar a comunicação entre os contêineres do aplicativo e do servidor Web, adicionaremos também uma rede bridge chamada app-network abaixo da definição de reinicialização:

      ~/node_project/docker-compose.yml

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

      Uma rede bridge definida pelo usuário permite a comunicação entre contêineres no mesmo host daemon do Docker. Isso simplifica o tráfego e a comunicação dentro do seu aplicativo, uma vez que todas as portas entre os contêineres na mesma rede bridge são abertas, ao mesmo tempo em que nenhuma porta é exposta ao mundo exterior. Assim, é possível ser seletivo abrindo apenas as portas que você precisar para expor seus serviços front-end.

      Em seguida, defina o serviço 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
      

      Algumas das configurações que definimos para o serviço nodejs permanecem as mesmas, mas também fizemos as seguintes alterações:

      Também especificamos os seguintes volumes nomeados e bind mounts:

      • web-root:/var/www/html: adicionará os ativos estáticos do nosso site, copiados para um volume chamado web-root, para o diretório /var/www/html no contêiner.
      • ./nginx-conf:/etc/nginx/conf.d: irá associar a montagem do diretório de configuração Nginx no host ao diretório relevante no contêiner, garantindo que quaisquer alterações que façamos em arquivos no host serão refletidas no contêiner.
      • certbot-etc:/etc/letsencrypt: irá montar os certificados e chaves relevantes do Let’s Encrypt do nosso domínio para o diretório apropriado no contêiner.
      • certbot-var:/var/lib/letsencrypt: monta o diretório de trabalho padrão do Let’s Encrypt para o diretório apropriado no contêiner.

      Em seguida, adicione as opções de configuração para o contêiner certbot. Certifique-se de substituir as informações de domínio e e-mail pelo seu próprio nome de domínio e e-mail:

      ~/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 sammy@example.com --agree-tos --no-eff-email --staging -d example.com  -d www.example.com
      

      Esta definição diz ao Compose para puxar a imagem certbot/certbot do Docker Hub. Ela também usa volumes nomeados para compartilhar recursos com o contêiner do Nginx, incluindo os certificados de domínio e chaves no certbot-etc, o diretório de trabalho do Let’s Encrypt no certbot-var e o código do aplicativo no web-root.

      Novamente, usamos o depends_on para especificar que o contêiner do certbot deve ser iniciado assim que o serviço webserver estiver funcionando.

      Também incluímos uma opção command que especifica o comando a ser executado quando o contêiner for iniciado. Ele inclui o subcomando certonly com as seguintes opções:

      • --webroot: diz ao Certbot para usar o plug-in webroot para colocar arquivos na pasta webroot para autenticação.
      • --webroot-path: especifica o caminho do diretório webroot.
      • --email: seu e-mail escolhido para o registro e recuperação.
      • --agree-tos: especifica que você concorda com o Acordo de Subscrição do ACME.
      • --no-eff-email: diz ao Certbot que você não deseja compartilhar seu e-mail com a Fundação Electronic Frontier (EFF). Sinta-se à vontade para omitir isso se preferir.
      • --staging: diz ao Certbot que você gostaria de usar o ambiente de preparo do Let’s Encrypt para obter certificados de teste. Usar essa opção permite que você teste suas opções de configuração e evite possíveis limites de solicitação de domínio. Para obter mais informações sobre esses limites, consulte a documentação sobre limites de taxas do Let’s Encrypt.
      • -d: permite que você especifique nomes de domínio que gostaria de aplicar ao seu pedido. Neste caso, incluímos o example.com e www.example.com. Certifique-se de substituí-los pelas suas próprias preferências de domínio.

      Como passo final, adicione as definições de volume e rede. Certifique-se de substituir o nome de usuário presente aqui pelo seu usuário não raiz:

      ~/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
      

      Nossos volumes nomeados incluem nosso certificado Certbot e os volumes de diretórios de trabalho, além dos volume para os ativos estáticos do nosso site, o web-root. Na maioria dos casos, o driver padrão para os volumes do Docker é o driver local, que no Linux aceita opções semelhantes ao comando mount. Graças a isso, é possível especificar uma lista de opções de drivers com o driver_opts que montam o diretório views no host, sendo que estes contém os ativos estáticos do nosso aplicativo, além do volume em tempo de execução. O conteúdo do diretório pode ser, então, compartilhado entre contêineres. Para obter mais informações sobre o conteúdo do diretório views, consulte o Passo 2 de Como construir um aplicativo Node.js com o Docker.

      O arquivo docker-compose.yml se parecerá com isto quando terminar:

      ~/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 sammy@example.com --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  
      

      Com as definições de serviço instaladas, você está pronto para iniciar os contêineres e testar seus pedidos de certificado.

      Passo 4 — Obtendo certificados e credenciais SSL

      Podemos iniciar nossos contêineres com o docker-compose up, que criará e executará nossos contêineres e serviços na ordem que especificamos. Se nossos pedidos de domínio forem bem sucedidos, veremos o status correto de saída no nosso resultado e os certificados corretos montados na pasta /etc/letsencrypt/live no contêiner webserver.

      Crie os serviços com o docker-compose up e a flag -d, que executarão os contêineres nodejs e webserver em segundo plano:

      Você verá um resultado confirmando que seus serviços foram criados:

      Output

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

      Com o uso do docker-compose ps, verifique o status dos seus serviços:

      Se tudo ocorreu bem, seus serviços nodejs e webserver devem estar Up e o contêiner certbot terá finalizado com uma mensagem de status 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

      Se você ver qualquer outra coisa além de Up na coluna State para os serviços nodejs e webserver, ou um status de saída que não seja 0 para o contêiner certbot, certifique-se de verificar os registros de serviço com o comando docker-compose logs:

      • docker-compose logs service_name

      Agora, é possível verificar se suas credenciais foram instaladas no contêiner webserver com o docker-compose exec:

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

      Se seu pedido foi bem sucedido, você verá um resultado similar a este:

      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 example.com

      Agora que você sabe que seu pedido será bem sucedido, edite a definição do serviço certbot para remover a flag --staging.

      Abra o docker-compose.yml:

      Encontre a seção do arquivo com a definição de serviço do certbot e substitua a flag --staging na opção command pela flag --force-renewal, que dirá ao Certbot que você quer solicitar um novo certificado com os mesmos domínios de um certificado existente. A definição de serviço do certbot deve agora se parecer com isto:

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

      Agora, é possível executar o docker-compose up para recriar o contêiner do certbot e seus volumes relevantes. Também vamos incluir a opção --no-deps para dizer ao Compose que ele pode pular a inicialização do serviço webserver, já que ele já está em funcionamento:

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

      Você verá um resultado indicando que seu pedido de certificado foi bem sucedido:

      Output

      certbot | 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

      Com seus certificados no lugar, você pode seguir em frente para modificar sua configuração Nginx para incluir o SSL.

      Passo 5 — Modificando as configuração do servidor Web e da definição de serviço

      Habilitar o SSL na nossa configuração Nginx envolverá a adição de um redirecionamento do HTTP para o HTTPS e a especificação dos nossos certificados e locais de chave SSL. Isso também envolverá especificar nosso grupo Diffie-Hellman, que usaremos para o Perfect Forward Secrecy.

      Como será recriado o serviço webserver para incluir essas adições, você pode interrompê-lo agora:

      • docker-compose stop webserver

      Em seguida, crie um diretório no seu diretório atual de projeto para sua chave Diffie-Hellman:

      Gere sua chave com o comando openssl:

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

      A chave será gerada após alguns instantes.

      Para adicionar as informações relevantes do Diffie-Hellman e SSL na sua configuração do Nginx, remova primeiro o arquivo de configuração do Nginx que você criou mais cedo:

      Abra outra versão do arquivo:

      • nano nginx-conf/nginx.conf

      Adicione o seguinte código ao arquivo para redirecionar o HTTP para o HTTPS e adicione credenciais, protocolos e cabeçalhos de segurança. Lembre-se de substituir o example.com pelo seu próprio domínio:

      ~/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;
      }
      

      O bloco de servidor HTTP especifica o webroot para pedidos de renovação do Certbot no diretório .well-knowb/acme-challenge. Isso também inclui uma diretriz de reescrita, que direciona pedidos HTTP ao diretório root para o HTTPS.

      O bloco de servidor HTTPS habilita o ssl e o http2. Para ler mais sobre como o HTTP/2 itera nos protocolos HTTP e os benefícios que ele pode ter para o desempenho do site, consulte a introdução de Como configurar o Nginx com suporte HTTP/2 no Ubuntu 18.04. Este bloco também inclui uma série de opções para garantir que você esteja usando os protocolos e criptografias SSL mais atualizados e que o grampeamento OSCP esteja ligado. O grampeamento OSCP permite a oferta de uma resposta com a data marcada da sua autoridade de certificação durante o handshake TLS, o que pode acelerar o processo de autenticação.

      O bloco também especifica suas credenciais e locais de chave do SSL e Diffie-Hellman.

      Por fim, transferimos as informações de passagem de proxy para este bloco, incluindo um bloco de localização com uma diretriz try_files, direcionando pedidos para nosso contêiner do aplicativo Node.js de alias, e um bloco de localização para aquele alias, que inclui cabeçalhos de segurança que nos permitirão obter classificações A em coisas como os sites de teste de servidor SSL Labs e Security Headers. Estes cabeçalhos incluem o X-Frame-Options, X-Content-Type-Options, Referrer Policy, Content-Security-Policy, e X-XSS-Protection. O cabeçalho HTTP Strict Transport Security (HSTS) é retirado do comentário - habilite isso apenas se você entender as implicações e avaliou sua funcionalidade de “precarregamento”.

      Assim que terminar a edição, salve e feche o arquivo.

      Antes de recriar o serviço webserver, será necessário adicionar algumas coisas na definição de serviço no seu arquivo docker-compose.yml, incluindo informações relevantes de porta para o HTTPS e uma definição de volume do Diffie-Hellman.

      Abra o arquivo:

      Na definição do serviço webserver, adicione o seguinte mapeamento de portas e o volume nomeado 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
      

      Em seguida, adicione o volume dhparam às suas definições de volumes:

      ~/node_project/docker-compose.yml

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

      De maneira similar ao volume web-root, o volume dhparam montará a chave Diffie-Hellman armazenada no host para o contêiner webserver.

      Salve e feche o arquivo quando você terminar a edição.

      Recrie o serviço webserver:

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

      Verifique seus serviços com o docker-compose ps:

      Você deve ver um resultado indicando que seus serviços nodejs e webserver estão funcionando:

      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

      Por fim, visite seu domínio para garantir que tudo está funcionando conforme esperado. Navegue com seu browser até https://example.com, certificando-se de substituir o example.com pelo seu próprio nome de domínio. Você verá a seguinte página de destino:

      Application Landing Page

      Você também deve ver o ícone de cadeado no indicador de segurança do seu navegador. Se quiser, navegue até a página de destino do teste de servidor do SSL Labs, ou a página de destino do teste de servidor do Security Headers. As opções de configuração que incluímos devem garantir ao seu site uma classificação A em ambos.

      Passo 6 — Renovando certificados

      Os certificados do Let’s Encrypt são válidos por 90 dias, então você vai querer configurar um processo de renovação automatizado para garantir que eles não expirem. Uma maneira de fazer isso é criando um trabalho com o utilitário de agendamento cron. Neste caso, vamos agendar uma tarefa do cron usando um script que renovará nossos certificados e recarregará nossa configuração do Nginx.

      Abra um script chamado ssl_renew.sh no seu diretório de projeto:

      Adicione o seguinte código ao script para renovar seus certificados e recarregar a configuração do seu servidor Web:

      ~/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
      

      Além de especificar o local do nosso binário docker-compose, também especificamos o local do nosso arquivo docker-compose.yml para que os comandos docker-compose sejam executados. Neste caso, estamos usando o docker-compose run para iniciar um contêiner do certbot e sobrepor o command fornecido na nossa definição de serviço com outro: o subcomando renew, que renova certificados que estão próximos de expirar. Incluímos a opção --dry-run aqui para testar nosso script.

      O script usa o docker-compose kill para enviar um sinal SIGHUP para o contêiner webserver de forma a recarregar a configuração do Nginx. Para obter mais informações sobre o uso deste processo para recarregar sua configuração do Nginx, consulte este post do blog do Docker sobre a implantação da imagem oficial do Nginx com o Docker.

      Feche o arquivo quando terminar a edição. Torne-o executável:

      Em seguida, abra seu arquivo root crontab para executar o script de renovação em um intervalo especificado:

      Se essa é a primeira vez que você edita esse arquivo, será solicitado que escolha um editor:

      crontab

      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]:
      ...
      

      No final do arquivo, adicione a seguinte linha:

      crontab

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

      Isso definirá o intervalo de trabalho para a cada cinco minutos, para que você possa testar se seu pedido de renovação funcionou como previsto ou não. Também criamos um arquivo de registro, cron.log, para gravar o resultado relevante do trabalho.

      Após cinco minutos, verifique o cron.log para ver se o pedido de renovação foi bem sucedido ou não:

      • tail -f /var/log/cron.log

      Um resultado confirmando uma renovação bem-sucedida deve aparecer:

      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

      Agora, é possível modificar o arquivo crontab para definir um intervalo diário. Para executar o script todos os dias ao meio-dia, por exemplo, você modificaria a última linha do arquivo para que se pareça com isto:

      crontab

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

      Você também vai querer remover a opção --dry-run do seu script 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
      

      Seu trabalho cron irá garantir que seus certificados do Let’s Encrypt não expirem, através da renovação deles quando forem elegíveis. Você também pode configurar uma rotação de registro com o utilitário Logrotate para rotacionar e comprimir seus arquivos de registro.

      Conclusão

      Você usou contêineres para configurar e executar um aplicativo Node com um proxy reverso Nginx. Você também utilizou certificados SSL para proteger o domínio do seu aplicativo e configurou um trabalho cron para renovar esses certificados quando necessário.

      Se estiver interessado em aprender mais sobre plug-ins do Let’s Encrypt, consulte nossos artigos sobre o uso do plug-in Nginx ou do plug-in standalone.

      Você também pode aprender mais sobre o Docker Compose consultando os seguintes recursos:

      A documentação do Compose também é um ótimo recurso para aprender mais sobre aplicativos multi-contêiner.



      Source link