Libro "BPF para monitoreo de Linux"

Libro "BPF para monitoreo de Linux"¡Hola residentes de Khabro! La máquina virtual BPF es uno de los componentes más importantes del kernel de Linux. Su uso adecuado permitirá a los ingenieros de sistemas encontrar fallas y resolver incluso los problemas más complejos. Aprenderá cómo escribir programas que monitoreen y modifiquen el comportamiento del kernel, cómo implementar código de forma segura para monitorear 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 monitorear y modificar el comportamiento del kernel de Linux. - Inyecte código para monitorear de forma segura los eventos del kernel sin tener que volver a compilar el kernel o reiniciar el sistema. — Utilice ejemplos de código convenientes en C, Go o Python. - Tome el control siendo dueño del ciclo de vida del programa BPF.

Seguridad del kernel de Linux, sus características y Seccomp

BPF proporciona una forma poderosa de ampliar el kernel sin sacrificar la estabilidad, la seguridad o la velocidad. Por esta razón, los desarrolladores del kernel pensaron que sería una buena idea utilizar su versatilidad para mejorar el aislamiento de procesos en Seccomp implementando filtros Seccomp soportados por programas BPF, también conocidos como Seccomp BPF. En este capítulo explicaremos qué es Seccomp y cómo se utiliza. Luego aprenderá a escribir filtros Seccomp usando programas BPF. Después de eso, veremos los ganchos BPF integrados que se incluyen en el kernel para los módulos de seguridad de Linux.

Los módulos de seguridad de Linux (LSM) son un marco que proporciona un conjunto de funciones que se pueden utilizar para implementar varios modelos de seguridad de manera estandarizada. LSM se puede utilizar directamente en el árbol de fuentes del kernel, como Apparmor, SELinux y Tomoyo.

Comencemos discutiendo las capacidades de Linux.

oportunidades

La esencia de las capacidades de Linux es que necesita otorgar permiso a un proceso sin privilegios para realizar una determinada tarea, pero sin usar suid para ese propósito, o de otra manera privilegiar el proceso, reduciendo la posibilidad de ataque 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 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 inmediatamente 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 generará un error al vincular el puerto:

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

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

En este caso, como ya se mencionó, en lugar de otorgar derechos completos de root, puede habilitar la vinculación de puertos privilegiados proporcionando la capacidad cap_net_bind_service junto con todo lo demás que ya está en el programa. Para hacer esto, podemos encerrar 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 a este equipo.

  • capsh: utiliza 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 ejecutarlo como root), especificaremos cap_net_bind_service y la capacidad de cambiar el ID de usuario desde root a nadie, es decir, cap_setuid y cap_setgid.
  • —keep=1: queremos conservar las capacidades instaladas al cambiar desde la cuenta raíz.
  • —user=“nadie”: el usuario final que ejecuta el programa no será nadie.
  • —addamb=cap_net_bind_service: configura la eliminación de capacidades relacionadas después de cambiar del modo raíz.
  • - -c "./capabilities" - simplemente ejecute el programa.

Las capacidades vinculadas son un tipo especial de capacidades que heredan los programas secundarios cuando el programa actual las ejecuta usando execve(). Sólo se pueden heredar las capacidades que se permite asociar, o en otras palabras, como capacidades del entorno.

Probablemente se esté preguntando qué significa +eip después de especificar la capacidad en la opción --caps. Estas banderas se utilizan para determinar que la capacidad:

-debe estar activado (p);

-disponible para su uso (e);

-Puede ser heredado por procesos secundarios (i).

Como queremos usar cap_net_bind_service, debemos hacerlo con la bandera e. Luego iniciaremos el shell en el comando. Esto ejecutará el binario de capacidades y debemos marcarlo con la bandera i. Finalmente, queremos que la función esté habilitada (lo hicimos sin cambiar el UID) con p. Parece cap_net_bind_service+eip.

Puedes comprobar el resultado usando ss. Acortemos un poco el resultado para que quepa en la página, pero mostrará el puerto asociado y el ID de usuario distintos de 0, en este caso 65:

# 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. Para obtener más información, consulte man 3 libcap.

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

Para comprender mejor las capacidades de nuestro programa, podemos utilizar la herramienta compatible con 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 una kprobe de una sola línea en la función del kernel cap_capable:

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

