Libro de supervisión de BPF para Linux

imagen¡Hola habitantes! La máquina virtual BPF es uno de los componentes más importantes del kernel de Linux. Su aplicación inteligente permitirá a los ingenieros de sistemas encontrar fallas y resolver incluso los problemas más complejos. Aprenderá a escribir programas que monitoreen y modifiquen el comportamiento del kernel, podrá inyectar código de manera segura para observar eventos en el kernel y mucho más. David Calavera y Lorenzo Fontana te ayudarán a desbloquear el poder de BPF. Amplíe sus conocimientos sobre optimización del rendimiento, redes y seguridad. - Utilice BPF para rastrear y modificar el comportamiento del kernel de Linux. - Inyecte código para monitorear eventos en el kernel de manera segura, sin la necesidad de recompilar el kernel o reiniciar el sistema. - Utilice ejemplos de código útiles en C, Go o Python. - Manejar la situación siendo dueño del ciclo de vida del programa BPF.





Seguridad, características y Seccomp del kernel de Linux



BPF proporciona una forma poderosa de extender el kernel sin comprometer la estabilidad, seguridad o velocidad. Por esta razón, los desarrolladores del kernel pensaron que sería una buena idea aprovechar su versatilidad para mejorar el aislamiento de procesos en Seccomp mediante la implementación de filtros Seccomp compatibles con los programas BPF, también conocido como Seccomp BPF. En este capítulo, explicaremos qué es Seccomp y cómo se aplica. Luego, aprenderá a escribir filtros Seccomp utilizando programas BPF. Después de eso, echemos un vistazo a los ganchos BPF incorporados que tiene el kernel para los módulos de seguridad de Linux.



Linux Security Modules (LSM) es una plataforma que proporciona un conjunto de funciones que se pueden utilizar para estandarizar la implementación de varios modelos de seguridad. LSM se puede usar directamente en el árbol de fuentes del kernel, como Apparmor, SELinux y Tomoyo.



Comencemos discutiendo las características de Linux.



Capacidades



La esencia de las capacidades de Linux es que debe otorgar un permiso de proceso sin privilegios para realizar una tarea específica, pero sin suid para este propósito, o hacer que el proceso sea privilegiado, reduciendo la posibilidad de ataques y permitiendo que el proceso realice ciertas tareas. Por ejemplo, si su aplicación necesita abrir un puerto privilegiado, digamos 80, en lugar de ejecutar el proceso como root, simplemente puede darle la capacidad CAP_NET_BIND_SERVICE.



Considere un programa de Go llamado main.go:



package main
import (
            "net/http"
            "log"
)
func main() {
     log.Fatalf("%v", http.ListenAndServe(":80", nil))
}


Este programa sirve a un servidor HTTP en el puerto 80 (este es un puerto privilegiado). Normalmente lo ejecutamos justo después de la compilación:



$ go build -o capabilities main.go
$ ./capabilities


Sin embargo, dado que no otorgamos privilegios de root, este código arrojará un error al vincular el puerto:



2019/04/25 23:17:06 listen tcp :80: bind: permission denied
exit status 1


capsh (herramienta de control de shell) es una herramienta que lanza un shell con un conjunto específico de capacidades.


En este caso, como ya se mencionó, en lugar de otorgar derechos de root completos, puede habilitar enlaces de puertos privilegiados habilitando cap_net_bind_service junto con todo lo demás que ya está en el programa. Para hacer esto, podemos envolver nuestro programa en mayúsculas:



# capsh --caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' \
   --keep=1 --user="nobody" \
   --addamb=cap_net_bind_service -- -c "./capabilities"


Entendamos un poco sobre este comando.



  • capsh: use capsh como caparazón.
  • --caps = 'cap_net_bind_service + eip cap_setpcap, cap_setuid, cap_setgid + ep' - dado que necesitamos cambiar el usuario (no queremos ejecutar como root), especificaremos cap_net_bind_service y la capacidad de cambiar el ID de usuario de root a nadie, es decir, cap_setuid y cap_setgid ...
  • --keep=1 — , root.
  • --user=«nobody» — , , nobody.
  • --addamb=cap_net_bind_service — root.
  • — -c "./capabilities" — .


— , , execve(). , , , , .


