12 junio 2009

Opciones de seguridad en bash

Fortificar un sistema Unix es una tarea más compleja de lo que en un principio se presupone, por desgracia no basta con aplicar unos cuantos parches y deshabilitar servicios.

En general estos servidores están administrados por un grupo amplio de técnicos, entre los que se pueden encontrar DBAs, operadores, auditores, administradores de sistemas... e incluso en algunos casos desarrolladores que hacen modificaciones directamente en producción...

La shell del sistema se convierte en un componente importante a que debe ser asegurado mediante unas cuantas modificaciones que optimizarán su comportamiento de seguridad.

En el caso de bash (lo siento, me pirra Matt Damon), permite mejorar sus controles mediante las siguientes opciones:

1.- Definición de variables de solo lectura. Mediante esta configuración es posible evitar que una variable de entorno sea sobreescrita por el usuario. Es común su uso en aquellas que definen como se comportará el histórico de comandos ejecutados. Se puede utilizar con las siguientes sintaxis:
declare -r HISTFILE=~/.bash_history
O bien mediante:
readonly HISTFILE=~/.bash_history
Para mostrar todas las variables de solo lectura basta con llamar a readonly con el parámetro -p
Las variables que configuran el histórico de comandos y que se deberían marcar como solo lectura son:
  • HISTFILE: define el archivo en el que se almacenarán cada uno de los comandos. Por defecto tiene el valor "~/.bash_history". Es conveniente que se le asignar atributos extendidos del sistema de ficheros para que el usuario no pueda eliminar el fichero: chttr +i .bash_history
  • HISTFILESIZE: determina el número de líneas que serán almacenadas en el fichero de histórico. El valor por defecto es de 500 aunque es recomendable que sea superior.
  • HISTSIZE: especifica el número de comandos que son almacenados en memoria y que serán mostrados cuando se soliciten mediante el comando "history"
  • HISTTIMEFORMAT= mediante esta variable se configura el formato que almacenará el archivo con los comandos ejecutados. Es recomendable modificarlo y añadir fecha y hora: mediante los valores: "%h/%d - %H:%M:%S ", de tal forma que podamos tener mayor detalle.
Otra variable interesante a modificar que amplía la funcionalidad del almacenado de histórico es "PROMPT_COMMAND", mediante la cual es posible forzar el salvado a fichero después de cada ejecución y no cuando el usuario haga logout. Evitando perdidas ""accidentales"" . Para configurarlo:
readonly PROMPT_COMMAND="history -a"
2.- Argumentos y opciones. Bourne dispone de un modo restringido que es invocado como argumento mediante el parámetro -r, --restricted o llamando a la shell mediante "rbash". Pese a que no es un método seguro, evita algunas acciones como:
  • Cambio de directorios mediante el comando "cd"
  • Modificar el valor de las variables: SHELL, PATH, ENV, y BASH_ENV
  • Ejecutar comandos que contengan "/"
  • Especificar ficheros que contengan / como argumento de "."
  • Redirección de salidas mediante >, >|, <>, >&, &>, y >>
  • Restricción de exec
El uso de rbash es poco común ya que en general es sencillo evadir los controles, pero en ocasiones muy concretas puede ser interesante su uso.

El comando "shopt" permite controlar variables que son utilizadas por la shell para configurar su funcionamiento. La ejecución sin argumentos muestra todas las variables modificables y el valor que tienen. Con el valor -s se activan y con el valor -u se desactivan.

Una variable interesante que por defecto viene desactivada es "histappend", que determina si el fichero de históricos será sobre-escrito o si se añadirá contenido cada vez el usuario sale de la shell.
shopt -s histappend
4.- Tiempos de espera. Para disminuir el riesgo de que la shell sea utilizada por manos de Freddy Kruger, bash dispone de una variable que provocará la salida de bash. Esta variable también puede ser configurada en solo lectura y se define en segundos. Su valor por defecto es 0 (no activado).
declare -r TMOUT=120
3.- Límites. Bash permite la especificación de límites de recursos disponibles a consumir mediante el comando "ulimit". Existen tres tipos de límites, limites duros, blandos (hard y soft) y sin límites. El límite duro, configurado mediante el parámetro -H, no permite ser incrementado a usuarios sin privilegios de root. El límite mimosín permite ser incrementado hasta el número del límite van damme. Si no se especifica el tipo, ambos serán configurados con el mismo valor.

