Problema de instalación RHEL 6 con kickstart en hardware Dell

Al instalar RHEL 6 usando kickstart en servidores Dell (modelos PowerEdge 620 y 630, aunque por lo que he investigado es común a hardware Dell), nos encontramos el problema que durante la instalación dispositivos usb como el Virtual Floppy se reconocían antes que el disco y por lo tanto se mapeaban como /dev/sda. Esto es un problema si tu kickstart instala el SO en el primer disco que encuentra (/dev/sda). El problema aparece cuando cargas la iso, ya que la iDRAC (integrated Dell Remote Access Controller) conecta tanto el Virtual CD (reconocido por el SO como /dev/sr0) como el Virtual Floppy (reconocido por el SO como /dev/sda), dejando al disco mapeado como /dev/sdb:

# lsscsi
[0:0:0:0] cd/dvd iDRAC Virtual CD 0329 /dev/sr0
[0:0:0:1] disk iDRAC Virtual Floppy 0329 /dev/sda
[1:2:0:0] disk DELL PERC H730 4.24 /dev/sdb <–disco del sistema

Es un problema que por lo que he podido investigar trae muchos quebraderos de cabeza y hay diferentes formas de solucionarlo, pero ninguno parece ser muy limpio. La mejor solución que encontramos al problema sin tener que modificar nuestros ficheros kickstart fue deshabilitar durante la instalación el reconocimiento de dispositivos de almacenamiento usb, lo que hizo que el sistema no reconociera el dispositivo Virtual Floppy y por lo tanto mapeara el disco como /dev/sda:

vmlinuz initrd=initrd.img ks=http://server/my_file_kickstart blacklist=usb_storage ksdevice=eth0…

El parámetro blacklist=usb_storage se puede dejar grabajado dentro de la iso de instalación para no tener que especificarlo siempre.

Error “No space left on device” en filesystem ext3 con espacio libre

Al escribir en un filesystem, se puede dar el caso de obtener el error de filesystem lleno o “No space left on device”. Lo primero que se debe comprobar es que realmente no esté lleno:

testminator:/pruebaFS # touch prueba
touch: cannot touch `prueba’: No space left on device

testminator:/pruebaFS # df -h .
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/system_vg-pruebafs_lv
1.5G 773M 652M 55% /pruebaFS

En este caso se observa que el filesystem no esta lleno, de hecho tiene casi la mitad del espacio libre. ¿Cuál puede ser el problema entonces?

Uno de los problemas puede ser que se hayan agotado los inodes del filesystem. Los filesystem ext3 imponen un límite en cuanto al número de inodes que puede tener un filesystem en el momento de su creación, esto es, número máximo de ficheros que el filesystem puede contener. Según la página man del comando mke2fs, el límite de inodes se calcula según la siguiente relación:

-N number-of-inodes
Overrides the default calculation of the number of inodes that should be reserved for the filesystem (which is based on the number of blocks and the bytes-per-inode ratio). This allows the
user to specify the number of desired inodes directly.

Es decir, según esta explicación, el sistema por defecto establece un limite máximo de inodes en relación al número de bloques y del tamaño que ocupa una entrada inode. Si en el momento de la creación del filesystem se especifica el parámetro “-N”, se puede aumentar o disminuir el límite de inodes. Si comprobamos el número de inodes en el ejemplo de arriba, se puede ver como se ha llegado al 100%:

testminator:/pruebaFS # df -hi .
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/mapper/system_vg-pruebafs_lv
96K 96K 0 100% /pruebaFS

Ya que una vez creado el filesystem el número de inodes máximo no se puede cambiar, hay diferentes opciones para solucionar el problema:

  • Borrar ficheros, lo que dejaría espacio para crear nuevos inodes (ficheros).
  • Recrear el filesystem especificando un número de inodes mayor (implica realizar un backup de los fs, desmontarlo, etc).
  • Si el fs está montado sobre un logical volume y hay espacio libre en el vg, ampliar. Esto aumentará el número de inodes máximo:
testminator:/pruebaFS # lvextend -L +500m /dev/system_vg/pruebafs_lv
Extending logical volume pruebafs_lv to 1.98 GB
Logical volume pruebafs_lv successfully resized

testminator:/pruebaFS # resize2fs /dev/system_vg/pruebafs_lv
resize2fs 1.41.9 (22-Aug-2009)
Filesystem at /dev/system_vg/pruebafs_lv is mounted on /pruebaFS; on-line resizing required
old desc_blocks = 1, new_desc_blocks = 1
Performing an on-line resize of /dev/system_vg/pruebafs_lv to 518144 (4k) blocks.
The filesystem on /dev/system_vg/pruebafs_lv is now 518144 blocks long.

testminator:/pruebaFS # df -hi .
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/mapper/system_vg-pruebafs_lv
128K 96K 32K 75% /pruebaFS

Se puede observar como el número de inodes máximo ha pasado de 96K a 128K. La info del filesystem también se puede ver con diferentes comandos como tune2fs -l /dev/system_vg/pruebafs_lv o dumpe2fs /dev/system_vg/pruebafs_lv.

testminator:~ # tune2fs -l /dev/system_vg/pruebafs_lv
tune2fs 1.41.9 (22-Aug-2009)
Filesystem volume name: <none>
Last mounted on: <not available>
Filesystem UUID: fded033b-1969-4182-8fb3-40c79a8da13f
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery sparse_super large_file
Filesystem flags: signed_directory_hash
Default mount options: (none)
Filesystem state: clean
Errors behavior: Continue
Filesystem OS type: Linux
Inode count: 131072
Block count: 518144
Reserved block count: 25750
Free blocks: 229740
Free inodes: 42352
First block: 0
Block size: 4096
Fragment size: 4096
Reserved GDT blocks: 63
Blocks per group: 32768
Fragments per group: 32768
Inodes per group: 8192
Inode blocks per group: 512
Filesystem created: Mon May 13 18:19:36 2013
Last mount time: Thu Sep 4 19:13:02 2014
Last write time: Thu Sep 4 19:13:02 2014
Mount count: 3
Maximum mount count: 21
Last checked: Mon Jun 2 17:08:12 2014
Check interval: 15552000 (6 months)
Next check after: Sat Nov 29 16:08:12 2014
Reserved blocks uid: 0 (user root)
Reserved blocks gid: 0 (group root)
First inode: 11
Inode size: 256
Required extra isize: 28
Desired extra isize: 28
Journal inode: 8
First orphan inode: 49155
Default directory hash: half_md4
Directory Hash Seed: be5df525-e649-43a5-b8e3-9f5b618a4461
Journal backup: inode blocks

Este problema no afecta a filesystems como reiserfs, ya que por diseño no estable un límite máximo de inodes.

Si se quiere customizar el sistema para que por defecto establezca un límite de inodes máximo superior, se puede modificar la configuración por defecto de mke2fs, /etc/mke2fs.conf.

Este problema se dió en un sistema SLES 11 SP1 (x86,64) con kernel 2.6.32.59-0.7-default.

Swap consumida por proceso

A partir de cierta versión del kernel (¿2.6.18-99?) el fichero smaps bajo /proc/pid_proceso contiene las porciones de swap que tiene mapeadas el proceso, por lo que mediante un script se puede calcular de forma fácil el total de swap utilizado por un proceso:

#!/bin/bash

#
# infoSwap.sh: muestra la swap consumida por proceso, consultando el fichero
# /proc/pid_proceso/smaps. Funciona a partir de kernel ¿2.6.18-99?
#
# Script basado en http://northernmost.org/blog/find-out-what-is-using-your-swap/
#
# www.joadmin.com
#

# Variables

SUM=0
SWAPTOTAL=0

echo
echo ” –> Calculando swap utilizada por proceso…”
echo

# Bucle sobre procesos (/proc/pid_proceso)
cd /proc
for PROC in `ls -d [0-9]*`
do

NAME=`ps -p $PROC -o comm –no-headers`

# Bucle sumando porciones de swap utilizadas
for SWAP in `grep Swap $PROC/smaps 2> /dev/null | grep -v ” 0 kB” | awk ‘{ print $2 }’`
do
let SUM=$SUM+$SWAP
done

echo ” Pid $PROC (${NAME}) – $SUM KB”
let SWAPTOTAL=$SWAPTOTAL+$SUM
SUM=0
done | grep -v “0 KB” | sort -n -k 5

echo

# EOF

El script está basado en el de Erik Ljungstrom (http://northernmost.org/blog/find-out-what-is-using-your-swap) y se debe ejecutar como root.

Ejemplo de ejecución:

[root@testminator ~]# free -m
total used free shared buffers cached
Mem: 3951 3889 61 0 240 1287
-/+ buffers/cache: 2361 1590
Swap: 2047 132 1915

[root@testminator ~]# ./infoSwap.sh

–> Calculando swap utilizada por proceso…

Pid 3921 (hald-addon-stor) – 28 KB
Pid 3927 (hald-addon-stor) – 28 KB
Pid 4607 (gam_server) – 44 KB
Pid 3682 (portmap) – 48 KB
Pid 3651 (irqbalance) – 52 KB
Pid 3307 (iscsid) – 56 KB
Pid 11577 (mingetty) – 64 KB
Pid 11578 (mingetty) – 64 KB
Pid 11574 (mingetty) – 68 KB
Pid 11575 (mingetty) – 68 KB
Pid 11576 (mingetty) – 68 KB
Pid 11580 (mingetty) – 68 KB
Pid 3620 (audispd) – 88 KB
Pid 3876 (acpid) – 96 KB
Pid 3899 (hald-addon-acpi) – 108 KB
Pid 3907 (hald-addon-keyb) – 108 KB
Pid 3913 (hald-addon-keyb) – 108 KB
Pid 4508 (avahi-daemon) – 136 KB
Pid 3618 (auditd) – 168 KB
Pid 4009 (rsyslogd) – 168 KB
Pid 4507 (avahi-daemon) – 168 KB
Pid 3890 (hald-runner) – 184 KB
Pid 11557 (smartd) – 456 KB
Pid 771 (udevd) – 468 KB
Pid 4465 (xfs) – 636 KB
Pid 3862 (pcscd) – 664 KB
Pid 4253 (p_ctmag) – 756 KB
Pid 4043 (automount) – 832 KB
Pid 29904 (crond) – 868 KB
Pid 3809 (dbus-daemon) – 876 KB
Pid 4275 (p_ctmat) – 896 KB
Pid 3743 (rpc.statd) – 1156 KB
Pid 4438 (crond) – 1444 KB
Pid 3889 (hald) – 2424 KB
Pid 26720 (kcawd) – 2528 KB
Pid 25732 (k08agent) – 5892 KB
Pid 21037 (klzagent) – 7964 KB
Pid 8576 (perl) – 8216 KB
Pid 4605 (yum-updatesd) – 11764 KB
Pid 8650 (java) – 46224 KB

ssh tunneling

Mediante túneles ssh podemos conectar con máquinas a las que no tenemos acceso directo pero que si tenemos desde una tercera máquina. Esto puede ser útil para pasar sobre firewalls por ejemplo.

Imaginemos que un usuario (desde una máquina con hostname maqOrigen) quiere acceder a una url pero que no llega a la máquina que tiene el servidor web (maqSrvWeb), pero llega (y accede) a una tercera máquina (maqPuente) que si llega a la máquina que tiene el servidor web. Mediante el siguiente comando se crearía un túnel que permitiría al usuario acceder a la url:

ssh -L [ip_maqOrigen:]puerto_maqOrigen:ip_maqDestino:puerto_maqDestino usuario@maqPuente

***ip_maqOrigen: este parámetro es opcional. Si no se pone, por defecto es localhost. Si se define como 0.0.0.0, * o vacío (poniendo solo los “:”), se escuchará en todas las ips de la máquina y por tanto será accesible desde fuera. También se podría especificar una ip concreta de la máquina.

Según la página man de ssh, la opción “-L” dice lo siguiente:

-L [bind_address:]port:host:hostport
Specifies that the given port on the local (client) host is to be forwarded to the given host and port on the remote side. This works by allocating a socket to listen to port on the local side, optionally bound to the specified bind_address. Whenever a connection is made to this port, the connection is forwarded over the secure channel, and a connection is made to host port hostport from the remote machine. Port forwardings can also be specified in the configuration file. IPv6 addresses can be specified with an alternative syntax: [bind_address/]port/host/hostport or by enclosing the address in square brackets. Only the superuser can forward privileged ports. By default, the local port is bound in accordance with the GatewayPorts setting. However, an explicit bind_address may be used to bind the connection to a specific address. The bind_address of “localhost” indicates that the listening port be bound for local use only, while an empty address or `*’ indicates that the port should be available from all interfaces.