Probablemente se esté preguntando qué significa + eip después de especificar una capacidad en la opción --caps. Estos indicadores se utilizan para especificar que la función:



-debe estar activada (p);



-disponible para la aplicación (e);



-puede ser heredado por procesos hijo (i).



Como queremos usar cap_net_bind_service, necesitamos hacerlo con la bandera e. Luego iniciamos el shell al mando. Esto lanzará el binario de capacidades y debemos marcarlo con la bandera i. Finalmente, queremos que se active la función (hicimos esto sin cambiar el UID) con p. Parece cap_net_bind_service + eip.



Puede comprobar el resultado con ss. Reduzca un poco la salida para que quepa en la página, pero mostrará el puerto asociado y el ID de usuario distinto de 0, en este caso 65,534:



# ss -tulpn -e -H | cut -d' ' -f17-
128 *:80 *:*
users:(("capabilities",pid=30040,fd=3)) uid:65534 ino:11311579 sk:2c v6only:0


En este ejemplo usamos capsh, pero puedes escribir un shell usando libcap. Consulte man 3 libcap para obtener más información.



Al escribir programas, el desarrollador a menudo no conoce de antemano todas las capacidades requeridas por el programa en tiempo de ejecución; además, estas características pueden cambiar en nuevas versiones.



Para comprender mejor las capacidades de nuestro programa, podemos usar la herramienta con capacidad BCC, que configura kprobe para la función del kernel cap_capable:



/usr/share/bcc/tools/capable
TIME      UID  PID   TID   COMM               CAP    NAME           AUDIT
10:12:53 0 424     424     systemd-udevd 12 CAP_NET_ADMIN         1
10:12:57 0 1103   1101   timesync        25 CAP_SYS_TIME         1
10:12:57 0 19545 19545 capabilities       10 CAP_NET_BIND_SERVICE 1


Podemos lograr lo mismo usando bpftrace con kprobe de una línea en la función del kernel cap_capable:



bpftrace -e \
   'kprobe:cap_capable {
      time("%H:%M:%S ");
      printf("%-6d %-6d %-16s %-4d %d\n", uid, pid, comm, arg2, arg3);
    }' \
    | grep -i capabilities


Esto generará algo como lo siguiente si las capacidades de nuestro programa se activan después de kprobe:



12:01:56 1000 13524 capabilities 21 0
12:01:56 1000 13524 capabilities 21 0
12:01:56 1000 13524 capabilities 21 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 10 1


La quinta columna son las capacidades que necesita el proceso y, dado que esta salida incluye eventos que no son de auditoría, vemos todas las comprobaciones que no son de auditoría y, finalmente, la capacidad requerida con la marca de auditoría (la última en la salida) establecida en 1. Capacidad. lo que nos interesa es CAP_NET_BIND_SERVICE, se define como una constante en el código fuente del kernel en el archivo include / uapi / linux / skill.h con un identificador de 10:



/* Allows binding to TCP/UDP sockets below 1024 */
/* Allows binding to ATM VCIs below 32 */
#define CAP_NET_BIND_SERVICE 10<source lang="go">


Las características a menudo se aprovechan en tiempo de ejecución para contenedores como runC o Docker para que se ejecuten en modo sin privilegios, pero solo se permiten aquellas características que son necesarias para ejecutar la mayoría de las aplicaciones. Cuando una aplicación requiere capacidades específicas, Docker puede proporcionarles --cap-add:



docker run -it --rm --cap-add=NET_ADMIN ubuntu ip link add dummy0 type dummy


Este comando proporcionará al contenedor la capacidad CAP_NET_ADMIN, lo que le permitirá configurar un enlace de red para agregar la interfaz dummy0.



La siguiente sección demuestra el uso de características como el filtrado, pero con un método diferente que nos permite implementar programáticamente nuestros propios filtros.



Seccomp



Seccomp significa Secure Computing, es una capa de seguridad implementada en el kernel de Linux que permite a los desarrolladores filtrar ciertas llamadas al sistema. Si bien Seccomp es comparable a las capacidades de Linux, su capacidad para manejar llamadas específicas del sistema lo hace mucho más flexible de lo que es.



