Nginx Proxy Manager o la manera sencilla de manejar acceso a tus servicios docker

Cualquier servidor necesita manejar el tráfico de entrada HTTP y HTTPs a los servicios que manejamos a nivel interno. En este artículo queremos explicar el funcionamiento de un servicio llamado Nginx Proxy Manager para realizar esta gestión.

Log Nginx Proxy Manager

Requisitos

  • Acceso a un DNS real
  • Conexión a nuestro router
  • Creación de una DMZ
  • Servidor con Docker y Docker Compose

¿Qué es Reverse Proxy o Proxy Reverso?

Nginx es un servidor HTTP que permite realizar la redirección a otro servidor web o proxy inverso como se llama técnicamente.

flujo de la petición a través de un reverse proxy

Como puede verse el servidor proxy recibe la petición y la redirige al servidor web que la procesa, esto evidentemente lo hace por configuración, el Nginx debe saber que URL/Petición corresponde a qué servidor.

Este tipo de proxies sirven también para bastionar los servicios que estén detrás de del proxy con una cobertura HTTPS, es decir nos permitirá tener certificados TLS en el servidor proxy para evitar tener que hacer esta configuración en cada uno de los servicios internos que queremos exponer.

Para ello NPM (Nginx Proxy Manager) usa internamente lestencrypt para gestionar y solicitar los certificados TLS que se aplicarán en cada vhost.

Peticiones HTTPs a Nginx Reverse Proxy

Configuración DNS

En nuestro caso el servidor Nginx va a ser colocado en una máquina Docker que arrancará en el puerto 80 y 443 que debe ser accesible mediante una ip pública y un nombre dns real. Para ello nosotros usamos un cliente de DDNS llamado dinaip, tal como explicamos ya en el blog.

Pero con una pequeña salvedad, que debemos crear un registro dns que los angloparlantes llaman wildcard, vamos un * de toda la vida. Lo que queremos conseguir es que podamos asociar diferentes nombres DNS a un mismo servidor.

Imagina que tu servidor está disponible en la IP fija, entonces deberías crear un registro de tipo A que tenga como nombre de host *.nombre_servidor dentro de la configuración de zonas de tu dominio.

Por lo que cualquier URL que sea *.nombre_servidor.dominio.com será válida.

En nuestro caso cualquier nombre por delante de mordor.cursosdedesarrollo.com, por ejemplo wordpress.mordor.cursosdedesarrollo.com

Nosotros recomendamos que se tenga siempre que sea posible una IP fija en la conexión a internet donde esté el servidor, sino tendremos que usar algún mecanismo DDNS

Si podemos hacer uso de terraform para acceder al sistema de DNS de nuestro proveedor en la nube también podríamos hacer uso de esta wildcard

Una vez que hayas cambiado la configuración lo suyo es que compruebes si el dns está configurado correctamente con el comando nslookup

$ nslookup servicio.servidor.dominio.com

Si queremos acceder al servidor con el nombre que queramos a nivel interno, dentro de nuestra red y no desde internet podemos configurar nuestro /etc/hosts o nuestro servidor DNS local para que apunte al servidor con los nombres que queramos. Internamente nostros usamos Pihole. Cómo configurarlo lo veremos en otra entrada más adelante.

Configuración de Nginx Proxy Manager con Docker Compose

Lo primero que debemos confirmar es que no tenemos ningún servicio ocupando los puertos 80 y 443 en la máquina que nos ocupa. Su estás usando portainer esto debería ser bastante sencillo vete a los contenedores muestra todos y verás si esos puertos está ocupados.

Si lo están deberás llevar esos servicios a otros puertos para que podamos levantar adecuadamente Nginx Proxy Manager (npm a partir de ahora)

Con esto ya configurado podemos empezar con la instalación del servicio, para empezar deberemos crear una carpeta para este proyecto por ejemplo

$ mkdir nginx-proxy-manager
$ cd nginx-proxy-manager

Crearemos un fichero docker-compose.yaml:

version: '3.8'
services:
  web:
    image: nginx
    container_name: nginx_docker
    restart: always
    networks:
      - npm
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: npm_server
    restart: unless-stopped
    ports:
      # These ports are in format <host-port>:<container-port>
      - '80:80' # Public HTTP Port
      - '443:443' # Public HTTPS Port
      - '81:81' # Admin Web Port
      # Add any other Stream port you want to expose
      # - '21:21' # FTP

    # Uncomment the next line if you uncomment anything in the section
    environment:
      # Uncomment this if you want to change the location of
      # the SQLite DB file within the container
      DB_SQLITE_FILE: "/data/database.sqlite"

      #Uncomment this if IPv6 is not enabled on your host
      DISABLE_IPV6: 'true'

    networks:
      - npm
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
networks:
  npm:
    name: npm
    driver: bridge

Este fichero tiene bastante chicha así que vamos a explicarlo poco a poco.

Como vemos tenemos tres servicios a arrancar:

  • app: es la aplicación en, veremos que usa los puerto 80, 443 y un uĺtimo puerto que es el gestor de npm, en este caso lo exponemos como el puerto 81, aunque internamente usa el 81
  • db: es la bbdd mariadb que se usa para guardar todas las configuraciones
  • web: en el servidor nginx que procesará las peticiones

Además disponemos de dos volúmenes:

  • data: donde meterá todos los ficheros de configuración
  • lestencrypt: donde meterá todos los certificados generados

Por otro lado las redes:

  • npm: es la red principal de NPM y es la que deberemos compartir con otros servicios si queremos poder llegar a ellos con nuestro proxy. Como se puede ver es de tipo bridge

Como vemos sólo hay unos puertos expuestos que son los de NPM pero ni están expuestos los de Mariadb ni los de nginx.

Ahora sólo deberíamos ejecutar el docker-compose up -d para arrancarlo:

$ docker-compose up -d

Acceso al Nginx Proxy Manager

Si todo va bien deberíamos poder acceder al servicio de configuración en este caso:

http://servidor:81

Primer Login

Una vez entremos al servicio tendremos que loguearnos con los datos por defecto:

Email:    admin@example.com
Password: changeme

Y tendríamos que entrar al Dashboard principal de NPM

dashboard principal de npm

En este caso tenemos el menú de usuario, que ya tengo configurado y el menú de acceso.

Lo primero que debemos hacer es cambiar el usuario en la parte de Users, donde podremos cambiar el email del usario y la contraseña con el menú de tres puntos del final de cada fila de usuario

Red de Docker

Cuando hayamos arrancado NPM con Docker Compose nos debería haber creado una red, que podemos mirarla por ejemplo desde portainer en en el host en el apartado de networks

Detalles de la red NPM

Esta red seguramente la haya llamado nginx-proxy-manager_npm y es la que deberemos configurar en el resto de docker compose para que hagan uso de la misma para poder acceder a los contenedores que arranquemos más tarde.

Nosotros por ejemplo lo hacemos par muchos servicios:

Contenedores asociados a la red NPM

En vuestro caso sólo deberían aparecer los contenedores que acabamos de levantar, pero esta bien que recuerdes que más tarde deberían aparecer todos los contenedores que asocies a esta red.

Portainer

pantalla principal de portainer

Imagina que quieres arrancar portainer pero que pueda ser accesible desde internet pero usando NPM como paraguas para acceder a él con soporte de HTTPS.

Entonces lo primero que debemos hacer es modificar o crear un docker-compose.yml que arranque portainer y que pueda accederse a él desde la red de NPM, veamos un ejemplo:

version: '3.2'

services:
  portainer:
    image: portainer/portainer-ce:2.11.0
    container_name: portainer
    ports:
      - "9000:9000"
      #- "9443:9443"
      - "8000:8000"
    volumes:
      - ./volumes/portainer_data:/data
      - /var/run/docker.sock:/var/run/docker.sock
    restart: always
    networks:
      - npm
      - portainer
networks:
  portainer:
    name: portainer
    driver: bridge
  npm:
    external: true

Como vemos aquí no hay mucha modificación respecto a un portianer normal y corriente salvo por la parte de las redes (networks), tanto el contenedor como el fichero docker compose definen la red nginx-proxy-manager_npm como red externa con el parámetro external a true, a parte de definir una red propia para portainer.

Ahora deberemos levantar portainer de la manera habitual

$ docker-compose up -d

Si todo ha ido bien deberías poder acceder a portainer desde http://servidor:9000/ como siempre y si vamos a portainer en el apartado de redes debería aparecer el contenedor dentro de la red nginx-proxy-manager_npm, como vimos antes.

De esta manera el contenedor de npm debería tener visibilidad al contenedor de portainer

