Tras 15 años de amor y odio, trabajando con cPanel, habiendo desechado Plesk, y soportado Virtualmin, Webmin, VestaCp, DirectAdmin, y alguno que otro que se me queda en la memoria (Ensim), la final he optado por trabajar más con los clientes profesionales, sin panel de control ningún tipo, usando LEMP (Linux, Nginx, MySQL, Postfix). Pero sobre todo cPanel? No, gracias. La mejor alternativa a cPanel es no usar ningún panel de hosting
Actualizado: 03/12/2021
Contenidos
Escenario
Durante muchos años, he visto como las reacciones de cPanel y su equipo ante los avances de la industria han sido muy lentos, y muy ineficaces.
En su contra,
- mala implementación de los cambios en seguridad SSL,
- pésimo soporte antispam,
- sistema de documentación penoso,
- no incorporación de Nginx como servidor web,
- sistema de backups deficitario
- y un largo etc.
A su favor decir, que es la herramienta perfecta para no dedicar mucho tiempo, sobre todo si tenemos un buen conocimiento de ella y creamos nuestras propias herramientas auxiliares.
Pero, hasta ahí. La compra de cPanel por parte de un fondo de inversón (aka fondo buitre), Oakley Capital, cuya primera medida fue la de comprar a su directo competidor, Plesk, ademas de adquirir el desastroso WHMCS, y subir los precios basándose en el cambio de potencia de las máquinas desde los últimos 20 años, con una desmesura inconcebible para los tiempos que vivimos, que me ha llevado a comenzar a guiar a los usuarios más avanzados a otra dirección, más eficaz y económica.
Como ni siquiera los paneles OpenSource como VestaCp o ISPConfig me convencen, pues al final es más de lo mismo, ya que suponen la esclavitud de conocer y soportar a sus desarrolladores y su elenco de seguidores, para ofrecer una estabilidad que no es tal, y un ahorro de tiempo que tampoco lo es, lleno de dificultades intrínsecas a las peculiaridades de los distintos equipos de desarrollo, sus manías y sus preferencias. Así que creo que es mejor, aprender la base, que aprender lo que se sustenta sobre la base y que encima te quieren cobrar a precio de oro.
LEMP, Linux, Nginx, MySQL y Postfix
Para el artículo voy a usar Ubuntu 18.04 LTS, por muchas razones, aunque en próximos artículos trataré de describir lo mismo, pero usando Alpine Linux, que ya uso en mis deploys para el testing en Docker Gitlab. Aún no lo describo, pues es mucho más técnico y requiere bastante más especialización de usar Ubuntu LTS.
Instalación Nginx
Esta no es una guía para copy & paste. Sin entender muchos de los mecanismos de los que aquí se hablan, y sin comprenderlos, es posible que en un futuro tras una actualización, tu sistema quede roto, y no seas capaz de levantar Nginx. Es altamente recomendable, leer la documentación original de que cada apartado y tener en cuenta que las actualizaciones no son del estilo, sistema de paquetes tipo yum, apt-get, o similares. Abdelkarim Mateos
Es altamente recomendable, usar una instalación limpia de Ubuntu 18.04 LTS, para evitar posibles diferencias con aquellos ficheros de configuración que ya estuvieran instalado y/o manipulados.
El uso de versiones LTS es altamente recomendable en entornos de desarrollo. Ir a la última, es el mejor camino para perder el tiempo y procastinar con nuestros trabajoAbdelkarim Mateos
Nginx oficial
En lugar de instalar los paquetes de Ubuntu, para Nginx instalaremos el repositorio oficial de nginx, que esta más actualizado y nos permite usar su estructura para crear (compilar módulos) dinámicos.
$ sudo apt install curl gnupg2 ca-certificates lsb-release $ echo "deb http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \ | sudo tee /etc/apt/sources.list.d/nginx.list $ echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \ | sudo tee /etc/apt/preferences.d/99nginx $ curl -o /tmp/nginx_signing.key https://nginx.org/keys/nginx_signing.key $ gpg --dry-run --quiet --import --import-options show-only /tmp/nginx_signing.key $ sudo mv /tmp/nginx_signing.key /etc/apt/trusted.gpg.d/nginx_signing.asc $ sudo apt update $ sudo apt install nginx
Antiguamente usaba los repositorios PPA de Ondrej, porque tiene varios módulos compilados y es bastante bueno, pero su compilación no me permite crear tus propios módulos en modo shared, o la menos a la escritura de este artículo no supe como hacerloAbdelkarim Mateos
Nginx con PPA
No instalar con PPA, el texto de abajo, es sólo para conocerlo, porque habrá lectores que no quieran instalar Mod Security o porque se sientan más cómodos usando este método, suficiente para sus necesidades.
lsb_release -cs debe ir entre « comillas invertidas. Un error en el plugin de code no las muestra
$ sudo apt install -y build-essential git tree ntp ntpdate curl gnupg2 ca-certificates lsb-release $ echo "deb http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" | sudo tee /etc/apt/sources.list.d/nginx.list $ curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo apt-key add - $ sudo apt-key fingerprint ABF5BD827BD9BF62 pub rsa2048 2011-08-19 [SC] [expires: 2024-06-14] 573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62 uid [ unknown] nginx signing key <signing-key@nginx.com> $ sudo apt update && sudo apt install nginx
Nginx + Mod Security 3
Es una versión actualizada y optimizada de la versión oficial Compiling and Installing ModSecurity for NGINX Open Source
En mi caso prefiero tener un usuario, el mio, en el que suele colocar un directorio llamado software, donde realizo las operaciones tanto de compilación como de actualización de ciertos paquetes, como la pesadilla de WHMCS.
$ sudo apt-get install -y apt-utils autoconf automake build-essential git libcurl4-openssl-dev libgeoip-dev liblmdb-dev libpcre++-dev libtool libxml2-dev libyajl-dev pkgconf wget zlib1g-dev $ mkdir soft && cd $_ $ git clone --depth 1 -b v3/master --single-branch https://github.com/SpiderLabs/ModSecurity $ cd ModSecurity $ git submodule init $ git submodule update $ ./build.sh # No hacer caso del mensaje fatal: No names found, cannot describe anything. $ ./configure $ make $ sudo make install $ cd .. $ git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git $ nginx -v nginx version: nginx/1.19.9 $ version=1.19.9 $ wget http://nginx.org/download/nginx-$version.tar.gz $ tar xvfz nginx-$version.tar.gz $ cd nginx-$version/ $ ./configure --with-compat --add-dynamic-module=../ModSecurity-nginx $ make modules $ sudo cp objs/ngx_http_modsecurity_module.so /usr/lib/nginx/modules/ $ sudo echo 'load_module modules/ngx_http_modsecurity_module.so;' > /usr/share/nginx/modules-available/mod-security.conf $ sudo ln -s /usr/share/nginx/modules-available/mod-security.conf /etc/nginx/modules-enabled/50-mod-security.conf $ sudo nano /etc/nginx/nginx.conf # antes de events, http o similar para activar los modulos dinamicos include /etc/nginx/modules-enabled/*.conf; $ sudo mkdir /etc/nginx/modsec $ sudo wget -P /etc/nginx/modsec/ https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/modsecurity.conf-recommended $ sudo mv /etc/nginx/modsec/modsecurity.conf-recommended /etc/nginx/modsec/modsecurity.conf $ sudo cp ../ModSecurity/unicode.mapping /etc/nginx/modsec/ $ sudo sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/nginx/modsec/modsecurity.conf $ sudo tee -a /etc/nginx/modsec/main.conf > /dev/null <<EOT # From https://github.com/SpiderLabs/ModSecurity/blob/master/ # modsecurity.conf-recommended # # Edit to set SecRuleEngine On Include "/etc/nginx/modsec/modsecurity.conf" # Basic test rule SecRule ARGS:testparam "@contains test" "id:1234,deny,status:403" EOT # Comprobar $ sudo nginx -t
Durante la configuración de ModSecurity sale una serie de lineas con el error fatal: No names found, cannot describe anything. Es normal. Léase la documentación de los enlaces relativos a la compilación de ModSecurity
Editamos o creamos el fichero /etc/nginx/conf.d/default.conf en el cual haremos la carga de Mod Security.
$ sudo tee -a /etc/nginx/conf.d/default.conf > /dev/null <<EOT
server {
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
}
EOT
El uso de ficheros para atomizar las configuraciones, la carga de módulos, es una muy buena práctica, ya que así es más fácil modificar la configuración en ciertos caso como una actualización que rompa el funcionamiento de nginx, y que localizamos en uno u otro módulo.
Nginx + Módulo rDNS
Para algunos sitios uso el módulo rDNS que me permite bloquear el acceso por nombre de host dinámicos o estáticos. Más información de como compilarlo en mi wiki, Limitación de acceso por host dinámico – Apache y Nginx
Instalar OWASP Rules
Una vez más recomendar la lectura de la documentación final por si hay cambios y adecuarla al paso del tiempo, como con esté artículo. Ver la opción de más abajo más actualizada y eficaz.
$ wget https://github.com/SpiderLabs/owasp-modsecurity-crs/archive/v3.0.2.tar.gz $ tar -xzvf v3.0.2.tar.gz $ sudo mv owasp-modsecurity-crs-3.0.2 /usr/local/ $ sudo ln -s /usr/local/owasp-modsecurity-crs-3.0.2 /usr/local/owasp-modsecurity $ sudo cp /usr/local/owasp-modsecurity/crs-setup.conf.example /usr/local/owasp-modsecurity/crs-setup.conf
Alternativa actualizada basada en Git
Yo soy más amigo de revisar el Git por aquello de usar la versión más actualizada en producción (nunca uso las versiones de desarrollo)
$ sudo cd /usr/local$sudogit clone https://github.com/SpiderLabs/owasp-modsecurity-crs.git $ cd owasp-modsecurity-crs $ git branch * v3.3/dev $ git branch -r origin/HEAD -> origin/v3.3/dev origin/gh-pages origin/master origin/v2.2/master origin/v2.2/owasp-honeypots origin/v3.0/dev origin/v3.0/master origin/v3.1/dev origin/v3.2/dev origin/v3.2/master # Esta es la apropiada origin/v3.3/devq $ git checkout -b v3.2 origin/v3.2/master Branch 'v3.2' set up to track remote branch 'v3.2/master' from 'origin'. Switched to a new branch 'v3.2' $ git status # Verificación On branch v3.2 Your branch is up to date with 'origin/v3.2/master'. nothing to commit, working tree clean $sudocp crs-setup.conf.example crs-setup.conf $ cp rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf $ cp rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf $ cd .. $ sudo cp -ar owasp-modsecurity-crs/ /usr/local/
Inicializar el fichero de configuración de Mod Security
Editar /etc/nginx/modsec/main.conf
sudo tee -a /etc/nginx/modsec/main.conf > /dev/null <<EOT # # From https://github.com/SpiderLabs/ModSecurity/blob/master/ # modsecurity.conf-recommended # # Edit to set SecRuleEngine On Include "/etc/nginx/modsec/modsecurity.conf" # Basic test rule # SecRule ARGS:testparam "@contains test" "id:1234,deny,status:403" # OWASP CRS v3 rules Include /usr/local/owasp-modsecurity-crs/crs-setup.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-901-INITIALIZATION.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-905-COMMON-EXCEPTIONS.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-910-IP-REPUTATION.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-911-METHOD-ENFORCEMENT.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-912-DOS-PROTECTION.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-913-SCANNER-DETECTION.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-921-PROTOCOL-ATTACK.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf Include /usr/local/owasp-modsecurity-crs/rules/RESPONSE-950-DATA-LEAKAGES.conf Include /usr/local/owasp-modsecurity-crs/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf Include /usr/local/owasp-modsecurity-crs/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf Include /usr/local/owasp-modsecurity-crs/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf Include /usr/local/owasp-modsecurity-crs/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf Include /usr/local/owasp-modsecurity-crs/rules/RESPONSE-959-BLOCKING-EVALUATION.conf Include /usr/local/owasp-modsecurity-crs/rules/RESPONSE-980-CORRELATION.conf Include /usr/local/owasp-modsecurity-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf # Mias Include /usr/local/owasp-modsecurity-crs/rules/REQUEST-903.9002-WORDPRESS-EXCLUSION-RULES.conf # Whitelisting Include /etc/nginx/modsec/whitelist/*.conf EOT
Comprobar y hacer un reload
$ sudo nginx -s reload nginx: [emerg] "modsecurity_rules_file" directive Rule id: 200000 is duplicated
in /etc/nginx/conf.d/default.conf:3
Ahora es momento de hacer algún testing como recomienda el documento de referencia.
Las pruebas, son algo necesario también de la administración de sistemas. No sólo ocupan el ámbito de los programadores. Los administradores de sistemas, también estamos obligados a verificar nuestro trabajo
Mod Security y las listas blancas
No es el alcance de este artículo describir como adecuar nuestras listas blancas, globales o por virtualhost, pero al menos voy a describir el proceso para la lista blanca global, ya que muchas reglas de OWASP son tremendas, y prácticamente hay decenas de ellas que son incompatibles con las enrevesadas formas de programar de algunos módulos, temas de Worpdress y de otros programas, además de la propia dureza de las reglas.
ATENCION Añadir rules a la lista blanca sin un minimo análisis, es el camino más rápido para convertir tu flamante sistema de seguridad, en un sistema de alarma que no funciona y te da la sensación de estar seguro.
$ sudo mkdir -p /etc/nginx/modsec/whitelist $ sudo nano /etc/nginx/modsec/whitelist/global.conf SecRuleRemoveById 920170 SecRuleRemoveById 921130 SecRuleRemoveById 941100 SecRuleRemoveById 941140 SecRuleRemoveById 941160 SecRuleRemoveById 949110
Securización Nginx
Generamos un certificado DHParam (duro)
También conocido como Forward Secrecy & Diffie Hellman Ephemeral Parameters
$ sudo openssl dhparam -out /etc/nginx/dh4096.pem 4096
Ciphers seguros
Tenemos dos opciones una para el 99,99% que es muy dura ya que muchos SO y navegadores se quedaran fuera de la posibilidade navegar por nuestro servidor (se puede ver la lista haciendo un check SSL en SSL Labs Test) u otra que es suficiente para obtener un grade A+, y no tener excesivos problemas con clientes antiguos.
Ciphers 99,99% seguros
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
Ciphers grado A+
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !MEDIUM";
Edición completa /etc/nginx/nginx.conf para seguridad adicional
Añadimos estas y otras que creemos de correcta aplicación. Si quieres más lectura al respecto tienes un buen artículo en Strong SSL Security on nginx y la configuración usada en nuestro gitlab.
# Nginx hardening ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !MEDIUM"; #ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384; ssl_dhparam dh4096.pem; ssl_ecdh_curve secp384r1; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; resolver 1.1.1.1 8.8.8.8 valid=300s; resolver_timeout 5s; add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"; add_header X-Frame-Options SAMEORIGIN; add_header X-Content-Type-Options nosniff;
Entorno multiusuario o virtual hosts en nginx
Bien, esta es una decisión de como plantear las cosas de acuerdo a tus necesidades. Hay decenas si no centenas de posibles vías. Lo suyo es que uno adecue a sus necesidades el como hacer esto, y que sea suyo.
Base de datos MySQL o MariaDB
Bueno, aquí llegamos a uno de esos puntos en los que el ser humano se enzarza en mil cuestiones, discusiones, con evangelistas de uno y otro lado. Yo soy más práctico, y uso MySQL 8 allí donde quiero por sus características y lo mismo con MariaDB con las suyas. Cada herramienta tiene sus pros y sus contras.
Lo que si hay que tener en cuenta es que los backups de ambos no son iguales en muchos casos, y ni siquiera entre sus propias versiones lo son, así que ese mundo idílico de los forks, de la confianza en los backups, y en el de no probar nuestros sistemas de backups, mejor olvidarlo. Hay que saber que y que no puedo hacer con cada sistema y con cada versión.[/]
MariadB
Nunca instalo el paquete de mi distribución ya sea esta, Ubuntu, Centos, Debian… Prefiero acudir a su página de instalación e instalar su repositorio original.
$ sudo apt-get install software-properties-common $ sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc' $ sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el] http://mirror.terrahost.no/mariadb/repo/10.4/ubuntu bionic main' $ sudo apt update $ sudo apt install mariadb-server
MySQL
Con MySQL lo mismo usando sus repositorios oficiales. Primero hay que ver allí la versión actual, para evitar el descargar y subir, simplemente nos quedaremos con el dato de la version, al momento de escribir el artículo mysql-apt-config_0.8.15-1_all.deb
$ cd soft/ $ sudo apt-get install software-properties-common $ wget https://dev.mysql.com/get/mysql-apt-config_0.8.15-1_all.deb $ sudo dpkg -i mysql-apt-config_0.8.15-1_all.deb $ sudo apt-get update $ sudo apt-get install mysql-server
Merece la pena dedicar un poco de atención a la lectura del nuevo modelo de autentificación en MySQL 8. Socket vs Passwords, y tambien en el caso de las passwords por la posibilidad de usar un nuevo sistema más complejo. En la red se encunetran barbaridades sobre este tema, ya que como muchos copy & paste, no saben como trabajar con esto, prefieren ir directamente a seguir el modelo legacy, es decir el antiguo, con compatibilidad MySQL 5.7 y pasar por alto la oprtunidad de avanzar en la seguridad y el aprendizaje.
PHP, PHP-FPM con multiples versiones y usuarios
Instalar PHP con repositorio ppa
Excepto si se trata de un sólo usuario, un solo sitio, deberíamos plantear el sistema para usar PHP-FPM , con la posibilidad de usar multiples versiones de PHP.
Vamos a instalar PHP con FPM en múltiples versiones usando los repositorios PPA de Ondrej.
$ sudo add-apt-repository ppa:ondrej/php $ sudo apt update # Version 7.2 $ sudo apt install php7.2 php7.2-gd php-mysql php7.2-curl php7.2-zip php7.2-ldap php7.2-mbstring php-imagick php7.2-intl php7.2-xml php7.2-fpm unzip wget curl -y # Version 7.4 requiere instalarlo en otro orden o se instalara apache $ sudo apt install php7.4-fpm -y
$ sudo apt install php7.4-gd php-mysql php7.4-curl php7.4-zip php7.4-ldap php7.4-mbstring php-imagick php7.4-intl php7.4-xml php7.4-bcmath php7.4-common php7.4-soap php7.4-xsl php7.4-zip unzip wget curl -y
En esta instalación he usado php7.2 pero puedes instalar esta u otras versiones como la 7.3 ó la 7.4 al mismo tiempo por si tienes distintas webs o desarrollos con distintas versiones
php.ini
Los ficheros ini de la instalación vienen sin configuración de la zona horaria, lo cual suele producir algunos quebraderos de cabeza si los obviamos. Así que editaremos el fichero php.ini de a cada versión instalada
$ echo 'date.timezone = Europe/Madrid' | sudo tee -a /etc/php/7.2/fpm/php.ini $ echo 'date.timezone = Europe/Madrid' | sudo tee -a /etc/php/7.3/fpm/php.ini
Entorno multiusuario o virtual hosts en nginx
En mi caso opto por que cada usuario tenga dos directorios. Uno para ubicar sus sitio web, y otro para sus configuraciones.
En la configuración primaria de un sitio, no instalo más que la version http, ya que después se encargará Cerbot de hacer la redirección a HTTPS.
$ mkdir -p ~/web/mysite.tld # Donde pondré el software mi sitio $ mkdir -p ~/conf/web # Donde pondré la configuración nginx mysite.tld.conf $ sudo nano /etc/nginx/conf.d/sites.conf # Donde pondré los enlaces a las configuraciones
Esta configuración requiere que por ejemplo al final del fichero de configuración de nginx (/etc/nginx/nginx.conf) este la linea que llama a la lectura de los ficheros *.conf y en ella ubicar un fichero que contenga la llamadas a todos los ficheros de configuración de sitios virtuales.
Esta es una opinión personal, que puede ser tomada en consideración o hacerse de la manera habitual, que sería que la la con¡figuración de los sitios virtuales estuviera en sites-available, y en sites-enabled el enlace simbólico a su correspondiente fichero.
include /etc/nginx/conf.d/sites.conf;
El fichero sites.conf añadiríamos la linea a cada uno de los sitios virtuales.
include /home/MYUSER/conf/web/mysitio.tld.conf; include /home/MYUSER/conf/web/mysitio2.tld.conf; include /home/MYUSER2/conf/web/mysitio3.tld.conf ...
Dejo a disposición en mi Gitlab los dos ficheros de base que uso para este artículo, misitio.conf y nginx.conf
server { listen 80; server_name DOMAIN.TLD; root /home/USER/web/DOMAIN.TLD; index index.php index.html index.htm; access_log /var/log/nginx/domains/DOMAIN.TLD.log combined; access_log /var/log/nginx/domains/DOMAIN.TLD.bytes bytes; error_log /var/log/nginx/domains/DOMAIN.TLD.error.log error; expires $expires; include /etc/nginx/modsec/active.conf; location = /favicon.ico { log_not_found off; access_log off; } location = /robots.txt { allow all; log_not_found off; access_log off; } location / { try_files $uri $uri/ /index.php; if (!-e $request_filename) { rewrite ^(.+)$ /index.php?q=$1 last; } location ~* ^.+\.(jpeg|jpg|png|gif|bmp|ico|svg|css|js)$ { expires max; } location ~* \.php$ { fastcgi_split_path_info ^(.+?\.php)(/.*)$; if (!-f $document_root$fastcgi_script_name) {return 404;} fastcgi_pass unix:/run/php/MYSITE-fpm.sock; fastcgi_index index.php; include /etc/nginx/fastcgi_params; fastcgi_param SCRIPT_FILENAME; $document_root$fastcgi_script_name; } } location ~* "/\.(htaccess|htpasswd)$" { deny all; return 404; } }
La configuración de arriba muestra tres palabras en MAYÚSCULAS que deberemos cambiar por nuestros valores
- DOMAIN.TLD correspondiente al nombre de nuestro dominio
- USER que correspodne al nombre de usuario donde estarán ubicados los ficheros del virtualhost
- MYSITE que es el nombre que usaremos para el socket de php-fpm que usará este sitio virtual
Para trabajar con cualquier demonio o programa que me permita el uso de sockets, siempre preferire el uso de sockets frente al uso de puertos. Es muy habitual en los tutoriales encontrar la configuracion de php-fpm, basada en el uso de puertos. Más información PostgreSQL UNIX domain sockets vs TCP sockets
PHP-FPM por sitio virtual
Las bondades de usar una configuración por sitio virtual, son más de las queuno puede imaginar, y no es el alcance del artículo explicarlas. En nuestro caso es el sistema que usaremos, es decir, un fichero de configuración por sitio virtual
Creamos el fichero de configuración dentro del directorio correspondiente a la version de php a utilizar:
/etc/php/NUMBER.VERSION/fpm/pool.d/DOMAINNAME.conf
[DOMAINNAME]
;prefix = /path/to/pools/$pool
user = USER
group = GROUP
listen = /run/php/DOMAINNAME_com_MAYORNUMBERPHPVERSION_MINORNUMBERPHPVERSION-fpm.sock
listen.allowed_clients = 127.0.0.1
listen.owner = USER
listen.group = www-data # User nginx
;listen.mode = 0660
; Recomended initial setup
pm = ondemand
pm.max_children = 10
pm.max_requests = 500
pm.process_idle_timeout = 10s
pm.status_path = /status
php_admin_value[upload_tmp_dir] = /home/USER/tmp
php_admin_value[session.save_path] = /home/USER/tmp
php_admin_value[session.gc_maxlifetime] = 7200
php_admin_value[zlib.output_compression] = 0
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /home/USER/tmp
env[TMPDIR] = /home/USER/tmp
env[TEMP] = /home/USER/tmp
Lista de cambios a realizar
- DOMAINNAME por el nombre del dominio. No hace falta poner el FDQN sino la parte nominal. En la primera etiqueta sirve para crear el proceso FPM con esa etiqueta, lo que nos permite conocer en el sistema el proceso de cada sitio
- MAYORNUMBERPHPVERSION_MINORNUMBERPHPVERSION = 7_4 ó 8_0
- USER y GROUP para el user y su grupo en el que estamos ubicando los ficheros. Es importante ya que de lo contrario tendremos problemas con los permisos de directorios y ficheros. Muchas veces leeremos en internet, que debemos poner permisos 777, etc… nada más lejos de la realidad y de la seguridad de tu sistema.
Los valores ofrecidos aquí, son para una sitio con carga media-alta, y 12GB de RAM. Este artículo no pretenden ser un artículo completo, ni mucho menos una especialización en tunning o ajuste de rendimientos, para lo cual hay mucha literatura y buena, que te llevará a aprender a tunear tu servidor nginx con php, de forma más adecuada a tus necesidades.
Comprobación y reinicio
Antes de proseguir deberiamos comprobar que nuestro sitio esta operativo y funciona
Podemos crear un fichero de informacion de php (luego deberiamos borrarlo por seguridad) en nuestro home
phpinfo(); // Muestra toda la informacion, pero debería eliminarse por seguridad
Recordar que las palabras en MAYUSCULAS deben cambairse por el valor apropiado (7.2 por ejemplo)
$ sudo systemctl restart phpNUMBER.VERSION-fpm.service $ sudo systemctl status phpNUMBER.VERSION-fpm.service ● php5.6-fpm.service - The PHP 5.6 FastCGI Process Manager Loaded: loaded (/lib/systemd/system/php5.6-fpm.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2020-03-31 16:19:25 UTC; 17h ago Docs: man:php-fpm5.6(8) Process: 9806 ExecStopPost=/usr/lib/php/php-fpm-socket-helper remove /run/php/php-fpm.sock /etc/php/5.6/fpm/pool.d/www.conf 56 (code=exited, status=0/SUCCESS) Process: 9820 ExecStartPost=/usr/lib/php/php-fpm-socket-helper install /run/php/php-fpm.sock /etc/php/5.6/fpm/pool.d/www.conf 56 (code=exited, status=0/SUCCESS) Main PID: 9807 (php-fpm5.6) Status: "Processes active: 0, idle: 0, Requests: 1, slow: 0, Traffic: 0req/sec" Tasks: 1 (limit: 4915) CGroup: /system.slice/php5.6-fpm.service └─9807 php-fpm: master process (/etc/php/5.6/fpm/php-fpm.conf) Mar 31 16:19:25 hq systemd[1]: Starting The PHP 5.6 FastCGI Process Manager... Mar 31 16:19:25 hq systemd[1]: Started The PHP 5.6 FastCGI Process Manager. $ sudo nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful $ sudo systemctl restart nginx
HTTPS con Cerbot
Nuestro consejo es que acudas directamente a la página oficial de Cerbot, para obtener tu proceso de instalación.
Si que te aconsejamos a que dejes el proceso de forma global a Cerbot, incluido la ghestion de crearte las redirecciones a https de forma automatica con la opcion 2 redirect HTTP traffic to HTTPS, removing HTTP access
$ sudo apt-get update $ sudo apt-get install software-properties-common $ sudo add-apt-repository universe $ sudo add-apt-repository ppa:certbot/certbot $ sudo apt-get update $ sudo apt-get install certbot python-certbot-nginx $ sudo certbot --nginx Saving debug log to /var/log/letsencrypt/letsencrypt.log Plugins selected: Authenticator nginx, Installer nginx Which names would you like to activate HTTPS for? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1: hostname.full_qulified_name.tld 2: mysite.tld - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Select the appropriate numbers separated by commas and/or spaces, or leave input blank to select all options shown (Enter 'c' to cancel): Obtaining a new certificate Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/hostname.full_qulified_name.tld.conf Deploying Certificate to VirtualHost /home/MYUSER/conf/web/mysite.tld.conf Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2 Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/hostname.full_qulified_name.tld.conf Redirecting all traffic on port 80 to ssl in /home/MYUSER/conf/web/mysite.tld.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Congratulations! You have successfully enabled https://hostname.full_qulified_name.tld.conf Congratulations! You have successfully enabled https://mysite.tld.conf You should test your configuration at: https://www.ssllabs.com/ssltest/analyze.html?d=hostname.full_qulified_name.tld https://www.ssllabs.com/ssltest/analyze.html?d=mysite.tld - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/hostname.full_qulified_name.tld/fullchain.pem /etc/letsencrypt/live/mysite.tld/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/hostname.full_qulified_name.tld/privkey.pem /etc/letsencrypt/live/mysite.tld/privkey.pem Your cert will expire on 2020-06-10. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le
Correo con Postfix, Postfixadmin, Dovecot con SASL
Postfix servidor de correo
Postfix en un servidor de correo, que en combinación con Dovecot, es una de las mejores opciones para esta tarea, y que nos premitira una fácil gestión de los dominios y usuarios gracias a PostfixAdmin
Esta es una instalación para Postfix usando MySQL + Dovecot + SASL
$ sudo service sendmail stop; sudo update-rc.d -f sendmail remove $ sudo apt update $ sudo DEBIAN_PRIORITY=low apt install postfix ... $ sudo postconf -e 'home_mailbox= Maildir/' $ sudo postconf -e 'virtual_alias_maps= hash:/etc/postfix/virtual' $ sudo apt-get -y install postfix postfix-mysql postfix-doc mariadb-client openssl getmail4 binutils dovecot-imapd dovecot-pop3d dovecot-mysql dovecot-sieve dovecot-lmtpd saslauthd
Editar la configuración de Postfix
Editamos /etc/postfix/master.cf
[...] submission inet n - - - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_client_restrictions=permit_sasl_authenticated,reject # -o smtpd_reject_unlisted_recipient=no # -o smtpd_client_restrictions=$mua_client_restrictions # -o smtpd_helo_restrictions=$mua_helo_restrictions # -o smtpd_sender_restrictions=$mua_sender_restrictions # -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject # -o milter_macro_daemon_name=ORIGINATING smtps inet n - - - - smtpd -o syslog_name=postfix/smtps -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes -o smtpd_client_restrictions=permit_sasl_authenticated,reject # -o smtpd_reject_unlisted_recipient=no # -o smtpd_client_restrictions=$mua_client_restrictions # -o smtpd_helo_restrictions=$mua_helo_restrictions # -o smtpd_sender_restrictions=$mua_sender_restrictions # -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject # -o milter_macro_daemon_name=ORIGINATING [...]
Si queremos una buena referencia la tenemos en el documento de Linuxize.com
Instalar y configurar PostfixAdmin
Se recomienda no usar caracteres extendidos en la contraseña ya que esto da algunos problemas, que a la edición de este artículo no habia solventado y que preferí obviar
$ sudo systemctl restart postfix $ VERSION=3.3.1
$ #EDITADO: 2021/01/29 El enlace ya no funcionaba proque han cambiado de formato el URI$ wget -q https://downloads.sourceforge.net/project/postfixadmin/postfixadmin/postfixadmin-${VERSION}/postfixadmin-${VERSION}.tar.gz$ wget -O PostfixAdmin-${VERSION}.tar.gz https://sourceforge.net/projects/postfixadmin/files/latest/download $ tar xzf postfixadmin-${VERSION}.tar.gz$ sudo mv postfixadmin-${VERSION}/ /var/www/postfixadmin$ # Revisar el nombre al descomprimir $ rm -f postfixadmin-${VERSION}.tar.gz $ sudo mkdir -p /var/www/postfixadmin/templates_c $ sudo chown -R www-data: /var/www/postfixadmin $ sudo mysql (si tocamos mysql 8 y pusimos old system password en lugar del sistema de sockets -> mysql -u root -p mysql > CREATE DATABASE postfixadmin;
mysql > CREATE USER 'postfixadmin'@'localhost' IDENTIFIED BY 'PaswordSinCaracateresPeroLargo';mysql > GRANT ALL ON postfixadmin.* TO 'postfixadmin'@'localhost';
mysql > FLUSH PRIVILEGES; $ sudo nano /var/www/postfixadmin/config.local.php
Inicialización Postfixadmin
$ sudo -u www-data php /var/www/postfixadmin/public/upgrade.php
Updating database: - old version: 0; target version: 1840 (If the update doesn't work, run setup.php?debug=1 to see the detailed error messages and SQL queries.) updating to version 1 (MySQL)... done updating to version 2 (MySQL)... done updating to version 3 (MySQL)... done updating to version 4 (MySQL)... done updating to version 5 (MySQL)... done updating to version 79 (MySQL)... done updating to version 81 (MySQL)... done updating to version 90 (MySQL and PgSQL)... done updating to version 169 (MySQL)... done updating to version 318 (MySQL)... done updating to version 344 (MySQL)... done updating to version 373 (MySQL)... done ... updating to version 1836 (MySQL)... done updating to version 1837 (all databases)... done updating to version 1839 (all databases)... done updating to version 1840 (MySQL and PgSQL)... done
Nginx Virtualhost para PostfixAdmin
Para el tema de crear host virtuales para PostfixAdmin, Webmail, SOGo Groupware, uso el método habitual de nginx para sitios virtuales, ya que esto estan controlados por root. Es decir, usaremos /sites-available/ y /sites-enabled/
/etc/nginx/sites-available/HOSTANAME.DOMAINNAME.conf
Tenemos que recordar que hay que cambiar las variables por los valores deseados
server { server_name HOSTNAME.TLD; root /var/www/postfixadmin/public; index index.php index.html index.htm; access_log /var/log/nginx/domains/HOSTNAME.TLD.log combined; access_log /var/log/nginx/domains/HOSTNAME.TLD.bytes bytes; error_log /var/log/nginx/domains/HOSTNAME.TLD.error.log error; modsecurity on; modsecurity_rules_file /etc/nginx/modsec/main.conf; location = /favicon.ico { log_not_found off; access_log off; } location = /robots.txt { allow all; log_not_found off; access_log off; } location / { try_files $uri $uri/ /index.php; if (!-e $request_filename) { rewrite ^(.+)$ /index.php?q=$1 last; } location ~* ^.+\.(jpeg|jpg|png|gif|bmp|ico|svg|css|js)$ { expires max; } location /postfixadmin { index index.php; try_files $uri $uri/ /postfixadmin/index.php; } location ~* \.php$ { fastcgi_split_path_info ^(.+?\.php)(/.*)$; if (!-f $document_root$fastcgi_script_name) {return 404;} fastcgi_pass unix:/run/php/php7.2-fpm.sock; fastcgi_index index.php; include /etc/nginx/fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } } location ~* "/\.(htaccess|htpasswd)$" { deny all; return 404; } }
Finalizar la instalación del sitio virtual
$ sudo mkdir /var/log/nginx/domains/ $ sudo chown -R nginx:adm /var/log/nginx/domains/ $ sudo nginx -t $ sudo systemctl restart nginx
Si ejecutamos nuevamente la instalación de los certificados con cerbot este instalará el certificado para este nuevo sitio, y si usamos la mismas opciones (2) la redirección a https automatica.
... listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/HOSTNAME.TLD/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/HOSTNAME.TLD/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot } server { if ($host = HOSTNAME.TLD) { return 301 https://$host$request_uri; } # managed by Certbot server_name HOSTNAME.TLD; listen 80; return 404; # managed by Certbot }
Administrador Postfixadmin
Deberemos de ir a la url de nuestro administrador de postfixadmin https://FDQN/setup.php, donde crearemos una contraseña de aplicacion.
Al crearla nos indicará donde escribirla y en nuestro caso las escribermos en /var/www/postfixadmin/config.local.php
$CONF['setup_password'] = 'c3782aeLOQUEWSEAc9a4203f58dd:f6f8cc9acefb1LOMEJORQUEe92669394a3'; // Evidentemente se sustituye por el hash que nos ha facilitado el instalador
Una vez salvada la edición, si hacemos un reload, podremos añadir un administrador y su contraseña, que una vez creado nos permitira acceder en el index normal, como administrador
Preparación de Postfix para dominios virtuales
Necesitamos crear un usuario vmail para este propósito.
$ sudo useradd -r -u 150 -g mail -d /var/vmail -s /sbin/nologin -c "Virtual Mail User" vmail $ sudo mkdir -p /var/vmail $ sudo chmod -R 770 /var/vmail $ sudo chown -R vmail:mail /var/vmail $ sudo mkdir -p /etc/postfix/sql/
Edición de /etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf
Recordar que será de aplicación usar los valores que hallamos usado en el paso de crear el usuario de postfixadmin y su contraseña
user = postfixadmin password = strong_password hosts = localhost dbname = postfixadmin query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
Edición de /etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf
user = postfixadmin password = strong_password hosts = localhost dbname = postfixadmin query = SELECT maildir FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' and mailbox.username = CONCAT('%u', '@', alias_domain.target_domain) AND mailbox.active = 1 AND alias_domain.active='1'
Edición de /etc/postfix/sql/mysql_virtual_alias_domain_maps.cf
user = postfixadmin password = strong_password hosts = localhost dbname = postfixadmin query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('%u', '@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
Edición de /etc/postfix/sql/mysql_virtual_alias_maps.cf
user = postfixadmin password = strong_password hosts = localhost dbname = postfixadmin query = SELECT goto FROM alias WHERE address='%s' AND active = '1' #expansion_limit = 100
Edición de /etc/postfix/sql/mysql_virtual_domains_maps.cf
user = postfixadmin password = strong_password hosts = localhost dbname = postfixadmin query = SELECT domain FROM domain WHERE domain='%s' AND active = '1' #query = SELECT domain FROM domain WHERE domain='%s' #optional query to use when relaying for backup MX #query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = '0' AND active = '1' #expansion_limit = 100
Edición de /etc/postfix/sql/mysql_virtual_mailbox_limit_maps.cf
user = postfixadmin password = strong_password hosts = localhost dbname = postfixadmin query = SELECT quota FROM mailbox WHERE username='%s' AND active = '1'
Edición de /etc/postfix/sql/mysql_virtual_mailbox_maps.cf
user = postfixadmin password = strong_password hosts = localhost dbname = postfixadmin query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1' #expansion_limit = 100
Las lineas comentadas esta ahi para conocerlas ya que segun sea al caso, como por ejemplo tener configurado un servidor MX Backup será necesario descomentarlas y configurarlas
Actualizar el fichero main.cfg de Postfix desde el shell
$ sudo postconf -e "myhostname = $(hostname -f)" $ sudo postconf -e "virtual_mailbox_domains = proxy:mysql:/etc/postfix/sql/mysql_virtual_domains_maps.cf" $ sudo postconf -e "virtual_alias_maps = proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_maps.cf, proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_maps.cf, proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf" $ sudo postconf -e "virtual_mailbox_maps = proxy:mysql:/etc/postfix/sql/mysql_virtual_mailbox_maps.cf, proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf" $ sudo postconf -e "smtpd_tls_cert_file = /etc/letsencrypt/live/FDQN/fullchain.pem" $ sudo postconf -e "smtpd_tls_key_file = /etc/letsencrypt/live/FDQN/privkey.pem" $ sudo postconf -e "smtpd_use_tls = yes" $ sudo postconf -e "smtpd_tls_auth_only = yes" $ sudo postconf -e "smtpd_sasl_type = dovecot" $ sudo postconf -e "smtpd_sasl_path = private/auth" $ sudo postconf -e "smtpd_sasl_auth_enable = yes" $ sudo postconf -e "smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination" $ sudo postconf -e "mydestination = localhost" $ sudo postconf -e "mynetworks = 127.0.0.0/8" $ sudo postconf -e "inet_protocols = ipv4" $ sudo postconf -e "inet_interfaces = all" $ sudo postconf -e "virtual_transport = lmtp:unix:private/dovecot-lmtp"
Actualizar el fichero master.cfg de Postfix desde el shell
[...] submission inet n - n - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes # -o smtpd_reject_unlisted_recipient=no # -o smtpd_client_restrictions=$mua_client_restrictions # -o smtpd_helo_restrictions=$mua_helo_restrictions # -o smtpd_sender_restrictions=$mua_sender_restrictions # -o smtpd_recipient_restrictions= -o smtpd_relay_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING smtps inet n - n - - smtpd -o syslog_name=postfix/smtps # -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes # -o smtpd_reject_unlisted_recipient=no # -o smtpd_client_restrictions=$mua_client_restrictions # -o smtpd_helo_restrictions=$mua_helo_restrictions # -o smtpd_sender_restrictions=$mua_sender_restrictions # -o smtpd_recipient_restrictions= -o smtpd_relay_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING [...]
Activar el servicio al arranque y reiniciar
$ sudo systemctl enable postfix $ sudo systemctl restart postfix
SASL
SASL o Simple Authentication and Security Layer es un marco de trabajo que provee de mecanismo de autentificación y servicios de seguridad a distintos protocolos de conexión via reemplazo, creando una interface que permite el uso de nuevos y antiguos mecanismos con una capa añadida de seguridad.
Editamos el fichero /etc/default/saslauthd
# Should saslauthd run automatically on startup? (default: no) START=yes $ sudo systemctl restart saslauthd
Dovecot
Editar fichdero de configuración de Dovecot
/etc/dovecot/conf.d/10-mail.conf
mail_location = maildir:/var/vmail/%d/%n mail_privileged_group = mail mail_uid = vmail mail_gid = mail first_valid_uid = 150 last_valid_uid = 150
/etc/dovecot/conf.d/10-auth.conf
auth_mechanisms = plain login #!include auth-system.conf.ext !include auth-sql.conf.ext
/etc/dovecot/dovecot-sql.conf.ext
driver = mysql connect = host=localhost dbname=postfixadmin user=postfixadmin password=strong_password default_pass_scheme = MD5-CRYPT password_query = SELECT username as user, password, '/var/vmail/%d/%n' as userdb_home, 'maildir:/var/vmail/%d/%n' as userdb_mail, 150 as userdb_uid, 8 as userdb_gid FROM mailbox WHERE username = '%u' AND active = '1' user_query = SELECT '/var/vmail/%d/%u' as home, 'maildir:/var/vmail/%d/%u' as mail, 150 AS uid, 8 AS gid, concat('dirsize:storage=', quota) AS quota FROM mailbox WHERE username = '%u' AND active = '1'
/etc/dovecot/conf.d/10-ssl.conf
No debe haber espacio entre el «menor que» y /etc. Se trata de un error del plugin que uso para mostrar el código que me obliga a separarlo
ssl = yes ssl_cert = < /etc/letsencrypt/live/FDQN/fullchain.pem ssl_key = < /etc/letsencrypt/live/FDQN/privkey.pem
/etc/dovecot/conf.d/15-lda.conf
postmaster_address = postmaster@your_domain_name.com
/etc/dovecot/conf.d/10-master.conf
# find lmtp service lmtp { unix_listener /var/spool/postfix/private/dovecot-lmtp { mode = 0600 user = postfix group = postfix } } #find the service auth section and change it to: service auth { unix_listener /var/spool/postfix/private/auth { mode = 0666 user = postfix group = postfix } unix_listener auth-userdb { mode = 0600 user = vmail #group = vmail } user = dovecot } # Change the service auth-worker section to the following: service auth-worker { user = vmail }
Ajustes en sistema
$ sudo chown -R vmail:dovecot /etc/dovecot $ sudo chmod -R o-rwx /etc/dovecot $ sudo systemctl enable dovecot $ sudo systemctl restart dovecot $ sudo systemctl restart postfix
Logs por separado para dovecot
Me gustan los logs por separado para los servicios, y no soy amigo de dejarlo todo al fichero de logs del sistema, asi que editamos /etc/dovecot/conf.d/10-logging.conf
log_path = /var/log/dovecot.log
Rotacion de logs de Dovecot
Editamos el fichero /etc/logrotate.d/dovecot
# dovecot SIGUSR1: Re-opens the log files. /var/log/dovecot*.log { missingok notifempty delaycompress sharedscripts postrotate /bin/kill -USR1 `cat /var/run/dovecot/master.pid 2>/dev/null` 2> /dev/null || true endscript }
Posibles problemas en permisos
Si no hicimo sbien la instalación, o si hicimos algo extraños, al visualizar el log de Postfix tras un reinicio podemos encontrarnos con mensajes como los de abajo.
Mar 2 18:06:10 templateu18 postfix/postsuper[6436]: fatal: scan_dir_push: open directory defer: Permission denied ... Mar 2 18:11:50 templateu18 postfix/postfix-script[7534]: warning: not owned by root: /var/spool/postfix/etc Mar 2 18:43:29 templateu18 postfix/qmgr[10264]: warning: private/smtp socket: malformed response
Solución a estos problemas
$ sudo chown -R postfix /var/spool/postfix/ $ sudo postfix set-permissions $ sudo chown -R root:root /var/spool/postfix/etc $ sudo chown -R root:root /var/spool/postfix/lib $ sudo chown -R root:root /var/spool/postfix/usr $ sudo chown postfix:postfix /var/spool/postfix/dev $ sudo chown root:root /var/spool/postfix/dev/* $ sudo chmod 755 /var/spool/postfix/etc $ sudo chmod 755 /var/spool/postfix/dev $ sudo chmod 755 /var/spool/postfix/lib $ sudo systemctl restart postfix
Testing dovecot
Benditas las pruebas (testing), aunque estas sean unitarias, pues de ellas son la tranquilidad del administrador. Dovecot te pone a disposición una baterias de pruebas que deberías hacer, antes de pasar a producción tu servidor.
RoundCube
Roundcube es un webmail de amplio uso, que a mi entender no debe faltar en una instalación de un servidor o servicio que tiene correo electrónico.
Instalacion
Descarga e instalacion
Consultamos en la página oficial cual es la versión estable que podemos usar
$ VERSION=1.4.3 $ wget https://github.com/roundcube/roundcubemail/releases/download/$VERSION/roundcubemail-$VERSION-complete.tar.gz $ tar xvfz roundcubemail-$VERSION-complete.tar.gz $ sudo mv roundcubemail-$VERSION /var/www/html/webmail $ sudo mysql mysql > create database roundcubedb; mysql > create user 'roundcube'@'localhost' IDENTIFIED BY 'MyPasWord_Not_use_extended'; mysql > GRANT ALL PRIVILEGES ON roundcubedb.* to 'roundcube'@'localhost'; mysql > FLUSH PRIVILEGES; mysql > exit; $ sudo mysql roundcubedb < /var/www/html/webmail/SQL/mysql.initial.sql $ sudo chown -R www-data:www-data /var/www/html/webmail/ $ sudo find /var/www/html/webmail/ -type d -exec chmod 750 {} \; $ sudo find /var/www/html/webmail/ -type f -exec chmod 640 {} \;
Virtualhost para RoundCube
Como hicimos con PostfixAdmin, necesitamos un servidor virtual para nuestro RoundCube. Bien, en eeste caso, no voy a extender el tutorial sobre como hacer la instalación para cada usuario, sino que haré una instalación global
Debemos crear el fichero /etc/nginx/sites-available/webmail.conf recordando que debemos de sustituir aquello que esta en mayúscula (excepto la variable de nginx SCRIPT_FILENAME)
server { server_name webmail.HOSTNAME.TLD; root /var/www/html/webmail; index index.php index.html index.htm; access_log /var/log/nginx/domains/webmail.HOSTNAME.TLD.log combined; access_log /var/log/nginx/domains/webmail.HOSTNAME.TLD.bytes bytes; error_log /var/log/nginx/domains/webmail.HOSTNAME.TLD.error.log error; modsecurity on; modsecurity_rules_file /etc/nginx/modsec/main.conf; client_max_body_size 100M; location = /favicon.ico { log_not_found off; access_log off; } location = /robots.txt { allow all; log_not_found off; access_log off; } location / { try_files $uri $uri/ /index.php?q=$uri&$args; location ~* ^.+\.(jpeg|jpg|png|gif|bmp|ico|svg|css|js)$ { expires max; } location ~ [^/]\.php(/|$) { fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; if (!-f $document_root$fastcgi_script_name) { return 404; } fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; fastcgi_index index.php; include /etc/nginx/fastcgi_params; } location ^~ /data { deny all; } } location ~* "/\.(htaccess|htpasswd)$" { deny all; return 404; } }
Como en ocasiones anteriores si ejecutamos cerbot y seleccionamos la opción de que sea Cerbot quien maneje la redirección de HTTPS, se encargará de editar y configurar https en nuestra configuración.
Instalador en navegador
Después de instalarlo como paquete, debemos configurarlo, para lo cual acudiremos a la url htps://url-virtual-host-roundcube/installer/ que nos entregará un wizard o asistente de configuración. Más información en el Wiki de RoundCube
Redis
Hay muchas opciones para la mejora de las aplicaciones PHP, basadas cachear ciertas partes de la aplicación. Memcached, Redis y otros. Pero la verdad, para mi gusto las aplicaciones que soportan Redis, tienen las de ganar. Redis es un motorde bases de datos en memoria basado en almacenamiento de tablas de hashes. Su eficacia en software como Worpdress o Magento, es mas que notable, y altamente recomendable.
Instalar Redis
$ sudo nano /etc/redis/redis.conf supervised systemd $ sudo systemctl restart redis.service $ sudo systemctl status redis.service ● redis-server.service - Advanced key-value store Loaded: loaded (/lib/systemd/system/redis-server.service; enabled; vendor preset: enabled) Active: active (running) since Thu 2020-03-19 13:59:51 CET; 5s ago Docs: http://redis.io/documentation, man:redis-server(1) Process: 14037 ExecStop=/bin/kill -s TERM $MAINPID (code=exited, status=0/SUCCESS) Process: 14040 ExecStart=/usr/bin/redis-server /etc/redis/redis.conf (code=exited, status=0/SUCCESS) Main PID: 14059 (redis-server) Tasks: 4 (limit: 4915) CGroup: /system.slice/redis-server.service └─14059 /usr/bin/redis-server 127.0.0.1:6379 Mar 19 13:59:51 hq.castris.com systemd[1]: Starting Advanced key-value store... Mar 19 13:59:51 hq.castris.com systemd[1]: Started Advanced key-value store.
Comprobar el estado de redis con una tarea cron
Algunas aplicaciones o sus plugins, cuando estan configurados para usar Redis, no estan programadas de forma que si hay un fallo de comunicación con Redis son incapaces de detectar el fallo, y continuar sin usar Redis, por lo que es necesario tener un monitorizador del demonio para que en caso de que falle, nos avise y trate de reiniciar el demonio
Asi que vamos a crear un script que haga esta función en /usr/bin/redischeck.sh (como root)
#!/bin/sh active=$(redis-cli ping) # redis sin contraseña # SI usamos proteccion por contraseña # active=$(redis-cli -a +S62tFFXqTXhAQ1Y2X1PxUQNJASHSHSHFu4aS5iBZiCQfCz4wp6hrpCc62vNLlKXE3LPsJxBIgM6 ping) hostname=$(hostname -f) if [ "$active" != "PONG" ]; then echo "Redis ${hostname} down" | mail -s "Redis ${hostname} down" micorreodemonitorizacion@domain.tld systemctl restart redis fi
Después creamos la tarea crontab
$ sudo crontab -e */1 * * * * /usr/local/bin/redischeck.sh > /dev/null 2>&1
Comprobación local de la interface de respuesta
$ sudo netstat -lnp | grep redis tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 14059/redis-server tcp6 0 0 ::1:6379 :::* LISTEN 14059/redis-server
Instalación de php-redis
sudo apt-get install php7.2-redis php7.3-redis
Enlaces interesantes
Disclaimer
La intención de este artículo es informativa, y formativa. No esta exento de errores, ni de posibles formas de hacer las cosas que puedan y deban ser mejoradas. Es un documento realizado y enteregado así, sin ningina garantía y ni obligación.
Si ves algo que crees que es mejorable, alguna errata, puedes comentarlo o puedes contactar conmigo para indicarme que cosas están mal o que cosas mejorarias. Lo estudiaré y en su caso lo incorporare con las debidas menciones a tu trabajo.
Aun si, si usas el documento, visitas los enlaces e investigas, seguro que aprenderás mucho más que leyendo los tutoriales y docuemntación de los paneles típicos de Hosting.
Agradecimientos
Como siempre gracias Unsplash y a Filiberto Santillán por la imagen que sirve de portada, que edite gracias a Canva
Comparte este articulo en
Comments 6
Increíble artículo, la verdad. Yo uso HestiaCP, es como la revolución de Vesta luego de que los desarrolladores lo dejaran abandonado. También es cierto que muchas tareas las hago manualmente por lo que bien explicás en tu post: ineficacia, lentitud, intrusión, recursos excesivos. Te mando un abrazo y admiro tu conocimiento.
buen artículo pero la duda para los que venimos de vesta cp , es que sucede si necesitamos migrar a otro server, los backup de vesta son interesantes ya que de un click puedes migrar un usuario con sus dominios vinculados empaquetadas en un tar…..
Author
Bueno. La verdad es que tengo dos vps vestaCp, y no vuelvo a instalar nada con ese panel. Intrusivo, y malo. En cuanto a los backups. Mejor tener tu propio sistema de backup que confiar en terceros. Un buen día lo comprobarás.
Pingback: cPanel? Historias de desamor – Diario de un preso
Pedazo de artículo, mis felicitaciones. Muy detallado
Author
Gracias…