Las capacidades de Seccomp y Linux no se excluyen mutuamente y, a menudo, se utilizan juntas para beneficiarse de ambos enfoques. Por ejemplo, es posible que desee otorgarle a un proceso la capacidad CAP_NET_ADMIN, pero no permitir que acepte conexiones de socket bloqueando las llamadas del sistema accept y accept4.



El método de filtrado Seccomp se basa en filtros BPF que operan en modo SECCOMP_MODE_FILTER, y el filtrado de llamadas al sistema se realiza de la misma forma que para los paquetes.



Los filtros Seccomp se cargan usando prctl a través de la operación PR_SET_SECCOMP. Estos filtros tienen la forma de un programa BPF que se ejecuta para cada paquete Seccomp representado por la estructura seccomp_data. Esta estructura contiene la arquitectura de referencia, un puntero a las instrucciones del procesador durante la llamada al sistema y un máximo de seis argumentos de llamada al sistema, expresados ​​como uint64.



Así es como se ve la estructura seccomp_data desde la fuente del kernel en el archivo linux / seccomp.h:



struct seccomp_data {
int nr;
      __u32 arch;
      __u64 instruction_pointer;
      __u64 args[6];
};


Como puede ver en esta estructura, podemos filtrar por la llamada al sistema, sus argumentos o una combinación de ambos.



Después de recibir cada paquete Seccomp, el filtro debe realizar el procesamiento para tomar una decisión final y decirle al kernel qué hacer a continuación. La decisión final se expresa en uno de los valores de retorno (códigos de estado).



- SECCOMP_RET_KILL_PROCESS - terminación de todo el proceso inmediatamente después de filtrar una llamada al sistema que no se ejecuta debido a esto.



- SECCOMP_RET_KILL_THREAD: terminación del hilo actual inmediatamente después de filtrar una llamada al sistema, que debido a esto no se ejecuta.



- SECCOMP_RET_KILL: alias de SECCOMP_RET_KILL_THREAD, a la izquierda por compatibilidad con versiones anteriores.



- SECCOMP_RET_TRAP: la llamada al sistema se deshabilita y la señal SIGSYS (llamada incorrecta al sistema) se envía a la tarea que llama.



- SECCOMP_RET_ERRNO: la llamada al sistema no se ejecuta y parte del valor de retorno del filtro SECCOMP_RET_DATA se pasa al espacio de usuario como errno. Se devuelven diferentes valores de errno dependiendo de la causa del error. Los números de error se enumeran en la siguiente sección.



- SECCOMP_RET_TRACE: se utiliza para notificar al ptrace con - PTRACE_O_TRACESECCOMP para interceptar cuando se realiza una llamada al sistema para ver y controlar este proceso. Si el rastreador no está conectado, se devuelve un error, errno se establece en -ENOSYS y no se ejecuta la llamada al sistema.



- SECCOMP_RET_LOG: la llamada al sistema está permitida y registrada.



- SECCOMP_RET_ALLOW: la llamada al sistema simplemente se permite.



ptrace es una llamada al sistema para implementar mecanismos de rastreo en un proceso llamado tracee, con la capacidad de monitorear y controlar la ejecución del proceso. El programa de seguimiento puede influir eficazmente en la ejecución y cambiar los registros de memoria de seguimiento. En el contexto de Seccomp, ptrace se usa cuando lo activa el código de estado SECCOMP_RET_TRACE, por lo que el trazador puede evitar que se ejecute la llamada al sistema e implementar su propia lógica.


Errores seccomp



De vez en cuando, al trabajar con Seccomp, encontrará varios errores, que se identifican mediante un valor de retorno de tipo SECCOMP_RET_ERRNO. Para informar de un error, la llamada al sistema seccomp devolverá -1 en lugar de 0.



Los siguientes errores son posibles:



- EACCESS: la persona que llama no puede realizar una llamada al sistema. Esto suele ocurrir porque no tiene los privilegios CAP_SYS_ADMIN o no_new_privs no está configurado con prctl (más sobre esto más adelante);



- EFAULT: los argumentos pasados ​​(argumentos en la estructura seccomp_data) no tienen una dirección válida;



- EINVAL - puede haber cuatro razones aquí: - la



operación solicitada es desconocida o no es compatible con el kernel en la configuración actual;



-los indicadores especificados no son válidos para la operación solicitada;