Prueba práctica

Desde maqUser se quiere acceder a una url en maqDestino:7020, pasando por maqPuente. Se usará el puerto 5000 (por ejemplo) en maqOrigen para crear el túnel.

Creamos el túnel (mientras no se usen puertos privilegiados no se necesitan permisos de root) y comprobamos que el puerto está escuchando:

user@maqOrigen:~> ssh -L 0.0.0.0:5000:maqDestino:7020 user@maqPuente
(tendremos que tener acceso a maqPuente, porque nos pedirá logarnos)

user@maqOrigen:~> netstat -anp | grep -i 5000
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 0.0.0.0:5000 0.0.0.0:* LISTEN 59328/ssh

Con este comando, estamos diciendo que cualquier conexión que vaya a cualquier ip de maqOrigen y puerto 5000, se redirija hacia el puerto 7020 de la máquina maqDestino PASANDO por maqPuente.

Mediante el comando netstat, vemos que el proceso ssh con pid 59328 está escuchando en el puerto 5000. Si vieramos que más conexiones abiertas tiene este proceso, veríamos que ha establecido una conexión con maqPuente:

user@maqOrigen:~> lsof -p 59328 -P 2> /dev/null | grep -e LISTEN -e ESTABLISHED
ssh 59328 user 3u IPv4 483511 0t0 TCP maqOrigen:50279->maqDestino:3105 (ESTABLISHED)
ssh 59328 user 4u IPv4 483517 0t0 TCP *:5000 (LISTEN)

