Aislar procesos en sandboxing con Firejail

Las técnicas de sandboxing (caja de arena) se utilizan para aislar determinados procesos con el fin de que tengan una visión parcial y limitada de los recursos disponibles del sistema. El objetivo es limitar la posibilidad de acceder a recursos, principalmente en el caso de fallo o que el proceso se vea comprometido y explotado por algún malware o atacante, limitando entonces el alcance que pueden hacer dentro del sistema.

La idea de limitar los recursos disponibles a un determinado proceso se vienen usando desde muy temprano en los entornos GNU/Linux. Sistemas como chroot por el que se crea un árbol de directorios nuevo para una aplicación o limitar las llamadas a sistema con ptrace servían justamente para este propósito. Pero los entornos actuales incluyen nuevas y mucho más potentes formas de controlar los procesos y limitar el alcance que pueden tener dentro de un sistema. Lo que hace firejail es justamente eso, usar las propiedades y características de seguridad que incluyen actualmente los sistemas GNU/Linux como son seccomp, capabilities, namespaces o AppArmor entre otras, de forma transparente para el usuario.

Para instalar firejail:

❯ sudo apt install firejail firejail-profiles

El uso de firejail es muy sencillo. Cuando instalamos el paquete nos instala un gran número de perfiles predeterminados para muchas aplicaciones que podemos usar directamente. Por supuesto también podemos crear nuestros propios perfiles, creando directamente el fichero con la configuración o usando el wizard que incluye en el paquete firetools.

Si queremos usar el lanzador y el wizard de configuración tenemos que instalar el paquete firetools

Podemos ver fácilmente como encapsula una aplicación, por ejemplo vamos a usar firefox. Por defecto, si lanzamos firefox en el escritorio y pedimos que nos muestre el árbol de carpetas de nuestra carpeta home, nos muestra todos los ficheros de nuestra home:

Firefox

Si lanzamos firefox dentro de firejail (podemos hacer esto desde la línea de mandatos o desde el lanzador de firejail):

❯ firejail firefox
Reading profile /etc/firejail/firefox.profile
Reading profile /etc/firejail/whitelist-usr-share-common.inc
Reading profile /etc/firejail/firefox-common.profile
Reading profile /etc/firejail/disable-common.inc
Reading profile /etc/firejail/disable-devel.inc
Reading profile /etc/firejail/disable-exec.inc
Reading profile /etc/firejail/disable-interpreters.inc
Reading profile /etc/firejail/disable-programs.inc
Reading profile /etc/firejail/whitelist-common.inc
Reading profile /etc/firejail/whitelist-var-common.inc
Warning: networking feature is disabled in Firejail configuration file
Parent pid 23156, child pid 23157
Warning: An abstract unix socket for session D-BUS might still be available. Use --net or remove unix from --protocol set.
Warning: cleaning all supplementary groups
Warning: cleaning all supplementary groups
Warning: cleaning all supplementary groups
Warning: cleaning all supplementary groups
Post-exec seccomp protector enabled
Seccomp list in: !chroot, check list: @default-keep, prelist: unknown,
Child process initialized in 156.57 ms

Si ahora miramos nuestra carpeta home desde firefox tendremos una versión limitada de nuestro árbol de directorios:

Firefox en Firejail

Podemos ocultar todos los archivos del directorio personal a un proceso utilizando el modo privado. Para habilitarlo usamos la opción --private en la línea de comandos

Para ver los procesos que están corriendo dentro de firejail (en este caso firefox) ejecutamos desde otro terminal:

❯ firejail --list
23156:david::firejail firefox

Al lanzar firejail sin indicarle un perfil como parámetro utiliza el perfil predeterminado para firefox que se encuentra en la carpeta /etc/firejail:

❯ ls /etc/firejail/firefox*
/etc/firejail/firefox-beta.profile /etc/firejail/firefox-esr.profile
/etc/firejail/firefox-common-addons.inc /etc/firejail/firefox-nightly.profile
/etc/firejail/firefox-common.profile /etc/firejail/firefox.profile
/etc/firejail/firefox-developer-edition.profile /etc/firejail/firefox-wayland.profile