-La operación incluye BPF_ABS, pero hay problemas con el desplazamiento especificado, que puede exceder el tamaño de la estructura seccomp_data;



- el número de instrucciones pasadas al filtro supera el máximo;



- ENOMEM: no hay suficiente memoria para ejecutar el programa;



- EOPNOTSUPP: la operación indicó que una acción estaba disponible con SECCOMP_GET_ACTION_AVAIL, pero el kernel no admite la devolución de argumentos;



- ESRCH: hubo un problema al sincronizar otra transmisión;



- ENOSYS: no se adjunta ningún trazador a la acción SECCOMP_RET_TRACE.



prctl es una llamada al sistema que permite que un programa de espacio de usuario manipule (establezca y obtenga) aspectos específicos de un proceso, como secuencia de bytes, nombres de subprocesos, modo de computación segura (Seccomp), privilegios, eventos Perf, etc.


Seccomp puede sonarle como tecnología sandbox, pero no lo es. Seccomp es una utilidad que permite a los usuarios desarrollar un mecanismo de espacio aislado. Ahora veamos cómo crear programas de interacción personalizados usando un filtro llamado directamente por la llamada al sistema Seccomp.



Ejemplo de filtro BPF Seccomp



Aquí mostraremos cómo combinar las dos acciones discutidas anteriormente, a saber:



- escribir el programa Seccomp BPF, que se utilizará como filtro con diferentes códigos de retorno según las decisiones tomadas;



- cargue el filtro con prctl.



Primero necesita encabezados de la biblioteca estándar y el kernel de Linux:



#include <errno.h>
#include <linux/audit.h>
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <unistd.h>


Antes de intentar este ejemplo, debemos asegurarnos de que el kernel esté compilado con CONFIG_SECCOMP y CONFIG_SECCOMP_FILTER configurados en y. En una máquina de producción, puede probarlo así:



cat /proc/config.gz| zcat | grep -i CONFIG_SECCOMP



El resto del código es una función install_filter de dos partes. La primera parte contiene nuestra lista de instrucciones de filtrado de BPF:



static int install_filter(int nr, int arch, int error) {
  struct sock_filter filter[] = {
    BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, arch))),
    BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3),
    BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, nr))),
    BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1),
    BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (error & SECCOMP_RET_DATA)),
    BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
  };


Las instrucciones se establecen utilizando las macros BPF_STMT y BPF_JUMP definidas en el archivo linux / filter.h.

Repasemos las instrucciones.



- BPF_STMT (BPF_LD + BPF_W + BPF_ABS (offsetof (struct seccomp_data, arch))) - el sistema carga y acumula con BPF_LD en la forma de la palabra BPF_W, los datos del paquete se ubican en un desplazamiento fijo BPF_ABS.



- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3) - verifica usando BPF_JEQ si el valor de la arquitectura en la constante del acumulador BPF_K es igual a arch. Si lo hace, salta en el desplazamiento 0 a la siguiente instrucción; de lo contrario, salta en el desplazamiento 3 (en este caso) para arrojar un error, porque el arco no coincide.



- BPF_STMT (BPF_LD + BPF_W + BPF_ABS (offsetof (struct seccomp_data, nr))) - descarga y acumula con BPF_LD en la forma de la palabra BPF_W, que es el número de llamada del sistema contenido en el desplazamiento fijo BPF_ABS.



- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1) - compara el número de llamada del sistema con el valor de la variable nr. Si son iguales, continúa con la siguiente instrucción y no permite la llamada al sistema; de lo contrario, habilita la llamada al sistema con SECCOMP_RET_ALLOW.



- BPF_STMT (BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (error & SECCOMP_RET_DATA)) - termina el programa con BPF_RET y, como resultado, emite un error SECCOMP_RET_ERRNO con un número de la variable err.



- BPF_STMT (BPF_RET + BPF_K, SECCOMP_RET_ALLOW) - finaliza el programa con BPF_RET y permite la ejecución de una llamada al sistema usando SECCOMP_RET_ALLOW.



SECCOMP IS CBPF Es

posible que se pregunte por qué se utiliza una lista de instrucciones en lugar de un objeto ELF compilado o un programa C compilado con JIT.



Hay dos razones para esto.