En la máquina maqPuente se ha creado por tanto un proceso ssh que redirigirá todo el tráfico que le llegue hacia el puerto 7020 de maqPuente

[root@maqPuente ~]# netstat -anp | grep -i 50279
tcp 0 0 maqPuente:3105 maqOrigen:50279 ESTABLISHED 2407/sshd

Ahora vamos a probar si funciona, cargando la url a la que queríamos acceder:

maqOrigen:~ # wget http://localhost:5000/console
–2014-05-21 14:27:42– http://localhost:5000/console
Resolving localhost… ::1, 127.0.0.1
Connecting to localhost|::1|:5000… failed: Connection refused.
Connecting to localhost|127.0.0.1|:5000… connected.
HTTP request sent, awaiting response… 302 Moved Temporarily
Location: http://localhost:5000/console/ [following]
–2014-05-21 14:27:42– http://localhost:5000/console/
Connecting to localhost|127.0.0.1|:5000… connected.
HTTP request sent, awaiting response… 302 Moved Temporarily
Location: http://localhost:5000/console/login/LoginForm.jsp [following]
–2014-05-21 14:27:42– http://localhost:5000/console/login/LoginForm.jsp
Connecting to localhost|127.0.0.1|:5000… connected.
HTTP request sent, awaiting response… 200 OK
Length: 3162 (3.1K) [text/html]
Saving to: `LoginForm.jsp’

100%[==========================================================================================================================================================================>] 3,162 –.-K/s in 0s

2014-05-21 14:27:42 (193 MB/s) – `LoginForm.jsp’ saved [3162/3162]