Esto generará algo como lo siguiente si las capacidades de nuestro programa están habilitadas 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 este resultado incluye eventos que no son de auditoría, vemos todas las comprobaciones que no son de auditoría y, finalmente, la capacidad requerida con el indicador de auditoría (el último en el resultado) establecido en 1. Capacidad. uno que nos interesa es CAP_NET_BIND_SERVICE, está definido como una constante en el código fuente del kernel en el archivo include/uapi/linux/ability.h con identificador 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 capacidades a menudo se habilitan en tiempo de ejecución para contenedores como runC o Docker para permitirles ejecutarse en modo sin privilegios, pero solo se les permiten las capacidades necesarias para ejecutar la mayoría de las aplicaciones. Cuando una aplicación requiere ciertas capacidades, Docker puede proporcionarlas usando --cap-add:

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

Este comando le dará al contenedor la capacidad CAP_NET_ADMIN, permitiéndole configurar un enlace de red para agregar la interfaz dummy0.

La siguiente sección muestra cómo usar funciones como el filtrado, pero usando una técnica diferente que nos permite implementar nuestros propios filtros mediante programación.

Seccomp

Seccomp significa Secure Computing y es una capa de seguridad implementada en el kernel de Linux que permite a los desarrolladores filtrar ciertas llamadas al sistema. Aunque Seccomp es comparable en capacidades a Linux, su capacidad para administrar ciertas llamadas al sistema lo hace mucho más flexible en comparación con ellos.

Las características 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 darle a un proceso la capacidad CAP_NET_ADMIN pero no permitirle aceptar conexiones de socket, bloqueando las llamadas al sistema aceptar y aceptar4.

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

Los filtros Seccomp se cargan usando prctl a través de la operación PR_SET_SECCOMP. Estos filtros toman 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 en el momento de 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 el código 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 un procesamiento para tomar una decisión final e indicarle al núcleo qué hacer a continuación. La decisión final se expresa mediante uno de los valores de retorno (códigos de estado).

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

- SECCOMP_RET_KILL_THREAD: finaliza el hilo actual inmediatamente después de filtrar una llamada al sistema que no se ejecuta debido a esto.

— SECCOMP_RET_KILL: alias de SECCOMP_RET_KILL_THREAD, dejado por compatibilidad con versiones anteriores.

- SECCOMP_RET_TRAP: la llamada al sistema está prohibida y la señal SIGSYS (Bad System Call) se envía a la tarea que la 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 del usuario como valor de error. Dependiendo de la causa del error, se devuelven diferentes valores de errno. En la siguiente sección se proporciona una lista de números de error.

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

- SECCOMP_RET_LOG: la llamada al sistema se resuelve y registra.

- SECCOMP_RET_ALLOW: simplemente se permite la llamada al sistema.

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 modificar los registros de memoria del usuario. En el contexto de Seccomp, ptrace se usa cuando lo activa el código de estado SECCOMP_RET_TRACE, por lo que el rastreador puede evitar que se ejecute la llamada al sistema e implementar su propia lógica.

Errores de seguridad

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

Son posibles los siguientes errores:

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

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

— EINVAL — aquí puede haber cuatro razones:

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

-las banderas especificadas no son válidas 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 — memoria insuficiente para ejecutar el programa;

- EOPNOTSUPP: la operación indicó que con SECCOMP_GET_ACTION_AVAIL la acción estaba disponible, pero el kernel no admite retornos en los argumentos;

— ESRCH: ocurrió un problema al sincronizar otra transmisión;

- ENOSYS: no hay ningún rastreador adjunto a la acción SECCOMP_RET_TRACE.

prctl es una llamada al sistema que permite a un programa de espacio de usuario manipular (establecer y obtener) aspectos específicos de un proceso, como bytes endianness, nombres de subprocesos, modo de cálculo seguro (Seccomp), privilegios, eventos de rendimiento, etc.

Seccomp puede parecerle una tecnología sandbox, pero no lo es. Seccomp es una utilidad que permite a los usuarios desarrollar un mecanismo de zona de pruebas. Ahora veamos cómo se crean los programas de interacción del usuario utilizando 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:

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

— cargue el filtro usando prctl.

Primero necesitas encabezados de la biblioteca estándar y del 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 en funcionamiento, puede verificar esto de esta manera:

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 configuran 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 desde BPF_LD en la forma de la palabra BPF_W, los datos del paquete se encuentran en un desplazamiento fijo BPF_ABS.

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

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, nr))) - Carga y acumula desde BPF_LD en forma de palabra BPF_W, que es el número de llamada del sistema contenido en el desplazamiento fijo de 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, pasa a la siguiente instrucción y deshabilita la llamada al sistema, de lo contrario permite la llamada al sistema con SECCOMP_RET_ALLOW.

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

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW): finaliza el programa con BPF_RET y permite que la llamada al sistema se ejecute usando SECCOMP_RET_ALLOW.