• Primero, Seccomp usa cBPF (BPF clásico), no eBPF, lo que significa que no tiene registros, sino solo un acumulador para almacenar el último resultado del cálculo, como puede ver en el ejemplo.



• Segundo, Seccomp toma un puntero a una matriz de instrucciones BPF directamente y nada más. Las macros que hemos utilizado solo ayudan a especificar estas instrucciones en una forma conveniente para los programadores.


Si necesita más ayuda para comprender este ensamblado, considere el pseudocódigo que hace lo mismo:



if (arch != AUDIT_ARCH_X86_64) {
    return SECCOMP_RET_ALLOW;
}
if (nr == __NR_write) {
    return SECCOMP_RET_ERRNO;
}
return SECCOMP_RET_ALLOW;


Después de definir el código de filtro en la estructura socket_filter, debe definir un sock_fprog que contenga el código y la longitud del filtro calculada. Esta estructura de datos es necesaria como argumento para declarar el trabajo del proceso en el futuro:



struct sock_fprog prog = {
   .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
   .filter = filter,
};


Solo queda una cosa por hacer en la función install_filter: ¡descargar el programa en sí! Para hacer esto, usamos prctl, tomando PR_SET_SECCOMP como una opción para ingresar al modo de computación segura. Luego le decimos al modo que cargue el filtro usando SECCOMP_MODE_FILTER, que está contenido en la variable prog del tipo sock_fprog:



  if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
    perror("prctl(PR_SET_SECCOMP)");
    return 1;
  }
  return 0;
}


Finalmente, podemos usar nuestra función install_filter, pero antes de eso necesitamos usar prctl para configurar PR_SET_NO_NEW_PRIVS para la ejecución actual y así evitar una situación en la que los procesos secundarios obtengan más privilegios que sus padres. Con esto, podemos realizar las siguientes llamadas a prctl en la función install_filter sin tener derechos de root.



Ahora podemos llamar a la función install_filter. Bloqueemos todas las llamadas al sistema de escritura que estén relacionadas con la arquitectura X86-64, y simplemente demos permiso, lo que bloquea todos los intentos. Después de instalar el filtro, continúe con la ejecución usando el primer argumento:



int main(int argc, char const *argv[]) {
  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
   perror("prctl(NO_NEW_PRIVS)");
   return 1;
  }
   install_filter(__NR_write, AUDIT_ARCH_X86_64, EPERM);
  return system(argv[1]);
 }


Empecemos. Podemos usar clang o gcc para compilar nuestro programa, en cualquier caso, solo compila el archivo main.c sin opciones especiales:



clang main.c -o filter-write


Como se señaló, hemos bloqueado todas las entradas en el programa. Para probar esto, necesita un programa que genere algo; ls parece un buen candidato. Así es como se comporta habitualmente:



ls -la
total 36
drwxr-xr-x 2 fntlnz users 4096 Apr 28 21:09 .
drwxr-xr-x 4 fntlnz users 4096 Apr 26 13:01 ..
-rwxr-xr-x 1 fntlnz users 16800 Apr 28 21:09 filter-write
-rw-r--r-- 1 fntlnz users 19 Apr 28 21:09 .gitignore
-rw-r--r-- 1 fntlnz users 1282 Apr 28 21:08 main.c


¡Perfectamente! Así es como se ve nuestro programa de shell: simplemente pasamos el programa que queremos probar como el primer argumento:



./filter-write "ls -la"


Cuando se ejecuta, este programa produce una salida completamente vacía. Sin embargo, podemos usar strace para ver qué está pasando:



strace -f ./filter-write "ls -la"


El resultado del trabajo se acorta mucho, pero la parte correspondiente muestra que los registros están bloqueados con el error EPERM, el mismo que configuramos. Esto significa que el programa no genera nada porque no puede acceder a la llamada al sistema de escritura:



[pid 25099] write(2, "ls: ", 4) = -1 EPERM (Operation not permitted)
[pid 25099] write(2, "write error", 11) = -1 EPERM (Operation not permitted)
[pid 25099] write(2, "\n", 1) = -1 EPERM (Operation not permitted)


Ahora comprende cómo funciona Seccomp BPF y tiene una buena idea de lo que se puede hacer con él. Pero, ¿no le gustaría hacer lo mismo con eBPF en lugar de cBPF para utilizar toda su potencia?