Vemos que se ha descargado el fichero, pero ¿cómo saber que realmente hemos pasado por la máquina maqPuente? Filtrando por tcpdump en maqDestino por las ips de maqOrigen y maqPuente y comprobando que solo hay paquetes que proceden de maqPuente:

maqDestino:~ # tcpdump | grep -e maqOrigen -e maqPuente
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes

14:21:39.085392 IP maqPuente.47579 > maqDestino.7020: S 1307663851:1307663851(0) win 14600 <mss 1460,sackOK,timestamp 19558343 0,nop,wscale 5>
14:21:39.085412 IP maqDestino.7020 > maqPuente.47579: S 1466941367:1466941367(0) ack 1307663852 win 14480 <mss 1460,sackOK,timestamp 25496128 19558343,nop,wscale 7>
14:21:39.085806 IP maqPuente.47579 > maqDestino.7020: . ack 1 win 457 <nop,nop,timestamp 19558343 25496128>
14:21:39.086174 IP maqPuente.47579 > maqDestino.7020: P 1:127(126) ack 1 win 457 <nop,nop,timestamp 19558343 25496128>
14:21:39.086183 IP maqDestino.7020 > maqPuente.47579: . ack 127 win 114 <nop,nop,timestamp 25496128 19558343>
14:21:39.086468 IP maqDestino.7020 > maqPuente.47579: P 1:168(167) ack 127 win 114 <nop,nop,timestamp 25496128 19558343>
14:21:39.086483 IP maqDestino.7020 > maqPuente.47579: P 168:423(255) ack 127 win 114 <nop,nop,timestamp 25496128 19558343>
14:21:39.086510 IP maqDestino.7020 > maqPuente.47579: F 423:423(0) ack 127 win 114 <nop,nop,timestamp 25496128 19558343>
14:21:39.086841 IP maqPuente.47579 > maqDestino.7020: . ack 168 win 490 <nop,nop,timestamp 19558344 25496128>
14:21:39.086847 IP maqPuente.47579 > maqDestino.7020: . ack 423 win 524 <nop,nop,timestamp 19558344 25496128>
14:21:39.087357 IP maqPuente.47579 > maqDestino.7020: F 127:127(0) ack 424 win 524 <nop,nop,timestamp 19558344 25496128>
14:21:39.087364 IP maqDestino.7020 > maqPuente.47579: . ack 128 win 114 <nop,nop,timestamp 25496128 19558344>
14:21:39.087507 IP maqPuente.47580 > maqDestino.7020: S 1822175628:1822175628(0) win 14600 <mss 1460,sackOK,timestamp 19558344 0,nop,wscale 5>

Se puede probar incluso a acceder desde otra máquina, ya que al crear el túnel, al especificar 0.0.0.0 definimos que todas las ips de maqOrigen escucharían en el puerto 5000:

maqOtra:~ # wget http://maqOrigen:5000/console
–2014-05-21 14:32:36– http://maqOrigen:5000/console
Connecting to maqOrigen:5000… connected.
HTTP request sent, awaiting response… 302 Moved Temporarily
Location: http://maqOrigen:5000/console/ [following]
–2014-05-21 14:32:36– http://maqOrigen:5000/console/
Connecting to maqOrigen:5000… connected.
HTTP request sent, awaiting response… 302 Moved Temporarily
Location: http://maqOrigen:5000/console/login/LoginForm.jsp [following]
–2014-05-21 14:32:36– http://maqOrigen:5000/console/login/LoginForm.jsp
Connecting to maqOrigen:5000… connected.
HTTP request sent, awaiting response… 200 OK
Length: 3162 (3.1K) [text/html]
Saving to: `LoginForm.jsp’

100%[==========================================================================================================================================================================>] 3,162 –.-K/s in 0s

2014-05-21 14:32:36 (342 MB/s) – `LoginForm.jsp’ saved [3162/3162]

 

