Si eres un ingenios@ de sistemas, probablemente tengas un VPS en el que puedas albergar mas de una aplicación auto alojada. Hoy día puedes encontrar servidores con 4 CPU 8 GB de Ram y 200 GB de disco duro por 6€ al mes IVA incluido en CONTABO . También debes saber que existen dos servidores web muy populares. Apache es un servidor web muy conocido desde los años 90. Nginx fue desarrollado por primera vez en 2004 y rápidamente ganó tracción debido a su ligera huella de memoria y su rápida velocidad de procesamiento para archivos HTML estáticos.
En este tutorial aprenderas como instalar y configurar Apache Nginx y HAProxy en el mismo servidor
Tanto Apache como Nginx admiten el alojamiento virtual, lo que significa que puedes alojar varios sitios o aplicaciones web en el mismo servidor. Sin embargo, te encontrarás con situaciones en las que tienes un servidor web en funcionamiento, pero una aplicación web en particular requiere el uso de un servidor web diferente. El puerto 80 o 443 en la dirección IP pública sólo puede ser utilizado por un proceso. Si Apache está usando el puerto, entonces Nginx no puede usarlo (o enlazarlo). Entonces, ¿qué puedes hacer?
Puedes configurar Nginx como un proxy inverso a Apache, para que Nginx pueda redirigir las peticiones HTTP a Apache. En mi experiencia, he encontrado que esto no es siempre la mejor manera porque alguna vez ha causado problemas extraños que no puedo solucionar. En su lugar, prefiero usar HAProxy como proxy inverso tanto para Nginx como para Apache. HAProxy es un balanceador de carga y servidor proxy de alta disponibilidad, gratuito y de código abierto, para aplicaciones basadas en TCP y HTTP.
Ejecutar Apache Nginx y HAProxy en el mismo servidor
Así es como funciona.
- Nginx escucha en 127.0.0.1:80 y 127.0.0.1:443
- Apache escucha en 127.0.0.2:80 y 127.0.0.2:443
- HAProxy escucha en los puertos 80 y 443 de la dirección IP pública. Redirige las peticiones HTTP del puerto 80 al puerto 443. Cuando una solicitud llega al puerto 443, elegirá entre Nginx y Apache analizando la cabecera SNI (indicación del nombre del servidor) en la solicitud HTTPS.
Paso 1: Detener Nginx y Apache
Para detener Nginx, ejecuta
sudo systemctl stop nginx
Para detener Apache, ejecuta
sudo systemctl stop apache2
Paso 2: Cambiar el puerto de escucha en Nginx
Necesitamos hacer que Nginx escuche en 127.0.0.1:80. Abre tus archivos de configuración de Nginx en /etc/nginx/conf.d/ o /etc/nginx/sites-enabled/ y encuentra la siguiente línea.
listen 80;
Cámbialo por
listen 127.0.0.1:80;
Si https está habilitado en el bloque del servidor Nginx, entonces también encuentre
listen 443 ssl;
Y cambiarla por
listen 127.0.0.1:443 ssl;
El archivo de configuración principal de Nginx /etc/nginx/nginx.conf puede incluir un host virtual por defecto escuchando en el puerto 80 o 443, por lo que es posible que tengas que editar este archivo también.
Reinicie Nginx para que los cambios surtan efecto.
sudo systemctl restart nginx
Paso 3: Cambiar el puerto de escucha en Apache
Tenemos que hacer que Apache escuche en 127.0.0.2:80.
Edita este archivo /etc/apache2/ports.conf
.
sudo nano /etc/apache2/ports.conf
Cambia
Listen 80 Listen 443
Por
Listen 127.0.0.2:80 Listen 127.0.0.2:443
Guarde y cierre el archivo. También vaya al directorio /etc/apache2/sites-enabled/ y edite los archivos del host virtual. Cambia
<VirtualHost *:80>
a
<VirtualHost 127.0.0.2:80>
Si hay host virtual SSL, entonces también cambia
<VirtualHost *:443>
A
<VirtualHost 127.0.0.2:443>
Reinicia Apache.
sudo systemctl restart apache2
Paso 4: Configurar HAProxy
Instale HAProxy en su distribución.
Debian/Ubuntu
sudo apt install haproxy
Editar el archivo de configuración de HAProxy.
sudo nano /etc/haproxy/haproxy.cfg
Añade el siguiente fragmento de código al final del archivo, que hará que HAPorxy escuche en el puerto 80 de la dirección IP pública y redirija las peticiones HTTP del puerto 80 al puerto 443. Sustituye 12.34.56.78 por la dirección IP pública de tu servidor.
frontend http
bind 12.34.56.78:80
mode http
redirect scheme https code 301
Ahora también tenemos que añadir un front end HTTPS.
frontend https
bind 12.34.56.78:443
mode tcp
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
A continuación, define los back-end Nginx y Apache. El parámetro check indica a HAProxy que realice comprobaciones de salud en el back-end enviando un paquete TCP.
backend nginx mode tcp option ssl-hello-chk server nginx 127.0.0.1:443 check backend apache mode tcp option ssl-hello-chk server apache 127.0.0.2:443 check
Puedes definir un back end por defecto con:
default_backend nginx
Utilizaremos la cabecera SNI en la petición HTTPS para redirigir al back end correcto. Por ejemplo, si Nginx está sirviendo al dominio1.com y Apache está sirviendo al dominio2.com, entonces se añaden las siguientes dos líneas. También puede utilizar subdominios, siempre que sean diferentes.
use_backend nginx if { req_ssl_sni -i domain1.com } use_backend apache if { req_ssl_sni -i domain2.com }
Observa que las directivas default_backend y use_backend deben colocarse por encima de las definiciones del backend.
frontend http bind 12.34.56.78:80 mode http redirect scheme https code 301 frontend https bind 12.34.56.78:443 mode tcp tcp-request inspect-delay 5s tcp-request content accept if { req_ssl_hello_type 1 } default_backend nginx use_backend nginx if { req_ssl_sni -i domain1.com } use_backend apache if { req_ssl_sni -i domain2.com } backend nginx mode tcp option ssl-hello-chk server nginx 127.0.0.1:443 check backend apache mode tcp option ssl-hello-chk server apache 127.0.0.2:443 check
En la configuración anterior, utilizamos la función SNI (Server Name Indication) en TLS para diferenciar el tráfico HTTPS.
- Cuando dominio1.com está en el Cliente Hello TLS, HAProxy redirige el tráfico al backend nginx.
- Cuando dominio2.com está en el Cliente Hello TLS, HAProxy redirige el tráfico al backend de apache.
Si el cliente no especifica el nombre del servidor en TLS Cliente Hello, entonces HAproxy utilizará el backend por defecto (nginx).
Guarde y cierre el archivo. A continuación, reinicie HAproxy.
sudo systemctl restart haproxy
Ahora Apache, Nginx y HAProxy pueden funcionar en el mismo servidor.
Cómo reenviar la dirección IP del cliente al backend
Por defecto, Apache y Nginx sólo pueden ver la dirección IP de HAProxy. Para obtener la dirección IP real del cliente, asegúrese de añadir la opción send-proxy-v2 en la definición del back-end de HAProxy como se indica a continuación.
backend nginx mode tcp option ssl-hello-chk server nginx 127.0.0.1:443 send-proxy-v2 check backend apache mode tcp option ssl-hello-chk server apache 127.0.0.2:443 send-proxy-v2 check
También tenemos que añadir alguna configuración en Nginx y Apache para que funcione, de lo contrario su sitio web será inaccesible.
Nginx
Añade proxy_protocol en la directiva de escucha de Nginx como se indica a continuación.
listen 127.0.0.2:443 ssl http2 proxy_protocol;
A continuación, añade las siguientes dos directivas en el bloque Nginx http { } del archivo /etc/nginx/nginx.conf.
set_real_ip_from 127.0.0.1; real_ip_header proxy_protocol;
Guarde y cierre el archivo. A continuación, vuelva a cargar Nginx.
sudo systemctl reload nginx
Nota: Si tu sitio web se ejecuta detrás de Cloudflare CDN, entonces debes cambiar real_ip_header proxy_protocol; a real_ip_header CF-Connecting-IP; para mostrar la dirección IP real del cliente. También puede añadir esta directiva real_ip_header al archivo de bloque del servidor individual para anular la configuración global en el archivo /etc/nginx/nginx.conf.
Por último, reinicia HAProxy.
sudo systemctl restart haproxy
Apache
Si utiliza Apache en Debian/Ubuntu, necesita habilitar el módulo remoteip. (Este módulo está habilitado en CentOS por defecto).
sudo a2enmod remoteip
A continuación, añade las siguientes 3 líneas en tu archivo de configuración del host virtual de Apache.
RemoteIPProxyProtocol On RemoteIPHeader X-Forwarded-For RemoteIPTrustedProxy 127.0.0.1
Así.
<VirtualHost 127.0.0.2:443> ServerName www.example.com RemoteIPProxyProtocol On RemoteIPHeader X-Forwarded-For RemoteIPTrustedProxy 127.0.0.1
Guarda y cierra el archivo. A continuación, también tenemos que cambiar el formato de registro combinado. Edita el archivo de configuración principal de Apache.
sudo nano /etc/apache2/apache2.conf
o
sudo nano /etc/httpd/conf/httpd.conf
Encuentra la siguiente línea.
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
Sustitúyelo por:
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
Guarda y cierra el archivo. A continuación, reinicie Apache para que los cambios surtan efecto.
sudo systemctl restart apache2
o
sudo systemctl restart httpd
Nota que la directiva RemoteIPProxyProtocol On sólo está disponible en Apache 2.4.31 y posteriores. Para comprobar su versión de Apache, ejecute
sudo apache2 -v
o
sudo httpd -v
Ubuntu 18.04 viene con Apache 2.4.29. Si su versión de Apache no cumple con este requisito, entonces se debe eliminar el send-proxy-v2 en la definición del back-end de HAProxy. CentOS 8 viene con Apache 2.4.37.
Por último, reinicia HAProxy.
sudo systemctl restart haproxy
Cómo añadir nuevos hosts virtuales
Si tienes servidores web Apache y Nginx, entonces sólo necesitas dos backends: uno para Apache y otro para Nginx.
Cuando necesites añadir otro dominio (o subdominio), no necesitas añadir un nuevo backend en HAProxy. En su lugar, debes crear un nuevo host virtual para el nuevo dominio (o subdominio) en Apache o Nginx, y luego utilizar la directiva use_backend en HAProxy para redirigir el tráfico al backend correcto.
frontend http bind 12.34.56.78:80 mode http redirect scheme https code 301 frontend https bind 12.34.56.78:443 mode tcp tcp-request inspect-delay 5s tcp-request content accept if { req_ssl_hello_type 1 } default_backend nginx use_backend nginx if { req_ssl_sni -i domain1.com } use_backend nginx if { req_ssl_sni -i sub.domain1.com } use_backend apache if { req_ssl_sni -i domain2.com } use_backend apache if { req_ssl_sni -i sub.domain2.com } backend nginx mode tcp option ssl-hello-chk server nginx 127.0.0.1:443 check backend apache mode tcp option ssl-hello-chk server apache 127.0.0.2:443 check
Recomiendo usar apache y nginx como nombres de backend como en el código anterior, para que sepas qué servidor web aloja cada aplicación.
Obtención del nuevo certificado SSL de Let’s Encrypt
http-01 challenge
En primer lugar, es necesario entender cómo utilizar el autentificador Certbot apache y nginx para obtener el certificado TLS.
El autentificador de Certbot apache y nginx utiliza el desafío http-01, que funciona en el puerto TCP 80. Como HAProxy utiliza el puerto TCP 80, es probable que el desafío http-01 de Certbot falle. Sin embargo, puedes hacer que funcione con el siguiente método.
- Habilite el host SSL en Apache o Nginx con un certificado autofirmado o un certificado para otro dominio.
- A continuación, utilice el autentificador Certbot apache y nginx como de costumbre.
Este método funciona porque el desafío http-01 de Certbot puede ser redirigido desde el puerto TCP 80 al puerto TCP 443, y Certbot acepta certificados no válidos en el puerto 443.
dns-01 challenge
También puedes utilizar el desafío dns-01 de certbot, que funciona creando un registro TXT temporal para tu dominio para certificar que realmente eres el propietario de este dominio, por lo que puede evitar el puerto TCP 80 y el puerto TCP 443. La desventaja de este método es que no todos los registradores de dominios son compatibles.
En primer lugar, necesitas instalar el plugin DNS de Certbot. Hay varios plugins de DNS disponibles en el repositorio de software de Debian y Ubuntu, que puedes encontrar con
apt search python3-certbot-dns
La salida:
python3-certbot-dns-cloudflare/bionic,bionic 0.23.0-1 all Cloudflare DNS plugin for Certbot python3-certbot-dns-digitalocean/bionic,bionic 0.23.0-1 all DigitalOcean DNS plugin for Certbot python3-certbot-dns-dnsimple/bionic,bionic 0.23.0-1 all DNSimple DNS plugin for Certbot python3-certbot-dns-google/bionic,bionic 0.23.0-1 all Google DNS plugin for Certbot python3-certbot-dns-rfc2136/bionic,bionic 0.23.0-1 all RFC 2136 DNS plugin for Certbot python3-certbot-dns-route53/bionic,bionic 0.23.0-1 all Route53 DNS plugin for Certbot
Encuentra los plugins de DNS en el repositorio de EPEL.
sudo dnf install epel-release dnf search certbot-dns
Salida:
python3-certbot-dns-ovh.noarch : OVH DNS Authenticator plugin for Certbot python3-certbot-dns-nsone.noarch : NS1 DNS Authenticator plugin for Certbot python3-certbot-dns-gehirn.noarch : Gehirn Infrastructure Service DNS Authenticator plugin for Certbot python3-certbot-dns-google.noarch : Google Cloud DNS Authenticator plugin for Certbot python3-certbot-dns-linode.noarch : Linode DNS Authenticator plugin for Certbot python3-certbot-dns-luadns.noarch : LuaDNS Authenticator plugin for Certbot python3-certbot-dns-rfc2136.noarch : RFC 2136 DNS Authenticator plugin for Certbot python3-certbot-dns-route53.noarch : Route53 DNS Authenticator plugin for Certbot python3-certbot-dns-cloudxns.noarch : CloudXNS DNS Authenticator plugin for Certbot python3-certbot-dns-dnsimple.noarch : DNSimple DNS Authenticator plugin for Certbot python3-certbot-dns-cloudflare.noarch : Cloudflare DNS Authenticator plugin for Certbot python3-certbot-dns-cloudflare.noarch : Cloudflare DNS Authenticator plugin for Certbot python3-certbot-dns-dnsmadeeasy.noarch : DNS Made Easy DNS Authenticator plugin for Certbot python3-certbot-dns-sakuracloud.noarch : Sakura Cloud DNS Authenticator plugin for Certbot
Instala uno de estos plugins según el servicio de alojamiento de DNS que estés utilizando. Yo estoy usando Cloudflare, así que usaré Cloudflare como ejemplo. Ejecuta el siguiente comando para instalar el paquete python3-certbot-dns-cloudflare en Debian/Ubuntu.
sudo apt install python3-certbot-dns-cloudflare
A continuación, cree un archivo de configuración para Cloudflare.
sudo nano /etc/letsencrypt/cloudflare.ini
Tenemos que añadir la dirección de correo electrónico de nuestra cuenta de Cloudflare y la clave de la API en este archivo.
# Cloudflare API credentials used by Certbot dns_cloudflare_email = you@example.com dns_cloudflare_api_key = 0123456789abcdef0123456789abcdef01234567
Puede encontrar la clave de la API de Cloudflare en https://dash.cloudflare.com/profile. Observa que el plugin de Cloudflare de Certbot no admite actualmente los “tokens de API” de Cloudflare, así que asegúrate de utilizar la “clave de API global” para la autenticación.
Guarda y cierra el archivo. La clave de la API omite la autenticación de dos factores de Cloudflare, por lo que solo debe permitir que el usuario root lea este archivo.
sudo chmod 600 /etc/letsencrypt/cloudflare.ini
Ahora ejecuta certbot.
sudo certbot --agree-tos -a dns-cloudflare -i nginx --redirect --hsts --staple-ocsp --email you@example.com -d www.your-domain.com,your-domain.com
En el comando anterior, especificamos que usaremos dns-cloudflare como autentificador para obtener un nuevo certificado TLS y usaremos el plugin nginx para crear el bloque del servidor HTTPS. Si usas Apache, entonces sustituye nginx por apache.
Este comando le pedirá que introduzca la ruta del archivo .ini, así que introduces /etc/letsencrypt/cloudflare.ini y pulsas la tecla Enter.
Una vez obtenido e instalado el certificado SSL en el archivo de configuración del servidor web. Tienes que hacer que escuche en 127.0.0.1:443 o 127.0.0.2:443. Por ejemplo, si utiliza Nginx, busque
listen 443 ssl
Y cámbialo por
listen 127.0.0.1:443 ssl http2
Además, cambia el puerto 80 por
listen 127.0.0.1:80;
Guarda y cierra el archivo. A continuación, reinicie su servidor web.
Common Errors
Nombre de dominio incorrecto
Si tienes varios hosts virtuales Nginx en un servidor, y cuando escribes un nombre de dominio, el navegador web te lleva a otro nombre de dominio alojado en el mismo servidor, podría ser que uno de tus nombres de archivo de host virtual Nginx no termina con la extensión de nombre de archivo .conf, por lo que Nginx no cargó este host virtual. También podría ser que hayas establecido un registro AAAA para el nombre de dominio, pero no hayas configurado Nginx para servir el nombre de dominio en IPv6.
Lo anterior también se aplica al servidor web Apache.
Violación del protocolo de red
Si estableces la cabecera send-proxy-v2 en HAProxy, pero no activas proxy_protocol en Nginx o Apache, tu sitio web mostrará el siguiente error.
The site has experienced a network protocol violation that cannot be repaired.
Recuerda reiniciar Nginx/Apache después de hacer cambios en el archivo de configuración.
PR_CONNECT_RESET_ERROR
Si habilita proxy_protocol en Nginx o Apache, pero no ha establecido la cabecera send-proxy-v2 en HAProxy, su sitio web mostrará este error.
secure connection failed. PR_CONNECT_RESET_ERROR
Y encontrarás el siguiente mensaje de error en el registro de errores de Nginx o Apache.
broken header while reading PROXY protocol
Recuerda reiniciar Nginx/Apache después de hacer cambios en el archivo de configuración.
También hay que tener en cuenta que si se habilita el protocolo proxy en un archivo de host virtual de Apache/Nginx, entonces también se habilita implícitamente para todos los demás hosts virtuales. Y no he encontrado una manera de deshabilitar proxy_protocol para un host virtual individual. Dado que está ejecutando Apache y Nginx en el mismo servidor, una solución es que sólo habilite el protocolo proxy en Nginx y no lo habilite en Apache. Si un host virtual no necesita obtener la dirección IP real del cliente, configúrelo para usar Apache.
Actualización: Se me ha ocurrido que puedes configurar un nuevo host virtual Apache/Nginx en otra dirección loopback como 127.0.0.4. De esta forma, el nuevo host virtual no habilitará el protocolo proxy.
PR_END_OF_FILE_ERROR
If you enabled proxy protocol in Apache/Nginx, but you try to access the virtual host directly (bypassing HAProxy), then you will see the following error.
PR_END_OF_FILE_ERROR
Espero que este tutorial te ayude a sacar mejor partido de tu infraestructura VPS y poder disponer de varias aplicaciones en un mismo servidor, a veces por conveniencia y otras veces por necesidad.
¡Que tengas un Ingenioso día!
¡que tengas un ingenioso día!
Contrata tu plan Ingenios@ de Sistemas por 5€ al mes y responderé a todas tus preguntas sobre tecnología en el menor tiempo posible. Pasa a formar parte de la comunidad Ingenios@s de Sistemas y disfruta de contenido exclusivo y aprende sobre sistemas Open Source, Blockchain, SmarContract, Web3, Inteligencia Artificial y Reaidad Virtual, súbete al tren de la Revolución 4.0
Si quieres estar al día y no perderte nada Suscribete al Podcast Ingenios@s de Sistemas, un episodio diario que te mantendrá informado y formado en esta vertiginosa carrera.