Proxy Hosts

Todo esto para ahora empezar configurando los proxy hosts en NPM.

Para ello dentro del Nginx Proxy Manager vía web entraremos al apartado de Hosts->Proxy Hosts donde daremos de alta los diferentes servicios dependiendu de su URL de entrada.

hosts definidos en el Nginx Proxy Manager

Para ello deberemos configurar dos proxy host, uno para el uso interno y otro para el externo.

Acceso Interno

Empezaremos con el uso interno por lo que pulsamos en el botón Add Proxy Host y nos saldrá un diálogo donde meteremos la configuración.

Detalles del proxy host

Como vemos en domain names indicamos el nombre o ip de la máquina, en nuestro caso portainer que hemos asociado con la IP del servidor.

Elegimos el esquema http, en el forward hostname/ip podemos poner o el nombre del contenedor de portainer o la ip que tenga ese contenedor dentro de la red de NPM, e indicamos el puerto de acceso.

Después podremos salvar y acceder a través de la url http://portainer/

Recuerda que si no tienes el registro dns o la modificación del del fichero hosts esta redirección no te funcionará, repasa el apartado de Configuración de DNS

Acceso externo

Si todo ha ido bien con la configuración del acceso interno, podemos pasar a la configuración del servicio desde fuera, para ello recuerda que debe existir un registro DNS que apunte a tu servidor con el nombre que vayas a usar en el acceso externo.

Como en el caso anterior deberemos crear un nuevo Proxy Host, pero esta vez cambiando el domain name por el nombre que le hayamos dado

Cabe recordar que en el ejemplo de la captura vemos un domain name que se ha configurado un registro A con una wildcard *.mordor apuntando a la IP del servidor

Pero esta vez ya podremos configurar la parte de los certificados SSL/TLS dentro de la pestaña SSL:

Pestaña SSL del Virtual Host

Conviene recordar que el despleguable del certificado SSL seleccionaremos “Request new certificate with letsencrypt” para que cuando lo guarde pida a letsencrypt que genere el certificado SSL para ese dominio. Si tuviéramos otro certificado anterior podríamos seleccionarlo. Pero en nuestro caso pondremos el correo de registro en letsencrypt y aceptaremos los términos de uso.

Como vemos en esa misma pantalla podremos forzar a usar SSL, soportar http2 y el uso de HSTS entre otros…

Guardamos y si todo ha ido bien en poco tiempo habrá solicitado el certificado y asociado a ese nombre de dominio.

Por lo que si entramos a https://nombre_dominio/ deberíamos entrar al servicio indicado pero con el candado reconocido por nuestro navegador sin mucho problema.

Acceso a portainer por HTTPS

Configuración con MariaDB

Si quisiéramos configurar el entorno con una base de datos MariaDB tendríamos que usar el siguiente fichero docker-compose.yaml

version: '3.8'
services:
  web:
    image: nginx
    container_name: nginx_docker
    restart: always
    networks:
      - npm
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: npm_server
    restart: unless-stopped
    ports:
      # These ports are in format <host-port>:<container-port>
      - '80:80' # Public HTTP Port
      - '443:443' # Public HTTPS Port
      - '81:81' # Admin Web Port
      # Add any other Stream port you want to expose
      # - '21:21' # FTP

    # Uncomment the next line if you uncomment anything in the section
    environment:
      # Uncomment this if you want to change the location of
      # the SQLite DB file within the container
      DB_SQLITE_FILE: "/data/database.sqlite"

      #Uncomment this if IPv6 is not enabled on your host
      DISABLE_IPV6: 'true'

    networks:
      - npm
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
networks:
  npm:
    name: npm
    driver: bridge

Conclusiones

Como hemos visto es un proceso algo complejo al principio por el tema DNS y de redes pero una vez establecido pordemos hacer el uso que nosotros queramos sin mucho problema.

Conviene recordar que lo suyo es que hagamos esto con contenedores docker pero podríamos extenderlos a otros servicios no ejecutados desde docker.

Comments

  1. jeansito

    Internamente nostros usamos Pihole. Cómo configurarlo lo veremos en otra entrada más adelante.

    ESO LO DIJISTE Y NO ENCUENTRO ESA INFORMACION
    CREES PODER HACERLA!
    GRACias

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información. ACEPTAR

Aviso de cookies