1 de octubre de 2008

ps para monitorear activamente los procesos

El comando ps, como casi todo el mundo sabe, sirve para devolver el estado de uno o varios procesos en un momento determinado. Supongo que algunos de nosotros hemos usado en Linux el famoso "ps aux", o su equivalente en Solaris "ps -fea".

Por motivos de trabajo me vi en la necesidad de monitorear procesos que consumían tiempo de procesador e incluso memoria virtual. Afortunadamente me encontré con que exíste la opción -o que permite especificar el formato de salida, es decir, los datos que queremos que aparezcan.

Las opciones que hallé son las siguientes (tomadas de la página manual de ps de Solaris)

user The effective user ID of the process. This will be the textual user ID, if it can be obtained and the field width permits, or a decimal representation otherwise.

ruser The real user ID of the process. This will be the textual user ID, if it can be obtained and the field width permits, or a decimal representation otherwise.

group The effective group ID of the process. This will be the textual group ID, if it can be obtained and the field width permits, or a decimal representation otherwise.

rgroup The real group ID of the process. This will be the textual group ID, if it can be obtained and the field width permits, or a decimal representation otherwise.

pid The decimal value of the process ID.

ppid The decimal value of the parent process ID.

pgid The decimal value of the process group ID.

pcpu The ratio of CPU time used recently to CPU time available in the same period, expressed as a percentage. The meaning of ``recently'' in this context is unspecified. The CPU time vailable is determined in an unspecified manner.

vsz The total size of the process in virtual memory, in kilobytes.

nice The decimal value of the system scheduling priority of the process. See nice(1).

etime In the POSIX locale, the elapsed time since the process was started, in the form:
[[dd-]hh:]mm:ss
where

dd is the number of days
hh is the number of hours
mm is the number of minutes
ss is the number of seconds

time In the POSIX locale, the cumulative CPU time of the process in the form:

[dd-]hh:mm:ss

The dd, hh, mm, and ss fields will be as described in the etime specifier.

tty The name of the controlling terminal of the process (if any) in the same format used by the who(1) command.

comm The name of the command being executed (argv[0] value) as a string.

args The command with all its arguments as a string. The implementation may truncate this value to the field width; it is implementation-dependent whether any further truncation occurs. It is unspecified whether the string represented is a version of the argument list as it was passed to the command when it started, or is a version of the arguments as they may have been modified by the application. Applications cannot depend on being able to modify their argument list and having that modification be reflected in the output of ps. The Solaris implementation limits the string to 80 bytes; the string is the version of the argument list as it was passed to the command when it started.

f Flags (hexadecimal and additive) associated with the process.

s The state of the process.

c Processor utilization for scheduling (obsolete).

uid The effective user ID number of the process as a decimal integer.

ru+id The real user ID number of the process as a decimal integer.

gid The effective group ID number of the process as a decimal integer.

rgid The real group ID number of the process as a decimal integer.

projid The project ID number of the process as a decimal integer.

project The project ID of the process as a textual value if that value can be obtained; otherwise as a decimal integer.

sid The process ID of the session leader.

taskid The task ID of the process.

class The scheduling class of the process.

pri The priority of the process. Higher numbers mean higher priority.

opri The obsolete priority of the process. Lower numbers mean higher priority.

lwp The decimal value of the lwp ID. Requesting this formatting option causes one line to be printed for each lwp in the process.

nlwp The number of lwps in the process.

psr The number of the processor to which the process or lwp is bound.

pset The ID of the processor set to which the process or lwp is bound.

addr The memory address of the process.

osz The total size of the process in virtual memory, in pages.

wchan The address of an event for which the process is sleeping (if -, the process is running).

stime The starting time or date of the process, printed with no blanks.

rss The resident set size of the process, in kilobytes.

pmem The ratio of the process's resident set size to the physical memory on the machine, expressed as a percentage.

fname The first 8 bytes of the base name of the process's executable file.

Ahora, construí un pequeño script que ejecuta

ps -ea -o pid,pcpu,pmem,vsz,osz,rss,etime,s,args | awk '{print $1","$2","$3","$4","$5","$6","$7","$8",\""$9,$10,$11,$12,$13,$14,$15"\""}' > estadoDeMemoria.csv

De manera que obtengo el PID, el porcentaje de uso del procesador, el porcentaje del uso de la memoria, el tamaño de la memoria virtual usada en Kbs, el tamaño de memoria virtual en páginas, el tamaño del segmento residente, el tiempo que lleva de ejecución, el estado del proceso y el comando asociado junto con sus argumentos.
Esto, más crontab y grep devuelven una cantidad considerable de información, sobre todo si lo ejecutamos cada cinco minutos.