Pero si queremos crear un perfil personalizado lo pondremos en la carpeta ~/.config/firejail.

Crear perfiles nuevos o modificar perfiles existentes

Podemos crear perfiles personalizados o perfiles nuevos para aplicaciones que no se encuentran dentro de los perfiles incluidos por defecto en firejail.

Firejail diferencia dos tipos de perfiles denominados whitelisted y blacklisted. El uso de esta terminología está actualmente desaconsejada para evitar la discriminación racial en el lenguaje, pero es la que utiliza actualmente la aplicación de modo que haré uso de ella para evitar confusiones.

  • Los perfiles whitelisted tienen todo limitado y es necesario añadir en la “whitelist” los recursos a los que se quiere acceder
  • En los perfiles blacklisted todo esta permitido y se limitan los recursos añadiendo estos a una lista “blacklist”

Vamos a utilizar una sencilla aplicación que lee dos ficheros y muestra su contenido. La idea para el ejemplo es limitar a la aplicación para que pueda leer uno de los ficheros pero no el otro (el código esta comentado, pero no es necesario entender el código para seguir el ejemplo):

miAppFJ.vala
/*  
 * miAppFJ.vala  
 *  
 * (c) Author: David Quiroga  
 * e-mail: david [at] clibre [dot] io  
 *  
 ****************************************************************  
 * Descripción:  
 *  
 * Sencilla app GTK para probar acceso a ficheros  
 * usando firejail  
 *  
 * SPDX-License-Identifier: GPL-3.0  
 */  
  
public class miAppFJ.Window : Gtk.ApplicationWindow {  
    public Window (Gtk.Application app) {  
        Object (application: app);  
  
        this.default_height = 140;  
        this.default_width = 350;  
        this.title = "Prueba con Firejail";  
  
        // El contenedor buttonbox  
        Gtk.ButtonBox box = new Gtk.ButtonBox (Gtk.Orientation.VERTICAL);  
        box.set_layout (Gtk.ButtonBoxStyle.EXPAND);  
        this.add (box);  
  
        // Creamos los botones  
        var btconfig = new Gtk.Button.with_label ("Leer config");  
        var btpass = new Gtk.Button.with_label ("Leer filesec"); 
        var salir = new Gtk.Button.with_label ("Salir");  
  
        // Conectamos las acciones al pulsar los botones  
        btconfig.clicked.connect (() => {  
            open_file (GLib.Environment.get_home_dir() 
                            + "/.miAppFJ/config"); 
        }); 
         
        btpass.clicked.connect (() => {  
            open_file (GLib.Environment.get_home_dir() 
                         + "/filesec.txt"); 
        }); 
                  
        // y el boton salir  
        salir.clicked.connect (() => {  
            this.destroy ();  
        });  
  
        box.add (btconfig);  
        box.add (btpass); 
        box.add (salir);  
  
        this.show_all ();  
    }  
     
    public int open_file (string fichero) 
    { 
        var file = File.new_for_path (fichero); 
 
        if (!file.query_exists ()) { 
            stderr.printf ("El fichero '%s' no existe.\n", file.get_path ()); 
            return 1; 
        } 
 
        try { 
            // Abrimos el fichero en modo lectura y obtenemos un  
            // DataInputStream 
            var dis = new DataInputStream (file.read ()); 
            string linea; 
            // Leemos linea a linea hasta encontrar un final de fichero (null) 
            while ((linea = dis.read_line (null)) != null) { 
                stdout.printf ("%s\n", linea); 
            } 
        } catch (Error e) { 
            error ("%s", e.message); 
        }  
         
        return 0;  
    } 
}  
  
int main (string[] args) {  
    var app = new Gtk.Application ("org.comunidadlibre.miAppFJ", ApplicationFlags.FLAGS_NONE);  
    app.activate.connect (() => {  
        var win = app.active_window;  
        if (win == null) {  
            win = new miAppFJ.Window (app);  
        }  
        win.present ();  
    });  
  
    return app.run (args);  
} 

