Archives abril 2014

Filtros de red para OSPF (Open Shortest Path First)

Si en nuestros routers utilizamos la estrategia de denegar todo por defecto y permitir expresamente lo que deseamos, en el caso de que estos equipos tengan configurado que aprendan sus rutas con OSPF, debemos habilitar dicho protocolo. Para ello, debemos tener en cuenta que OSPF

  • se encapsula en datagramas IP con el número 89 de protocolo (esto implica que NO utiliza UDP ni TCP ni, por tanto, tiene puertos),
  • usa tanto paquetes multicast (los grupos 224.0.0.5 y 224.0.0.6 y lo que nos obliga a configurar, usar y habilitar en nuestros filtros IGMP*) como unicast,
  • el TTL de sus paquetes es 1, por lo que no atravesarán un cortafuegos, salvo que configuremos un túnel (http://www.cisco.com/en/US/tech/tk583/tk372/technologies_configuration_example09186a00800a43f6.shtml),
  • y por último, dispone de un identificador de tipo de paquete (nos permite mejorar las reglas, aunque dependemos del software de filtrado).

Con iptables, la forma más fácil es ejecutar las siguientes ordenes**:

iptables -A INPUT -p 89 -j ACCEPT
iptables -A OUTPUT -p 89 -j ACCEPT

Si queremos ser más «finos», debemos indicar las IPs de origen, destino y, si lo permite el software de filtrado, el tipo de paquete de OSPF. Las reglas en este caso***:

#hello ospf between rt_ext and rt_int
iptables -A INPUT -s rt_ext -d 224.0.0.5 -p 89 -j ACCEPT
iptables -A OUTPUT -s rt_int -d 224.0.0.5 -p 89 -j ACCEPT
#database description, link state request, asking for information about a particular link and request/update link state
iptables -A INPUT -s rt_ext -d rt_int -p 89 -j ACCEPT
iptables -A OUTPUT -d rt_int -s rt_int -p 89 -j ACCEPT
#link state update, flooding all link states from rt_ext router
iptables -A INPUT -s rt_ext -d 224.0.0.5 -p 89 -j ACCEPT
iptables -A OUTPUT -s rt_int -d 224.0.0.6 -p 89 -j ACCEPT
iptables -A INPUT -s rt_ext -d 224.0.0.6 -p 89 -j ACCEPT
iptables -A OUTPUT -s rt_int -d 224.0.0.5 -p 89 -j ACCEPT
#rt_int router link state update from rt_ext router
iptables -A OUTPUT -s rt_int -d 224.0.0.5 -p 89 -j ACCEPT
iptables -A INPUT -s rt_ext -d 224.0.0.6 -p 89 -j ACCEPT
iptables -A OUTPUT -s rt_int -d 224.0.0.6 -p 89 -j ACCEPT
iptables -A INPUT -s rt_ext -d 224.0.0.5 -p 89 -j ACCEPT

Referencias

  1. Building Internet Firewalls, de Elizabeth D. Zwicky, Simon Cooper y D. Brent Chapman

* Es muy sencillo, pero lo dejo para otra entrada 😉

**Estamos considerando que NO queremos reenviar (forward). Si quisiéramos hacerlo, ver el punto 3 de las consideraciones de OSPF que he escrito en la entrada. Además, estas reglas deben ejecutarse en los routers designados.

***Las reglas se deben de ejecutar en los router afectados (en rt_ext cuando ‘-s rt_ext’ o en rt_int si ‘-s rt_int’), salvo que dispongamos de un cortafuegos específico y, en ese caso, se ejecutan todas en él. Recordad que debe disponer de un túnel (ver **)


Mecanismos de comunicación entre procesos (y V)

En esta última entrada dedicada a los mecanismos IPC veremos las tuberías con nombre o FIFO (First in, First out). El mecanismo se basa en abrir y utilizar el fichero nombrado igual que haríamos con cualquier otro fichero ordinario, donde unos procesos lo abren en modo lectura y otros en modo escritura.

Un ejemplo de proceso lector:

#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#define MAX_BUF 1024
int main(){
 int fd;
 char *procPipe = "/tmp/fifo";
 char msj[MAX_BUF];
 /* Open the file*/ 
 fd = open(procPipe,O_RDONLY);
 /* We read the message*/
 read(fd,msj,MAX_BUF);
 /*We show the read message*/ 
 printf("Received message: %sn",msj);
 /* We close the named pipe*/
 close(fd);
return 0;
}

Un ejemplo del escritor:

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
 int fd;
 char *procPipe = "/tmp/fifo";
/*we make de FIFO file*/
 mkfifo(procPipe,0666);
 /*We open the file */
 fd = open(procPipe,O_WRONLY);
 /*We write the message*/ 
 write(fd,"Writer process messagen",sizeof("Writer process messagen"));
 /* We close the named pipe*/
 close(fd);
 /*We delete pipe*/
 unlink(procPipe);
 return 0;
}