SECCOMP ES CBPF
Quizás se pregunte por qué se utiliza una lista de instrucciones en lugar de un objeto ELF compilado o un programa C compilado JIT.

Hay dos razones para esto.

• En primer lugar, Seccomp utiliza cBPF (BPF clásico) y no eBPF, lo que significa: no tiene registros, sino sólo un acumulador para almacenar el resultado del último cálculo, como se puede ver en el ejemplo.

• En segundo lugar, Seccomp acepta un puntero a una serie de instrucciones BPF directamente y nada más. Las macros que hemos utilizado simplemente ayudan a especificar estas instrucciones de una manera fácil de usar para el programador.

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 calculada del filtro. Esta estructura de datos es necesaria como argumento para declarar que el proceso se ejecutará más tarde:

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

Sólo queda una cosa por hacer en la función install_filter: ¡cargar el programa! Para hacer esto usamos prctl, tomando PR_SET_SECCOMP como 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 de 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 necesitamos usar prctl para configurar PR_SET_NO_NEW_PRIVS para la ejecución actual y así evitar la situación en la que los procesos secundarios reciban más privilegios que sus padres. Con esto, podemos realizar las siguientes llamadas 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 relacionadas con la arquitectura X86-64 y simplemente otorguemos un permiso que bloquee todos los intentos. Después de instalar el filtro, continuamos 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. Para compilar nuestro programa podemos usar clang o gcc, de cualquier manera solo compilamos el archivo main.c sin opciones especiales:

clang main.c -o filter-write

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

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

¡Maravilloso! Así es como se ve el uso de nuestro programa contenedor: simplemente pasamos el programa que queremos probar como 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 del 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 puede hacer con él. ¿Pero no le gustaría lograr lo mismo con eBPF en lugar de cBPF para aprovechar todo su poder?

Cuando piensa en programas eBPF, la mayoría de la gente piensa que simplemente los escriben y los cargan con privilegios de administrador. Si bien esta afirmación es cierta en general, 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 de eventos del sistema independiente de la arquitectura, 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 una capa de abstracción puede ayudar a evitar problemas encontrados al tratar con llamadas al sistema en diferentes arquitecturas.

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

El código fuente de las trampas 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á convocado en diferentes etapas de ejecución:

— security_bpf — realiza una verificación inicial de las llamadas al sistema BPF ejecutadas;

- security_bpf_map: comprueba cuándo el kernel devuelve un descriptor de archivo para el mapa;

- security_bpf_prog: comprueba cuándo 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 se borra dentro de los mapas BPF;

— 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 está borrado dentro de los programas BPF.

Ahora, al ver todo esto, entendemos: la idea detrás de los interceptores LSM BPF es que pueden brindar protección a cada objeto eBPF, asegurando que solo aquellos con los privilegios adecuados puedan realizar operaciones en tarjetas y programas.

Resumen

La seguridad no es algo que pueda implementar de manera única para todo lo que desea proteger. Es importante poder proteger los sistemas a diferentes niveles y de diferentes maneras. Lo creas o no, la mejor manera de proteger un sistema es organizar diferentes niveles de protección desde diferentes posiciones, de modo que reducir la seguridad de un nivel no permita el acceso a todo el sistema. Los desarrolladores principales han hecho un gran trabajo al brindarnos 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 el CTO de Netlify. Trabajó en soporte de Docker y contribuyó al desarrollo de herramientas Runc, Go y BCC, así como otros proyectos de código abierto. Conocido por su trabajo en proyectos Docker y el desarrollo del ecosistema de complementos Docker. A David le apasionan los gráficos de llamas y siempre busca optimizar el rendimiento.

Lorenzo Fontana trabaja en el equipo de código abierto de Sysdig, donde se centra 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 de un módulo de kernel y eBPF. Le apasionan los sistemas distribuidos, las redes definidas por software, el kernel de Linux y el análisis de rendimiento.

» Puede encontrar más detalles sobre el libro en sitio web del editor
» tabla de contenidos
» Extracto

Para Khabrozhiteley 25% de descuento usando cupón - Linux

Previo pago de la versión impresa del libro, se enviará por correo electrónico un libro electrónico.

Fuente: habr.com

Añadir un comentario