Volver al blog

Optimizando Let’s encrypt Nginx

Lets-Encrypt-https

Bueno ya hace unos meses publique el articulo de #SysAdmin – Asegurar nuestro server web nginx+let’s encrypt+securityheaders bueno después de unas cuantas configuraciones he optimizado mi código el cual comparto con ustedes. Actualmente he logrado la migración del lugares de algunos socios como: frcuba.cu, geocuba.cu, minjus.cu, gacetaoficial.cu, jovenclub.cu, uci.cu Espero que tu seas el proximo

Perfeccionado el Let's Encrypt

Ubiquemos que tenemos una red donde la única pc que saldría directo a Internet seria el firewall, la misma tiene la función de proxy inverso.

A la hora de sacar el certificado hacemos lo siguiente

Instalación del Let’s Encrypt

apt-get install git
git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
cd /opt/letsencrypt

Creación del certificadoPara la creación del mismo recomiendo no tener ningún servidor web funcionando en ese momento es decir tener libre el 80 y 443.

En este caso crear el certificado como para dominio.cu

/etc/init.d/nginx stop
/opt/letsencrypt/letsencrypt-auto certonly --standalone -d dominio.cu -d mail.dominio.cu -d smtp.dominio.cu -d pop.dominio.cu -d pop3.dominio.cu -d imap.dominio.cu -d www.dominio.cu -d webmail.dominio.cu -d proxy.dominio.cu -d download.dominio.cu -d openvpn.dominio.cu
/etc/init.d/nginx start

si se dan cuenta ahí hago referencia en a los dominios siguientes:

dominio.cu  <-- Dominio principal
www.dominio.cu  <-- Server Web
download.dominio.cu  <-- Web de Programas
mail.dominio.cu webmail.dominio.cu pop.dominio.cu pop3.dominio.cu smtp.dominio.cu imap.dominio.cu <-- Server de Correo
proxy.dominio.cu  <-- Server Proxy
openvpn.dominio.cu  <-- Server Openvpn

Esto es para que a la hora de crear los sitios y esas cosas puedas poner un único certificado en las configuraciones de nginx.

Transmitir el certificado a otro servidor

Creamos la llave sshssh-keygen -t rsa

Creamos la llave ssh para el servidorssh-copy-id -i ~/.ssh/id_rsa.pub [email protected]

Pasamos el Certificado de Let's Encryptrsync -avi --delete /etc/letsencrypt [email protected]:/etc/

Reiniciamos el serviciossh [email protected] /etc/init.d/nginx restart

Todos estos comandos una vez que ya lo hicimos la primera vez lo podemos automatizar en un script para que cuando valla a renovar el certificado lo haga automáticamente:

touch ~/ssl.sh
nano ~/ssl.sh

#!/bin/bash

#
/etc/init.d/nginx stop
/opt/letsencrypt/letsencrypt-auto renew >> /var/log/le-renew.log
#/opt/letsencrypt/letsencrypt-auto certonly --standalone -d dominio.cu -d mail.dominio.cu -d smtp.dominio.cu -d pop.dominio.cu -d pop3.dominio.cu -d imap.dominio.cu -d www.dominio.cu -d webmail.dominio.cu -d proxy.dominio.cu -d download.dominio.cu -d openvpn.dominio.cu
/etc/init.d/nginx start

#Server Web
rsync -avi --delete /etc/letsencrypt [email protected]:/etc/
ssh [email protected] /etc/init.d/nginx restart

Configuración del Nginx

apt-get install nginx
/etc/nginx/nginx.conf

user www-data;
worker_processes 2;
pid /run/nginx.pid;


events {
        worker_connections 768;
}