Referencias:

  1. Advanced Linux Programming, de Mark Mitchell, Jeffrey Oldham y Alex Samuel

 

 


¿Hay algo que no pueda hacer root?

Pues si y no. Y no es por quedarme con el personal. Es que, directamente, hay cosas que NO puede hacer, aunque siempre puede buscar otro camino para conseguirlo.
Si empezamos con los ficheros y sus permisos y atributos, nos encontramos con:

  • Si un fichero está protegido ante cambios (atributo inmutable ‘i‘), root no podrá escribir en él (ver imagen).
  • Si un fichero está protegido con el atributo ‘a‘, solo «crece», añade contenido y root no podrá borrar datos de él.

Captura de pantalla de 2014-04-14 22:55:50

Aunque, en ambos casos, siempre podrá quitar el atributo, independientemente de a quien pertenezca el fichero y hacer lo que considere.

Además, si queremos desmontar un sistema de archivo (FS) que está siendo utilizado, por ejemplo, porque un proceso que tiene abierto un fichero de este FS, root no podrá desmontarlo. Para conseguir su objetivo deberá, para el ejemplo, buscar qué proceso tiene abierto el fichero (lsof le ayudará), pararlo (kill)y, entonces, podrá desmontar el FS. Si, por otra parte, este FS es de solo lectura (porque se ha montado así, y no porque sea un CD-R), root no podrá escribir o hacer cualquier cambio en él aunque, para lograrlo, podrá

  • acceder directamente al dispositivo
  • remontarlo como RW

Por otra parte las contraseñas de los usuarios se guardan cifradas en el sistema por lo que root no conoce las contraseñas de los usuarios, aunque siempre podrá:

  • emplear mecanismos de fuerza bruta, no hay que olvidar que tiene acceso a la contraseña cifrada,
  • emplear mecanismos que la obtengan (captura de pulsaciones del teclado,…) ya que tiene acceso a todo el sistema,
  • cambiarla por otra…

Y por último, una acción que tampoco podrá hacer root es parar un proceso que entre en modo de espera en el núcleo (aunque sí que podrá parar TODO el sistema, que para chulo,…)

Referencias:

  1. Administración de sistemas operativos Windows y Linux. Un enfoque práctico, de Juan Antonio Gil, Julio Gómez y Nicolás Padilla.
  2. Linux system Administration, de Tom Adelstein y Bill Lubanovic

Mecanismos de comunicación entre procesos (IV)

Pipes

El tercer mecanismo IPC que vamos a tratar en esta saga son las tuberías (Pipes) y cuya principal característica es que son unidireccionales. Tal y como hace el shell en una orden del estilo ls | grep txt, el proceso a la izquierda de ‘|’ escribe en la tubería y el proceso de la derecha, lee.

Para hacer un uso correcto de las tuberías, debemos saber y comprender el funcionamiento  de los descriptores de archivo en el mundo Unix y, en particular, Linux (haremos uso de las funciones pipe y close).

Un ejemplo clásico de uso en programación de este tipo de IPC es cuando tenemos que desarrollar una aplicación que necesita comunicar a un padre con sus procesos hijos (o threads diferentes):

#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
 
#define SIZE 1024
 
int main( int argc, char **argv )
{
    pid_t pid;
    int p[2], readbytes;
    char msg[SIZE];
    int state, i=0;
    pipe(p);
    if ( (pid=fork()) == 0 ){ 
      /* Child process*/
      /* First: We close pipe write side*/
      close(p[1]);
      while((readbytes=read(p[0], msg, SIZE)) > 0)
       /* We write it in stdout*/
          write(1, msg, readbytes);
      close(p[0]);
    }
    else {
        /* Father proccess*/
        /* Firts: We close pipe read side*/
        close(p[0]); 
        while (i<10) {
            sprintf(msg, "Message number %dn", i);
            write(p[1], msg, strlen(msg));
            i++;
        }
        close(p[1]);
        wait(&state);
    }
    exit(0);
}

