Últimamente he escrito varios posts sobre estándares y aspectos más estratégicos de negocio e IT, como por ejemplo:
- BS 25999-1: Gestión de la Continuidad del Negocio
- Cobit, estándar para el buen gobierno de los Sistemas de Información
- ITIL y ISO 20000, marcos de trabajo para servicios IT
- Metodologías de desarrollo
- Estructurar las organizaciones en procesos
- PMBOK, Project Management / Gestión de proyectos
- PRINCE2 como complemento a PMBOK para la gestión de proyectos (project management)
- Metodologías ágiles de gestión de proyectos (Scrum, DSDM, Extreme Programming – XP…)
- Scrum, gestión ágil de proyectos / agile project management
- Getting Things Done (GTD), organízate con eficacia de David Allen
Es habitual escuchar críticas a este tipo de enfoque más estratégico, aludiendo al hecho de que se habla y teoriza mucho pero no se concreta nada. En este nuevo artículo, en el que ha colaborado Raúl Gómez (experto en seguridad técnica), se plasman de forma técnica muchas de las ideas de buena gestión estratégica IT o de negocio.
Vamos a ver cómo podemos montar un servidor con Ubuntu Linux que cumpla mínimamente con las buenas prácticas de seguridad:
- Efectuar una configuración segura
- Establecer una política de contraseñas
- Utilizar herramientas para la gestión de usuarios
- Proteger los servicios de red
- Garantizar la trazabilidad
- Proporcionar herramientas de monitorización en tiempo real e histórica para facilitar proyecciones de futuras necesidades
- Establecer política de copias de seguridad
- Efectuar tests de stress del sistema
Y es que en definitiva el software libre, por más libre y abierto que sea, no tiene porque ser seguro a no ser que se establezca una configuración adecuada y alineada con las políticas/normativas/procedimientos de la organización.
Contenido
1. Configuración mínima
2. Securización del sistema base
Kernel: parámetros de seguridad
Limitando los recursos del sistema
Memoria, número de procesos y sesiones concurrentes
Espacio en disco
3. Interacción básica con el sistema
Shells
Shell Bash
Locales: configuración del idioma
Mensajes legales e informativos: Issue & motd
Usuarios privilegiados: sudo
Sistema de archivos
Esquema de los permisos de ficheros/directorios
Permisos por defecto: UMASK
Permisos especiales: SUID y SGID
Directorios con permisos de escritura global
Archivos sin propietarios
Detección de modificaciones de ficheros: Tripwire
4. Política de contraseñas
Política global
Contraseñas de un solo uso: OTP (One time password)
Gestión del acceso
5. Gestión de usuarios
Creación de usuarios
Gestion de cuentas
6. Servicios básicos del sistema
Activación en arranque
Servicio de correo: Postfix
Servicio web: Apache
PHP
Apache básico
Módulos de Apache
Servir por localhost las aplicaciones más criticas
Base de datos MySQL
Acceso remoto: SSH
7. Trazabilidad
Fecha y hora del sistema
Logs de usuario con Bash
Rotación y conservación de logs
Análisis de logs de Apache mediante Awstats
Envio por correo de los eventos más significativos con logcheck
8. Monitorización del sistema
Actualización automática de la lista de paquetes del sistema
Monitorizar desde la consola
Espacio en disco
CPU
Ancho de banda / Bandwidth
Ancho de banda / Bandwidth
Memoria RAM / Swap
Monitorización en tiempo real: Nagios
Ancho de banda consumido con Bandwidthd
SNMP
Soporte ‘Devices I/O’ en net-snmp
Monitorización historica: Cacti
Script Device I/O para cacti
9. Seguridad
Cortafuegos / Firewall
Monitorización de firewall con fwlogwatch
Detección de intrusos / IDS: Snort
Rootkits
Control del ancho de banda
10. Copias de seguridad
Utilidades rsnapshot
Información de estado
dpkg y mysql
Tars semanales
Reportes
11. Tests de stress
Sistema
Conexiones HTTP con httperf
Conexiones HTTP con Apache Benchmarking tool
12. Issue tracking
1. Configuración mínima
Recomiendo echar un vistazo a la Guía de referencia rápida para la personalización de Ubuntu Edgy (6.10) GNU/Linux, aunque la versión de Ubuntu es bastante antigua, la mayoría de secciones aún son válidas. En el caso de los servidores, resultan especialmente interesante las siguientes secciones:
- Editor de textos de consola Vim
- (Des)Compresores
- Compartir directorios/ficheros
- Antivirus
- Hacking
- Conexión SSH sin password
- Otros
- Otro artículo: Mutt como cliente de correo de consola
2. Securización del sistema base
Kernel: parámetros de seguridad
El kernel de Linux dispone de diversos parámetros que nos permiten configurar su actuación frente al tráfico de red. Al iniciar el sistema se establecen los valores de los parámetros indicados en ‘/etc/sysctl.conf’. No obstante, también es posible modificarlos mediante el comando ‘sysctl’.
Los parámetros más importantes a tener en cuenta son:
– Activar la protección contra IP spoofing:
sysctl -w net.ipv4.conf.default.rp_filter=1 sysctl -w net.ipv4.conf.all.rp_filter=1
– Activar TCP SYN Cookie Protection para minimizar la posibilidad de ataques de denegación de servicio por ataques de paquetes SYN. No obstante, actualmente ya no se recomienda esta opción dado que las capacidades de los sistemas han aumentado considerablemente y activarlo nos hace perder otras características de TCP (ver recomendación de Andi Kleen, autor de las SynCookies):
sysctl -w net.ipv4.tcp_syncookies=1
– Desactivar el reenvío de paquetes IP (solo es necesario cuando queremos que la máquina actue como una pasarela mediante NAT):
sysctl -w net.ipv4.ip_forward=0 sysctl -w net.ipv6.ip_forward=0
– Ignorar Broadcasts Request:
sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1
– Activar la protección contra mensajes de error mal construidos:
sysctl -w net.ipv4.icmp_ignore_bogus_error_responses=1
– Desactivar ICMP Redirect Acceptance, mediante los cuales un atacante podría indicar al sistema una ruta mejor para llegar a otras redes (habitualmente utilizado por routers). Únicamente los permitiremos si proceden de nuestro gateway:
sysctl -w net/ipv4/conf/all/accept_redirects=0 sysctl -w net/ipv4/conf/all/send_redirects=0 sysctl -w net/ipv4/conf/all/secure_redirects=1
– Desactivar IP Sourcing routing mediante el cual un atacante podría especificar la ruta a seguir para ir desde una IP origen a una destino:
sysctl -w net.ipv4.conf.all.accept_source_route=0
– Logear paquetes: Spoofed Packets, Source Routed Packets, Redirect Packets
sysctl -w net.ipv4.conf.all.log_martians=1
– Ignorar ICMP Requests PINGs, personalmente prefiero dejarlo a 0 y controlar desde donde y hacia donde permito los pings mediante iptables (firewall). En cualquier caso, si queremos desactivar completamente que el sistema responda a pings:
sysctl -w net.ipv4.icmp_echo_ignore_all=1
Para consultar los valores actuales:
sysctl net.ipv4.conf.default.rp_filter sysctl net.ipv4.conf.all.rp_filter sysctl net.ipv4.tcp_syncookies sysctl net.ipv4.ip_forward sysctl net.ipv6.ip_forward sysctl net.ipv4.icmp_echo_ignore_broadcasts sysctl net.ipv4.icmp_ignore_bogus_error_responses sysctl net.ipv4.conf.all.accept_redirects sysctl net.ipv4.conf.all.send_redirects sysctl net.ipv4.conf.all.accept_source_route sysctl net.ipv4.conf.all.log_martians
Para establecer los nuevos valores de forma permanente para sucesivos reinicios es necesario modificar /etc/sysctl.conf.
Limitando los recursos del sistema
Se considera una buena práctica limitar los recursos del sistema utilizados por los usuarios, de forma que minimicemos las probabilidades de que se efectúen ataques de denegación de servicio contra todo el sistema.
Memoria, número de procesos y sesiones concurrentes
Para limitar la memoria, el número de procesos y las sesiones concurrentes que puede tener cada usuario debemos editar ‘/etc/security/limits.conf’. A modo de ejemplo:
# Prevents anyone from dumping core files. * hard core 0 # This will prevent anyone (except root) from having more than 150 processes, and a warning will be given at 100 processes. * soft nproc 100 * hard nproc 150 # This will prevent anyone in the 'users' group from having more than 150 processes, and a warning will be given at 100 processes. @users soft nproc 100 @users hard nproc 150 @users - maxlogins 4 # Address space limit: 2GB * hard as 2097152 # Maximum data size: 128MB * hard data 131072 # Maximum locked-in-memory address space: 128MB * hard memlock 131072 # Maximum resident set size: 1GB * hard rss 1013352
En este caso estamos limitando cada usuario (excepto root) a un máximo de:
- 150 procesos para todos los usuarios
- 100 procesos para los usuarios del grupo ‘users’
- 2GB de RAM
- 4 logins concurrentes
Una vez configurado, podemos realizar una sencilla prueba para verificar que el sistema no nos permite crear más de 100 procesos:
$ for i in `seq 500`; do sleep 5 & done
Espacio en disco
Para habilitar las limitaciones de espacio debemos tener montada las particiones afectadas con los parámetros ‘usrjquota’ y ‘grpjquota’ en ‘/etc/fstab’ (journalling quotas). Por ejemplo:
/dev/sda4 / ext4 defaults,errors=remount-ro,usrjquota=aquota.user,grpjquota=aquota.group,jqfmt=vfsv0 1 1
Por otra parte, el servicio de quotas (/etc/init.d/quota) debe estar activo. Podemos comprobarlo mediante la aplicación ‘sysv-rc-conf’ (consultar sección de Servicios de esta guía).
Podemos activar inmediatamente las quotas mediante:
touch /aquota.user /aquota.group chmod 600 /aquota.* mount -o remount / quotacheck -avugm quotaon -avug
Si encontramos algun tipo de problema con quotacheck (p.ej. puede ser que entre en conflicto con gvfs), podemos probar a reiniciar el sistema.
A continuación, para modificar las quotas de un grupo de usuario determinado:
EDITOR="vi" edquota -g users
Tendremos que cambiar los números correspondientes, por ejemplo:
Disk quotas for group users (gid 100): Filesystem blocks soft hard inodes soft hard /dev/sda6 23808 1048576 1572864 37 0 0 /dev/sda7 10312844 47185920 52428800 8780 0 0
En este caso, el sistema realizará un aviso por mail local a los 45GB (45*1024*1024), mientras que el límite real se encuentra en los 50GB (50*1024*1024) para el grupo ‘users’ y el dispositivo sda7 (p.ej. /home/). En el caso de sda6 (p.ej. raíz /), el limite blando es 1GB (1*1024*1024) y el duro 1.5 GB (1.5*1024*1024).
Los ficheros que contabilizan dentro de la quota de grupo son aquellos que tienen como propietario el mismo grupo. Es decir, un usuario que pertenece al grupo A y B puede crear archivos asignándolos a uno u a otro hasta agotar ambas quotas. Por este motivo, establecer los límites por usuario permite un mayor control:
EDITOR="vi" edquota -u usuario
Es también interesante limitar todos aquellos grupos que corren servicios como apache2.
Podemos ver un resumen de todas las quotas aplicadas:
# repquota -s -a *** Informe para user quotas en dispositivo /dev/mapper/isw_beahgicji_Volume07 Período de gracia de bloque: 7días; período de gracia de Inode: 7días Límites de bloque límites de archivo Usuario usado blando duro gracia usado bland duro gracia ---------------------------------------------------------------------- root -- 63404 0 0 18960 0 0 usuario1 -- 457G 0 0 303k 0 0 usuario2 +- 10072M 5120M 5120M 6días 8780 0 0 ftp -- 4 0 0 1 0 0 freevo -- 44 0 0 18 0 0 mythtv -- 48 0 0 15 0 0 couchdb -- 132 0 0 33 0 0
Un usuario puede consultar el estado de su cuota:
$ quota -s Disk quotas for user usuario (uid 1000): Filesystem blocks quota limit grace files quota limit grace /dev/vzfs 24 26 28 6 0 0
Cabe destacar que el periodo de gracia (grace limit) es el tiempo que transcurre antes de que el usuario es obligado a cumplir el “soft limit”. Para modificarlo:
# edquota -t Período de gracia antes de imponer límites blandos para users: La unidad de tiempo puede ser: días, horas, minutos, o segundos Sistema de archivos Período de gracia de bloque Período de gracia de inode /dev/mapper/isw_beahgicji_Volume06 7días 7días /dev/mapper/isw_beahgicji_Volume07 7días 7días
Finalmente, en caso de agotar la cuota, el usuario recibirá el siguiente mensaje:
$ cp /usr/bin/dpkg . cp: writing `./dpkg': Disk quota exceeded
3. Interacción básica con el sistema
Shells
La shell es la aplicación mediante la cual los usuarios pueden interactuar con el sistema. Es recomendable habilitar únicamente las shells que nos interesen editando ‘/etc/shells’:
# /etc/shells: valid login shells #/bin/csh /bin/sh #/usr/bin/es #/usr/bin/ksh #/bin/ksh #/usr/bin/rc #/usr/bin/tcsh #/bin/tcsh #/usr/bin/esh #/usr/bin/screen #/bin/dash /bin/bash #/bin/rbash
Únicamente las shells no comentadas seran permitidas, siempre y cuando añadamos al principio de ‘/etc/pam.d/common-auth’:
# This will not allow a user to change their shell unless # their current one is listed in /etc/shells. This keeps # accounts with special shells from changing them. auth required pam_shells.so
Shell Bash
Es interesante configurar la shell Bash para permitir el auto-completado inteligente de comandos (presionando [tab]) editando ‘/etc/bash.bashrc’ y descomentado:
# enable bash completion in interactive shells if [ -f /etc/bash_completion ]; then . /etc/bash_completion fi
En ese mismo fichero, es recomendable añadir los siguiente alias:
alias mv='mv -i' alias cp='cp -i' alias rm='rm -i' alias ln='ln -i' export EDITOR='vim' export TMOUT=3600
Esto evitará borrados involuntarios de ficheros dado que cada vez que modifiquemos un fichero mediante mv, cp, rm o ln se pedirá confirmación.
Por otra parte, la variable TMOUT provocará automáticamente un logout de la terminal si no se ejecuta nada durante los segundos especificados (si se está visualizando un programa interactivo como top, no se produce timeout). De esta forma reduciremos las probabilidades de que un usuario se deje su sesión abierta y accesible por otra persona que tenga acceso físico a su ordenador.
Cabe destacar que si se ejecuta screen después de establecer la variable TMOUT, se hereda y se produce logout primero de las ventanas de screen y despues de la sesión que ejecutó screen.
Locales: configuración del idioma
Para que el sistema muestre los mensajes en un idioma determinado (p.ej. Castellano):
apt-get install language-pack-es language-pack-en
Y a continuación editamos ‘/etc/environment’ y ‘/etc/default/locale’:
#LANG="en_GB.utf8" LANG="es_ES.UTF-8" LANGUAGE="es_ES:en_EN" #LANGUAGE="en_GB:en:es_ES:es"
Mensajes legales e informativos: Issue & motd
Se recomienda establecer un mensaje legal que notifique a los usuarios que el acceso al sistema es exclusivo para aquéllos que han sido autorizados. Para ello se debe editar los ficheros ‘/etc/issue’ y ‘/etc/issue.net’, por ejemplo:
*************************************************************************** NOTICE TO USERS This computer system is for authorized use only. Use of this system constitutes consent to security monitoring and testing. All activity is logged with your host name and IP address. Any or all uses of this system and all files on this system may be intercepted, monitored, recorded, copied, audited, inspected, and disclosed to law enforcement personnel, as well as authorized officials of other agencies, both domestic and foreign. Unauthorized or improper use of this system may result in administrative disciplinary action and civil and criminal penalties. By continuing to use this system you indicate your awareness of and consent to these terms and conditions of use. LOG OFF IMMEDIATELY if you do not agree to the conditions stated in this warning. *****************************************************************************
Cabe destacar que si queremos que el banner se muestre a los usuarios que se conecten mediante SSH (ver sección de Servicios de esta guía), debemos editar ‘/etc/ssh/sshd_config’ y descomentar:
Banner /etc/issue.net
Por otra parte, una configuración segura implica proporcionar la mínima información sobre el sistema al usuario en cuanto a las características del mismo (p.ej. distribución, kernel, etc). Para minimizar ese aspecto podemos poner en blanco el mensaje de bienvenida (o incluir el mensaje genérico que nos interese):
echo > /etc/motd echo > /etc/motd.tail
Si lo dejamos en blanco, debemos asegurarnos que el script de inicio ‘/etc/init.d/bootmisc.sh’ no lo reconstruye. Para ello lo editamos y comentamos las siguientes lineas:
# Update motd #uname -snrvm > /var/run/motd [ -f /etc/motd.tail ] && cat /etc/motd.tail >> /var/run/motd
Y por otra parte, editamos ‘/etc/cron.d/update-motd’ y comentamos:
#*/10 * * * * root [ -x /usr/sbin/update-motd ] && /usr/sbin/update-motd 2>/dev/null
Usuarios privilegiados: sudo
Con el objetivo de garantizar la máxima trazabilidad, se requiere que el usuario root no sea utilizado directamente. Para ello podemos configurar el sistema para que el grupo de usuarios que nos interese puedan adquirir privilegios de root editando ‘/etc/group’ y añadiendo:
admin:x:114:miusuario
Ademas debemos tener en '/etc/sudoers' la siguiente línea:
# Members of the admin group may gain root privileges %admin ALL=(ALL) ALL
Para editar sudoers siempre debemos utilizar el comando ‘visudo’. Con esta configuración, el usuario o usuarios del grupo seleccionado (‘admin’ en este caso) podrán acceder al sistema normalmente y una vez dentro ejecutar ‘sudo comando’ o ‘sudo -s’ para adquirir privilegios de root.
Cabe destacar que Ubuntu suele venir configurada así por defecto.
Sistema de archivos
Esquema de los permisos de ficheros/directorios
Permisos por defecto: UMASK
Es posible establecer los permisos con los que se crean los nuevos ficheros/directorios, para ello tendremos que editar varios ficheros. El primero es ‘/etc/login.defs’:
UMASK 022
El segundo ‘/etc/profile’:
umask 022
Y finalmente, editamos ‘/etc/pam.d/common-session’ y añadimos al final:
session optional pam_umask.so umask=0022
Son necesarios tantos cambios porque en función de como nos conectemos a la máquina (físicamente, remotamente por ssh, X Window, etc.) se utilizaran unos scripts u otros.
En cuanto a la mascara, nos podría interesar establecer umask como 027 para que todos los ficheros/directorios creados no tengan permisos de acceso para otros usuarios que no sean los del grupo del propietario.
Ejemplo de funcionamiento de umask:
$ umask 000 $ touch file1 $ ls -l file1 -rw-rw-rw- 1 oracle oinstall 0 Dec 26 19:24 file1 $ umask 002 $ touch file2 $ ls -l file2 -rw-rw-r-- 1 oracle oinstall 0 Dec 26 19:24 file2 $ umask 022 $ touch file3 $ ls -l file3 -rw-r--r-- 1 oracle oinstall 0 Dec 26 19:25 file3
Permisos especiales: SUID y SGID
Cuando el bit SUID (set user ID) o SGID (set group ID) está activo en un ejecutable, esto implica que ese archivo se ejecutará con los permisos del propietario o grupo propietario respectivamente. Por ejemplo:
# ls -la /bin/ping -rwsr-xr-x 1 root root 30856 2007-12-10 18:33 /bin/ping
Ping tiene activado el bit SUID y su propietario es root, por tanto cuando sea ejecutado por un usuario, el programa adquirirá los privilegios de root. En ocasiones se requiere este comportamiento, pero en otras puede ser innecesario y únicamente contribuye a debilitar la seguridad del sistema (p.ej. se descubre una vulnerabilidad/overflow en ping que permite al usuario obtener una shell, esta shell seria con permisos de root).
Para identificar todos los ficheros SUID o GUID podemos utilizar:
find / -path /proc -prune -o -type f -perm +6000 -ls
Solo SUID:
find / -path /proc -prune -o -type f -perm +2000 -ls
Solo GUID:
find / -path /proc -prune -o -type f -perm +4000 -ls
Una vez identificados, podemos proceder a seleccionar cuales queremos dejar con el bit SUID o SGID activado.
Para activar/desactivar SUID:
chmod u+s fichero chmod u-s fichero
Y para activar/desactivar SGID:
chmod g+s fichero chmod g-s fichero
Directorios con permisos de escritura global
Para identificar directorios con permisos de escritura global ejecutaremos:
find / -path /proc -prune -o -perm -2 ! -type l -ls
En ocasiones estos son necesarios, como por ejemplo ‘/tmp’. Pero en otras puede suponer un peligro para el sistema. Está en nuestras manos decidir cuales dejar con dichos permisos.
A modo de ejemplo, con el usuario user01 creamos un directorio con permisos de escritura universales:
$ mkdir test $ chmod 777 test $ sudo chown -R root:root test $ cd test $ touch hola $ ls -la total 8 drwxrwxrwx 2 root root 4096 2008-08-22 13:56 . drwxr-x--- 126 root root 4096 2008-08-22 13:55 .. -rw-r--r-- 1 user01 group1 0 2008-08-22 13:56 hola
En el ejemplo anterior, como el directorio tiene permisos de escritura para todo el mundo, el usuario user02 podría borrar/modificar el archivo ‘hola’ a pesar de que este pertenece a user01 y sus permisos sean -rw-r–r–. Por ejemplo, con el usuario user02 ejecutamos el borrado:
$ rm hola rm: ¿borrar el archivo regular vacío «hola» protegido contra escritura? (s/n) y
Por otra parte, los directorios con permisos de escritura global que tienen el Sticky bit activado como ‘/tmp’:
$ ls -lad /tmp/ drwxrwxrwt 13 root root 49152 2008-08-22 13:54 /tmp/
Este comportamiento no tiene lugar, los ficheros creados únicamente pueden ser modificados por sus propietarios o por el propietario del directorio. Por ejemplo, con el usuario user02:
$ rm hola rm: ¿borrar el archivo regular vacío «hola» protegido contra escritura? (s/n) y rm: no se puede borrar «hola»: Operación no permitida
Consecuentemente, del listado de directorios con permisos globales de escritura, los que tienen el bit Sticky activado son casos especiales que debemos tener en cuenta.
Para activar/desactivar el bit sticky en un directorio:
chmod +t directorio/ chmod -t directorio/
Archivos sin propietarios
Para identificar archivos sin propietarios:
find / -path /proc -prune -o -nouser -o -nogroup
Si en el futuro se crease un usuario con el mismo ID, este adquiriría permisos automáticamente sobre estos ficheros y podría suponer un problema de seguridad.
Detección de modificaciones de ficheros: Tripwire
Podríamos utilizar tripwire para detectar modificaciones en ficheros del sistema. Tripwire construye una BBDD con los MD5 de los ficheros:
apt-get install tripwire
4. Política de contraseñas
Política global
Todas las contraseñas deben ser cifradas y por tanto, contenidas en /etc/shadow. Para validar que passwd no contiene passwords:
$ grep -v ':x:' /etc/passwd
En ‘/etc/passwd’ todos los usuarios deben tener el campo password una x.
Se recomienda forzar a los usuarios a cambiar sus contraseñas de forma periódica (p.ej. 60 días) y que no puedan ser cambiadas más de una vez en un día (p.ej. en caso de que tengamos configurado el sistema para mantener un histórico de contraseñas irrepetibles, el usuario no podrá cambiar su contraseña N veces en el mismo día hasta volver a tener la misma). Para ello editamos ‘/etc/login.defs’ y establecemos:
# Maximum number of days a password is valid. PASS_MAX_DAYS 60 # Minimum number of days before a user can change the password since the last change. PASS_MIN_DAYS 1 #Number of days when the password change reminder starts. PASS_WARN_AGE 15
Adicionalmente, si un usuario no cambia su contraseña 2 semanas después de haberse caducado, se recomienda bloquear la cuenta. De esta forma el sistema desactivará las cuentas de usuario que no estén siendo utilizadas y minimizaremos el riesgo de accesos indebidos. Debemos editar ‘/etc/default/useradd’:
# Number of days after password expiration that account is disabled. INACTIVE=14
Por otra parte, es recomendable que el sistema lleve un control de contraseñas históricas para evitar que el usuario reutilice los últimos N passwords. Además, es interesante que el sistema también valide la robustez de las nuevas contraseñas. Con este objetivo, debemos instalar cracklib:
apt-get install libpam-cracklib update-cracklib
A continuación editamos ‘/etc/pam.d/common-password’ y lo dejamos así:
password required pam_cracklib.so retry=3 minlen=8 difok=1 lcredit=0 ucredit=1 dcredit=1 ocredit=2 password requisite pam_unix.so use_authtok obscure md5 remember=12
En este ejemplo hemos hecho que:
- El usuario tiene que repetir 3 veces la nueva contraseña para realizar el cambio.
- Longitud mínima de 8 caracteres, no obstante puede ser inferior si se utilizan caracteres que valen más: Mayúsculas, minúsculas, dígitos, etc…
- Al menos 1 caracteres deben ser diferentes al password anterior.
- Según el manual, el parámetro ‘obscure’ provoca que se realicen las siguientes validaciones:
- Palindrome: Verifies that the new password is not a palindrome of (i.e., the reverse of) the previous one.
- Case Change Only: Verifies that the new password isn´t the same as the old one with a change of case.
- Similar: Verifies that the new password isn´t too much like the previous one
- Simple: Is the new password too simple? This is based on the length of the password and the number of different types of characters (alpha, numeric, etc.) used.
- Rotated: Is the new password a rotated version of the old password? (E.g., “billy” and “illyb”)
- Por su parte, cracklib realiza estas otras comprobaciones:
- Que los cambios de passwords no se simplemente invertir la palabra o cambiar mayúsculas por minúsculas.
- Comprobaciones contra un diccionario interno, construido a partir de datos que hay en los ficheros del sistema (update-cracklib)
- Finalmente ‘remember=12’ hace que el sistema recuerde las últimas 12 contraseñas y no deja que el usuario las repita. Para que funcione correctamente vamos a necesitar ejecutar también:
touch /etc/security/opasswd chown root:root /etc/security/opasswd chmod 600 /etc/security/opasswd
Existen alternativas a cracklib como por ejemplo libpam-passwdqc, el cual es más configurable aunque hoy por hoy menos popular.
Finalmente, validamos el estado actual de algún usuario del sistema:
# chage -l miusuario Last password change : Aug 05, 2008 Password expires : Oct 04, 2008 Password inactive : never Account expires : never Minimum number of days between password change : 1 Maximum number of days between password change : 60 Number of days of warning before password expires : 15
Cabe destacar que para usuarios ya creados podemos modificar sus opciones mediante chage (ver man).
Contraseñas de un solo uso: OTP (One time password)
Podemos configurar el sistema para que en el momento del acceso, si introducimos incorrectamente o en blanco nuestro password, el sistema nos ofrezca la alternativa de logearnos con un password de un solo uso. Esto puede ser de gran utilidad si nos conectamos a la máquina desde un ordenador que no podemos confiar (p.ej. un cibercafé).
Para esto necesitamos instalar opie:
apt-get install opie-server
A continuación, como usuario establecemos una contraseña y el programa nos proporciona un challenge:
opiepasswd -c -f ID marble OTP key is 499 vp5957 CAB TILE YOU NINA BRED ELLA
Podemos obtener un listado de los 10 futuros password de un solo uso que vamos a poder usar mediante el comando siguiente:
$ opiekey -n 10 499 vp5957 490: WARD PIE ROSE SKY SKI SHAY 491: I MITE VET AN IQ DENY 492: BED BIEN TWIN WACK GELD SENT 493: NECK MASH FLIT RICE GALE BY 494: FLUB GET NOB MOOD TUM CITE 495: WOO SNOB THEY SARA JILT GUSH 496: VALE HOWE GAIT EEL HUED ODE 497: I WON FOOL TOAD FEE HOFF 498: FULL SOWN EGAN RICH WEAN HUGE 499: CAB TILE YOU NINA BRED ELLA
No obstante, resulta interesante utilizar algún programa escrito en J2ME para móviles como vejotp, con el cual podremos generar el password de un solo uso que necesitemos indicando el challenge correspondiente que nos indique el servidor al conectarnos.
Por último, falta configurar el sistema para que acepte estas contraseñas. Editamos ‘/etc/pam.d/common-auth’:
auth sufficient pam_unix.so nullok_secure auth sufficient pam_opie.so auth required pam_deny.so #auth requisite pam_unix.so nullok_secure #auth optional pam_smbpass.so migrate missingok
Y en el caso de que usemos SSH, editamos ‘/etc/ssh/sshd_config’:
ChallengeResponseAuthentication yes
Existen alternativas a OPIE como por ejemplo libpam-otpw, el cual utiliza la típica tarjeta de coordenadas en lugar de passwords de un solo uso.
Gestión del acceso
Después de 3 intentos fallidos de un usuario, es recomendable bloquear su cuenta para los siguientes 30 segundos por ejemplo (durante los cuales será denegado su acceso aunque ponga correctamente sus credenciales). A partir de ese punto, cada nuevo intento fallido representará otros 30 segundos de bloqueo. Si el usuario indica su contraseña correcta pasado ese tiempo, el contador de intentos fallidos se resetea a 0.
Para ello utilizaremos ‘pam_tally’ editando ‘/etc/pam.d/common-auth’:
# 3 auth attempts, after that user will be locked for 30 seconds for each new failed attempt auth required pam_tally.so onerr=succeed deny=3 unlock_time=30 per_user auth sufficient pam_unix.so auth sufficient pam_opie.so auth required pam_deny.so # Reset user lock if auth suceeds account required pam_tally.so onerr=succeed
El parámetro ‘per_user’ hará que tally valide si se ha establecido algún valor individual (diferente de 0) al usuario mediante faillog. Por ejemplo, para limita el número de reintentos fallidos y establecer el tiempo de bloqueo para un usuario concreto:
faillog --maximum 5 --lock-time 120 -u miusuario
Otro ejemplo, no permitir que un usuario se bloquee aunque sobrepase los valores por defecto de intentos fallidos de autenticación:
faillog --maximum -1 -u miusuario
Para ver el estado de los usuarios podemos utilizar:
pam_tally pam_tally --user misusuario faillog -a faillog -u miusuario
Para desbloquear:
pam_tally --user miusuario --reset faillog -u miusuario -r
Es importante eliminar todos los parámetros ‘nullok’ o ‘nullok_secure’ del modulo pam_unix.o dado que permiten passwords en blanco (tanto en common-password como en common-auth).
Ver más en opciones (bloqueos permanentes, etc.) en el siguiente manual.
5. Gestión de usuarios
Creación de usuarios
Para crear un usuario y asignarle un password:
useradd -d /home/miusuario -m -G admin,users -K PASS_MAX_DAYS=60,PASS_MIN_DAYS=1,PASS_WARN_AGE=15,UMASK=0022 -s /bin/bash miusuario passwd miusuario
Estos comando crean su directorio de usuario con el skeleton básico (-m), crea un grupo con el mismo nombre que el usuario y será su grupo primario (-n) y además pertenecerá al grupo admin. Por otra parte, también se especifica la política de contraseñas específica.
Para establecer contraseñas aleatorios podemos ayudarnos del siguiente programa:
apt-get install makepasswd makepasswd --chars=8
Gestión de cuentas
Tomando como ejemplo un usuario que dispone de la siguiente entrada en ‘/etc/shadow’:
usuario:$1$ZyihUAz0$RqBWU1OINGOsUILv3g2Kh/:14111:0:99999:7:::
Veamos diversas acciones de utilidad….
- Expira la contraseña de un usuario para obligarle a cambiarla:
passwd -e usuario
Establece el 3er campo de /etc/shadow a 0:
usuario:$1$ZyihUAz0$RqBWU1OINGOsUILv3g2Kh/:0:0:99999:7:::
- Bloquea o desbloquea (recuperando password anterior) un usuario:
passwd -l usuario
Añade una exclamación delante del hash de la contraseña en /etc/shadow y en el octavo campo añade un 1 (cuenta caducada el 2 de Enero de 1970):
usuario:!$1$ZyihUAz0$RqBWU1OINGOsUILv3g2Kh/:14111:0:99999:7::1:
Para deshacer los cambios:
passwd -u usuario
- Desactiva un usuario (sin posibilidad de recuperar el password):
passwd -d usuario
Se asigna un hash en blanco y en el octavo campo añade un 1 (cuenta caducada el 2 de Enero de 1970):
usuario::14111:0:99999:7::1:
- Activar un usuario que ha sido desactivado: eliminar la expiración y asignar una nueva contraseña
chage -E -1 aurora passwd aurora
- Estado:
passwd -S usuario chage -l usuario
- Si no queremos que root pueda logearse directamente al sistema y que el acceso a la cuenta de administración se realice mediante sudo. Aparte de configurar correctamente sudo, debemos bloquear y desactivar el usuario root:
passwd -l -d root
Quedando algo parecido a:
root:!:13449:0:99999:7::1:
En resumen, campos del /etc/shadow:
[username]:[password]:[date]:PASS_MIN_DAYS:PASS_MAX_DAYS:PASS_WARN_AGE:INACTIVE:EXPIRE:
Según el manual:
· login name · encrypted password · days since Jan 1, 1970 that password was last changed · days before password may be changed · days after which password must be changed · days before password is to expire that user is warned · days after password expires that account is disabled · days since Jan 1, 1970 that account is disabled
Otras funcionalidades útiles:
- Listar usuarios no bloqueados:
egrep -v '.*:*|:!' /etc/shadow | awk -F: '{print $1}'
- Listar archivos de un usuario concreto (nos puede servir para identificar usuarios que no disponen de información en el sistema):
find / -path /proc -prune -o -user usuario -ls
- Comprobamos que el fichero de grupos es correcto:
# grpck -r 'www-data' is a member of the 'adm' group in /etc/group but not in /etc/gshadow 'miusuario' is a member of the 'users' group in /etc/group but not in /etc/gshadow no matching group file entry in /etc/gshadow add group 'admin' in /etc/gshadow ?No group marble: no user 0 delete member '0'? No no matching group file entry in /etc/gshadow add group 'marble' in /etc/gshadow ?No 'miusuario' is a member of the 'adm' group in /etc/gshadow but not in /etc/group grpck: no changes
6. Servicios básicos del sistema
Activación en arranque
Para la activación/desactivación de los servicios que queremos en arranque podemos utilizar la herramienta de consola ‘sysv-rc-conf’:
apt-get install sysv-rc-conf
Ejecutando ‘sysv-rc-conf’ podremos marcar los servicios/scripts/demonios que queremos arrancar al iniciar el sistema. La aplicación aplica los cambios de forma instantanea al situarnos sobre alguna de las X y pulsar ‘espacio’. Para salir pulsamos la ‘q’.
Si queremos saber en que runlevel arranca el sistema por defecto:
$ grep ':initdefault' /etc/inittab id:2:initdefault:
En caso de que no exista el fichero, es porque probablemente tengamos el sistema de arranque Upstart, el runlevel por defecto suele ser el 2 (es posible verificarlo en ‘/etc/event.d/rc-default’).
Suele ser recomendable desactivar la posibilidad de reiniciar el ordenador mediante CTRL+ALT+SUPR para evitar reinicios involuntarios (p.ej. la señora de la limpieza).
En caso de que utilicemos el tradicional sistema de arranque de Unix/Linux (System V init), bastará con comentar (/etc/inittab):
# What to do when CTRL-ALT-DEL is pressed. #ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now
Y aplicar los cambios:
# init q
O si como mecanismo de arranque usamos Upstart (como es el caso de Ubuntu), editamos ‘/etc/event.d/control-alt-delete’ y comentamos:
#exec /sbin/shutdown -r now "Control-Alt-Delete pressed"
Servicio de correo: Postfix
El sistema requerirá un servicio de correo aunque únicamente sea para uso local, dado que varias tareas automáticas reportaran su estado mediante correo.
En caso de que nuestro sistema venga configurado con sendmail, es recomendable desinstalarlo e instalar algún otro sistema más robusto como postfix (historicamente sendmail ha tenido muchos fallos de seguridad):
apt-get purge sendmail sendmail-base sendmail-bin sendmail-cf sendmail-doc apt-get install procmail apt-get install postfix
Para una configuración básica y segura, primero definiremos que usuario del sistema recibirá los correos destinados a root (p.ej. muchas tareas automáticas del sistema envían correo a root). Editamos ‘/etc/aliases’ y añadimos:
root: miusuario
Y ejecutamos:
newaliases
A continuación, securizaremos la instalación de postfix de forma que muestre la información mínima necesaria. Editamos ‘/etc/postfix/main.cf’:
#smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu) smtpd_banner = Welcome biff = no # Rewrite "user" to "user@$myorigin" with locally submitted mail # Or "user" to "user@$remote_header_rewrite_domain" with remote connection # only if $remote_header_rewrite_domain is not empty (by default it is empty) # # By default: myorigin = $mydomain = /etc/mailname append_at_myorigin = yes
Asi ocultamos la versión y obligamos a que todo mail enviado por los procesos automáticos del sistema tengan como dominio el especificado en ‘/etc/mailname’ (si se envian a usuario@localhost se reescribira como usuario@dominio).
Editamos ‘/etc/mailname’ para establecer el dominio que queremos, por ejemplo:
un.ejemplo.com
Si no vamos a utilizar Postfix como un servicio de correo remoto y únicamente lo queremos usar de forma local, editamos ‘/etc/postfix/master.cf’ para asegurarnos que únicamente se aceptan conexiones localhost:
127.0.0.1:smtp inet n - - - - smtpd
Con esto ya tenemos postfix preparado:
/etc/init.d/postfix restart
Finalmente, si nos interesa que el correo local que reciba un usuario sea reenviado a otra dirección, en el directorio personal del usuario creamos ‘.procmailrc’:
:0 c /var/mail/miusuario # Copia del correo personal a gmail :0 !miotrousuario@gmail.com
Para realizar configuraciones más complejas de Postfix podemos consultar esta otra guía.
Servicio web: Apache
Una vez instalado apache (habitualmente con soporte PHP), podemos proceder a realizar una securización de su configuración.
PHP
En primer lugar, nos aseguramos que PHP no informe de su presencia editando ‘/etc/php5/apache2/php.ini’ y modificando:
; Misc ; ; Decides whether PHP may expose the fact that it is installed on the server ; (e.g. by adding its signature to the Web server header). It is no security ; threat in any way, but it makes it possible to determine whether you use PHP ; on your server or not. expose_php = On
por:
expose_php = Off
Por otra parte, también se recomienda establecer límites a los recursos que puede utilizar una web PHP:
;;;;;;;;;;;;;;;;;;; ; Resource Limits ; ;;;;;;;;;;;;;;;;;;; max_execution_time = 30 ; Maximum execution time of each script, in seconds max_input_time = 60 ; Maximum amount of time each script may spend parsing request data ;max_input_nesting_level = 64 ; Maximum input variable nesting level memory_limit = 16M ; Maximum amount of memory a script may consume (16MB)
Apache básico
Veamos como mejorar la configuración de apache. Por un lado editamos ‘/etc/apache2/apache2.conf’ y cambiamos los siguientes parámetros para que Apache no muestre información innecesaria:
#ServerTokens Full ServerTokens Prod #ServerSignature On ServerSignature Off
También debemos aseguramos que no se cambian estos parámetros en los sites activos (‘/etc/apache2/sites-enabled/*).
En general los comando HTTP TRACE y TRACK no son utilizados para servir páginas y por tanto, podemos desactivarlos para evitar que un usuario malintencionado los utilice para intentar atacar. Para cada uno de los sites-enabled, lo editamos y añadimos dentro del VirtualHost:
RewriteEngine on RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK) RewriteRule .* - [F]
Activamos el modulo ‘rewrite’ y reiniciamos el servicio:
a2enmod rewrite /etc/init.d/apache2 restart
A continuación podemos validar que realmente ha surgido efecto el bloqueo de los comandos TRACE y TRACK:
$ telnet 127.0.0.1 80 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. TRACE / HTTP/1.0 A: b C: d Host: foo HTTP/1.1 403 Forbidden Date: Tue, 05 Aug 2008 17:09:34 GMT Server: Apache Content-Length: 255 Connection: close Content-Type: text/html; charset=iso-8859-1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>403 Forbidden</title> </head><body> <h1>Forbidden</h1> <p>You don't have permission to access / on this server.</p> <hr> <address>Apache Server at foo Port 80</address> </body></html> Connection closed by foreign host.
Otra medida de seguridad consiste en desactivar el uso de FollowSymLinks para no permitir que un usuario pueda crear enlaces simbólicos hacia directorios que no deseamos que estén expuestos. Para ello, en las opciones de los sites-enabled debemos indicar:
Options None
Finalmente, es conveniente que le indiquemos a Apache exactamente en que IPs queremos que escuche. De esta forma podemos limitarlo por ejemplo únicamente para uso local. Para ello editamos ‘/etc/apache2/ports.conf’ y cambiamos Listen 80 por Listen ip_maquina:80.
Otro aspecto importante es proteger el servicio de ataques de denegación de servicio (DoS). En el fichero ‘/etc/apache2/apache2.conf’ podemos realizar algunos cambios:
- Si establecemos un timeout bajo, minimizamos el impacto de los ataques pero puede que algunos CGIs que requieran más tiempo dejen de funcionar (p.ej. 300 segundos):
Timeout 300
- Si tenemos activado el HTTP KeepAlive, definir un timeout bajo minimiza el impacto de ataques (p.ej. 15 segundos):
KeepAliveTimeout 15 MaxKeepAliveRequests 100
- Habitualmente, aunque apache2-mpm-worker, se utiliza apache2-mpm-prefork como versión de apache por defecto dado que esta es compatible con PHP. Prefork puede ser customizado, pero estos son los valores recomendados por defecto:
<IfModule mpm_prefork_module> # Number of child server processes created on startup StartServers 5 # Sets the desired minimum number of idle child server processes MinSpareServers 5 # Sets the desired maximum number of idle child server processes MaxSpareServers 10 # Sets the limit on the number of simultaneous requests that can be supported; MaxClients 150 # Sets the limit on the number of requests that an individual child server process will handle MaxRequestsPerChild 0 </IfModule>
De todos ellos, los únicos parámetros con los que se suele jugar son MaxClient y MaxRquestPerChild. Con este último, indicar un valor diferente de cero nos permitiría limitar la memoria utilizada por procesos que tienen memory leakages.
- Limitar el tamaño del BODY de las peticiones puede ser muy útil por ejemplo para evitar que los usuarios suban ficheros excesivamente grandes (p.ej. 500 KB = 1024*500 bytes):
LimitRequestBody 512000
- Limitamos el número de campos que puede tener una petición en el HEADER (de media suelen tener 20) y su tamaño (7KB = 8190 bytes)
LimitRequestFields 50 LimitRequestFieldSize 8190 LimitRequestLine 8190
Módulos de Apache
Apache se encuentra programado con funciones residentes en módulos independientes al core de la aplicación, eso nos permite activar únicamente aquellas características que vamos a utilizar para minimizar riesgos. Desactivación de módulos:
a2dismod alias a2dismod auth_basic a2dismod authn_file a2dismod authz_default a2dismod authz_groupfile a2dismod authz_host a2dismod authz_user a2dismod autoindex a2dismod cgi a2dismod dir a2dismod env a2dismod mime a2dismod negotiation a2dismod php5 a2dismod rewrite a2dismod setenvif a2dismod ssl a2dismod status
Activación de modulos mínimos:
# Default page (e.g. index.html) a2enmod dir a2enmod mime # Language preference a2enmod negotiation a2enmod php5 a2enmod rewrite # Workarounds for certain browser bugs a2enmod setenvif # Allows access rules based on hosts (allow from...) a2enmod authz_host
Servir por localhost las aplicaciones más criticas
Finalmente, si vamos a utilizar Apache como servidor web remoto pero queremos también instalar aplicaciones de gestión como phpmyadmin, cacti, nagios, etc… Es recomendable que estas se encuentren en una instancia del servidor que solo sea accesible desde local para los usuarios de la máquina, pero no para el resto del mundo. Es decir, vamos a evitar que cualquier persona pueda acceder libremente por ejemplo a ‘http://www.midominio.com/phpmyadmin’, pero si vamos a permitir que un usuario con cuenta en el sistema pueda acceder mediante SSH realizando Port Forwarding:
ssh -L 8080:localhost:80 miusuario@IP_DEL_SERVIDOR
Con el anterior comando, ssh abrirá el puerto 8080 en la máquina del cliente y redireccionará todo lo que le llegue al puerto 80 de la interfaz localhost del servidor. Por tanto, el cliente (una vez se ha autenticado con su usuario por SSH) podrá acceder a phpmyadmin mediante ‘http://localhost:8080/phpmyadmin’. Adicionalmente, todo el tráfico viajará cifrado sin necesidad de configurar el SSL de Apache.
Con este objetivo, vamos a crear ‘/etc/apache2/sites-available/localhost’:
NameVirtualHost 127.0.0.1:80 <VirtualHost 127.0.0.1:80> ServerAdmin admin@admin.com ServerName localhost ServerAlias localhost.localdomain DocumentRoot /var/www/localhost <Directory /var/www/localhost> AllowOverride All Options FollowSymLinks Order allow,deny allow from all </Directory> # Possible values include: debug, info, notice, warn, error, crit, # alert, emerg. LogLevel warn ErrorLog /var/log/apache2/localhost/error.log CustomLog /var/log/apache2/localhost/access.log combined ServerSignature Off Include /etc/apache2/conf.d/ </VirtualHost>
Activamos el site:
mkdir /var/www/localhost mkdir /var/log/apache2/localhost a2ensite localhost /etc/init.d/apache2 restart
Cuando instalamos phpmyadmin (u otras aplicaciones web similares) mediante el sistema de paquetes de Ubuntu, se generan ficheros de configuración en ‘/etc/apache2/conf.d/’ para que apache pueda encontrar la aplicación web (p.ej. mediante alias). Esto nos resta control y por defecto hace que la web sea accesible remotamente. Para minimizar riesgos, comentamos la línea de ‘/etc/apache2/apache2.conf’ que carga estas configuraciones estándar:
# Include generic snippets of statements #Include /etc/apache2/conf.d/
Y en contrapartida, tendremos que copiar/enlazar todo aquello que queramos que sea accesible únicamente por localhost en el directorio ‘/var/www/localhost’.
Base de datos MySQL
Securizamos la instalación básica ejecutando ‘mysql_secure_installation’. Según el manual, este comando permite:
- You can set a password for root accounts.
- You can remove root accounts that are accessible from outside the local host.
- You can remove anonymous-user accounts.
- You can remove the test database, which by default can be accessed by anonymous users.
Se recomienda aplicar las 4 acciones. Por otra parte, en la configuración de ‘/etc/mysql/my.cnf’:
# Both location gets rotated by the cronjob. # Be aware that this log type is a performance killer. # log = /var/log/mysql/mysql.log # # Error logging goes to syslog. This is a Debian improvement :) # # Here you can see queries with especially long duration log_slow_queries = /var/log/mysql/mysql-slow.log long_query_time = 2
Logeamos las queries que tardan más de 2 segundos en ejecutarse.
/etc/init.d/mysql restart
Finalmente, para crear un usuario con su propia base de datos:
CREATE USER 'miusuario'@'localhost' IDENTIFIED BY 'YB9gYu6I'; GRANT USAGE ON *.* TO 'miusuario'@'localhost' IDENTIFIED BY 'YB9gYu6I' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0 ; CREATE DATABASE IF NOT EXISTS miusuario; GRANT ALL PRIVILEGES ON miusuario.* TO 'miusuario'@'localhost'; FLUSH PRIVILEGES;
Acceso remoto: SSH
Para la securización de SSH, vamos a hacer que únicamente un grupo de usuario pueda conectarse. Creamos el grupo ‘sshlogin’ y añadimos usuarios al mismo:
sudo groupadd sshlogin sudo adduser miusuario sshlogin
Importante: Si utilizamos FreeNX o NX (artículo sobre FreeNX) para el acceso remoto al escritorio, también tendremos que añadir al grupo ‘sshlogin’ el usuario ‘nx’.
Editamos ‘/etc/ssh/sshd_config’ para impedir que root pueda acceder directamente y no se forwardeen las X:
PermitRootLogin no X11Forwarding no AllowGroups sshlogin
Reiniciamos el servicio:
sudo /etc/init.d/ssh restart
En caso de que desde nuestra máquina tengamos que acceder a diversos servicios SSH y no queremos tener que recordar las diferentes contraseñas, podemos generar una pareja de claves RSA (pública y privada) en la máquina cliente (se recomienda que asignar password para proteger la clave privada):
$ ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/home/user/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/user/.ssh/id_rsa. Your public key has been saved in /home/user/.ssh/id_rsa.pub. The key fingerprint is: 18:0f:fe:29:b2:17:c2:a0:8d:6e:11:80:de:01:21:be user@localhost
Con ssh-keygen hemos creado una clave privada ‘~/.ssh/id_rsa’ que no debemos compartir con nadie y una clave pública ‘~/.ssh/id_rsa.pub’ que hay que copiar en el directorio ‘~/.ssh/authorized_keys’ de los servidores a los que queramos conectar con SSH:
scp ~/.ssh/id_rsa.pub usuario@servidor:/home/user/
Y desde cada servidor:
$ cat id_rsa.pub >> .ssh/authorized_keys $ rm id_rsa.pub
Podemos poner determinadas condiciones bajo las cuales se utilizaran estas claves, por ejemplo:
from="81.11.105.10",command="/bin/ls",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAABdZjH4WE5y5EH/O9nHoplj8sZz0iIb1XPU5e5ZTxd7tWhMf9p391yAfM6ybjabmFXZZuFm9q/AjD53qjX+q7YhQUXC0I5JhFU0CAHxKuvuguogWio39x36Ag/zgJ5IJXcrCNsuoDw== root@servidor.com
Al inicio de la clave hemos especificado:
- from=”81.11.105.10″: únicamente puede ser utilizada desde un cliente con esa IP.
- command=”/bin/ls”: únicamente se permitirá la ejecución de ese fichero.
- no-port-forwarding: no permite redirigir puertos.
- no-X11-forwarding: no permite la redirección gráfica de programas (X Window)
- no-agent-forwarding: no permite redirigir peticiones entre servidores a agentes ssh de autenticación.
- no-pty: el cliente no obtendrá una terminal con shell, solo podra ejecutar comando (p.ej. ‘ssh usuario@servidor /bin/ls’)
A partir de este momento, cada vez que queramos conectarnos a uno de estos servidores SSH, ssh nos preguntará por la contraseña que le hemos puesto a nuestra clave privada y por tanto, solo hace falta recordar 1 contraseña. No obstante, aun podemos simplificar más la gestión:
- En la generación de las claves RSA no asignamos contraseña y por tanto esta no será requerida para conectarnos a los servidores por SSH (en caso de que ya la hayamos asignado, podemos cambiarla mediante ‘ssh-keygen -p’). Este punto tiene el inconveniente de que en caso de que alguien se haga con el fichero que contiene la clave privada, tendrá acceso completo a los servidores configurados.
- Uso de keychain (ssh-agent) para que únicamente tengamos que teclear la contraseña que protege la clave privada una vez.
Veamos como configurar el segundo punto:
$ sudo apt-get install keychain
Editamos el fichero ‘~/.bashrc’ y añadimos al final:
## SSH Agent # Do not execute if I am root (sudo -s) if [ "$(id -u)" != "0" ]; then keychain id_rsa --nocolor --quiet . ~/.keychain/`uname -n`-sh fi
Salimos del sistema y volvemos a entrar para que sean efectivos los cambios de bashrc. La primera vez que se ejecute ‘keychain’ nos preguntará la contraseña que protege a la clave privada:
Enter passphrase for /home/user/.ssh/id_rsa: Identity added: /home/user/.ssh/id_rsa (/home/user/.ssh/id_rsa)
Mientras permanezca la máquina en marcha podremos acceder a los servidores mediante SSH sin que nos solicite de nuevo la contraseña.
En el siguiente enlace podéis encontrar una descripción detallada de cómo funciona SSH.
7. Trazabilidad
Fecha y hora del sistema
Para que los logs resulten de utilidad y tengamos una buena trazabilidad de todo lo que ocurre en el sistema, es necesario que la máquina disponga de la fecha/hora correcta. Para ello podemos configurar la sincronización automática con otros servidores de Internet via NTP:
apt-get install ntpdate
Creamos ‘/etc/cron.daily/ntpdate’:
#! /bin/bash /usr/sbin/ntpdate -u ntp.ubuntu.com
Y damos permisos de ejecución:
chmod 755 /etc/cron.daily/ntpdate
Por defecto el sistema utilizará los servidores de Ubuntu (ntp.ubuntu.com) para la sincronización.
En caso de que fuese necesario, también conviene asegurarnos que el sistema tiene configurada la zona horaria que nos interese (p.ej. Europa/Madrid):
# date Wed Aug 6 07:14:36 EDT 2008 # cd /etc # mv timezone timezone.old # echo "Europe/Madrid" > timezone # mv localtime localtime.old # ln -s /usr/share/zoneinfo/Europe/Madrid localtime # date Wed Aug 6 13:16:08 CEST 2008
Logs de usuario con Bash
En ‘/etc/profile’ podemos añadir las siguientes variables de entorno (personalmente únicamente añado la que permite tener histórico con timestamp, visualizable con el comando history de cada usuario):
######## ## declare -r: Variables de entorno que no pueden ser modificadas ## readonly -p: Lista variables que no pueden ser modificadas # Nombre del archivo de historial (default '~/.bash_history') export HISTFILE='~/.bash_history' #declare -r HISTFILE='~/.bash_history' # nº máximo de comandos que puede contener el archivo (default 500). export HISTFILESIZE='500' # nº de comandos por los que podrá navegar el usuario con las teclas de cursor (default 500) export HISTSIZE='500' # Ignorar comandos duplicados y espacios en blanco delante (default blank) export HISTCONTROL=ignorespace:ignoredups # Save history with timestamp (default blank) export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S " # No guardar en el historial los siguientes comandos (por defecto en blanco) export HISTIGNORE=”:ls:cd ..:cd /:”
Esto mismo también lo podemos establecer en ‘/etc/bash.bashrc’ si queremos que no solo afecte a las login shells.
Rotación y conservación de logs
En el sistema debemos tener instalado anacron o cron, si tenemos ambos simultaneamente (aunque uno de los dos demonios esté parado) no se ejecutaran las tareas de /etc/cron.daily, weekly, etc.. (debido a como viene por defecto /etc/crontab). En nuestro caso, al ser un servidor y estar siempre en marcha usaremos cron:
apt-get purge anacron apt-get install cron
La política de gestión de logs será:
- Rotación mensual
- Mantenimiento de las últimas 24 copias (2 años de log)
- Incorporar la fecha de creación al nombre del fichero
Preparamos las tareas de rotacion de log a ejecutar por cron de forma mensual:
chmod 644 /etc/cron.daily/sysklogd chmod 644 /etc/cron.weekly/sysklogd cp /etc/cron.weekly/sysklogd /etc/cron.monthly/ chmod 755 /etc/cron.monthly/sysklogd
Editamos ‘/etc/cron.monthly/sysklogd’ y modificamos la linea que llama a save_log para incluir en la rotación todos los ficheros log gestionados por syslogd:
logs=$(syslogd-listfiles --weekly)
por:
logs=$(syslogd-listfiles --all)
También modificaremos:
savelog -g adm -m 640 -u ${USER} -c 4 $LOG >/dev/null
por:
# Rotate monthly (script placed at /etc/cron.monthly/) # Keep 24 copies: -c 24 # Clean older copies than 24: -C # Add date instead of .0 .1: -d # Create a new file owned by adm and syslog with permission 640 savelog -g adm -m 640 -u ${USER} -c 24 -d -C $LOG >/dev/null
De esta forma, syslogd cumplirá con la política de gestión de logs que hemos definido.
Mediante ‘syslogd-listfiles –all’ podemos ver todos los ficheros que serán rotados, por ejemplo:
/var/log/mail.warn /var/log/uucp.log /var/log/user.log /var/log/daemon.log /var/log/messages /var/log/debug /var/log/auth.log /var/log/mail.err /var/log/syslog /var/log/mail.log /var/log/mail.info /var/log/kern.log /var/log/lpr.log
El resto de logs que no son generados por syslogd, los gestionaremos a través de logrotate.
Editamos /etc/logrotate.conf y establecemos la política general de logs:
# rotate log monthly monthly # keep 24 months of backlog rotate 24 # uncomment this if you want your log files compressed compress # Postpone compression of the previous log file to the next rotation cycle delaycompress # create new (empty) log files after rotating old ones create # uncomment this if you want your log files compressed compress # Postpone compression of the previous log file to the next rotation cycle delaycompress # Archive old versions of log files adding a daily extension like YYYYMMDD instead of simply adding a number. dateext # Rotate the log file even if it is empty ifempty # packages drop log rotation information into this directory include /etc/logrotate.d # no packages own wtmp, or btmp -- we'll rotate them here /var/log/wtmp { missingok # monthly create 0664 root utmp # rotate 1 } /var/log/btmp { missingok # monthly create 0664 root utmp # rotate 1 }
En ‘/etc/logrotate.d’ tenemos ficheros con los logs a rotar y en algunos casos, con políticas concretas diferentes a la global. Para nos aseguramos que no haya políticas diferentes a la global para logs específicos podemos ejecutar:
find /etc/logrotate.d/ -type f -exec sed -i 's/daily/#daily/g' {} ; find /etc/logrotate.d/ -type f -exec sed -i 's/weekly/#weekly/g' {} ; find /etc/logrotate.d/ -type f -exec sed -i 's/monthly/#monthly/g' {} ; find /etc/logrotate.d/ -type f -exec sed -i 's/rotate/#rotate/g' {} ; find /etc/logrotate.d/ -type f -exec sed -i 's/post#rotate/postrotate/g' {} ; find /etc/logrotate.d/ -type f -exec sed -i 's/pre#rotate/prerotate/g' {} ; find /etc/logrotate.d/ -type f -exec sed -i 's/notifempty/#notifempty/g' {} ; find /etc/logrotate.d/ -type f -exec sed -i 's/size/#size/g' {} ;
Cabe destacar que es necesario revisar la configuración existente en ‘/etc/logrotate.d’ por si faltase realizar alguna modificación extra (p.ej. logs extra de apache como ‘/var/log/apache2/localhost/*.log’).
Del resto de logs del sistema que queramos rotar y no estén ya incluidos, tendremos que crear un fichero nuevo en ‘/etc/logrotate.d’ indicándolo (en general, todo paquete de Ubuntu ya viene preparado para que sus logs sean gestionados automáticamente por syslogd o logrotate). Es importante tener en cuenta que no debemos rotar:
- Binarios como /var/log/faillog o /var/log/lastlog
- Logs de nagios dado que romperíamos el historial interno. Mejor que lo gestione la propia aplicación directamente.
- Logs generados directamente por syslogd (como hemos visto antes). Visualizar con syslogd-listfiles –all
Análisis de logs de Apache mediante Awstats
Con awstats podremos ver las visitas, tráfico y otros aspectos relacionados con cada uno de los dominios virtuales que tengamos configurados en Apache. Para su instalación presuponemos que ya tenemos configurado Apache y que awstats va ser accesible únicamente de forma local:
apt-get install awstats mkdir /var/lib/awstats/localhost chown www-data:www-data /var/lib/awstats/localhost
Creamos ‘/etc/awstats/awstats.localhost.conf’ apuntando al log que queramos analizar:
LogFile="/var/log/apache2/localhost/access.log" LogFormat=1 DNSLookup=1 DirData="/var/lib/awstats/localhost" DirCgi="/cgi-bin" DirIcons="/icon" SiteDomain="localhost" AllowToUpdateStatsFromBrowser=1 AllowFullYearView=3
Copiamos iconos básicos:
cp -r /usr/share/awstats/icon /var/www/localhost/
Actualizamos output HTML:
/usr/lib/cgi-bin/awstats.pl -config=localhost -update
Creamos el fichero ‘/etc/apache2/conf.d/awstats.conf’ con el siguiente contenido:
ScriptAlias /cgi-bin/awstats.pl /usr/lib/cgi-bin/awstats.pl
Reiniciamos apache:
/etc/init.d/apache2 restart
Y accedemos a la web mediante ‘http://localhost:8080/cgi-bin/awstats.pl?config=localhost’.
Para que se actualice periódicamente el análisis de Awstats, editamos /etc/cron.d/awstats (cada noche):
0 1 * * * www-data /usr/lib/cgi-bin/awstats.pl -config=localhost -update >/dev/null
Envío por correo de los eventos más significativos con logcheck
Con logcheck podremos resumir los eventos del sistema registrados en los logs y enviarlos a nuestro correo de forma diaria:
apt-get install logcheck logcheck-database
Editamos ‘/etc/logcheck/logcheck.logfiles’:
# Estos 2 ficheros incluyen todos los mensajes enviados a syslog ()/etc/syslog.conf) /var/log/syslog /var/log/auth.log # Apache /var/log/apache2/error.log /var/log/apache2/localhost/error.log # MySQL #/var/log/mysql/mysql.log # En debian, si descomentas la linea de log de /etc/mysql/my.cnf, el log va por syslog #/var/log/mysql/mysql-slow.log
La tarea cron que se encarga de la ejecución diaria es ‘/etc/cron.d/logcheck’.
Por otra parte, la configuración de logcheck se encuentra en ‘/etc/logcheck/logcheck.conf’:
# Controls the address mail goes to: # *NOTE* the script does not set a default value for this variable! # Should be set to an offsite "emailaddress@some.domain.tld" SENDMAILTO="miusuario" # Send the results as attachment or not. # 0=not as attachment; 1=as attachment # Default ist 0 MAILASATTACH=1
Una vez configurado, podemos probar su funcionamiento ejecutando:
sudo -u logcheck logcheck
Logcheck incorpora por defecto toda una serie de reglas para la selección o el descarte de las lineas del log a enviar en el resumen. Es posible que nos interese refinar estas reglas, haciendo que sean descartadas cadenas que no nos aporten nada. Por ejemplo, veamos como ignorar las lineas generadas por el firewall o nagios (ver secciones correspondientes), dado que esa información ya la analizamos con otras herramientas.
Creamos ‘/etc/logcheck/ignore.d.server/000-custom’ y añadimos:
^w{3} [ :0-9]{11} [._[:alnum:]-]+ kernel: [FW:[ ._[:alnum:]-]+] ^w{3} [ :0-9]{11} [._[:alnum:]-]+ nagios2: CURRENT SERVICE STATE: ^w{3} [ :0-9]{11} [._[:alnum:]-]+ nagios2: CURRENT HOST STATE: ^w{3} [ :0-9]{11} [._[:alnum:]-]+ nagios2: Warning: A system time change of [0-9]+ seconds (backwards in time) has been detected. Compensating...
Si queremos incluir más reglas, podemos validarlas contra el log que nos interese:
sed -e 's/[[:space:]]*$//' /var/log/ulog/syslogemu.log | egrep '^w{3} [ :0-9]{11} [._[:alnum:]-]+ kernel: [FW:[ ._[:alnum:]-]+]' sed -e 's/[[:space:]]*$//' /var/log/syslog | egrep '^w{3} [ :0-9]{11} [._[:alnum:]-]+ nagios2: CURRENT SERVICE STATE:' sed -e 's/[[:space:]]*$//' /var/log/syslog | egrep '^w{3} [ :0-9]{11} [._[:alnum:]-]+ nagios2: CURRENT HOST STATE:' sed -e 's/[[:space:]]*$//' /var/log/syslog | egrep '^w{3} [ :0-9]{11} [._[:alnum:]-]+ nagios2: Warning: A system time change of [0-9]+ seconds (backwards in time) has been detected. Compensating...'
Si la regla funciona, aparecerán los mensajes que queremos eliminar.
8. Monitorización del sistema
Actualización automática de la lista de paquetes del sistema
Para comprobar que nuestro sistema se encuentra actualizado, primero necesitamos que se actualice la lista de paquetes automáticamente. Creamos ‘/etc/apt/apt.conf.d/10periodic’:
APT::Periodic::Update-Package-Lists "1"; APT::Periodic::Download-Upgradeable-Packages "0"; APT::Periodic::AutocleanInterval "0";
De esta forma se actualizará el listado de paquetes (apt-get update) cada día mediante el fichero ‘/etc/cron.daily/apt’.
Si hemos instalado nagios (ver Sección correspondiente), este nos informará de cuando hay paquetes pendientes de ser actualizados.
Aviso de actualizaciones pendientes
Con apticron podremos hacer que el sistema nos envíe un correo cada vez que se disponga de paquetes pendientes de ser actualizados (p.ej. parches de seguridad):
apt-get install apticron
Para su uso necesitamos tener configurado un servidor de correo (SMTP), podemos configurar un postfix (ver sección anterior de servicios) o utilizar ssmtp para conectar contra un servidor de correo ya configurado.
En el segundo caso, instalamos mediante ‘apt-get install ssmtp’ y editamos ‘/etc/ssmtp/ssmtp.conf’:
# Config file for sSMTP sendmail # # The person who gets all mail for userids < 1000 # Make this empty to disable rewriting. root=miusuario # The place where the mail goes. The actual machine name is required no # MX records are consulted. Commonly mailhosts are named mail.domain.com mailhub=servidor.correo.com:25 # Where will the mail seem to come from? #rewriteDomain=nonsense.non # The full hostname hostname=el.dominio.de.esta.maquina.com # Are users allowed to set their own From: address? # YES - Allow the user to specify their own From: address # NO - Use the system generated From: address FromLineOverride=NO
En el fichero anterior indicamos a quien redirigimos los correos de root, el servidor de correo que utilizaremos y el dominio con el que saldrán los correos de la máquina actual.
Tanto si ya tenemos un servidor postfix configurado como si hemos optado por SSMTP, debemos probar que el comando 'mail' nos permite enviar correos:
echo "Prueba" | mail -s "Prueba" miusuario@otrolugar.com
Una vez validado, podemos continuar con la configuración de apticron editando ‘/etc/apticron/apticron.conf’ y estableciendo el correo al que queremos recibir las alertas:
EMAIL="miusuario@otrolugar.com"
Esto es todo. Cron ejecutará diariamente el script ‘/etc/cron.daily/apticron’, el cual comprobará las actualizaciones pendientes y enviará un correo en caso de que sea necesario.
Cabe destacar que para que funcione, el sistema debe estar configurado para que actualice automáticamente el listado de paquetes (ver sección anterior).
Monitorizar desde la consola
Espacio en disco
Para la monitorización del espacio en disco podemo usar los comandos:
df -h du -ms
El primero muestra el estado de todas las particiones, el segundo presenta los megas que ocupa el directorio actual.
Puede que nos interese ordenar los directorios en función de su tamaño, para ello podemos descargar un script específico:
cd /usr/local/bin wget http://www.pixelbeat.org/scripts/dutop chmod 755 /usr/local/bin/dutop
Que al ejecutar facilita la siguiente información:
# dutop 27% 183.8M ./Fotos 26% 180.5M ./Fotos.zip 19% 135.0M ./Documentos 5% 37.4M ./Trabajo 5% 35.6M ./Musica
CPU
La monitorización de la CPU (más otros parámetros) podemos llevarla a cabo mediante ‘top’ o ‘htop’:
apt-get install htop
Ancho de banda / Bandwidth
Aplicaciones recomendadas:
apt-get install bwm-ng
Y también:
apt-get install jnettop
Para este último debemos crear el fichero ‘~/.jnettop’:
#interface "eth0" # Show IP and Ports per separate local_aggregation none # I don't care about remote port remote_aggregation port # Do not generate more traffic resolving every IP resolve off
Al ejecutar jnettop podemos interactuar con la aplicación para cambiar los parámetros establecidos. Por ejemplo:
- ‘l’: cambia la agregación local
- ‘r’: cambia la agregación remota
Conexiones de red
Conexiones de la máquina:
# netstat -atunp
Si la máquina se encuentra haciendo NAT en una red, podemos ver también las conexiones “NATeadas”:
# apt-get install netstat-nat # netstat-nat -N
Si hemos configurado el firewall con iptables (ver sección correspondiente), podremos ver las conexiones controladas por el módulo CONNTRACK:
# apt-get install iptstate # iptstate
Memoria RAM / Swap
Para obtener un listado de los procesos del sistema ordenados por la memoria consumida:
ps -A --sort=-rsz -o pid,vsz,rssize,pmem,comm
Cada parámetro indica:
- pid: Process ID
- vsz: Virtual Memory Size (includes RAM & Swap)
- rssize: Resident Memory Size (only RAM)
- pmem: Ratio of the process’s resident set size to the physical memory on the machine
Para ver la memoria total utilizada tenemos dos opciones:
# vmstat -S M procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 0 0 34 6 12 134 0 0 348 60 556 521 10 1 75 14
# free -m total used free shared buffers cached Mem: 502 494 8 0 12 132 -/+ buffers/cache: 349 153 Swap: 972 34 938
Cada columna significa:
- Cache: Bloques de información leídos del disco duro
- Buffer: Memoria temporal utilizada por el kernel (p.ej. comunicaciones con dispositivos, envio/recepción de información por red, etc.)
- Swap: Cuando la memoria RAM escasea, el kernel transfiere partes de la memoria RAM a la memoria SWAP, la cual realmente se encuentra en el disco duro y por tanto es mucho más lenta (pero nos permite cargar más aplicaciones de las que podriamos con nuestra RAM actual).
Cuando se requiere más memoria, el kernel debe decidir si reducir la cache/buffers o enviar bloques de memoria a la Swap. Esta decisión se encuentra parametrizada mediante el valor swappines:
cat /proc/sys/vm/swappiness
Habitualmente su valor es de 60, pero puede situarse entre 0 (poco probable que se realice swap) y 100 (máxima prioridad a realizar swap). Según las aplicaciones de nuestro sistema quizás nos interese cambiarlo, p.ej. en un desktop, cuando se ejecutan procesos cron que consumen mucho disco duro (por ejemplo slocate), la cache crecerá y puede que se envíen aplicaciones a la swap (swap-in) y el usuario notará una ralentización notable del sistema en el momento que quiera acceder a ellas (swap-out). Para cambiar el parámetro:
# echo "10" > /proc/sys/vm/swappiness # cat /proc/sys/vm/swappiness
Este cambio se perderá en el siguiente reinicio, para que sea permanente editamos /etc/sysctl.conf y añadimos la linea:
vm.swappiness=10
‘ps_mem’ es un script (python) que imprime el total de memoria RAM sumarizada por proceso padre (p.ej. si apache realiza varios forks, al mostrar la información los unificará):
cd /usr/local/bin wget http://www.pixelbeat.org/scripts/ps_mem.py chmod 755 /usr/local/bin/ps_mem.py
Resultado de ejemplo:
# ps_mem.py |less Warning: Shared memory is slightly over-estimated by this system for each program, so totals are not reported. Private + Shared = RAM used Program 68.0 KiB + 320.0 KiB = 388.0 KiB klogd 116.0 KiB + 288.0 KiB = 404.0 KiB atd 80.0 KiB + 416.0 KiB = 496.0 KiB mysqld_safe 88.0 KiB + 416.0 KiB = 504.0 KiB dd 68.0 KiB + 436.0 KiB = 504.0 KiB logger 152.0 KiB + 520.0 KiB = 672.0 KiB syslogd 96.0 KiB + 584.0 KiB = 680.0 KiB init 152.0 KiB + 720.0 KiB = 872.0 KiB less 208.0 KiB + 740.0 KiB = 948.0 KiB cron 308.0 KiB + 1.3 MiB = 1.6 MiB pickup 332.0 KiB + 1.3 MiB = 1.7 MiB master 352.0 KiB + 1.4 MiB = 1.7 MiB qmgr 1.2 MiB + 856.0 KiB = 2.0 MiB screen (2) 780.0 KiB + 2.3 MiB = 3.0 MiB sshd (3) 1.2 MiB + 1.9 MiB = 3.1 MiB mutt 1.8 MiB + 2.3 MiB = 4.1 MiB snmpd 840.0 KiB + 7.0 MiB = 7.8 MiB apache2 (6) 9.0 MiB + 1.4 MiB = 10.4 MiB bash (6) 15.4 MiB + 5.1 MiB = 20.6 MiB mysqld
Monitorización en tiempo real: Nagios
Con Nagios podemos monitorizar diversos sistemas en tiempo real, generando alertas cuando se incumpla alguno de los parámetros especificados. También vale la pena considerar la opción de utilizar Monit en su lugar.
# apt-get install nagios2 nagios-images nagios-plugins
Configuramos un usuario para nagios:
# sudo htpasswd -c /etc/nagios2/htpasswd.users nagiosadmin New password: Re-type new password: Adding password for user nagiosadmin
Preparamos el sistema:
# chown nagios:nagios /var/log/nagios2/ # chown -R nagios:www-data /var/cache/nagios2/ # cd /bin/ # ln -s /usr/bin/mail mail
Creamos nuestra configuración específica:
mkdir /etc/nagios2/mysite cd /etc/nagios2/mysite cp cp ../conf.d/contacts_nagios2.cfg contacts.cfg cp ../conf.d/timeperiods_nagios2.cfg timeperiods.cfg cp ../conf.d/localhost_nagios2.cfg localhost.cfg cp ../conf.d/generic-service_nagios2.cfg generic-service.cfg cp ../conf.d/generic-host_nagios2.cfg generic-host.cfg
En estos ficheros de configuración especificaremos los servidores y servicios que queremos monitorizar. Para una guía más detallada consultar el wiki de Ubuntu sobre Nagios para así configurar Nagios según nuestras necesidades..
Hay que tener en cuenta que si alguno de los servicios a comprobar es una mysql, debemos asegurarnos que nagios tiene su propio usuario con acceso:
mysql -u root -p CREATE USER 'nagios'@'localhost'; FLUSH PRIVILEGES;
Nagios depende de apache, dado que su interfaz principal es via web. Para ello apache va a requerir algunos módulos extra:
a2enmod auth_basic a2enmod authn_file a2enmod authz_default a2enmod authz_groupfile a2enmod authz_host a2enmod authz_user a2enmod cgi a2enmod alias /etc/init.d/apache2 restart
Una vez configurado, la puesta en marcha:
# /etc/init.d/nagios2 stop # /etc/init.d/nagios2 start
Cabe destacar que los plugins de nagios se encuentran en ‘/usr/lib/nagios/plugins’, y puede que nos interese tener soporte SNMP (ver sección SNMP):
apt-get install nagios-snmp-plugins
Ancho de banda consumido con Bandwidthd
Bandwidthd dibuja gráficas y tablas HTML con el tráfico generado, así podemos controlar lo que sucede en el servidor:
apt-get install bandwidthd cd /var/www ln -s /var/lib/bandwidthd/htdocs/ bandwidthd
Configuración en /etc/bandwidthd/bandwidthd.conf:
subnet 0.0.0.0/0
Cabe destacar que bandwithd consume bastante memoria RAM (200 MB aprox. de media) y quizás con otras herramientas como Cacti ya tenemos información suficiente sobre el ancho de banda.
SNMP
SNMP es un protocolo que permite monitorizar servidores y dispositivos de red de forma remota. Nos puede resultar útil activarlo en nuestra máquina de forma local para que sea utilizado en conjunto con Nagios y/o Cacti.
# apt-get install snmpd snmp
Nos aseguramos que solo sea accesible por localhost /etc/default/snmpd y desactivamos los módulos smux, ipv6 :
# snmpd options (use syslog, close stdin/out/err). # Log by Syslog only: warning messages, errors, alerts and critical. # ingore: notice info and debug SNMPDOPTS='-Ls 01234 -Lf /dev/null -A -u snmp -I -smux,ipv6 -p /var/run/snmpd.pid 127.0.0.1'
En la configuración ‘/etc/snmp/snmpd.conf’ indicamos::
## sec.name source community ## ======== ====== ========= #com2sec local localhost public #com2sec network_1 172.16.1.0/24 public #com2sec network_2 192.168.2.0/24 public ## Access.group.name sec.model sec.name ## ================= ========= ======== #group MyROGroup_1 v1 local #group MyROGroup_1 v1 network_1 #group MyROGroup_2 v2c network_2 ## MIB.view.name incl/excl MIB.subtree mask ## ============== ========= =========== ==== #view all-mibs included .1 80 ## MIB ## group.name context sec.model sec.level prefix read write notif ## ========== ======= ========= ========= ====== ==== ===== ===== #access MyROGroup_1 "" v1 noauth exact all-mibs none none #access MyROGroup_2 "" v2c noauth exact all-mibs none none # rocommunity public syslocation NewYork syscontact admin@localhost #ignoredisk /dev/rdsk/c0t2d0 disk / #disk /usr #disk /var proc apache2 proc mysqld
La configuración anterior hace que SNMP sea accesible en modo lectura para cualquier usuario, además monitorizará el disco principal “/” y los procesos apache2 y mysqld.
Reiniciamos el servicio:
/etc/init.d/snmpd restart
Si queremos lanzar consultas SNMP desde la terminal:
snmpwalk -v 1 -c public localhost interface|less snmpwalk -v 1 -c public localhost system|less snmpget -v 1 -c const1payted localhost ifPhysAddress.2
Para una explicación más detallada vale la pena leer el siguiente tutorial: Monitoring Server Performance.
Soporte ‘Devices I/O’ en net-snmp
En Ubuntu, net-snmp no viene compilado con soporte para proporcionar información de I/O de los dispositivos del sistema (disco duro). Tenemos la opción de recompilar activando la funcionalidad, o delegar esta tarea a un script que consultará /proc/diskstats. Optamos por la segunda por su sencillez:
Editamos/etc/snmp/snmpd.conf y añadimos al final:
pass .1.3.6.1.4.1.2021.13.15 /usr/local/bin/snmp-diskio-collector
Creamos el script /usr/local/bin/snmp-diskio-collector:
#!/bin/sh # by Jamie Wilkinson jamie @anchor.net.au # this code is in the public domain # based on passtest from the net-snmp distribution examples # WARNING there is shitloads of IO required to get this information :) debug_flag=0 debug () { if [ $debug_flag -eq 1 ]; then echo $* >> /tmp/snmp-diskio-collector-debug.log fi } PLACE=".1.3.6.1.4.1.2021.13.15" REQ="$2" debug debug "new run" debug "args $*" if [ "$1" = "-s" ]; then exit 0 fi # the 'tail' of the oid, everything after $PLACE oidtail=`echo $REQ | sed "s/^$PLACE//"` debug "oidtail=$oidtail" # number of devices we can export devcount=`wc -l /proc/diskstats | cut -f1 -d' '` debug "devcount=$devcount" item=`echo $oidtail | cut -f4 -d.` index=`echo $oidtail | cut -f5 -d.` debug "oidtail=$oidtail, item=$item, index=$index" if [ "$1" = "-n" ]; then if [ -z "$item" ]; then item=1 index=1 elif [ -z "$index" ]; then index=1 else index=`expr $index + 1` if [ "$index" -gt "$devcount" ]; then index=1 item=`expr $item + 1` if [ "$item" -gt 6 ]; then # break out of the loop exit 0; fi fi fi RET=$PLACE.1.1.$item.$index else case "$REQ" in $PLACE) exit 0;; *) RET=$REQ ;; esac fi debug "after -n, item=$item, index=$index" debug "RET is now $RET" echo "$RET" debug "oidtail=$oidtail, item=$item, index=$index" # awk uses this variable in the environment below export index # see linux kernel Documentation/iostats.txt for format if [ -n "$index" ]; then case "$item" in 1) # diskIOIndex debug "result: diskIOIndex $index" echo "integer" echo $index exit 0 ;; 2) # diskIODevice debug "result: diskIODevice $index" echo "string" awk 'FNR == ENVIRON["index"] { print $3 }' /proc/diskstats exit 0 ;; 3) # diskIONRead debug "result: diskIONRead $index" echo "counter" awk 'FNR == ENVIRON["index"] { print $6 }' /proc/diskstats exit 0 ;; 4) # diskIONWritten debug "result: diskIONWritten $index" echo "counter" awk 'FNR == ENVIRON["index"] { print $10 }' /proc/diskstats exit 0 ;; 5) # diskIOReads debug "result: diskIOReads $index" echo "counter" awk 'FNR == ENVIRON["index"] { print $4 }' /proc/diskstats exit 0 ;; 6) # diskIOWrites debug "result: diskIOWrites $index" echo "counter" awk 'FNR == ENVIRON["index"] { print $8 }' /proc/diskstats exit 0 ;; *) exit 0; #echo "string"; echo "debug... $RET $REQ"; exit 0 ;; esac else exit 0 fi
Damos permisos de ejecución y reiniciamos SNMP:
chmod 755 /usr/local/bin/snmp-diskio-collector /etc/init.d/snmpd restart
Para probar que funciona correctamente:
snmpwalk -m ALL -v1 -c public localhost diskIOTable
Monitorización historica: Cacti
Cacti es una herramienta que almacena un histórico del estado de diferentes aspecto del sistema local (p.ej. CPU, Memoria, ancho de banda, etc.) o remoto via SNMP. Vale la pena considerar también muni como alternativa.
Cacti requerirá Apache y SNMP para funcionar (ver secciones correspondientes).
apt-get install cacti ln -s /usr/bin/php5 /usr/bin/php
Una vez instalado, podemos acceder vía web por defecto en ‘http://localhost/cacti’ (aunque puede variar depende de como configuremos apache).
A través de la web, crearemos un “dispositivo SNMP” que realmente será nuestro sistema y añadiremos unas gráficas estándar:
- Crear dispositivo “ucd/net SNMP Host” apuntando a localhost.
- Añadir graph templates:
1) ucd/net – CPU Usage
2) ucd/net – Load Average
3) ucd/net – Memory Usage
4) Unix – Logged in Users
5) Unix – Ping Latency
6) Unix – Processes - Añadir data queries:
1) SNMP – Get Mounted Partitions
2) SNMP – Get Processor Information
3) SNMP – Interface Statistics
4) ucd/net – Get Monitored Partitions - Añadir gráficas desde “New graphs”
- Desde “Graph Management”, seleccionar las nuevas gráficas y añadirlas al al árbol (Place on a Tree)
Más información en la web de cacti.
Script Device I/O para cacti
Si hemos activado el soporte I/O para dispositivos (disco duro) en el servicio SNMP, podemos hacer que cacti monitorice también esa información.
Lo mejor es seguir las instrucciones del foro de cacti, pero a grandes rasgos los pasos a seguir son los siguientes:
# cd /usr/share/cacti/resource/snmp_queries/ # wget http://forums.cacti.net/download.php?id=2635 # mv download.php?id=2635 Cacti_Net-SNMP_DevIO_v2.zip # unzip Cacti_Net-SNMP_DevIO_v2.zip # rm -f *TMPL.xml Cacti_Net-SNMP_DevIO_v2.zip
Desde un PC cliente, descargamos el mismo fichero e importamos via web los ficheros:
- ‘net-snmp_devIO-BytesRW_graphTMPL.xml’:
Cacti has imported the following items: GPRINT Preset [success] Normal [update] Data Input Method [success] Get SNMP Data (Indexed) [update] Data Template [success] ucd/net - Device I/O [new] Graph Template [success] ucd/net - Device I/O - Bytes Read/Written [new]
- ‘net-snmp_devIO-ReadsWrites_graphTMPL.xml’
Cacti has imported the following items: GPRINT Preset [success] Exact Numbers [update] Data Input Method [success] Get SNMP Data (Indexed) [update] Data Template [success] ucd/net - Device I/O [update] Graph Template [success] ucd/net - Device I/O - Reads/Writes [new]
- ‘net-snmp_devIO-LoadAVG_graphTMPL’: solo es compatible con sistemas BSD. No lo cargamos en Linux.
Creamos el Data Query mediante “Data Queries – Add”:
Name: ucd/net - Get Device I/O Description: XML Path: <path_cacti>/resource/snmp_queries/net-snmp_devio.xml Data Input Method: Get SNMP Data (indexed)
Pulsamos “Create”.
Ahora es el momento de añadir las gráficas asociadas. En el recuadro associate graph, pulsamos sobre Add y rellenamos el formulario:
Name: Device I/O - Bytes Read/Written Graph template: ucd/net Device I/O - Bytes Read/Written
Pulsamos Create.
A continuación, asociamos las siguientes fuentes seleccionando del desplegable la fuente que concuerde según la descripción y marcando el checkout de la derecha:
Data Source: Bytes_Read Data Source: Bytes_Written
Volvemos a añadir una nueva gráfica al Data Query:
Name: Device I/O - Reads/Writes Graph template: ucd/net Device I/O - Reads/Writes
Asociamos las siguientes fuentes:
Data Source: Device_Reads Data Source: Device_Writes
Finalmente al Host template “ucd/net SNMP Host” asociamos la Data Query “ucd/net – Get Device I/O “. A partir de aquí, todos los nuevos hosts que se asignen la plantilla “ucd/net SNMP Host”, podran crear gráficas de lectura/escritura al disco duro.
9. Seguridad
Cortafuegos / Firewall
Antes de configurar el cortafuegos, vamos a preparar el sistema ULOG que nos permitirá registrar eventos procedentes de iptables:
apt-get install ulogd ulogd-mysql
Por defecto ULOG viene configurado para que el fichero de log sea ‘/var/log/ulog/syslogemu.log’. No obstante, ULOG también nos permite almacenar la información en una MySQL. Para activarlo descomentamos de ‘/etc/ulogd.conf’:
plugin="/usr/lib/ulogd/ulogd_MYSQL.so"
Y, en el mismo fichero, configuramos el acceso a la MySQL:
[MYSQL] table="ulog" pass="secret" user="ulog" db="ulog" host="localhost"
Finalmente, en la MySQL creamos el usuario y la BBDD con su correspondiente estructura:
CREATE USER 'ulog'@'localhost' IDENTIFIED BY 'secret'; GRANT USAGE ON * . * TO 'ulog'@'localhost' IDENTIFIED BY 'secret' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0 ; CREATE DATABASE IF NOT EXISTS `ulog` ; GRANT ALL PRIVILEGES ON `ulog` . * TO 'ulog'@'localhost'; FLUSH PRIVILEGES; USE ulog; source /usr/share/doc/ulogd-mysql/mysql.table;
Para conocer las bases de “iptables” podemos ver este otro artículo con más explicaciones sobre iptables, veamos un ejemplo más avanzado que en general establece las siguientes reglas:
- Por defecto denegamos todo el tráfico.
- Limitamos los rangos de IP reservados para redes privadas (excepto si estamos en una de ellas)
- Bloqueamos ataques de SYN/RST flood.
- Bloqueamos paquetes malformados.
- Mantenemos una lista negra temporal de IPs que han intentado realizar un ataque de fuerza bruta: han intentado realizar más de 2 conexiones en 20 segundos al puerto de SSH. Las IPs se mantienen en la lista negra durante 10 minutos.
- Dejamos el puerto SSH de entrada abierto.
- De salida permitimos:
- DNS
- SMTP + IMAP SSL
- SSH
- HTTP/HTTPS
- NTP de Ubuntu
- Respecto al protocolo ICMP, permitimos:
- Echo request hacia Internet
- Echo reply desde Internet
- Destination Unrecheable desde Internet
- Time exceed desde Internet
- Registramos el tráfico que no ha sido aceptado por las reglas anteriores (no obstante limitamos el número de registros por segundo para no desbordar el sistema):
- Entrada
- Salida
- Redirecciones: en nuestro ejemplo no aplica.
Podemos guardar el script en ‘/usr/local/bin/firewall’:
#!/bin/bash # # Copyright (C) 2008 http://www.marblestation.com # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # ######### CONFIGURABLE ######### IPTABLES="/sbin/iptables"; ECHO="/bin/echo"; #LAN_INTERFACE_GETIP="eth0:0" LAN_INTERFACE="eth0" LOOPBACK_INTERFACE="lo" #LAN_IP1="71.11.10.19" LAN_IP1=`ifconfig $LAN_INTERFACE|grep inet|grep -v inet6|cut -d " " -f 12|cut -d ":" -f 2` LAN_IP2="" LAN_IP3="" DNS_IP="213.186.33.99" # NS1 NS2 of ISP #BROADCAST_IP1=71.11.10.255" BROADCAST_IP1=`ifconfig $LAN_INTERFACE|grep inet|grep -v inet6|cut -d " " -f 14|cut -d ":" -f 2` #LAN_NET=$LAN_IP/`ifconfig $LAN_INTERFACE|grep inet|grep -v inet6|cut -d " " -f 16|cut -d ":" -f 2` BANNED_IPs="" NTP_UBUNTU="91.189.94.4" ####### FIN CONFIGURABLE ####### ## # Required kernel modules: nf_conntrack, nf_nat, nf_nat_ftp, nf_conntrack_ftp # # * Non-standard ports: # modprobe ip_conntrack_ftp ports=21,2121 # modprobe ip_nat_ftp ports=21,2121 # # Current tracked connections: # cat /proc/net/ip_conntrack | wc -l # # Max allowed tracked connections: # sysctl -a net.ipv4.netfilter.ip_conntrack_max # # Possible modifications to /etc/sysctl.conf: # ## Time limit to consider a connection active: 432000 seconds (432000 / (60*60*24) = 8 days) # net.ipv4.netfilter.ip_conntrack_tcp_timeout_established = 28800 # ## Max tracked connections (65536 connections * 350 bytes per connection) / (1024 * 1024) = 21,87 MBytes # net.ipv4.netfilter.ip_conntrack_max = 65536 # ## Time limit to close a connection (60 seconds) # net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait = 60 # # Reload sysctl: sysctl -p ## # Reset tablas de filtrado $IPTABLES -t filter -Z $IPTABLES -t filter -F $IPTABLES -t filter -X # Reset tablas de nuevas conexiones $IPTABLES -t nat -Z $IPTABLES -t nat -F $IPTABLES -t nat -X # Reset tablas de modificación de paquetes $IPTABLES -t mangle -Z $IPTABLES -t mangle -F $IPTABLES -t mangle -X echo "[start] Default policy" # Politica general de tráfico entrante/saliente y redirigido $IPTABLES -t filter -P INPUT DROP $IPTABLES -t filter -P OUTPUT DROP $IPTABLES -t filter -P FORWARD DROP # Politica de las cadenas extendidas (donde se pueden realizar alteraciones de paquetes) $IPTABLES -t mangle -P INPUT ACCEPT $IPTABLES -t mangle -P OUTPUT ACCEPT $IPTABLES -t mangle -P PREROUTING ACCEPT $IPTABLES -t mangle -P POSTROUTING ACCEPT # Paquetes que crean nuevas conexiones y deben ser alterados por NAT $IPTABLES -t nat -P OUTPUT ACCEPT $IPTABLES -t nat -P PREROUTING ACCEPT $IPTABLES -t nat -P POSTROUTING ACCEPT echo "[end] Default policy" echo "[start] Local users control" #--uid-owner {USERNAME} : Matches if the packet was created by a process with the given effective USERNAME. #--gid-owner {groupid}: Matches if the packet was created by a process with the given effective group id. #--pid-owner {processed}: Matches if the packet was created by a process with the given process id. # Allow any TCP connection originated by myuser #$IPTABLES -A OUTPUT -o $LAN_INTERFACE -p tcp --sport 1024:65535 --dport 1:65535 -m state --state NEW,ESTABLISHED -m owner --uid-owner myuser -j ACCEPT #$IPTABLES -A INPUT -i $LAN_INTERFACE -p tcp --sport 1:65535 --dport 1024:65535 -m state --state RELATED,ESTABLISHED -j ACCEPT echo "[end] Local users control" echo "[start] Loopback" # Permitir trafico a la interfaz loopback $IPTABLES -A INPUT -i $LOOPBACK_INTERFACE -s 127.0.0.1 -j ACCEPT $IPTABLES -A OUTPUT -o $LOOPBACK_INTERFACE -d 127.0.0.1 -j ACCEPT # Permitir trafico a la interfaz loopback con IPs internas SPACE=" " INTERNAL_IPs=$LAN_IP1$SPACE$LAN_IP2$SPACE$LAN_IP3 for internal_ip in $INTERNAL_IPs do $IPTABLES -A INPUT -i $LOOPBACK_INTERFACE -s $internal_ip -j ACCEPT $IPTABLES -A OUTPUT -o $LOOPBACK_INTERFACE -d $internal_ip -j ACCEPT done echo "[end] Loopback" echo "[start] Banned IPs" ##################### IPs Baneadas for banned_ip in $BANNED_IPs do $IPTABLES -A INPUT -s $banned_ip -j DROP $IPTABLES -A OUTPUT -s $banned_ip -j DROP $IPTABLES -A FORWARD -s $banned_ip -j DROP done ##################### Fin IPs Baneadas echo "[end] Banned IPs" echo "[start] Reserved IPs blocks" ##################### Prevent spoofing # New chain $IPTABLES -N reserved_blocks $IPTABLES -F reserved_blocks # Redirect all traffic to new chain $IPTABLES -A INPUT -j reserved_blocks $IPTABLES -A OUTPUT -j reserved_blocks ### [start] private networks # 10.0.0.0/8 Private network RFC 1918 $IPTABLES -A reserved_blocks -s 10.0.0.0/8 -j DROP $IPTABLES -A reserved_blocks -d 10.0.0.0/8 -j DROP # 172.16.0.0/12 Private network RFC 1918 $IPTABLES -A reserved_blocks -s 172.16.0.0/12 -j DROP $IPTABLES -A reserved_blocks -d 172.16.0.0/12 -j DROP # 169.254.0.0/16 Link-Local RFC 3927 $IPTABLES -A reserved_blocks -s 169.254.0.0/16 -j DROP $IPTABLES -A reserved_blocks -d 169.254.0.0/16 -j DROP # 192.168.0.0/16 Private network RFC 1918 # !! Limited to LAN_INTERFACE, because in other interface we have 192.168.0.0 networks #$IPTABLES -A reserved_blocks -i $LAN_INTERFACE -s 192.168.0.0/16 -j DROP #$IPTABLES -A reserved_blocks -o $LAN_INTERFACE -d 192.168.0.0/16 -j DROP ### [end] private networks ### [start] Other reserved blocks # 0.0.0.0/8 Current network (only valid as source address) RFC 1700 $IPTABLES -A reserved_blocks -s 0.0.0.0/8 -j DROP $IPTABLES -A reserved_blocks -d 0.0.0.0/8 -j DROP # 14.0.0.0/8 Public data networks (per 2008-02-10, available for use[1]) RFC 1700 # Addresses within this block are assigned to users and should be treated as such # Do not drop # 127.0.0.0/8 Loopback RFC 3330 $IPTABLES -A reserved_blocks -s 127.0.0.0/8 -j DROP $IPTABLES -A reserved_blocks -d 127.0.0.0/8 -j DROP # 128.0.0.0/16 Reserved (IANA) RFC 3330 # Addresses in this block are subject to future allocation to a Regional Internet # Registry for assignment in the normal manner. ## Do not drop # 191.255.0.0/16 Reserved (IANA) RFC 3330 # Addresses in this block are subject to future allocation to a Regional Internet # Registry for assignment in the normal manner. ## Do not drop # 192.0.2.0/24 Documentation and example code RFC 3330 (Test-Net [RFC3330]) $IPTABLES -A reserved_blocks -s 192.0.2.0/24 -j DROP $IPTABLES -A reserved_blocks -d 192.0.2.0/24 -j DROP # 192.88.99.0/24 IPv6 to IPv4 relay RFC 3068 $IPTABLES -A reserved_blocks -s 192.88.99.0/24 -j DROP $IPTABLES -A reserved_blocks -d 192.88.99.0/24 -j DROP # 198.18.0.0/15 Network benchmark tests RFC 2544 $IPTABLES -A reserved_blocks -s 198.18.0.0/15 -j DROP $IPTABLES -A reserved_blocks -d 198.18.0.0/15 -j DROP # 223.255.255.0/24 Reserved (IANA) RFC 3330 $IPTABLES -A reserved_blocks -s 223.255.255.0/24 -j DROP $IPTABLES -A reserved_blocks -d 223.255.255.0/24 -j DROP # 224.0.0.0/3 includes: # - Reserved for Multicast (former Class D network) RFC 3171 (224.0.0.0/4): From 224/8 to 239/8 # - Reserved for Future use (former Class E network) RFC 1700 (240.0.0.0/4): From 240/8 to 255/8 # - Broadcast: 255.255.255.255 $IPTABLES -A reserved_blocks -s 224.0.0.0/3 -j DROP $IPTABLES -A reserved_blocks -d 224.0.0.0/3 -j DROP ### [end] Other reserved blocks ##################### Fin Direcciones privadas echo "[end] Reserved IPs blocks" echo "[start] Attack protection" ##################### Ataques echo "* SYN/RST flood" # Stop flood # New chain $IPTABLES -N flood $IPTABLES -F flood # Redirect all eth0 traffic to new chain $IPTABLES -A INPUT -i $LAN_INTERFACE -p tcp --syn -j flood $IPTABLES -A OUTPUT -o $LAN_INTERFACE -p tcp --syn -j flood $IPTABLES -A FORWARD -i $LAN_INTERFACE -p tcp --syn -j flood # Chain rules # Drop excessive RST packets to avoid TCP reset attacks, by given the # next real data packet in the sequence a better chance to arrive first. $IPTABLES -A flood -p tcp -m tcp --tcp-flags RST RST -m limit --limit 2/second --limit-burst 2 -j ACCEPT # Protect against SYN floods by rate limiting the number of new # connections from any host to 5 per second. $IPTABLES -A flood -m state --state NEW -p tcp -m tcp --syn -m recent --name synflood --set $IPTABLES -A flood -m state --state NEW -p tcp -m tcp --syn -m recent --name synflood --update --seconds 1 --hitcount 5 -m limit --limit 30/s --limit-burst 3 -j ULOG --ulog-nlgroup 1 --ulog-prefix "[FW:SYN Packets flood] " $IPTABLES -A flood -m state --state NEW -p tcp -m tcp --syn -m recent --name synflood --update --seconds 1 --hitcount 5 -j DROP echo "* Malformed packets" # Create new chain $IPTABLES -N malformed_packets $IPTABLES -F malformed_packets # Redirect all traffic to new chain $IPTABLES -A INPUT -j malformed_packets $IPTABLES -A OUTPUT -j malformed_packets $IPTABLES -A FORWARD -j malformed_packets # Log & Drop all incoming fragments $IPTABLES -A malformed_packets -f -m limit --limit 30/s --limit-burst 3 -j ULOG --ulog-nlgroup 1 --ulog-prefix "[FW:Fragments Packets] " $IPTABLES -A malformed_packets -f -j DROP # Log & Drop new connections with SYN flag not set #$IPTABLES -A malformed_packets -p tcp ! --syn -m state --state NEW -m limit --limit 30/s --limit-burst 3 -j ULOG --ulog-nlgroup 1 --ulog-prefix "[FW:New without SYN] " $IPTABLES -A malformed_packets -p tcp ! --syn -m state --state NEW -j DROP # Log & Drop all incoming malformed NULL packets (Block null scans) $IPTABLES -A malformed_packets -p tcp --tcp-flags ALL NONE -m limit --limit 30/s --limit-burst 3 -j ULOG --ulog-nlgroup 1 --ulog-prefix "[FW:NULL Packets] " $IPTABLES -A malformed_packets -p tcp --tcp-flags ALL NONE -j DROP # NULL packets # Drop all incoming malformed XMAS packets (Block xmas scans) $IPTABLES -A malformed_packets -p tcp --tcp-flags ALL ALL -m limit --limit 30/s --limit-burst 3 -j ULOG --ulog-nlgroup 1 --ulog-prefix "[FW:XMAS Packets] " $IPTABLES -A malformed_packets -p tcp --tcp-flags ALL ALL -j DROP #XMAS # Drop all incoming malformed FIN packets (Block FIN scans) $IPTABLES -A malformed_packets -p tcp --tcp-flags FIN,ACK FIN -m limit --limit 30/s --limit-burst 3 -j ULOG --ulog-nlgroup 1 --ulog-prefix "[FW:Fin Packets Scan] " $IPTABLES -A malformed_packets -p tcp --tcp-flags FIN,ACK FIN -j DROP # FIN packet scans # Drop bogus TCP packets $IPTABLES -A malformed_packets -p tcp -m tcp --tcp-flags SYN,FIN SYN,FIN -m limit --limit 30/s --limit-burst 3 -j ULOG --ulog-nlgroup 1 --ulog-prefix "[FW:Bogus Packets] " $IPTABLES -A malformed_packets -p tcp -m tcp --tcp-flags SYN,FIN SYN,FIN -j DROP $IPTABLES -A malformed_packets -p tcp -m tcp --tcp-flags SYN,RST SYN,RST -m limit --limit 30/s --limit-burst 3 -j ULOG --ulog-nlgroup 1 --ulog-prefix "[FW:Bogus Packets] " $IPTABLES -A malformed_packets -p tcp -m tcp --tcp-flags SYN,RST SYN,RST -j DROP # Log & Drop all incoming invalid packets #$IPTABLES -A malformed_packets -m state --state INVALID -m limit --limit 30/s --limit-burst 3 -j ULOG --ulog-nlgroup 1 --ulog-prefix "[FW:Invalid Packets] " $IPTABLES -A malformed_packets -m state --state INVALID -j DROP echo " - Bruce force attack prevention" ##### [start] Prevent Brute force attack $IPTABLES -N brute_force $IPTABLES -N brute_force_blacklist $IPTABLES -A brute_force_blacklist -m limit --limit 30/s --limit-burst 3 -j ULOG --ulog-nlgroup 1 --ulog-prefix "[FW:Bruteforce] " $IPTABLES -A brute_force_blacklist -m recent --name brute_force_blacklist --set $IPTABLES -A brute_force_blacklist -j DROP # Incoming connections from blacklisted hosts are dropped for 10 minutes (every droped connection, restarts the 10 minutes period). $IPTABLES -A brute_force -m recent --update --name brute_force_blacklist --seconds 600 --hitcount 1 -j DROP # These rules are just for counting of incoming connections. $IPTABLES -A brute_force -m recent --set --name counting1 $IPTABLES -A brute_force -m recent --set --name counting2 $IPTABLES -A brute_force -m recent --set --name counting3 $IPTABLES -A brute_force -m recent --set --name counting4 # A host is blacklisted if it exceeds: # - 2 connection attempts in 20 seconds # - 14 in 200 seconds # - 79 in 2000 seconds # - 399 attempts in 20000 seconds. $IPTABLES -A brute_force -m recent --update --name counting1 --seconds 20 --hitcount 3 -j brute_force_blacklist $IPTABLES -A brute_force -m recent --update --name counting2 --seconds 200 --hitcount 15 -j brute_force_blacklist $IPTABLES -A brute_force -m recent --update --name counting3 --seconds 2000 --hitcount 80 -j brute_force_blacklist $IPTABLES -A brute_force -m recent --update --name counting4 --seconds 20000 --hitcount 400 -j brute_force_blacklist $IPTABLES -A brute_force -j ACCEPT # Control SSH service: only new conections BRUTEFORCE_EXCEPTION_IP="132.106.3.12" #$IPTABLES -A INPUT -p TCP -s ! $BRUTEFORCE_EXCEPTION_IP --dport 22 -m state --state NEW -j brute_force $IPTABLES -A INPUT -p TCP --dport 22 -m state --state NEW -j brute_force ##### [end] Prevent Brute force attack ##################### Fin Ataques echo "[end] Attack protection" echo "[start] Internet < -> Host" echo "* Incoming traffic" ##################### Puertos de entrada abiertos echo " - SSH" # Aceptamos SSH $IPTABLES -A INPUT -i $LAN_INTERFACE -p tcp -s 0/0 --sport 1024:65535 -d $LAN_IP1 --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT $IPTABLES -A OUTPUT -o $LAN_INTERFACE -p tcp -s $LAN_IP1 --sport 22 -m state --state RELATED,ESTABLISHED -j ACCEPT ##################### Fin Puertos de entrada abiertos echo "* Outgoing traffic" ##################### Puertos de salida abiertos echo " - DNS" # Outgoing DNS # udp first for dns_server_ip in $DNS_IP do $IPTABLES -A OUTPUT -p udp -s $LAN_IP1 --sport 1024:65535 -d $dns_server_ip --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT $IPTABLES -A INPUT -p udp -s $dns_server_ip --sport 53 -d $LAN_IP1 -m state --state RELATED,ESTABLISHED -j ACCEPT # tcp next $IPTABLES -A OUTPUT -p tcp -s $LAN_IP1 --sport 1024:65535 -d $dns_server_ip --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT $IPTABLES -A INPUT -p tcp -s $dns_server_ip --sport 53 -d $LAN_IP1 -m state --state RELATED,ESTABLISHED -j ACCEPT done echo " - SMTP" #outgoing SMTP $IPTABLES -A OUTPUT -p tcp -s $LAN_IP1 --sport 1024:65535 -d 0/0 --dport 25 -m state --state NEW,ESTABLISHED -j ACCEPT $IPTABLES -A INPUT -p tcp -s 0/0 --sport 25 -d $LAN_IP1 -m state --state RELATED,ESTABLISHED -j ACCEPT echo " - IMAP SSL" #outgoing IMAP SSL $IPTABLES -A OUTPUT -p tcp -s $LAN_IP1 --sport 1024:65535 -d 0/0 --dport 993 -m state --state NEW,ESTABLISHED -j ACCEPT $IPTABLES -A INPUT -p tcp -s 0/0 --sport 993 -d $LAN_IP1 -m state --state RELATED,ESTABLISHED -j ACCEPT echo " - SSH" #outgoing SSH $IPTABLES -A OUTPUT -p tcp -s $LAN_IP1 --sport 1024:65535 -d 0/0 --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT $IPTABLES -A INPUT -p tcp -s 0/0 --sport 22 -d $LAN_IP1 -m state --state RELATED,ESTABLISHED -j ACCEPT echo " - HTTP & HTTPS" #outgoing http and https # for apt-get and other stuff $IPTABLES -A OUTPUT -p tcp -s $LAN_IP1 --sport 1024:65535 -d 0/0 --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT $IPTABLES -A INPUT -p tcp -s 0/0 --sport 80 -d $LAN_IP1 -m state --state RELATED,ESTABLISHED -j ACCEPT $IPTABLES -A OUTPUT -p tcp -s $LAN_IP1 --sport 1024:65535 -d 0/0 --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT $IPTABLES -A INPUT -p tcp -s 0/0 --sport 443 -d $LAN_IP1 -m state --state RELATED,ESTABLISHED -j ACCEPT echo " - NTP" #outgoing NTP to ubuntu servers $IPTABLES -A OUTPUT -p udp -s $LAN_IP1 --sport 1024:65535 -d $NTP_UBUNTU --dport 123 -m state --state NEW,ESTABLISHED -j ACCEPT $IPTABLES -A INPUT -p udp -s $NTP_UBUNTU --sport 123 -d $LAN_IP1 -m state --state RELATED,ESTABLISHED -j ACCEPT ##################### Fin Puertos de salida abiertos echo "* ICMP" ##################### ICMP ### Drop icmp, but only after letting certain types through. # Echo Reply #$IPTABLES -A INPUT -p icmp --icmp-type 0 -j ACCEPT # Destination Unreachable #$IPTABLES -A INPUT -p icmp --icmp-type 3 -j ACCEPT # Tiempo Excedido #$IPTABLES -A INPUT -p icmp --icmp-type 11 -j ACCEPT # Echo Request #$IPTABLES -A INPUT -p icmp --icmp-type 8 -m limit --limit 30/second -j ACCEPT ## Allow outgoing ping # Echo Request to Internet $IPTABLES -A OUTPUT -o $LAN_INTERFACE -p icmp --icmp-type 8 -d 0/0 -s $LAN_IP1 -m limit --limit 30/second -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT # Echo Reply from Internet $IPTABLES -A INPUT -i $LAN_INTERFACE -p icmp --icmp-type 0 -d $LAN_IP1 -s 0/0 -m state --state ESTABLISHED,RELATED -j ACCEPT # Destination Unreachable from Internet $IPTABLES -A INPUT -i $LAN_INTERFACE -p icmp --icmp-type 3 -d $LAN_IP1 -s 0/0 -m state --state ESTABLISHED,RELATED -j ACCEPT # Time Exceeded from Internet $IPTABLES -A INPUT -i $LAN_INTERFACE -p icmp --icmp-type 11 -d $LAN_IP1 -s 0/0 -m state --state ESTABLISHED,RELATED -j ACCEPT # Allow ping between internal IPs SPACE=" " INTERNAL_IPs=$LAN_IP1$SPACE$LAN_IP2$SPACE$LAN_IP3 for internal_ip1 in $INTERNAL_IPs do for internal_ip2 in $INTERNAL_IPs do # Receive Echo Reply $IPTABLES -A INPUT -p icmp --icmp-type 0 -d $internal_ip1 -s $internal_ip2 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT # Receive Echo Request $IPTABLES -A INPUT -p icmp --icmp-type 8 -d $internal_ip1 -s $internal_ip2 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT # Send Echo Request $IPTABLES -A OUTPUT -p icmp --icmp-type 8 -d $internal_ip1 -s $internal_ip2 -m limit --limit 30/second -m state --state ESTABLISHED,RELATED -j ACCEPT # Receive Echo Reply $IPTABLES -A OUTPUT -p icmp --icmp-type 0 -d $internal_ip1 -s $internal_ip2 -m limit --limit 30/second -m state --state ESTABLISHED,RELATED -j ACCEPT done done ##################### Fin ICMP # Use "-m state" (ip_conntrack module) in order to allow legitim traffic: # # - NEW: A packet which creates a new connection. # - ESTABLISHED: A packet which belongs to an existing connection (i.e., a reply packet, or outgoing packet on a # connection which has seen replies). # - RELATED: A packet which is related to, but not part of, an existing connection, such as an ICMP error, or (with # the FTP module inserted), a packet establishing an ftp data connection. # - INVALID: A packet which could not be identified for some reason: this includes running out of memory and ICMP # errors which don't correspond to any known connection. Generally these packets should be dropped. ### Log echo "[start] Logging" echo "* Logging exceptions" ## [start] Do not log # netbios UDP $IPTABLES -A INPUT -p udp -d $BROADCAST_IP1 --dport 137:138 -j DROP ## [end] Do not log echo "* Logging rules" # Limit: The first 3 packets that match this rule will be logged. After this, it will be 30 seconds until one of the # burst will be regained; if no packets hit the rule for 90 seconds, the burst will be fully recharged. $IPTABLES -A INPUT -m limit --limit 30/s --limit-burst 3 -j ULOG --ulog-nlgroup 1 --ulog-prefix "[FW:INPUT Drop] " $IPTABLES -A OUTPUT -m limit --limit 30/s --limit-burst 3 -j ULOG --ulog-nlgroup 1 --ulog-prefix "[FW:OUTPUT Drop] " $IPTABLES -A FORWARD -m limit --limit 30/s --limit-burst 3 -j ULOG --ulog-nlgroup 1 --ulog-prefix "[FW:FORWARD Drop] " echo "[end] Logging" # Disable firewall in 30 seconds... # ...so we can test it #SAFETY=`sleep 30;/usr/local/bin/firewall-off` &
Creamos el script ‘/usr/local/bin/firewall-off’:
#!/bin/bash ######### CONFIGURABLE ######### IPTABLES="/sbin/iptables"; ####### FIN CONFIGURABLE ####### # Reset $IPTABLES -Z $IPTABLES -F $IPTABLES -t nat -F $IPTABLES -t nat -X $IPTABLES -t mangle -F $IPTABLES -t mangle -X # Politica general $IPTABLES -P INPUT ACCEPT $IPTABLES -P OUTPUT ACCEPT $IPTABLES -P FORWARD ACCEPT
Es importante que establezcamos unos permisos seguros a los scripts:
# chmod 750 /usr/local/firewall # chmod 750 /usr/local/firewall-off # chown root:root /usr/local/firewall # chown root:root /usr/local/firewall-off
A partir de estos ejemplos, probablemente será necesario adaptarlos a nuestras necesidades. Cuando cubra nuestros requerimientos, podemos hacer que se ejecute automáticamente al iniciar el sistema si lo añadimos al fichero ‘/etc/rc.local’ (antes del exit 0).
Monitorización de firewall con fwlogwatch
fwlogwatch permite enviar resumenes diarios por correo o generar output en HTML sobre el estado del firewall:
# apt-get install fwlogwatch # dpkg-reconfigure fwlogwatch
Depues del reconfigure, /etc/default/fwlogwatch quedara parecido a lo siguiente:
START_DAEMON='true' PARAMS='-A ' MODE='' EMAIL='root@localhost' CRON_EMAIL='root@localhost' CRON_PARAMS='-l 1h -t -p -d -n -N -O Sapata' # 1 day: -l 1d # show time: -t # show protocol: -p # differenciate destination port: -d # reverse dns: -n # service (not only port): -N # sort by destination IP (ascending), protocol (asc) and time (asc): -O Sapata
Editamos /etc/fwlogwatch/fwlogwatch.config para indicarle el log:
input = /var/log/ulog/syslogemu.log # Use 'alert_threshold' to define how many connections must happen (within # the 'forget' time range) to activate an alert/response. # Command line option: -a [count] # alert_threshold = 10
Editamos /etc/group:
adm:x:4:logcheck,www-data
Para el reporte HTML, presuponemos que tenemos configurado Apache. Copiamos el CGI correspondiente:
cp /usr/share/doc/fwlogwatch/examples/fwlogsummary.cgi /usr/lib/cgi-bin/
Editamos /usr/lib/cgi-bin/fwlogsummary.cgi:
# 1 day info RECENT="-l 1d" WEBDIR="/var/www/localhost/fwlogwatch" ... MESSAGES="/var/log/ulog/syslogemu.log" ... #if [ -z $1 ] #then MESSAGES="/var/log/ulog/syslogemu.log" #else # MESSAGES="$1" #fi
Esta última parte la comento pq no me parece seguro permitir abrir un fichero del sistema indicado por parámetro.
Suponiendo que en /var/www/localhost tenemos un host virtual:
mkdir /var/www/localhost/fwlogwatch chown -R www-data:www-data /var/www/localhost/fwlogwatch/
Creamos el fichero /etc/apache2/conf.d/fwlogwatch.conf con el siguiente contenido:
ScriptAlias /cgi-bin/fwlogsummary.cgi /usr/lib/cgi-bin/fwlogsummary.cgi
Reiniciamos apache:
/etc/init.d/apache2 restart
Y accedemos a la versión web via http://localhost/cgi-bin/fwlogsummary.cgi.
En caso de que queramos recibir alertas por correo en tiempo real, activaremos el demonio en el fichero /etc/default/fwlogwatch mediante START_DAEMON=’true’ y iniciaremos el servicio:
/etc/init.d/fwlogwatch restart
Detección de intrusos / IDS: Snort
Podríamos instalar Snort como sistema de detección de intrusos (IDS) de nuestra red:
apt-get install snort
También disponemos de Acidbase que utiliza snort para facilitar el análisis y detección de intrusiones mediante una interfaz web:
apt-get install snort-mysql acidbase
Rootkits
Validamos que el sistema no dispone de ningún rootkit instalado:
apt-get install chkrootkit rkhunter sudo rkhunter --update sudo rkhunter --check sudo chkrootkit
Las desinstalo pq no me interesa que se ejecuten periodicamente:
apt-get purge chkrootkit rkhunter
Control del ancho de banda
Es recomendable establecer políticas para controlar el tráfico que recibimos y enviamos, o incluso dar más prioridad a determinados paquetes (p.ej. conexiones SSH). Para ello tenemos más información en otros tutoriales como Enrutamiento avanzado y control de tráfico en Linux.
Añado varios scripts de ejemplo que pueden ser de utilidad que establecen los siguientes parámetros utilizando Hierarchy Token Bucket (HTB):
- Limita la velocidad de bajada a 95 megabits
- Se crea una clase raiz “1:1” que limita la subida a 95 Megabits (para evitar la saturación del enlace) y de la cual cuelgan 3 colas de diferentes prioridades:
- “1:10” Cola de prioridad alta (ideal para tráfico interactivo como SSH).
- “1:20” Cola de prioridad media (por defecto)
- “1:30” Cola de prioridad baja que limita la velocidad de subida al 70% de los 95 Megabits (perfecto para hosts o servicios que no queremos que nos saturen la conexión)
Entre las 3 colas el sistema cogerá más paquetes de aquellas que tienen mayor prioridad, y dentro de las colas se utiliza un reparto uniforme entre las diferentes conexiones para que una no se haga con todo el caudal (Stochastic Fairness Queueing – SFQ).
Una vez establecidas las colas, por defecto todo el tráfico va a la cola “1:20” y podemos marcar mediante iptables aquellas que queremos que vayan a una cola diferente. Por ejemplo:
$IPTABLES -t mangle -I OUTPUT -p tcp --sport 22 -s $LAN_IP1 -m mark --mark 0 -j MARK --set-mark 10
En el ejemplo anterior, todo el tráfico que surjan del puerto 22 (servicio SSH) es etiquetado con la marca “10” que hará que el paquete vaya a la cola “1:10” (la más prioritaria).
Veamos el script completo ‘/usr/local/bin/traffic-control’:
#!/bin/bash ## http://www.docum.org/docum.org/monitor/ ## http://crysol.inf-cr.uclm.es/node/692 # In megabits DOWNLINK=95 UPLINK=95 DEV=eth0 LAN_IP1="71.11.5.10" IPTABLES="/sbin/iptables"; #LIMIT_IP1="192.168.22.3" # Clean existing down/up-link qdiscs tc qdisc del dev $DEV root 2> /dev/null > /dev/null tc qdisc del dev $DEV ingress 2> /dev/null > /dev/null ###### uplink #### QDISC # Hierarchy Token Bucket which (HBT) pointing by default to class 1:20 (HBT is the most easy one) tc qdisc add dev $DEV root handle 1: htb default 20 #### Classes # Root class that shape everything at $UPLINK speed preventing huge queues which destroy latency tc class add dev $DEV parent 1: classid 1:1 htb rate ${UPLINK}mbit burst 6k # High priority class (1:10) tc class add dev $DEV parent 1:1 classid 1:10 htb rate ${UPLINK}mbit burst 6k prio 1 # Medium priority class (1:20) tc class add dev $DEV parent 1:1 classid 1:20 htb rate ${UPLINK}mbit burst 6k prio 2 # Low priority class (1:20) with speed reduced to 70% tc class add dev $DEV parent 1:1 classid 1:30 htb rate $[7*$UPLINK/10]mbit burst 6k prio 3 #### Queueing # Stochastic Fairness Queueing: avoid that one connection consumes all the bandwidth tc qdisc add dev $DEV parent 1:10 handle 10: sfq perturb 10 tc qdisc add dev $DEV parent 1:20 handle 20: sfq perturb 10 tc qdisc add dev $DEV parent 1:30 handle 30: sfq perturb 10 #### Marking ### Mark traffic with 10 (highest), 20 or 30 (lowest) $IPTABLES -F -t mangle # Prioritize local SSH $IPTABLES -t mangle -I OUTPUT -p tcp --sport 22 -s $LAN_IP1 -m mark --mark 0 -j MARK --set-mark 10 # Limit host #$IPTABLES -t mangle -I FORWARD -s $LIMIT_IP1 -m mark --mark 0 -j MARK --set-mark 30 #### Classify # Traffic with MARK 10 to the highest prioritized class (1:10) tc filter add dev eth0 parent 1:0 prio 0 protocol ip handle 10 fw flowid 1:10 tc filter add dev eth0 parent 1:0 prio 0 protocol ip handle 20 fw flowid 1:20 tc filter add dev eth0 parent 1:0 prio 0 protocol ip handle 30 fw flowid 1:30 # By default, the rest ends up in 1:20 ########## downlink # Slow downloads down to somewhat less than the real speed to prevent queuing the ISP. # # Attach ingress policer: tc qdisc add dev $DEV handle ffff: ingress # Filter *everything* to it (0.0.0.0/0), drop everything that's coming in too fast: tc filter add dev $DEV parent ffff: protocol ip prio 50 u32 match ip src 0.0.0.0/0 police rate ${DOWNLINK}mbit burst 10k drop flowid :1
Por otra parte, podemos tener un script para listar las colas ‘/usr/local/bin/traffic-control-list’:
#!/bin/bash tc -s qdisc ls dev eth0
Y finalmente, para desactivar las colas ‘/usr/local/bin/traffic-control-list-off’:
#!/bin/bash DEV=eth0 TC=/sbin/tc # clean existing down- and uplink qdiscs, hide errors $TC qdisc del dev $DEV root 2> /dev/null > /dev/null $TC qdisc del dev $DEV ingress 2> /dev/null > /dev/null
Para ver en tiempo real el efecto de las colas establecidas, es recomendable utilizar el script ‘monitor_tc_top.pl‘ de la siguiente web:
# cd /usr/local/bin/ # wget -c http://www.docum.org/docum.org/monitor/download/monitor_tc_top.pl # chmod 755 monitor_tc_top.pl # monitor_tc_top.pl
Ejemplo:
20:37:11 up 18 days, 7:30, 2 users, load average: 0.18, 0.05, 0.01 Interval Cumulated Total Dev Classid Tokens Ctokens Rate Speed Send Send ------------------------------------------------------------------------- eth0 1:1 485 4033 2.42KB 624.32KB/s 6.91MB 8.47MB eth0 1:10 485 4033 6.67KB 1.37KB/s 18.52KB 23.42KB eth0 1:20 500 4048 18.41KB 558B/s 2.39KB 87.90KB eth0 1:30 4 3388 2.39KB 622.40KB/s 6.89MB 8.36MB
10. Copias de seguridad
Con rsnapshot podremos hacer copias de seguridad automáticas y periódicas, optimizando al máximo el espacio ocupado mediante el uso de enlaces simbólicos:
apt-get install rsnapshot
Para configurar las copias editamos /etc/rsnapshot.conf para indicar donde se guardaran los scripts:
snapshot_root /var/cache/rsnapshot/
Además, en este mismo fichero vamos a establecer nuestra política de backups: Se almacenaran las últimas 6 horas, los últimos 7 días, las últimas 4 semanas y los últimos 6 meses. Por otra parte, también indicaremos que directorios queremos copiar y que scripts ejecutar:
... #rsync_short_args -a rsync_long_args --delete --numeric-ids --relative --delete-excluded --stats --timeout=10 ... ######################################### # BACKUP INTERVALS # # Must be unique and in ascending order # # i.e. hourly, daily, weekly, etc. # ######################################### ## Tendremos backup de las últimas 6 horas, los últimos 7 días, las últimas 4 semanas y los últimos 6 meses: interval hourly 6 interval daily 7 interval weekly 4 interval monthly 6 ... # Excluimos algunos ficheros para hacer más pequeños los backups exclude access.log exclude access.log.* exclude access_log exclude error.log exclude error.log.* exclude .bash_history ... ############################### ### BACKUP POINTS / SCRIPTS ### ############################### # LOCALHOST backup /etc/ localhost/ backup /home/ localhost backup /var/www/ localhost backup /usr/local/ localhost backup /var/lib/awstats/ localhost backup /var/lib/bandwidthd/ localhost backup /var/lib/cacti/ localhost backup /var/cache/nagios2/ localhost backup /var/lib/nagios2/ localho backup_script /usr/local/bin/backup_dpkg.sh localhost/dpkg/ backup_script /usr/local/bin/backup_mysql.sh localhost/mysql/ backup_script /usr/local/bin/backup_info.sh localhost/info/ backup rsnapshot@webserver:/etc/ webserver/ ssh_args=-p 22 backup rsnapshot@webserver:/usr/local/ webserver/ ssh_args=-p 22 backup rsnapshot@webserver:/var/www/ webserver/ ssh_args=-p 22 backup_script /usr/bin/ssh -i /root/.ssh/id_rsa rsnapshot@webserver /home/rsnapshot/.ssh/backup_mysql_all.sh > db.sql webserver/mysql/all/
En este ejemplo estamos haciendo backup tanto de directorios locales (p.ej. /etc) como remotos del servidor ‘webserver’.
Es importante que los parámetros de este fichero de configuración se encuentren separados por tabulaciones y no por espacios, de lo contrario rsnapshot no podrá procesarlo correctamente. Comprobamos que el archivo de configuración sea correcto:
# rsnapshot configtest Syntax OK
Simula la creación de un snapshot:
# rsnapshot -t hourly
El snapshot más reciente siempre se encontrará en el número más bajo, en el caso de los snapshots realizados cada hora: /var/cache/rsnapshot/hourly.0/
Es recomendable evitar que slocate indexe el backup, editamos /etc/updatedb.conf:
FINDOPTIONS='-ignore_readdir_race' export FINDOPTIONS PRUNEFS="NFS nfs nfs4 afs binfmt_misc proc smbfs autofs iso9660 ncpfs coda devpts ftpfs devfs mfs shfs sysfs cifs lustre_lite tmpfs usbfs udf" export PRUNEFS PRUNEPATHS="/tmp /usr/tmp /var/tmp /var/spool /media /var/cache/rsnapshot /var/cache/rsnapshot-tar" export PRUNEPATHS NETPATHS="" export NETPATHS LOCALUSER="nobody" export LOCALUSER NICE=10 export NICE IONICE_CLASS=2 export IONICE_CLASS IONICE_PRIORITY=7 export IONICE_PRIORITY
Utilidades rsnapshot
A la hora de hacer backup, además de especificar directorios concretos podemos hacer que se ejecuten scripts (p.ej. que realicen volcados de las BBDD) para que también se efectue una copia de su resultado (en el fichero de configuración de ejemplo anterior, se puede observar como hay varios scripts especificados).
Información de estado
A modo de ejemplo, podemos crear un script a medida /usr/local/bin/backup_info.sh que genere varios ficheros con el estado de la máquina en el momento de realizar la copia:
#!/bin/bash # Date /bin/date -R > date.out # Host name and kernel /bin/uname -a > uname.out # Interfaces /sbin/ifconfig -a > ifconfig.out # Disk space /bin/df -h > df.out # Processes /bin/ps aux > ps.out # Open ports & conections /bin/netstat -atunp > netstat.out /usr/bin/lsof -i > lsof-i.out # Processes with lots of files open /usr/bin/lsof +c 15 | awk '{printf("%15s (%s)n", $1, $2)}' | sort | uniq -c | sort -rn | head > losf-processes-with-lots-of-open-files.out # Mounted filesystems /bin/mount > mount.out # Connected users /usr/bin/who > who.out # Firewall rules /sbin/iptables -L > iptables.out # Daemons & init scripts /usr/sbin/sysv-rc-conf --list > sysv-rc-conf.out # Files with special permissions #/usr/bin/find / -path /proc -prune -o -perm -2 ! -type l -ls > find-world-writable.out #/usr/bin/find / -path /proc -prune -o -nouser -o -nogroup > find-files-without-owner.out #/usr/bin/find / -path /proc -prune -o -type f -perm +6000 -ls > find-suid-guid.out
dpkg y mysql
Scripts que copian el listado de paquetes instalados en Ubuntu y un volcado de la MySQL:
cp /usr/share/doc/rsnapshot/examples/utils/backup_dpkg.sh /usr/local/bin/ cp /usr/share/doc/rsnapshot/examples/utils/backup_mysql.sh /usr/local/bin/
Editamos ‘/root/.my.cnf’ para establecer el password de root y así poder realizar volcados sin necesidad de interaccionar con la máquina:
[client] user = root password = secret host = localhost
Ejecutamos:
chmod 600 /root/.my.cnf
En el fichero de configuración especificaremos los scripts a ejecutar y el destino de su resultado:
backup_script /usr/local/bin/backup_dpkg.sh localhost/dpkg/ backup_script /usr/local/bin/backup_mysql.sh localhost/mysql/
Backups remotos
Si queremos hacer backup de directorios remotos o incluso ejecutar scripts remotos (p.ej. volcado mysql), en el servidor donde tenemos rsnapshot debemos generar una clave SSH con ssh-keygen (ver sección correspondiente) para el usuario root (/root/.ssh/).
En la máquina a la que queremos conectarnos para hacer las copias, crearemos un usuario bloqueado:
useradd -d /home/rsnapshot -m -K PASS_MAX_DAYS=-1,PASS_MIN_DAYS=0,PASS_WARN_AGE=0,UMASK=0022 -s /bin/bash rsnapshot passwd -l rsnapshot mkdir -p /home/rsnapshot/.ssh
Además, permitiremos que el usuario ejecute ‘rsync’ como root mediante ‘sudo’ sin necesidad de contraseña. Para ello ejecutamos ‘visudo’ y añadimos al final:
rsnapshot ALL=NOPASSWD: /usr/bin/rsync
Para que rsnapshot funcione se conecte remotamente sin intervención, necesitamos copiar la clave pública a ‘/home/rsnapshot/.ssh/authorized_keys’ y conviene establecer todas las limitaciones posibles (ver sección SSH):
from="71.21.5.19",command="/home/rsnapshot/.ssh/validate-ssh.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAUTQEbt7GOcR4jZ2bTgzxpgABIwAAAQEAwAQKRBv+ABDyOgvHfAxSVcWOEzhieC7rwrBjquLJw0W+U9vD7QgmffmuKDZksI7bAAB3NzaC1yc2EAAAdbHBOGg/zgXcrCNsuJ5IJoDw== root@server.com
En este ejemplo, el usuario solo podrá ser utilizado desde una IP concreta, siempre que tenga la clave privada y únicamente podrá ejecutar el script ‘/home/rsnapshot/.ssh/validate-ssh.sh’:
#!/bin/sh case "$SSH_ORIGINAL_COMMAND" in *&*) echo "Rejected" ;; *(*) echo "Rejected" ;; *{*) echo "Rejected" ;; *;*) echo "Rejected" ;; *< *) echo "Rejected" ;; *`*) echo "Rejected" ;; rsync --server*) # the calling user HAST TO BE in the sudoers list for executing rsync! sudo $SSH_ORIGINAL_COMMAND ;; mysqldump *) $SSH_ORIGINAL_COMMAND ;; /home/rsnapshot/.ssh/backup_mysql_all.sh) /home/rsnapshot/.ssh/backup_mysql_all.sh ;; *) echo "Rejected $SSH_ORIGINAL_COMMAND" ;; esac
Este script permitirá al usuario ejecutar únicamente 'rsync' (con permisos de root mediante 'sudo'), 'mysqldump' y el script '/home/rsnapshot/.ssh/backup_mysql_all.sh':
#!/bin/bash /usr/bin/mysqldump --all-databases --add-drop-table --password=secret -u miusuario
El script anterior realiza un volcado de todas las BBDD de MySQL utilizando un usuario/password determinado. Dado que contiene información sensible, el script debe tener permisos restrictivos:
chown -R rsnapshot:rsnapshot /home/rshapshot chmod 700 /home/rshapshot/.ssh/*.sh
Con esto ya tenemos preparado todo el montaje. Rsnapshot podrá conectarse al servidor (ver fichero de configuración de ejemplo anterior, donde se definían copias del servidor ‘webserver’) sin contraseña y hacer backups de directorios y MySQL. Adicionalmente, hemos establecido controles adicionales para minimizar la posibilidad de que un atacante pueda intentar aprovecharse de la configuración (cifrado SSH, autenticación por clave asimétrica limitada a una IP y a unos comandos concretos).
Tars semanales
De forma semanal, haremos que se cree un tar con la última copia para poder enviarlo cómodamente a otro ordenador:
cp /usr/share/doc/rsnapshot/examples/utils/rsnaptar /usr/local/bin/ mkdir /var/cache/rsnapshot-tar/
Editamos /usr/local/bin/rsnaptar para especificar el lugar donde guardaremos los tar, comentamos GPG y añadimos info al mail que se genera automaticamente.
# DIRECTORIES TAR_DIR="/var/cache/rsnapshot-tar" SNAPSHOT_DIR="/var/cache/rsnapshot/daily.0/" ... #GPG="/usr/bin/gpg" ... ${CAT} << EOF | ${SENDMAIL} To: ${TO_EMAIL} Subject: [rsnapshot] Tar backup job complete - ${HOSTNAME} Now is the time to backup the latest files from rsnapshot on ${HOSTNAME} Tar files created on ${TAR_DIR}/${DATE}/ EOF
Editamos ‘/etc/cron.d/rsnapshot’ para que se ejecute cada semana:
# Weekly # At 5:00 every monday 0 5 * * 1 root /usr/local/bin/rsnaptar marble@localhost
Reportes
Si queremos recibir un reporte del backup cada cierto tiempo como medida para saber que se están efectuando:
cp /usr/share/doc/rsnapshot/examples/utils/rsnapreport.pl.gz /usr/local/bin/ gzip -d /usr/local/bin/rsnapreport.pl.gz chmod 755 /usr/local/bin/rsnapreport.pl
Establecemos en /etc/rsnapshot.conf el nivel de verbose a 3 y añadimos –stats al comando rsync_long_args:
# Verbose level, 1 through 5. # 1 Quiet Print fatal errors only # 2 Default Print errors and warnings only # 3 Verbose Show equivalent shell commands being executed # 4 Extra Verbose Show extra verbose information # 5 Debug mode Everything # verbose 3 ... # Default rsync args. All rsync commands have at least these options set. # #rsync_short_args -a rsync_long_args --delete --numeric-ids --relative --delete-excluded --stats --timeout=10 # this script prints a pretty report from rsnapshot output # in the rsnapshot.conf you must set # verbose >= 3 # and add --stats to rsync_long_args # then setup crontab 'rsnapshot daily 2>&1 | rsnapreport.pl | mail -s"SUBJECT" backupadm@adm.com # don't forget the 2>&1 or your errors will be lost to stderr
Editamos /etc/cron.d/rsnapshot para hacer que el reporte se genere una vez a la semana:
# 4 times per day 0 */4 * * * root /usr/bin/rsnapshot hourly > /dev/null # At 3:30 every day 30 3 * * * root /usr/bin/rsnapshot daily > /dev/null # At 3:00 every monday 0 3 * * 1 root /usr/bin/rsnapshot weekly > /dev/null # At 2:30 every 1st of month 30 2 1 * * root /usr/bin/rsnapshot monthly > /dev/null # Run rsnapshot once a week as it were hourly but with rsnapreport (send report by mail) # I use it only to validate that backups are being done. # When we execute rsnapshot daily/weekly/... rsync is not called, only a move is done and rsnapreport can not generate a report with that info. # At 1:00 every monday 0 1 * * 1 root /usr/bin/rsnapshot hourly 2>&1 | /usr/local/bin/rsnapreport.pl | mail -s"[rsnapshot] Weekly report" marble@localhost
11. Tests de stress
Sistema
Para poner a prueba la resistencia del sistema, podemos utilizar la herramienta ‘stress’ para consumir memoria RAM, CPU o disco duro de forma masiva y forzada:
apt-get install stress
Opciones:
-t, --timeout N timeout after N seconds -c, --cpu N spawn N workers spinning on sqrt() -i, --io N spawn N workers spinning on sync() -m, --vm N spawn N workers spinning on malloc()/free() --vm-bytes B malloc B bytes per vm worker (default is 256MB) --vm-hang N sleep N secs before free (default is none, 0 is inf) --vm-keep redirty memory instead of freeing and reallocating -d, --hdd N spawn N workers spinning on write()/unlink() --hdd-bytes B write B bytes per hdd worker (default is 1GB) --hdd-noclean do not unlink files created by hdd workers
Ejemplo de consumo de memoria y CPU:
$ stress --cpu 8 --io 4 --vm 2 --vm-bytes 128M --vm-keep --timeout 10s stress: info: [13247] dispatching hogs: 8 cpu, 4 io, 2 vm, 0 hdd stress: info: [13247] successful run completed in 10s
Únicamente consumo de memoria:
stress --vm 2 --vm-bytes 128M --vm-keep --timeout 10s
Conexiones HTTP con httperf
Podemos realizar pruebas contra un servicio web para ver su capacidad de respuesta:
apt-get install httperf
Por ejemplo, crear 100 conexiones con un ratio de 10 por segundo:
httperf --hog --server www --num-conn 100 --rate 10 --timeout 5
Crear 10 sesiones a un ratio de 1 por segundo, cada sesion consisten en 5 llamadas espaciadas por 2 segundos:
httperf --hog --server=www --wsess=10,5,2 --rate 1 --timeout 5
Ejemplo de ejecución:
$ httperf --hog --server=localhost --port 3000 --wsess=10,5,2 --rate 1 --timeout 5 httperf --hog --timeout=5 --client=0/1 --server=localhost --port=3000 --uri=/ --rate=1 --send-buffer=4096 --recv-buffer=16384 --wsess=10,5,2.000 Maximum connect burst length: 1 Total: connections 50 requests 90 replies 50 test-duration 30.088 s Connection rate: 1.7 conn/s (601.8 ms/conn, < =11 concurrent connections) Connection time [ms]: min 308.3 avg 4186.5 max 6065.9 median 4252.5 stddev 1489.6 Connection time [ms]: connect 0.1 Connection length [replies/conn]: 1.000 Request rate: 3.0 req/s (334.3 ms/req) Request size [B]: 86.0 Reply rate [replies/s]: min 1.2 avg 1.6 max 1.8 stddev 0.2 (6 samples) Reply time [ms]: response 2530.6 transfer 52.8 Reply size [B]: header 414.0 content 12642.0 footer 0.0 (total 13056.0) Reply status: 1xx=0 2xx=0 3xx=0 4xx=0 5xx=50 CPU time [s]: user 2.70 system 12.57 (user 9.0% system 41.8% total 50.8%) Net I/O: 21.4 KB/s (0.2*10^6 bps) Errors: total 40 client-timo 0 socket-timo 0 connrefused 0 connreset 40 Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0 Session rate [sess/s]: min 0.00 avg 0.33 max 1.00 stddev 0.39 (10/10) Session: avg 5.00 connections/session Session lifetime [s]: 20.9 Session failtime [s]: 0.0 Session length histogram: 0 0 0 0 0 10
Conexiones HTTP con Apache Benchmarking tool
Otra herramienta para testear la capacidad de un servicio web:
apt-get install apache2-utils
Por ejemplo, enviar 1000 request mediante 5 procesos concurrentes:
$ ab -n 1000 -c 5 http://localhost/ Benchmarking localhost (be patient) Server Software: Apache Server Hostname: localhost Server Port: 80 Document Path: / Document Length: 612 bytes Concurrency Level: 5 Time taken for tests: 0.382452 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 852852 bytes HTML transferred: 612612 bytes Requests per second: 2614.71 [#/sec] (mean) Time per request: 1.912 [ms] (mean) Time per request: 0.382 [ms] (mean, across all concurrent requests) Transfer rate: 2175.44 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 1 Processing: 0 1 8.6 0 98 Waiting: 0 0 0.3 0 1 Total: 0 1 8.6 0 98 Percentage of the requests served within a certain time (ms) 50% 0 66% 1 75% 1 80% 1 90% 1 95% 1 98% 1 99% 74 100% 98 (longest request)
Resulta interesante hacer la prueba con URLs que hagan diferentes tipos de procesado:
- Páginas estáticas
- Páginas dinámicas PHP
- CGIs
Si consultamos el manual (man ab), es posible indicar a ab que utilice cookies (-C cookie-name=value) o HTTP authentication (-P proxy-auth-username:password).
Otro ejemplo para realizar conexiones con HTTP KeepAlive, se abriran 10 conexiones y durante 30 segundos se utilizaran para enviar peticiones al servidor:
# ab -kc 10 -t 30 http://localhost/ Benchmarking localhost (be patient) Server Software: Apache Server Hostname: localhost Server Port: 80 Document Path: / Document Length: 612 bytes Concurrency Level: 10 Time taken for tests: 5.824757 seconds Complete requests: 50000 Failed requests: 0 Write errors: 0 Keep-Alive requests: 49511 Total transferred: 44432403 bytes HTML transferred: 30603060 bytes Requests per second: 8584.05 [#/sec] (mean) Time per request: 1.165 [ms] (mean) Time per request: 0.116 [ms] (mean, across all concurrent requests) Transfer rate: 7449.41 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 1 Processing: 0 0 2.6 0 127 Waiting: 0 0 2.6 0 127 Total: 0 0 2.6 0 127 Percentage of the requests served within a certain time (ms) 50% 0 66% 1 75% 1 80% 1 90% 2 95% 2 98% 4 99% 4 100% 127 (longest request)
12. Issue tracking
La gestión de incidencias o las solicitudes de alta/baja/modificación del sistema puede ser efectuada mediante trac.
Xec, impressionant! Molt bo, molt bo.
No tindràs per aquí una versió en PDF, no?? 😉
Espero que sigui d’utilitat!
Amb un copy/paste al OpenOffice.org Writer del text, et manté el format i pots exportar a PDF 😉
Gracias por este pedazo guiaburros
k
Simplemente GRACIAS, un artículo estupendo y muy claro así da gusto 😉
Impresionante trabajo. Un placer. Eres de los que valorizan en serio el software libre 🙂
Increible, aquesta guia és genial!
Muy agradecido, terrible guía están regalando, muy buena, completisima y todo. como para recomendarla ..Gracias de nuevo 🙂
¡Sencillamente IMPRESIONANTE! Enhorabuena y gracias.
Interesante.
PUBLICIDAD: http://www.juegosenmovil.com , juegos y software para teléfonos móviles.
Tengo un problema con el quotacheck estoy usando Ubuntu 10.04, el problema es q aunq reinicie el sistema, siempre entra en conflicto con el famoso directorio .gvfs, alguna sugerencia please?
Una opción es habilitar el usuario root. Para ello tienes que ponerle una contraseña con passwd. Luego accedes al sistema con root y lo intentas de nuevo. Ya no debería de darte más problemas.
Gracias por tu tiempo…