Ahora, suponiendo que en cada ejecución se genere un archivo de salida (estadoDeMemoria.csv), podremos también crear un script que parsee las salidas a fin de obtener un concentrado que sólo indique los cambios y los porcentajes de uso por cada rubro.

Ya sé, ya sé, hay productos por ahí que ya hacen eso, pero ¿no es agradable crear tu propio monitor de procesos casero?

¡Saludos!

Lo más básico de la seguridad en GNU/Linux

Hoy precisamente vi el blog de mi amigo José (Cuetzpallin), escribió un artículo muy interesante sobre varios tips de FreeBSD/Linux/Solaris. En uno de los apartados habla sobre los permisos que se pueden imponer a los scripts de shell con limits. En él, hace referencia al archivo de límites de PAM. Eso me llevó a recordar que hace tiempo impartí un curso de Linux en el que entre otras cosas hablé sobre los detalles más básicos del fortalecimiento de un servidor.
Sobre esto, sucede que los administradores nos preocupamos mucho en cuanto al aseguramiento del perímetro del servidor, poniéndole firewalls y cuanta cosa encontramos en la red y olvidamos lo que podríamos hacer si es que alguien logra obtener un shell por medio de una cuenta de usuario. Afortunadamente el propio sistema operativo trae algunas herramientas simples que nos pueden ayudar en nuestra tarea:

1. Archivo /etc/lilo.conf

Este es el archivo de configuración del LInux Loader (Cargador de Linux), de ahí su nombre (LILO).
Página manual: man 5 lilo.conf

Para asegurar que se teclee un password cuando se quiera arrancar un kernel, es necesario usar el keyword:
password=
Si se agrega esta opción es necesario hacer que el archivo sea de lectura sólo por el root.
Esto se logra con un: chmod 600 /etc/lilo.conf

restricted. Indica que se requerirá de un password durante el proceso de inicio del sistema si es que se especifica algún parámetro del kernel en la línea de comandos, como single.

Para que los cambios tengan efecto es necesario re compilar el archivo de inicio con el comando: lilo -v

2. Archivo /etc/inittab

El archivo inittab describe qué procesos se inician durante la inicialización del sistema y la operación normal. Es aquí en donde se distinguen múltiples run levels (niveles de ejecución), cada uno de los que pueden tener sus propios conjuntos de procesos para ser iniciados.
Página manual: man 5 inittab

Hay que buscar una línea parecida a:

ca:ctrlaltdel:/sbin/shutdown –t5 –r now

En la que se especifica cómo responderá el sistema al recibir la combinación de teclas Ctrl.+Alt+Del, la cual típicamente se usa para reiniciar el sistema.

Al final de esta línea se observa el comando que se ejecutará al recibir esta señal. La idea aquí es modificar las opciones del comando shutdown, el cual tiene un modificador (-a), que indica al sistema que deberá buscar en el archivo /etc/shutdown.allow la lista de usuarios con privilegios de reiniciar el sistema mediante esta combinación de teclas (véase man 8 shutdown).

Hay que modificar esta línea para que quede así:
ca:ctrlaltdel:/sbin/shutdown –t5 –a –r now
Crear el archive /etc/shutdown.allow y escriba el nombre de usuario de aquellos usuarios privilegiados, típicamente sólo se le permitirá al root. De tal forma, nadie más podrá reiniciar el sistema con este método.


3. Archivo /etc/fstab

El fstab o tabla de sistemas de archivos (file systems table), controla la manera en la que son tratados los sistemas de archivos, las opciones con las que se montan, el orden y la frecuencia con la que se revisan.
Página manual: man 5 fstab

Un archivo fstab se compone típicamente de 6 columnas de datos, mediante las cuales se definen los sistemas de archivos de una máquina Linux.
En una máquina personal, este archivo suele tener entradas como estas:

/dev/sda2 swap swap defaults 0 0
/dev/sda3 / ext3 defaults 1 1
/dev/sda1 /root ext3 defaults 1 2
/dev/cdrom /mnt/cdrom auto noauto,owner,ro 0 0
/dev/fd0 /mnt/floppy auto noaout,owner 0 0
devpts /dev/pts devpts gid=5,mode=620 0 0
proc /proc proc defaults 0 0

La primera columna indica la partición, la segunda es el punto de montaje, la tercera es el sistema de archivos, la cuarta son las opciones con las que se monta (véase también man 8 mount para una lista completa de las opciones de montaje), la quinta columna indica si el sistema de archivos debe ser revisado por el fsck y la última indica el orden en que debe hacerse esta revisión.