Las funciones importantes del ejemplo son:

  • pipe: Crea la tubería. Necesita un vector de 2 elementos de enteros. El primero (posición 0) será la salida de la tubería (lectura) y el segundo, la entrada (escritura)
  • close: Cierra los descriptores que no vamos a usar. La lectura en el proceso que escriba y la escritura en el que lea. Además, cuando los procesos terminan, cierran los descriptores abiertos.
  • read y write: usadas para leer y escribir tomando como argumentos los descriptores de archivo apropiados (exactamente igual que haríamos con cualquier fichero)

 Referencias:

  1. Unix Programación avanzada, Fco. Manuel Márquez García

Sistema de archivo remoto accesible mediante SSH

Si queremos compartir o acceder a un directorio de un equipo al que ya accedemos mediante SSH, una buena opción es usar el mismo servicio (ssh) para conseguir este objetivo. Para ello necesitamos:

  • Que el núcleo incluya FUSE. Si no lo está: basta con instalar fuse-source e instalarlo con module-assistant.
  • Que el usuario local pertenezca al grupo fuse.
  • Instalar sshfs: apt-get install sshfs.
  • Tener correctamente configurado el servidor SSH en el equipo en el que queremos montar el directorio.

Hecho lo anterior ya podemos montar cualquier directorio y utilizarlo en nuestro equipo como si fuera un directorio local. La orden debería seguir el patrón:

sshfs [email protected]_del_sevidor:<directorio a exportar> <directorio local>

  • usuario será el del servidor al que tenemos que acceder,
  • ip del servidor en el que se encuentra el directorio a exportar,
  • y directorio local será el punto de montaje a partir del cual estarán disponibles los ficheros del directorio exportado del servidor desde el punto de vista del «cliente»

Si queremos que esté disponible en el arranque, tenemos que configurar el montaje en el fichero /etc/fstab. Para ello, debemos añadir una línea al fichero /etc/fstab que siga la siguiente sintaxis:

sshfs#[email protected]:<directorio a exportar>   <directorio local>   fuse   defaults 0 0

  • donde, al igual que con la orden dada anteriormente, tenemos que indicar el usuario del servidor con el que debemos conectarnos para acceder al directorio, el directorio y el punto local de montaje.
  • Aquí debemos indicar también el tipo de sistema de archivo (fuse). Como opciones de montaje podemos dejar las «defaults» o utilizar cualquier otra de las que disponemos (ro, rw, nosuid, async, noexec, noauto, …).
  • El penúltimo campo (dump) indica si se quiere o no realizar un volcado de los errores (0 ó 1) .
  • El último indica si no queremos (0) realizar un chequeo del sistema de archivos o y, en este caso, 1, 2, marcará el orden en el serán comprobados*. En nuestro caso, creo que es mejor usar la opción 0 y no chequear (ya lo haremos, en todo caso, en el propio servidor donde se encuentra el directorio).

Una vez introducida la línea en /etc/fstab, para probarla sin necesidad de reiniciar, podemos ejecutar mount -a.

Debemos tener en cuenta que si queremos montar automáticamente en el arranque, lo más lógico es autenticar mediante clave pública** para que se monte correctamente en el inicio del equipo y que no tenga que pedirnos la contraseña, además de guardar la clave pública del servidor en el fichero known_hosts de ssh para que no solicite confirmación.

Para desmontar/desasociar el directorio, debemos ejecutar la orden

 fusermount -u directorio_local

¡Ojo! Este mecanismo NO te cifra el directorio. Te da privacidad en la comunicación estableciendo una conexión cifrada. ¡Y tampoco lo puedes usar con Dropbox!

Todas las técnicas de protección que usemos para el servidor SSH, influirán positivamente en la seguridad de nuestro «servicio de disco», pero esto, lo dejamos para otro día.

Referencias:

  1. Hardening de servidores GNU/Linux, de Carlos Álvarez Martín y Pablo González Pérez
  2. Administración de sistemas operativos Windows y Linux. Un enfoque práctico, de Juan Antonio Gil, Julio Gómez y Nicolás Padilla.