Para mostrar todos los límites basta con ejecutar ulimit con el parámetro -a:
# ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 16304
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 16304
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
Los límites más interesantes son:
-c El tamaño máximo de los archivos core
-s El tamaño máximo de la stack
-t El tiempo máximo en segundos de cpu
-u El tiempo máximo de procesos disponibles por usuario
-v El tamaño máximo de mem virtual disponible para la shell
-x El número máximo de bloque de ficheros.
4.- Máscara de ficheros creados por usuario. Para evitar fugas de información, accesos no autorizados y otras gripes, todos los ficheros creados por usuarios deben tener permisos exclusivamente para ellos, eliminando grupos y otros. La configuración de mascara se lleva a cabo mediante el comando "umask", 077 configurará los permisos: u=rwx,g=,o=
umask 077
Cada uno de los distintos comandos vistos en esta entrada, se han de añadir al fichero /etc/profile para que sean ejecutados cuando un usuario se autentica.

Todas las protecciones no tienen sentido si en el sistema hay otras shells disponibles y pueden ser ejecutadas por el usuario, por lo que la configuración del archivo /etc/shells y la eliminación o modificación de permisos de otras shells es imprescindible.

16 comments :

eduardo dijo...

Hola,

No quiero empezar a contar batallitas, pero una administración conjunta de un linux con una base de datos o con un servidor de discos, por poner un ejemplo, es la locura ...

Al final, resulta que no se puede tocar nada porque nadie se hace responsable, y acaban dándose permisos 777 a todo, metiendo en /etc/sudoers hasta al gato, etc ...

Así que ya ni hablamos del umask, que está SIEMPRE mal (habéis pensado qué pasa cuando un admin hace un tar.gz del .ssh para tener una copia de seguridad? xD) ...

Y de actualizaciones del kernel mucho menos, claro, ¿quién va a ser el valiente que cambie el kernel de un servidor con HDLM o que forma parte de un cluster, uno de esos cluster que en realidad no funcionan como un cluster de verdad?

En fin, de la seguridad que un linux puede tener a la que realmente tiene va un mundo ....

Muy interesante el artículo. Por cierto, otra cosa interesante es usar el demonio "audit", porque bash history no refleja tampoco todo lo que hace el usuario, por ejemplo si usa un editor tipo mc y borra archivos eso no va a quedar en ningún lado ...

Saludos,
Eduardo.

Miguel dijo...

Interesante el artículo.

¡Queremos más! 8)

Podrías hacernos alguna recomendación sobre alguna guía en particular de fortificación de la shell (he visto alguna, pero todas "asustan" por grandes, por incoherentes, por complicadas...)

Ole dijo...

Me parece una entrada cojonuda

Anónimo dijo...

Muy útil la opción de poner como solo lectura el histórico y salvar en cada comando.
Yo, en el history de root pongo que cada conexión se guarde en un fichero diferente y que el nombre de dicho fichero contenga la ip del usuario que ha hecho el su - (al estar root capado en las conexiones ssh) y la fecha y hora de manera que el fichero de historico queda así:

fecha_hora.usuario.ip-origen

Es muy util y necesario cuando toooodo el mundo tiene la password de root ;)

Muy buenos artículos, seguid así.

Zerial dijo...

Interesantes las medidas de seguridad que propones.

Otra medida de protección al entregar shells a usuarios (por ej. un hosting) es lo que se llama enjaular al usuario en su $HOME, usando chroot y de esta forma, limitar los comandos y directorios a los que tiene acceso.

Tambien existen servicios que nos permiten logear lo que el usuario esta tipeando/ejecutando como el servicio "audit" o la utilidad "script".

Existen varias formas de proteccion, todo depende de los requerimientos de cada uno, obviamente las protecciones para un servidor de hosting no son las mismas que las tecnicas de seguridad que proponen en este post.


Por otro lado, es interesante tambien intentar, por lado del cliente, romper esa seguridad.

Alejandro Ramos dijo...

@eduardo abril, tienes razon, pero la gracia de un buen administrador de sistemas es conseguir que la seguridad de sus sistemas se acerque a la optima. Generalmente lo complicado no es aplicar comandos, si no establecer normas que lo respalden.

@Miguel, imagino que en las de Linux en general se hablará algo, pero no tengo ninguna en mente ahora mismo.

@Zerial, sipe, otro dia podemos entrar en chroots, aunque es un tema un poco mas trillado.

Muchas gracias a todos por vuestros comentarios!

rosgos dijo...

Hola a todos,

Magnífico artículo, y me viene que ni pintado, porque hace unos días que estoy buscando info sobre el tema sin encontrada nada tan claro y completito como este artículo.

En mi caso veo de gran utilidad el PROMPT_COMMAND (combinado con las otras técnicas), para casos cómo las bombas fork.

Aquí se habla de chroot, pero por lo poco que sé tiene bastantes deficiencias de seguridad. Des de aquí propongo algún artículo sobre el tema.

Saludos y adelante !!

rosgos dijo...

Para ser perfeccionista:

chattr +i .bash_history en lugar de:

chttr +i .bash_history

Unknown dijo...

en /etc/bashrc

function log2syslog
{
declare command
command=$(history 1)
logger -p local1.notice -t bash -i -- $USER : $command
}


PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME%%.*}:${PWD/#$HOME/~}"; echo -ne "\007"'
readonly PROMPT_COMMAND="history -a ; log2syslog"

Esto registra el history en el messages para que los users no root no lo puedan eliminar.

Puedes meter en el (r)syslog un

local1.* /var/log/user.log

Para splitearlo del messages

Muy buena la entrada, lo malo de estas opciones es que te las puedes saltar ejecutando:

$ test
$ bash --noprofile --norc
$ test2
cat /var/log/user.log
Jun 16 14:34:49 localhost bash[29720]: root : 1001 test
Jun 16 14:36:50 localhost bash[29728]: root : 1002 bash --noprofile --norc

Pero a partir de ahí, no registra nada más(no ha quedado registrado el bash ni el test2)

[crg@fogh ~]$ unset HISTFILE
-bash: unset: HISTFILE: cannot unset: readonly variable
[crg@fogh ~]$ bash --norc --noprofile
bash-3.2$ unset HISTFILE
bash-3.2$ :-)

Alejandro Ramos dijo...

@Crg, la función es genial, me suena de algo :?

En cuanto a lo que comentas de ejecutar nuevamente, es conocido el tema, alguna gente recomienda hacer una funcion llamada "bash" para poner mas complicada la cosa, pero se puede seguir saltando, llamandola con el path absoluto, por ejemplo.

La función a meter en el profile, sería así:

bash () { /bin/bash --login ; } ; readonly bash

Anónimo dijo...

He estado realizando pruebas y si le pones el flag de +i con el chattr el HISTFILE no puede escribir ya que no tiene permisos...

¿Alguna idea?

Alejandro Ramos dijo...

@anonimo: ¿has probado +a? (append), debería funcionar con este valor.

Anónimo dijo...

Pues no funciona...

-bash-3.00$ lsattr .bash_history2
----ia------- .bash_history2
-bash-3.00$ echo "TEST" >> .bash_history2
-bash: .bash_history2: Permission denied

He estado mirando el chattr para ver si el +a se puede restringir al igual que el chown pero no encuentro información en el man al respecto... sigo buscando a ver...

Gracias!

Anónimo dijo...

Finalmente hay que dejar solo el atributo "+a":

-bash-3.00$ lsattr .bash_history2
-----a------- .bash_history2
-bash-3.00$ echo "TEST" >> .bash_history2
-bash-3.00$ rm .bash_history2
rm: cannot remove `.bash_history2': Operation not permitted

Un saludo

Unknown dijo...

@eduardo
En el mundo feliz manejaras sistemas con personal totalmente contratado, feliz con su sueldo, donde habra personal redundado para todas las areas, siempre habra un backup para BBDD, otro equipo para front-end, otro equipo para seguridad, otro para comunicaciones... y tendras cocha,casa,copa y puro. Pero la realidad es que cuando manejas equipos grandes, al final tienes que reducir, pues los conocimientos, si bien puedes hacer buenos equipos multidisciplinares, es complicado su manejo en todos los aspectos (eso te lo intentara rebatir cualquier consultor en virtualizacion, que te contara que la virtualizacion permite, entre otras cosas, despedir a personas, pero claro, eso te lo envolvera en una charla de "celofan de colores").
Al final, como dice alex, salvo que seas una compañia totalmente dirigida a tu producto, y que entiendas la problematica, vas a tener que manejar a administradores torpes, a usuarios torpes, y a desarrolladores que tocan en produccion (aunque eso es perfectamente evitable).

Tambien hay que mirarse el ombligo, al final la complejidad la diseña uno mismo, y el "target" puede ser erroneo. Todos debemos replantearnos la arquitectura, no solo tecnologica, sino humana. Oye, se sorprende uno de como hace las cosas y otro enfoque permite otra implementacion. Esa implementacion, aunque a uno le suponga a veces un trabajo de mas, te puede permitir no tener que tener un sistema muy complejo, sino solo uno con las mismas herramientas para todo el mundo (excluyendo a los developers..a esos ni agua ;-)

Hablando en serio. El problema que plantea alex se da en las grandes corporaciones, y salvo que te dejen tener a cien tios, es complicado de manejar. Tambien te digo, que no solo se arregla fortificando la bash, sino desde mucho mas arriba. Rediseño continuo. La bash es algo de emergencia, nadie mas que el root deberia usarla. aunque el root sea mas de uno.

Ahora bien..quien vigila al "root"?

(PD: quien controla que el super-admin no hace el super-canelo?)

buen articulo alex

PD: no me valida mi openid..asique.
CarloX

rosgos dijo...

Hola,

Recupero un poco este tema....

¿Cómo podría modificar la función log2syslog para que solamente me registrara los comandos en el /var/log/user.log, y no lo hiciera en el messages?

Gracias