Ahora, en el caso de un servidor generalmente se recomienda tener más particiones, por lo que un esquema recomendable podría ser:

/dev/sda2 swap swap defaults 0 0
/dev/sda3 / ext3 defaults 1 1
/dev/sda1 /root ext3 defaults 1 2
/dev/sda4 /boot ext3 ro,nosuid,noexec,nouser 1 3
/dev/sda5 /tmp ext3 nosuid,noexec,nouser 1 4
/dev/sda6 /var/log ext3 nosuid,noexec,nouser 1 5
/dev/sda7 /usr/local ext3 defaults 1 6
/dev/sda8 /home ext3 defaults 0 0
/dev/cdrom /mnt/cdrom auto noauto,owner,ro 0 0
/dev/fd0 /mnt/floppy auto noaout,owner 0 0
devpts /dev/pts devpts gid=5,mode=620 0 0
proc /proc proc defaults 0 0

Las razones para poner tantas particiones son varias:

1. Según la definición de la LFH (http://tldp.org/LDP/Linux-Filesystem-Hierarchy/html/index.html), los directorios que se encuentran definidos aquí como particiones (o al menos la mayoría), no son necesarios para la inicialización de un sistema y por lo tanto pueden quedar como particiones independientes.
2. En un sentido más práctico, /root es en donde el administrador puede guardar todos sus archivos sin inundar el resto de la /. Lo mismo sucede con /home. /boot es en donde se guardan todos los archivos necesarios para el proceso de arranque del sistema, entre otras cosas, guarda la imagen del kernel, si bien aún existen dudas sobre si es sabio o no ponerlo como partición independiente, en este esquema se presenta así a fin de ponerle otorgar permisos de sólo lectura (ro), no ejecución de binairios suid (nosuid), no ejecución de programas (noexec), no manipulación de usuarios (nouser). Ya que nadie realmente más que el administrador debería modificar las imágenes del kernel.
Algo similar se aplica para /tmp, pues es esta partición en la que todos los usuarios pueden escribir, lo cual se torna potencialmente peligroso ya que aquí se pueden subir ciertos programas maliciosos mediante los cuales lanzar ataques internos al servidor. Por otro lado, se podría intentar una saturación del espacio libre desde esta partición.
En /var/log se guardan las bitácoras del sistema y no es buena idea que cualquiera puede ejecutar cosas en él. /usr/local es en donde generalmente (al menos en el caso de Slackware) se instalan los servidores de web /usr/local/apache, bases de datos (/usr/local/mysql y /usr/local/pgsql) y en general el software opcional.

En general, cada administrador puede crear el esquema de particionamiento que mejor le acomode, dependiendo sobre todo del propósito que va a tener el sistema que va a instalar.

4. Archivo /etc/login.defs

Este archivo define la configuración para la suite de programas que manejan los passwords del sistema, en este caso hablamos de la suite de shadow.

Página manual: man 5 login.defs

Este archivo típicamente trae entradas como las siguientes:

DIAPLUS_CHECK_ENAB yes
FAILLOG_ENAB yes
LOCK_UNFAIL_ENAB yes
LOG_OK_LOGINS yes
LASTLOG_ENAB yes
MAIL_CHECK_ENAB yes
OBSCURE_CHECKS_ENAB yes
PORTTIME_CHECKS_ENAB yes
QUOTAS_ENAB yes
SYSLOG_SU_ENAB yes
SYSLOG_SG_ENAB yes
CONSOLE /etc/securetty
MOTD_FILE /etc/motd
FTMP_FILE /var/log/btmp
NOLOGINS_FILE /etc/nologin
SU_NAME su
MAILDIR /var/spool/mail

5. Archivo /etc/securetty

El archive securetty contiene una lista con las terminales virtuales desde las cuales el administrador o root, puede entrar al sistema.

Página manual: man 5 securetty

En este archivo se definen todas aquellas terminales virtuales, tanto locales como remotas a través de las cuales un administrator del sistema puede acceder al mismo.

Por ejemplo, en le caso de las terminales locales se listan de la tty1 a la tty6, es decir, el administrator puede alejar cualquiera de las 6 terminales virtuales locales para poder acceder al servidor. Supóngase que queremos que el administrador solamente se conecte por medio de la tercera terminal local virtual (tty3).

Recordemos que para acceder a alguna de las terminales virtuales locales basta teclear la combinación Alt_FX, donde X es el número de la terminal a la que queremos entrar. Para el ejemplo, a fin de acceder a la tercera terminal local virtual deberemos teclear: Alt_F3 en donde veremos un prompt parecido a este:

Welcome to Linux 2.4.33.3 (tty3)
nunki login:

Ahora, para que esto tenga efecto es necesario dejar sin comentar en el archivo la línea referente al tty3. De tal manera, el usuario administrador del sistema no se podrá firmar en el sistema más que por la terminal local mencionada.


6. Archivo /etc/porttime

Este archive contiene una lista de los tty’s, los nombres de usuario y sus respectivas horas a las que tienen permitido entrar al sistema.

Página manual: man 5 porttime

Cada entrada del archivo consiste en tres campos separados por dos puntos (:) en donde el primer campo es una lista de terminales virtuales (separadas por coma), el segundo campo es una lista de nombres de usuario (separados por coma). En ambos casos puede utilizarse el comodín asterisco (*), que significa “todos”. Todos los nombres de usuario o bien, todas las terminales virtuales.
El tercer campo es una lista separada por coma de las horas y días en los cuales se permite el acceso.

Cada una de estas entradas se constuye a partir de cero o más días de la semana. Su, Mo, Tu, We, Th, Fr y Sa, seguidos de un par de horas separadas por un guión. También podemos usar la abreviación Wk (weekdays, o bien, entre semana), para representar los días de lunes a viernes; lo mismo Al que significa “todos los días”. Si no se especifica ningún día de la semana, se asume que se trata de Al.

Una entrada de la forma:

*:curso01:Wk0900-1700

Significa que el usuario curso01 puede acceder al sistema por medio de cualquier Terminal virtual, en cualquier día entre semana (de lunes a viernes), de las 9:00 AM a las 5:00 PM.

En el siguiente ejemplo se permite el acceso a los usuarios root y oper desde /dev/console en cualquier momento:

console:root,oper:Al0000-2400
console:*:

En cambio, la segunda línea especifica que el resto de los usuarios no tendrán acceso al sistema en ningún momento.

Como ejemplo final:

*:curso01:Wk1700-0900,SaSu0000-2400

Se describe una entrada que especifica que el usuario curso01 puede entrar al sistema desde cualquier terminal virtual fuera de horas de oficina (en este caso desde las 5:00 PM a las 9:00AM), así como los fines de semana durante todo el día.


7. Archivo /etc/limits

El archivo limits define las cotas superiores del uso de los recursos del sistema. En pocas palabras, cuánto y de qué pueden usar los usuarios.

Página manual: man 5 limits

Aquí describimos los límites que deseamos imponer a los usuarios en cuanto al uso de los recursos del sistema. Por defecto, el usuario root no tiene ningún tipo de cotas de hecho no exíste una manera de imponer límites a cuentas de usuario de tipo administrador (aquellas cuyo UID sea igual a cero).

Cada línea describe un límite para usuario de la forma:

usuario LIMITES

La cadena LIMITES se construye mediante la concatenación de límites de recursos. Cada límite consiste en una letra identificadora seguida de un límite numérico.

Los identificadores válidos son:

A: Máximo espacio de direcciones en Kb
C: Tamaño máximo de los archivos de volcado de memoria (core), en Kb.
D: Tamaño máximo de datos, en Kb.
F: Tamaño máximo de archivo, en Kb.
M: Tamaño máximo de direcciones bloqueadas en memoria (locked-in-memory), en Kb.
N: Tamaño máximo de archivos abiertos
R: Tamaño máximo de conjunto residente, en Kb.
S: Tamaño máximo del stack, en Kb.
T: Tiempo máximo del CPU (mins)
U: Número máximo de procesos
K: Máscara de creación de archivos, ver man 2 umask
L: Número máximo de logins en el sistema para el usuario.
P: Prioridad de los procesos, véase man 2 setpriority

Por ejemplo, L2D2048N5 es una cadena de límites válida. Para propósitos de facilitar la lectura, las siguientes entradas son equivalentes:

usuario L2D2048N5
usuario L2 D2048 N5

Debe tener en cuenta que cualquier cosa después del nombre del usuario se tomará como una cadena de límites y que no se permiten los comentarios. Así mismo, cualquier cadena con límites inválidos no será considerada.

La entrada por defecto se denota por el nombre de usuario ‘*’. Si se tienen varias reglas por defecto entonces se toma la última.

Por favor, note que estos limites son por login, no son globales ni permanentes.


8. Archivo /etc/nologin

El archivo nologin sirve para denegar de manera amistosa, el acceso de los usuarios al sistema.

Página manual: man 8 nologin

Si existe el archivo /etc/nologin, este comando despliega el contenido al usuario en lugar del mensaje por defecto del sistema.


Claro, exísten muchas otras maneras de asegurar el sistema y me parece que este es un buen inicio.