Creamos la aplicación y preparamos los ficheros:

❯ valac --pkg gtk+-3.0 miAppFJ.vala
❯ sudo cp miAppFJ /usr/bin
❯ mkdir ~/.miAppFJ
❯ echo "ComunidadLibre-SoftwareLibre" > ~/.miAppFJ/config
❯ echo "Esto es secreto" > filesec.txt

Perfiles blacklisted:

Creamos un directorio dentro de nuestra carpeta home:

❯ mkdir -p ~/.config/firejail

Copiamos en este directorio el perfil que queramos modificar o si se trata de una aplicación que no tiene un perfil por defecto, como este caso, copiamos el perfil default.profile:

❯ cp /etc/firejail/default.profile .config/firejail/miAppFJ.profile

(miAppFj es el nombre del comando que usamos para lanzar la aplicación)

Cuando no conocemos completamente el funcionamiento de la aplicación tendremos que gradualmente comentar y descomentar las diferentes opciones conforme vamos comprobando que la aplicación funciona bien dentro del sandbox que estamos creando.

Para este ejemplo editamos el fichero miAppFJ.profile y le añadimos una línea denegando explícitamente el acceso a /home/david/filesec.txt:

❯ echo "blacklist ${HOME}/filesec.txt" >> .config/firejail/miAppFJ.profile

Ejecutamos nuestra aplicación con firejail:

❯ firejail miAppFJ
Reading profile /home/david/.config/firejail/miAppFJ.profile
Reading profile /etc/firejail/disable-common.inc
Reading profile /etc/firejail/disable-passwdmgr.inc
Reading profile /etc/firejail/disable-programs.inc
Warning: networking feature is disabled in Firejail configuration file
Parent pid 5223, child pid 5224
Warning: cleaning all supplementary groups
Child process initialized in 41.83 ms

Podemos ver en la salida del comando que ha encontrado el perfil de nuestra aplicación. Comprobamos que podemos abrir sin problema el fichero de configuración ~/.miAppFJ/config, pero si intentamos abrir el fichero ~/filesec.txt obtenemos el siguiente error:

** (miAppFJ:3): ERROR **: 16:24:51.803: miAppFJ.vala:76: Error al abrir el archivo /home/david/filesec.txt: Permiso denegado

Parent is shutting down, bye...

Firejail deniega la lectura del fichero, pero ademas mata el proceso que intenta el acceso que esta en la blacklist.

Perfiles whitelisted:

En este caso, en la documentación se recomienda lanzar un bash dentro de un sandbox usando la opción --private y ejecutar la aplicación para ver que ficheros crea o necesita:

❯ firejail --private
Reading profile /etc/firejail/default.profile
Reading profile /etc/firejail/disable-common.inc
Reading profile /etc/firejail/disable-passwdmgr.inc
Reading profile /etc/firejail/disable-programs.inc
Warning: networking feature is disabled in Firejail configuration file

** Note: you can use --noprofile to disable default.profile **

Parent pid 3140, child pid 3141
Child process initialized in 48.13 ms
udesk20gcc% find .
.
./.zcompdump
./.config
./.config/pulse
./.config/pulse/client.conf
./.Xauthority
./.zshrc
udesk20gcc% miAppFJ
Warning: an existing sandbox was detected. /usr/bin/miAppFJ will run without any additional sandboxing features
El fichero '/home/david/.miAppFJ/config' no existe.
El fichero '/home/david/filesec.txt' no existe.
udesk20gcc% exit

Parent is shutting down, bye...

En nuestro caso sabemos que necesitamos acceso solo al fichero ~/.miAppFJ/config a si que creamos el perfil permitiendo solo acceso a ese fichero (primero borramos el perfil que acabamos de crear):

❯ rm .config/firejail/miAppFJ.profile
❯ vim .config/firejail/miAppFJ.profile

En el perfil tendremos el siguiente contenido:

❯ cat .config/firejail/miAppFJ.profile
whitelist ~/.miAppFJ/config

include /etc/firejail/whitelist-common.inc
include /etc/firejail/default.profile

Tenemos dos include en el fichero. Uno es /etc/firejail/whitelist-common.inc que incluye la configuración de la sesión, como fuentes, temas de escritorio, GTK, Qt, etc.  El otro es /etc/firejail/default.profile necesario para importar los filtros de seguridad como seccomp y capabilities.

Ejecutamos la aplicación con firejail:

❯ firejail miAppFJ
Reading profile /home/david/.config/firejail/miAppFJ.profile
Reading profile /etc/firejail/whitelist-common.inc
Reading profile /etc/firejail/default.profile
Reading profile /etc/firejail/disable-common.inc
Reading profile /etc/firejail/disable-passwdmgr.inc
Reading profile /etc/firejail/disable-programs.inc
Warning: networking feature is disabled in Firejail configuration file
Parent pid 5285, child pid 5286
Child process initialized in 15.65 ms
ComunidadLibre-SoftwareLibre
El fichero '/home/david/filesec.txt' no existe.

En este caso podemos leer sin problemas ~/.miAppFJ/config ya que lo incluimos en la whitelist pero el fichero ~/filesec.txt no existe para nuestra aplicación y por tanto no se puede leer. En este caso tenemos el mensaje de error de nuestra aplicación indicando que no encuentra el fichero, pero firejail no mata el proceso como en el caso anterior.

Algunas opciones de firejail

Lanzar una aplicación en sandbox por defecto

Si queremos que siempre se ejecute la aplicación dentro del sandbox podemos crear un enlace simbólico en /usr/local/bin, en el caso de nuestra aplicación sería:

❯ sudo ln -s /usr/bin/firejail /usr/local/bin/miAppFJ

Podemos crear los enlaces de todas las aplicaciones que tienen un perfil usando el comando firecfg.

❯ sudo firecfg

Ver e interactuar con las aplicaciones en el sandbox

Como hemos visto antes podemos ver las aplicaciones que corren actualmente dentro del sandbox con:

❯ firejail --list
5786:david::/usr/bin/firejail /usr/bin/miAppFJ

También podemos listar los procesos en sandbox en tiempo real, similar al comando top del sistema con:

❯ firejail --top
PID   User      RES(KiB) SHR(KiB) CPU%  Prcs Uptime    Command
5786  david     34972    27948    0.0   3    00:00:10  firejail /usr/bin/miAppFJ

Para acceder a alguno de los sandbox en ejecución usamos el comando --join seguido del PID del proceso que podemos ver en los comandos anteriores:

❯ firejail --join=5786
Switching to pid 5787, the first child process inside the sandbox
Warning: cleaning all supplementary groups
Child process initialized in 6.02 ms
udesk20gcc% cat .miAppFJ/config
ComunidadLibre-SoftwareLibre

Conclusión

Hemos visto solo unas pocas opciones de las que nos ofrece esta útil herramienta de sandboxing. Para una mayor cobertura y detalle es totalmente recomendable visitar la página de documentación de firejail.

Este tipo de sandboxing nos permite ejecutar aplicaciones confiables en modo seguro, con el fin de reducir los riesgos en caso de un problema de seguridad en alguna de ellas. Tenemos que tener en cuenta que esta no es una herramienta para ejecutar aplicaciones no confiables (como análisis de malware). Ese tipo de sandboxing es más riguroso y notablemente más peligroso, y queda fuera de las pretensiones de firejail.

Para añadir una capa extra de seguridad en nuestras aplicaciones de uso habitual y utilizar las extensiones de seguridad que nos ofrece el sistema, creo que firejail es una de las herramientas que siempre tenemos que tener a mano.

Modificado por última vez enMiércoles, 04 Noviembre 2020 09:00
(1 Voto)
Etiquetado como :

Deja un comentario

Asegúrese de introducir toda la información requerida, indicada por un asterisco (*). No se permite código HTML.