Al pensar en programas eBPF, la mayoría de la gente piensa que solo los está escribiendo y cargándolos con privilegios de administrador. Si bien esta afirmación es generalmente cierta, el kernel implementa un conjunto de mecanismos para proteger los objetos eBPF en varios niveles. Estos mecanismos se denominan trampas BPF LSM.



Trampas BPF LSM



Para proporcionar un monitoreo independiente de la arquitectura de los eventos del sistema, LSM implementa el concepto de trampas. Una llamada de gancho es técnicamente similar a una llamada al sistema, pero es independiente del sistema y está integrada con la infraestructura. LSM proporciona un nuevo concepto en el que la capa de abstracción puede ayudar a evitar los problemas que surgen al tratar con llamadas al sistema en diferentes arquitecturas.



En el momento de escribir este artículo, el kernel tiene siete ganchos asociados con programas BPF, y SELinux es el único LSM incorporado que los implementa.



El código fuente de los ganchos se encuentra en el árbol del kernel en el archivo include / linux / security.h:



extern int security_bpf(int cmd, union bpf_attr *attr, unsigned int size);
extern int security_bpf_map(struct bpf_map *map, fmode_t fmode);
extern int security_bpf_prog(struct bpf_prog *prog);
extern int security_bpf_map_alloc(struct bpf_map *map);
extern void security_bpf_map_free(struct bpf_map *map);
extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
extern void security_bpf_prog_free(struct bpf_prog_aux *aux);


Cada uno de ellos será llamado en diferentes etapas de ejecución:



- security_bpf - realiza verificaciones iniciales de las llamadas al sistema BPF ejecutadas;



- security_bpf_map: comprueba cuando el kernel devuelve un descriptor de archivo para el mapa;



- security_bpf_prog - Comprueba cuando el kernel devuelve un descriptor de archivo para el programa eBPF;



- security_bpf_map_alloc: comprueba si el campo de seguridad dentro de los mapas BPF está inicializado;



- security_bpf_map_free: comprueba si el campo de seguridad dentro de los mapas BPF está despejado;



- security_bpf_prog_alloc: comprueba si el campo de seguridad está inicializado dentro de los programas BPF;



- security_bpf_prog_free: comprueba si el campo de seguridad dentro de los programas BPF está despejado.



Ahora que vemos todo esto, entendemos que la idea detrás de los interceptores LSM BPF es que pueden brindar protección para cada objeto eBPF, asegurando que solo aquellos con los privilegios apropiados puedan realizar operaciones en mapas y programas.



Resumen



La seguridad no es algo que pueda hacer cumplir de una manera única para todo lo que desee proteger. Es importante poder proteger los sistemas en diferentes niveles y de diferentes formas. Lo crea o no, la mejor manera de asegurar un sistema es organizar diferentes niveles de protección desde diferentes posiciones para que la degradación de la seguridad de un nivel impida el acceso a todo el sistema. Los desarrolladores del kernel han hecho un gran trabajo al proporcionarnos un conjunto de diferentes capas y puntos de contacto. Esperamos haberle dado una buena comprensión de qué son las capas y cómo utilizar los programas BPF para trabajar con ellas.



Sobre los autores



David Calavera es director de tecnología de Netlify. Ha trabajado para Docker Support y ha contribuido al desarrollo de herramientas Runc, Go y BCC, así como otros proyectos de código abierto. Conocido por su trabajo en proyectos de Docker y el desarrollo del ecosistema de complementos de Docker. A David le gustan mucho los gráficos de llama y siempre se esfuerza por optimizar el rendimiento.



Lorenzo Fontana es parte del equipo de desarrollo de código abierto en Sysdig, donde está involucrado principalmente en Falco, un proyecto de Cloud Native Computing Foundation que proporciona seguridad en tiempo de ejecución de contenedores y detección de anomalías a través del módulo kernel y eBPF. Es un apasionado de los sistemas distribuidos, las redes definidas por software, el kernel de Linux y el análisis de rendimiento.



»Más detalles sobre el libro se pueden encontrar en el sitio web de la editorial

» Tabla de contenido

» Extracto



para los habitantes un 25% de descuento en cupón - Linux



Al pagar la versión impresa del libro, se envía un libro electrónico por correo electrónico.



All Articles