* Normalmente se utiliza 1 para el sistema de archivos raíz, 2 para el resto. La comprobación, por defecto, se realiza cada 29 desmontajes y se puede modificar por sistema de archivo (ver la orden tune2fs).

** Debemos copiar la clave pública de los usuarios al servidor (la podemos generar mediante la orden ssh-keygen) en la ruta que esté indicada por la directiva AuthorizedKeysFile del fichero /etc/ssh/sshd_config Por defecto, indica que está en el directorio HOME del usuario ($HOME/.ssh/authorized_keys) Es importante para la automatización, que NO activemos un PIN a la clave privada.


Mecanismos de comunicación entre procesos (III)

En esta entrada vamos a ver en qué consiste el siguiente IPC listado en la primera entrada de esta saga: mapped memory. Con este mecanismo podemos lograr comunicar procesos que se ejecutan en un mismo sistema operativo via un fichero compartido (usualmente este mecanismo se estudia como un modo de acceder de una forma rápida y fácil al contenido de ese fichero). Conceptualmente, es muy parecido a la memoria compartida. La función que se utiliza para mapear un fichero en memoria es mmap(), con el flag MAP_SHARED para que pueda ser compartida por varios procesos, y munmap() para el proceso inverso. El uso de estas funciones se puede consultar en la página man del sistema o en http://man7.org/linux/man-pages/man2/mmap.2.html