http {
        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;
        server_tokens off;
        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 /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

        gzip on;
        gzip_http_version 1.1;
        gzip_vary on;
        gzip_comp_level 6;
        gzip_proxied any;
        gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js;
        gzip_buffers 16 8k;
        gzip_disable "MSIE [1-6].(?!.*SV1)";
        gzip_static on;
        ignore_invalid_headers on;
        keepalive_requests 100;
        keepalive_disable none;
        max_ranges 1;
        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/ban_tor_net.conf;
        include /etc/nginx/sites-enabled/*;
}

/etc/nginx/ban_tor_net.confEsto es un listado que sacamos de https://www.dan.me.uk/torlist/ para banear en el Nginx las IP Tor aunque se aconseja que se DROP a través de iptables tambien

deny 100.0.240.30;
deny 100.11.109.99;
deny 100.34.15.226;
deny 100.36.111.217;
deny 100.36.136.10;
deny 100.38.159.190;
deny 101.100.140.94;
deny 101.100.144.174;
deny 101.164.68.36;
deny 101.167.33.247;
deny 101.55.125.10;
deny 101.98.11.146;
deny 103.10.197.50;

Bueno ahora pasamos a configurar los Virtual Hosts, en este primer momento lo hace como Proxy Inverso, pero primero crearemos archivos y configuraciones que usaremos en todos nuestros sitios:

Baneamos todas las expresiones de Exploits en Nginx
nano /etc/nginx/ban_exploits.conf

## Block SQL injections
    set $block_sql_injections 0;
    if ($query_string ~ "union.*select.*\(") {
        set $block_sql_injections 1;
    }
    if ($query_string ~ "union.*all.*select.*") {
        set $block_sql_injections 1;
    }
    if ($query_string ~ "concat.*\(") {
        set $block_sql_injections 1;
    }
    if ($block_sql_injections = 1) {
        return 403;
    }

    ## Block file injections
    set $block_file_injections 0;
    if ($query_string ~ "[a-zA-Z0-9_]=http://") {
        set $block_file_injections 1;
    }
    if ($query_string ~ "[a-zA-Z0-9_]=(\.\.//?)+") {
        set $block_file_injections 1;
    }
    if ($query_string ~ "[a-zA-Z0-9_]=/([a-z0-9_.]//?)+") {
        set $block_file_injections 1;
    }
    if ($block_file_injections = 1) {
        return 403;
    }
   ## Block common exploits
    set $block_common_exploits 0;
    if ($query_string ~ "(|%3E)") {
        set $block_common_exploits 1;
    }
    if ($query_string ~ "GLOBALS(=|\[|\%[0-9A-Z]{0,2})") {
        set $block_common_exploits 1;
    }
    if ($query_string ~ "_REQUEST(=|\[|\%[0-9A-Z]{0,2})") {
        set $block_common_exploits 1;
    }
    if ($query_string ~ "proc/self/environ") {
        set $block_common_exploits 1;
    }
    if ($query_string ~ "mosConfig_[a-zA-Z_]{1,21}(=|\%3D)") {
        set $block_common_exploits 1;
    }
    if ($query_string ~ "base64_(en|de)code\(.*\)") {
        set $block_common_exploits 1;
    }
    if ($block_common_exploits = 1) {
        return 403;
    }

  ## Block spam
    set $block_spam 0;
    if ($query_string ~ "\b(ultram|unicauca|valium|viagra|vicodin|xanax|ypxaieo)\b") {
        set $block_spam 1;
    }
    if ($query_string ~ "\b(erections|hoodia|huronriveracres|impotence|levitra|libido)\b") {
        set $block_spam 1;
    }
    if ($query_string ~ "\b(ambien|blue\spill|cialis|cocaine|ejaculation|erectile)\b") {
        set $block_spam 1;
    }
    if ($query_string ~ "\b(lipitor|phentermin|pro[sz]ac|sandyauer|tramadol|troyhamby)\b") {
        set $block_spam 1;
    }
    if ($block_spam = 1) {
        return 403;
    }

    ## Block user agents
    set $block_user_agents 0;

    # Don't disable wget if you need it to run cron jobs!
    #if ($http_user_agent ~ "Wget") {
    #    set $block_user_agents 1;
    #}

    # Disable Akeeba Remote Control 2.5 and earlier
    if ($http_user_agent ~ "Indy Library") {
        set $block_user_agents 1;
    }
  # Common bandwidth hoggers and hacking tools.
    if ($http_user_agent ~ "libwww-perl") {
        set $block_user_agents 1;
    }
    if ($http_user_agent ~ "GetRight") {
        set $block_user_agents 1;
    }
    if ($http_user_agent ~ "GetWeb!") {
        set $block_user_agents 1;
    }
    if ($http_user_agent ~ "Go!Zilla") {
        set $block_user_agents 1;
    }
    if ($http_user_agent ~ "Download Demon") {
        set $block_user_agents 1;
    }
    if ($http_user_agent ~ "Go-Ahead-Got-It") {
        set $block_user_agents 1;
    }
    if ($http_user_agent ~ "TurnitinBot") {
        set $block_user_agents 1;
    }
    if ($http_user_agent ~ "GrabNet") {
        set $block_user_agents 1;
    }

    if ($block_user_agents = 1) {
        return 403;
    }

Creamos el favicon que sera para todas nuestras webs en caso que no la tenga y lo ponemos en /var/www
nano /etc/nginx/favicon.conf

location ~ /favicon.ico {
        access_log off;
        log_not_found off;
        alias /var/www/favicon.ico;
}

Personalizamos las pagina de error del nginx
nano /etc/nginx/error.conf

if ($request_method !~ ^(GET|HEAD|POST)$ ) {

                 return 444;

}
error_page 502 /50x.html;
location = /50x.html {

      root  /var/www/;

}
error_page 403 /403.html;
        location = /403.html {
           root /var/www;
       }

Creamos la configuración para todos los Dominios que tendran let's encrypt
Generate Strong Diffie-Hellman Group

sudo openssl dhparam -out /etc/ssl/certs/dominio.pem 2048

nano /etc/nginx/ssl.conf

resolver 8.8.8.8 valid=300s;
 resolver_timeout 5s;
 ssl_certificate           /etc/letsencrypt/live/dominio.cu/fullchain.pem;
 ssl_certificate_key       /etc/letsencrypt/live/dominio.cu/privkey.pem;
 ssl_trusted_certificate   /etc/letsencrypt/live/dominio.cu/chain.pem;
        ssl on;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_dhparam /etc/ssl/certs/dominio.pem;
        ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
        ssl_session_timeout 1440m;
        ssl_session_cache builtin:1000 shared:SSL:10m;
        spdy_headers_comp 0;
        add_header Strict-Transport-Security "max-age=15768000;";
        add_header X-Xss-Protection "1; mode=block";
        add_header X-Frame-Options "SAMEORIGIN";
#        add_header X-Content-Type-Options "nosniff";
#        add_header X-Permitted-Cross-Domain-Policies "master-only";
        add_header Cache-Control "public";
        add_header Public-Key-Pins 'pin-sha256="94h51gJiEKZVEwaG4r2YHLAOQGepNRzE/PYytHrBr/A="; pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg="; max-age=2592000';

 location ~ /.well-known {
                allow all;
 }

Eliminamos el VirtualHost por defecto del Nginxrm /etc/nginx/sites-enabled/default
Ahora para una mejor gestion sugiero crear una conf por cada host virtual que vallas a usar
nano /etc/nginx/sites-enabled/maildominio

server {
 listen 80;
 server_name mail.dominio.cu;
 return 301 https://$host$request_uri;
 include /etc/nginx/ban_exploits.conf;
 include /etc/nginx/favicon.conf;
 include /etc/nginx/error.conf;
}

server {
listen 443 ssl;
 server_name mail.dominio.cu;
 include /etc/nginx/ssl.conf;
 include /etc/nginx/error.conf;
 include /etc/nginx/ban_exploits.conf;
 include /etc/nginx/favicon.conf;
 access_log            /var/log/nginx/mail-access.log;
 error_log            /var/log/nginx/mail-error.log;

location / {
  proxy_set_header        Host $host;
  proxy_set_header        X-Real-IP $remote_addr;
  proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header        X-Forwarded-Proto $scheme;
  proxy_pass          https://192.168.0.2;
  proxy_read_timeout  90;
  proxy_redirect      https://192.168.0.2 https://mail.dominio.cu;
}
location ~ /(\.|wp-config.php|readme.html|license.txt|schema.txt|password.txt|passwords.txt|phpmyadmin|admin|download|iredadmin|traza|colas)
{
        deny all;
}

}

En el caso que quieras habilitar el sitio por http y https seria de la siguiente forma

server {
 listen 80;
 server_name mail.dominio.cu;
 include /etc/nginx/ban_exploits.conf;
 include /etc/nginx/favicon.conf;
 include /etc/nginx/error.conf;

location / {
  proxy_set_header        Host $host;
  proxy_set_header        X-Real-IP $remote_addr;
  proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header        X-Forwarded-Proto $scheme;
  proxy_pass          https://192.168.0.2;
  proxy_read_timeout  90;
  proxy_redirect      https://192.168.0.2 https://mail.dominio.cu;
}
}

server {
listen 443 ssl;
 server_name mail.dominio.cu;
 include /etc/nginx/ssl.conf;
 include /etc/nginx/error.conf;
 include /etc/nginx/ban_exploits.conf;
 include /etc/nginx/favicon.conf;
 access_log            /var/log/nginx/mail-access.log;
 error_log            /var/log/nginx/mail-error.log;

location / {
  proxy_set_header        Host $host;
  proxy_set_header        X-Real-IP $remote_addr;
  proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header        X-Forwarded-Proto $scheme;
  proxy_pass          https://192.168.0.2;
  proxy_read_timeout  90;
  proxy_redirect      https://192.168.0.2 https://mail.dominio.cu;
}
location ~ /(\.|wp-config.php|readme.html|license.txt|schema.txt|password.txt|passwords.txt|phpmyadmin|admin|download|iredadmin|traza|colas)
{
        deny all;
}

}

Ya de esta forma debe funcionarte perfectamente el Nginx Proxy Inverso

Configurando el Nginx Web Server

En /etc/nginx/nginx.conf ponemos la misma conf del nginx proxy inverso acá las cosas donde van a variar es en los virtualhost y a la hora de darle soporte al php.

apt-get install nginx php5-fpm mysql-server-5.6 phpmyadmin
nano /etc/nginx/php.conf

location ~ \.php$ {
        try_files $uri /index.php;
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
}

Creamos la plantila para el phpmyadmin
ln -s /usr/share/phpmyadmin/ /var/www/phpmyadminmkdir -p /etc/nginx/templates/nano /etc/nginx/templates/phpmyadmin.tmpl

set $pma_doc_root /var/www;
set $pma_socket unix:/var/run/php5-fpm.sock;

location /phpmyadmin {
   root $pma_doc_root;
    index index.php index.html index.htm;

    location ~ ^/phpmyadmin/(.+\.php)$ {
        try_files $uri =404;
        root $pma_doc_root;

        fastcgi_pass $pma_socket;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $request_filename;
        include /etc/nginx/fastcgi_params;

        fastcgi_param PATH_INFO $fastcgi_script_name;
        fastcgi_buffer_size 128k;
        fastcgi_buffers 256 4k;
        fastcgi_busy_buffers_size 256k;
        fastcgi_temp_file_write_size 256k;
        fastcgi_intercept_errors on;
        }
    location ~* ^/phpmyadmin/(.+\.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt))$ {
    root $pma_doc_root;
    }
}

location /phpMyAdmin {
    rewrite ^/* /phpmyadmin last;
}

Si deseamos darle soporte a un sitio para acceder al phpmyadmin solamente tenemos que poner en http o https del virtualhost lo siguiente
include /etc/nginx/templates/phpmyadmin.tmpl;
Creamos el virtual host para www.dominio.cu con soporte para PHP
mkdir -p /var/www/www.dominio.cu/htmlnano /etc/nginx/sites-enabled/www

server {
 listen 80;
 server_name www.dominio.cu;
 return 301 https://$host$request_uri;
include /etc/nginx/ban_exploits.conf;
include /etc/nginx/favicon.conf;
}

server {
listen 443 ssl;
server_name www.dominio.cu;
include /etc/nginx/ssl.conf;
include /etc/nginx/php.conf;
include /etc/nginx/ban_exploits.conf;
include /etc/nginx/favicon.conf;
index index.php index.html index.htm;
root /var/www/www.dominio.cu/html;
    index index.php index.html index.htm;

location / {
        try_files $uri $uri/ /index.php?$args ;
}
    access_log  /var/log/nginx/www.dominio.cu-access.log;
    error_log   /var/log/nginx/www.dominio.cu-error.log;
}

Creamos el virtual host para download.dominio.cu con soporte para Indexar
nano /etc/nginx/sites-enabled/download

server {
 listen 80;
 server_name download.dominio.cu;
 return 301 https://$host$request_uri;
include /etc/nginx/ban_exploits.conf;
include /etc/nginx/favicon.conf;
}
server {
listen 443 ssl;
server_name download.dominio.cu;
include /etc/nginx/ssl.conf;
include /etc/nginx/ban_exploits.conf;
include /etc/nginx/favicon.conf;
autoindex on;
root /var/www/download.dominio.cu/html;

location / {
}
    access_log  /var/log/nginx/download.dominio.cu-access.log;
    error_log   /var/log/nginx/download.dominio.cu-error.log;
}

Bueno ya con esto tienes un servidor en Nginx con Let's Encrypt con todo lo que se debe, cualquier duda o algo me pueden escribir a armandof (at) armandof.com

Suscríbete · Newsletter

Mantente al día, cada semana

Artículos prácticos sobre Linux, redes, seguridad, virtualización y la nube — escritos por sysadmins cubanos.

  • Guías y tutoriales de administración de sistemas
  • Nuevos artículos sobre servidores, redes y nube
  • Sin spam, cancela cuando quieras

Respetamos tu bandeja de entrada. Cancela con un clic.

Patrocinado por SYSCU

¿Tu infraestructura en la nube te cuesta de más?

SYSCU optimiza tu nube de punta a punta — FinOps, DevOps & CI/CD, migraciones a AWS, seguridad y observabilidad. Equipo 100 % senior que reduce tu factura hasta un 40 % y acelera tus despliegues en semanas.

Nuevo · BetaSYSCU Atlas Insight — la plataforma FinOps que convierte tu gasto AWS en decisiones: visibilidad multi-cuenta, recomendaciones automáticas y alertas de anomalías.

No comments yet