Cómo usar el contenedor de Let''s Encrypt with Docker basado en la imagen Node.js
https lets-encrypt (5)
Estoy ejecutando un sitio web basado en Express en un contenedor de Docker basado en la imagen Node.js. ¿Cómo uso Let''s Encrypt con un contenedor basado en esa imagen?
Hay muchas maneras de lograr esto dependiendo de su configuración. Una forma popular es configurar nginx delante de su contenedor Docker y manejar los certificados completamente dentro de su configuración nginx.
La configuración de nginx puede contener una lista de ''usptreams'' (sus contenedores Docker) y ''servidores'' que esencialmente asignan solicitudes a flujos ascendentes particulares. Como parte de ese mapeo también puedes manejar SSL.
Puedes usar certbot para ayudarte a configurar esto.
Lo primero que he hecho es crear una imagen acoplable sencilla basada en Express.
Estoy usando la siguiente app.js
, tomada del ejemplo de Express hello world en sus documentos:
var express = require(''express'');
var app = express();
app.get(''/'', function (req, res) {
res.send(''Hello World!'');
});
app.listen(3000, function () {
console.log(''Example app listening on port 3000!'');
});
También terminé con el siguiente archivo packages.json
después de ejecutar su npm init
en el mismo documento:
{
"name": "exampleexpress",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo /"Error: no test specified/" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.14.0"
}
}
He creado el siguiente Dockerfile:
FROM node:onbuild
EXPOSE 3000
CMD node app.js
Aquí está la salida cuando hago mi paso de docker build
. He eliminado la mayor parte de la salida de npm install
por motivos de brevedad:
$ docker build -t exampleexpress .
Sending build context to Docker daemon 1.262 MB
Step 1 : FROM node:onbuild
# Executing 3 build triggers...
Step 1 : COPY package.json /usr/src/app/
Step 1 : RUN npm install
---> Running in 981ca7cb7256
npm info it worked if it ends with ok
<snip>
npm info ok
Step 1 : COPY . /usr/src/app
---> cf82ea76e369
Removing intermediate container ccd3f79f8de3
Removing intermediate container 391d27f33348
Removing intermediate container 1c4feaccd08e
Step 2 : EXPOSE 3000
---> Running in 408ac1c8bbd8
---> c65c7e1bdb94
Removing intermediate container 408ac1c8bbd8
Step 3 : CMD node app.js
---> Running in f882a3a126b0
---> 5f0f03885df0
Removing intermediate container f882a3a126b0
Successfully built 5f0f03885df0
Ejecutar esta imagen funciona así:
$ docker run -d --name helloworld -p 3000:3000 exampleexpress
$ curl 127.0.0.1:3000
Hello World!
Podemos limpiar esto haciendo: docker rm -f helloworld
Ahora, tengo mi sitio web muy básico basado en Express que se ejecuta en un contenedor Docker, pero todavía no tiene ninguna configuración TLS. Mirando nuevamente a los documentos de Expressjs, la mejor práctica de seguridad al usar TLS es usar nginx.
Como quiero introducir un nuevo componente (nginx), lo haré con un segundo contenedor.
Dado que nginx necesitará algunos certificados para trabajar, vamos a seguir adelante y generarlos con el cliente letsencrypt. Los documentos de letsencrypt sobre cómo usar letsencrypt en Docker se pueden encontrar aquí: http://letsencrypt.readthedocs.io/en/latest/using.html#running-with-docker
Ejecute los siguientes comandos para generar los certificados iniciales. Deberá ejecutar esto en un sistema que esté conectado a Internet público y que tenga acceso al puerto 80/443 desde los servidores de letsencrypt. También necesitarás configurar tu nombre de DNS y señalar el cuadro en el que ejecutas esto:
export LETSENCRYPT_EMAIL=<youremailaddress>
export DNSNAME=www.example.com
docker run --rm /
-p 443:443 -p 80:80 --name letsencrypt /
-v "/etc/letsencrypt:/etc/letsencrypt" /
-v "/var/lib/letsencrypt:/var/lib/letsencrypt" /
quay.io/letsencrypt/letsencrypt:latest /
certonly -n -m $LETSENCRYPT_EMAIL -d $DNSNAME --standalone --agree-tos
Asegúrese de reemplazar los valores de LETSENCRYPT_EMAIL
y DNSNAME
. La dirección de correo electrónico se utiliza para las notificaciones de caducidad.
Ahora, configuremos un servidor nginx que haga uso de este certificado recién generado. Primero, necesitaremos un archivo de configuración nginx que esté configurado para TLS:
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main ''$remote_addr - $remote_user [$time_local] "$request" ''
''$status $body_bytes_sent "$http_referer" ''
''"$http_user_agent" "$http_x_forwarded_for"'';
access_log /dev/stdout main;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
#add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
server_name www.example.com;
ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
location ^~ /.well-known/ {
root /usr/share/nginx/html;
allow all;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://expresshelloworld:3000;
}
}
}
Podemos poner este archivo de configuración en nuestra propia imagen nginx personalizada con el siguiente Dockerfile:
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf
Esto se puede compilar con el siguiente comando: docker build -t expressnginx .
A continuación, crearemos una red personalizada para poder aprovechar la función de descubrimiento de servicios de Docker:
docker network create -d bridge expressnet
Ahora, podemos encender los contenedores helloworld y nginx:
docker run -d /
--name expresshelloworld --net expressnet exampleexpress
docker run -d -p 80:80 -p 443:443 /
--name expressnginx --net expressnet /
-v /etc/letsencrypt:/etc/letsencrypt /
-v /usr/share/nginx/html:/usr/share/nginx/html /
expressnginx
Verifique que nginx haya aparecido correctamente echando un vistazo a la salida de los docker logs expressnginx
.
El archivo de configuración nginx debe redirigir cualquier solicitud en el puerto 80 al puerto 443. Podemos probar eso ejecutando lo siguiente:
curl -v http://www.example.com/
¡También deberíamos, en este punto, poder hacer una conexión TLS exitosa y ver nuestro Hello World!
respuesta de vuelta:
curl -v https://www.example.com/
Ahora, para configurar el proceso de renovación. El nginx.conf anterior tiene provisiones para la ruta de acceso de letencrypt .well-conocido para el método de verificación webroot. Si ejecuta el siguiente comando, manejará la renovación. Normalmente, ejecutará este comando en algún tipo de cron para que sus certificados se renueven antes de que caduquen:
export [email protected]
export DNSNAME=www.example.com
docker run --rm --name letsencrypt /
-v "/etc/letsencrypt:/etc/letsencrypt" /
-v "/var/lib/letsencrypt:/var/lib/letsencrypt" /
-v "/usr/share/nginx/html:/usr/share/nginx/html" /
quay.io/letsencrypt/letsencrypt:latest /
certonly -n --webroot -w /usr/share/nginx/html -d $DNSNAME --agree-tos
Puede echar un vistazo aquí: https://certbot.eff.org/docs/using.html?highlight=docker#running-with-docker
Entonces lo que yo personalmente hago es:
- Cree un volumen Docker para almacenar los certificados y generar los certificados con la imagen de arriba
- Cree una red definida por el usuario de Docker ( https://docs.docker.com/engine/userguide/networking/#/user-defined-networks )
- Cree una imagen basada en nginx con su configuración (tal vez this sea útil)
- Cree un contenedor Nginx basado en su imagen, monte el volumen en él y conéctelo a la red (también reenvíe los puertos 80 y 443 a lo que desee)
- Yo crearía un contenedor para su aplicación node.js y lo conectaría a la misma red
Ahora, si configuró nginx correctamente (señale la ruta correcta para los certificados TLS y el proxy a la URL correcta, como http: // my-app: 3210 ), debería tener acceso a su aplicación en https.
Recientemente he implementado https con Vamos a cifrar usando nginx. Estoy enumerando los desafíos a los que me he enfrentado y la forma en que lo he implementado paso a paso aquí.
Desafío
- El sistema de archivos Docker es efímero. Eso significa que después de cada vez que realice una compilación, los certificados que están almacenados o, si se generan dentro del contenedor, desaparecerán. Entonces es muy difícil generar certificados dentro del contenedor.
Pasos para superarlo :
La siguiente guía es independiente del tipo de aplicación que tenga, ya que solo involucra nginx y docker.
- Primero instale nginx en su servidor (no en el contenedor, sino directamente en el servidor). Puede seguir esta guía para generar un certificado para su dominio usando certbot.
Ahora detenga este servidor nginx e inicie la compilación de su aplicación. Instale nginx en su contenedor y abra el puerto 80, 443 en su contenedor docker. (si usa aws open en la instancia de ec2, también como por defecto aws open only port 80)
A continuación, ejecute su contenedor y monte los volúmenes que contienen el archivo de certificado directamente en el contenedor. He respondido una pregunta aquí sobre cómo hacer lo mismo.
Esto habilitará https en tu aplicación. En caso de que no pueda observar, y está usando Chrome, intente borrar el caché de DNS para Chrome
Proceso de renovación automática:
- Encriptaremos los certificados son válidos solo por 3 meses. En los pasos de la guía anterior para configurar la renovación automática también se configura. Pero debe detener y reiniciar su contenedor cada 3 meses al menos para asegurarse de que los certificados montados en su contenedor de la ventana acoplable estén actualizados. (Tendrá que reiniciar el servidor nginx que configuramos en el primer paso para que la renovación se realice sin problemas)
Front-end - NGINX - que escucha el puerto 443, y los proxies para llamar a fin
Extremo posterior - contenedor contenedor