Ejemplo de uso

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#define FILE_LENGTH 0x1000
int main (int argc, char* const argv[])
{
 int fd;
 void* file_memory;
 char msg[10]="";
 int state,pid;
if ((pid=fork()) != -1) {
 if (pid == 0) {
 /* Open the file. */
 fd = open (argv[1], O_RDWR, S_IRUSR | S_IWUSR);
 write (fd, "", 10); /* The file can not be empty*/
 lseek (fd, 0, SEEK_SET);
 /* Create the memory mapping. */
 file_memory = mmap(0, FILE_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
 close (fd);
 printf("Writingn");
 sprintf ((char*)file_memory, "%sn", "Message 1n");
 munmap (file_memory, FILE_LENGTH);
 }
 else {
 /* Open the file. */
 printf("Opening %sn", argv[1]);
 fd = open (argv[1], O_RDWR, S_IRUSR | S_IWUSR);
 lseek (fd, 0, SEEK_SET);
 /* Read the integer, print it out, and double it. */
 file_memory = mmap(0, FILE_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
 if (file_memory!= NULL) {
 close (fd);
 printf("Wating child endn");
 /*wait(&state);*/
 sleep(1);
 printf("Readingn");
 sscanf(file_memory, "%9s", msg);
 printf("I've read: %sn", msg);
 sprintf ((char*)file_memory, "%sn", "Reaaadn");
 /* Release the memory */
 munmap (file_memory, FILE_LENGTH);
 wait(&state);
 }
 else {
 printf("mmap error");
 }
 }
 }
 else {
 printf("Error making child");
 }
exit(0);
}

Al ejecutar este código, podemos ver como la variable msg toma, en el código ejecutado por el padre,  el valor escrito en la memoria por el hijo («Message«).

Referencias:

  1. Advanced Linux Programming, de Mark Mitchell, Jeffrey Oldham y Alex Samuel

Mecanismos de comunicación entre procesos (II)

Los mecanismos IPC del Unix System V (semáforos para sincronizar, memoria compartida y colas de mensajes) están implementados como una unidad y, por tanto, comparten características comunes de tal forma que, cada mecanismo dispone de una función:

  • get para crear o recuperar uno de estos elementos de comunicación (por ejemplo, para C, shmget, semget, msgget)
  • de control que permite leer y modificar el estado del elemento.

Existen más características que se pueden consultar en la documentación referenciada. Existen, además, 2 utilidades que nos permiten, desde el shell, obtener información y controlar los mecanismos usados en un sistema GNU/Linux concreto: ipcs e ipcrm. El primero sirve para saber los IPCs asignados junto con cierta información. El segundo permite borrarlos. Para saber más sobre ellos: man ipcs o man ipcrm.

Shared Memory

Quizás sea la forma más rápida de comunicar 2 procesos ya que para ello, solo debemos escribir en la zona de memoria compartida por ambos. Para poder utilizar este mecanismo, tenemos las funciones (en C):

  • shmget para crear (o habilitar si ya lo está) una zona de memoria
  • shmctl para acceder y modificar la información administrativa y de control asociada a la memoria
  • shmat para asociar una zona de memoria a un proceso
  • shmdt para desasociar la memoria del proceso

Para sincronizar el acceso a la memoria compartida entre distintos procesos debemos usar un mecanismo de sincronización: semáforos.

Ejemplo de uso

 int segment_id;
 char* shared_memory;
 struct shmid_ds shmbuffer;
 int segment_size;
 const int shared_segment_size = 0x6400;
 
 /* Allocate a shared memory segment. */
 segment_id = shmget (IPC_PRIVATE, shared_segment_size, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
 /* Attach the shared memory segment. */
 shared_memory = (char*) shmat (segment_id, 0, 0);
 printf ("shared memory attached at address %pn"€, shared_memory);
 /* Determine the segment'™s size. */
 shmctl (segment_id, IPC_STAT, &shmbuffer);
 segment_size = shmbuffer.shm_segsz;
 printf ("€œsegment size: %dn"€, segment_size);
 /* Write a string to the shared memory segment. */
 sprintf (shared_memory, "€œHello, world"€);
 /* Detach the shared memory segment. */
 shmdt (shared_memory);
 /* Reattach the shared memory segment, at a different address. */
 shared_memory = (char*) shmat (segment_id, (void*) 0x5000000, 0);
 printf ("shared memory reattached at address %pn"€, shared_memory);
 /* Print out the string from shared memory. */
 printf ("€œ%sn"€, shared_memory);
 /* Detach the shared memory segment. */
 shmdt (shared_memory);
 /* Deallocate the shared memory segment.
 shmctl (segment_id, IPC_RMID, 0);

Referencias:

  1. Advanced Linux Programming, de Mark Mitchell, Jeffrey Oldham y Alex Samuel
  2. Unix Programación avanzada, Fco. Manuel Márquez García
  3. Para Python: https://docs.python.org/2/library/multiprocessing.html

Mecanismos de Comunicación entre procesos (I)

En un estado normal de un sistema operativo, pueden cohabitar diferentes procesos en ejecución y que necesiten comunicarse. Por ejemplo, en esta entrada vimos que para la implementación de un sencillo servidor concurrente, podíamos crear un proceso hijo encargado de atender la petición del cliente. Dependiendo del servicio, podríamos necesitar la intercomunicación del proceso padre con los hijos* o, incluso, entre hijos.

En la próxima serie de entradas, comentaré los distintos tipos de IPC** que existen, excepto uno: sockets, porque de éste, ya hemos hablado aquí, aquí y aquí):

  • Shared Memory: Memoria compartida entre procesos que les permite comunicarse leyendo y escribiendo en una zona específica de memoria. Quizás sea el mecanismo IPC más simple de comunicación.
  • Mapped Memory: similar al anterior pero asociado a un fichero.
  • Pipes: canal secuencial entre procesos (es la ‘barra’ de esta orden: ‘ls | grep *.exe’)
  • FIFO: similar a las tuberías excepto que los procesos no tienen que estar relacionados debido a que la tubería se «construye» sobre un nombre en el sistema de archivos.

Si nos fijamos, la principal diferencia entre los 5 tipo de IPC (los 4 anteriores más los sockets) está en (1) función de si los procesos están o no relacionados (o si están en el mismo nodo de red), (2) si pueden escribir, leer o ambos y (3) el número de procesos que pueden comunicar.

Referencias:

  1. Advanced Linux Programming, de Mark Mitchell, Jeffrey Oldham y Alex Samuel

* En este caso no me refiero a, simplemente, saber el estado de finalización de los hijos que, en si mismo, podríamos aceptar como comunicación entre procesos aunque muy simple.

** Hay quien considera las señales como un mecanismo IPC. En mi opinión, éstas están destinadas a proporcionar una forma de control sobre un proceso, no como un mecanismo IPC. Además, debemos tener en cuanta que no contienen datos a excepción de ella misma (la señal que es) y, si lanzamos una gran cantidad de ellas, el sistema se verá interrumpido constantemente por algo que, encima, necesita de un tratamiento especial.

 


Las cookies nos permiten ofrecer nuestros servicios. Al utilizar nuestros servicios, aceptas el uso que hacemos de las cookies. Más información.