Nota: se han cambiado todos los hostname/ips por maqOrigen, maqDestino, etc por claridad.

 

Certificados SSL en servidores web

Para poder utilizar el protoco https es necesario disponer de un certificado SSL/TLS (TLS es el sucesor), permitiendo conexiones seguras (encriptación de la información) entre el servidor web y el navegador del usuario. Además, permite asegurar que el servidor web con el que nos conectamos es realmente el que dice ser.

Los pasos para crear un certificado SSL pasan por generar una semilla, formada por una clave privada y un fichero csr (certificate signing request). Posteriormente, el fichero csr debe ser firmado por una entidad certificadora (CA) o por nosotros mismos (da menos confianza y saldrá warning en el naveagador), creando el certificado final. Una vez instalado, el proceso de autenticación/encriptación pasa por los siguientes pasos:

  1. El usuario carga una url utilizando el protocolo https, descargándose la clave pública del certificado del servidor web. Este certificado debería haber sido firmado por una entidad certificadora (CA), garantizando la autenticidad del mismo. Los navegadores vienen con las claves públicas de los certificados de las CA, al menos de las más conocidas, por lo que el navegador utiliza la clave correspondiente para comprobar que el certificado del servidor web de verdad ha sido firmado por el CA que dice haberlo firmado. En el caso de que el navegador no incluya la clave pública del CA del certificado, se mostraría un mensaje de advertencia. El certificado contiene información del dominio y de la empresa propietaria, asegurando la autenticidad del sitio.
  2. El navegador genera una clave simétrica y la encripta utilizando la clave pública del certificado (de esta manera, solo podrá ser desencriptada por el servidor web utilizando la clave privada). Tanto el servidor web como el navegador utilizarán esta clave simétrica para encriptar los datos, ya que es más eficiente que encriptar usando las claves públicas/privadas.

Utilizando el paquete openssl, se pueden generar certificados con los siguientes comandos:

Generación de semilla de 2048 bits

openssl req -new -nodes -newkey rsa:2048 -keyout nombre_dominio.key -out nombre_dominio.csr

El proceso solicita varios datos sobre el propietario del certificado:

Country Name(2 letter code) [AU]: ES
State or Province Name (full name) [Some-State]: Madrid
Locality Name (eg, city) []: Madrid
Organization Name (eg, company) [Internet Widgits Pty Ltd]: joadmin Services SL
Organizational Unit Name (eg, section) []: blog joadmin
Common Name (eg, YOUR name) []: www.midominio.com
Email Address []: menganito@midominio.com

Please enter the following ‘extra’ attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Una vez firmado el csr por la entidad certificadora, se recibirá un fichero .crt (u otro formato). Se puede comprobar que la clave privada (.key) y el certificado se corresponden si coincide lo siguiente:

openssl x509 -noout -modulus -in domini.crt | openssl md5
39bd7991c62d4ad20474f56e710aa6fe

openssl rsa -noout -modulus -in dominio.key | openssl md5
39bd7991c62d4ad20474f56e710aa6fe

Comprobar la fecha de validez de un certificado

openssl x509 -noout -in midominio.crt -dates
notBefore=Mar 25 15:31:32 2014 GMT
notAfter=Mar 25 22:16:16 2016 GMT

o

openssl x509 -in midominio.crt -noout -enddate
notAfter=Mar 25 22:16:16 2016 GMT

Comprobar la fecha de validez de un certificado cargando una url mediante wget:

echo | openssl s_client -connect midominio:443 2>/dev/null | openssl x509 -noout -dates
notBefore=Feb 10 13:21:04 2012 GMT
notAfter=Feb 9 13:21:04 2015 GMT

Verificar un certificado con el certificado del CA que lo ha firmado

openssl verify -CAfile “Entrust Certification Authority – L1C.cer” midominio.crt
dominio.com: OK

Referencias