
Un lenguaje y una biblioteca para especificar políticas de filtrado de syscalls.
Kafel es un lenguaje y biblioteca para especificar políticas de filtrado de llamadas al sistema. Las políticas se compilan en código BPF que puede usarse con seccomp-filter.
Esto NO es un producto oficial de Google.
struct sock_fprog prog;
kafel_ctxt_t ctxt = kafel_ctxt_create();
kafel_set_input_string(ctxt, seccomp_policy);
if (kafel_compile(ctxt, &prog)) {
fprintf(stderr, "policy compilation failed: %s", kafel_error_msg(ctxt));
kafel_ctxt_destroy(&ctxt);
exit(-1);
}
kafel_ctxt_destroy(&ctxt);
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
free(prog.filter);
struct sock_fprog prog;
if (kafel_compile_string(seccomp_policy, &prog)) {
fputs("policy compilation failed", stderr);
exit(-1);
}
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
free(prog.filter);
Se utiliza un lenguaje simple para definir políticas.
El archivo de política consiste en declaraciones.
Una declaración puede ser:
Las declaraciones de definición de política colocadas en el ámbito del archivo se agregarán a la política de nivel superior implícita. Esta política de nivel superior se va a compilar.
DEFAULT the_action
Especifica que se debe tomar la acción the_action cuando ninguna regla coincida.
La acción predeterminada debe especificarse solo una vez.
Si el archivo de política no especifica acciones predeterminadas, la acción predeterminada será KILL
Kafel admite las siguientes notaciones numéricas:
420xfa107770b10101Puede definir constantes numéricas para hacer sus políticas más legibles. Las definiciones de constantes pueden colocarse casi en cualquier lugar del archivo de política. Una definición de constante no puede colocarse dentro de una definición de política. Las constantes definidas pueden entonces usarse en cualquier lugar donde se espere un número.
#define MYCONST 123
Una definición de política es una lista de bloques de acción y declaraciones de uso separadas por comas.
samples/ contiene algunos ejemplos de políticas que demuestran las características compatibles.
Una USE someOtherPolicy se comporta como si el cuerpo de someOtherPolicy se hubiera pegado en su lugar.
Solo puede usar políticas definidas antes de la declaración de uso.
Con las declaraciones de uso puede crear grupos significativos de reglas de filtrado que son bloques de construcción de políticas más grandes.
El bloque de acción consiste en un destino y una lista de reglas de coincidencia de llamadas al sistema separadas por comas.
El destino de la primera regla coincidente es la decisión de la política.
La siguiente tabla enumera los destinos de Kafel y sus valores de retorno correspondientes de seccomp-filter.
| Kafel | seccomp-filter |
|---|---|
ALLOW | SECCOMP_RET_ALLOW |
LOG | SECCOMP_RET_LOG |
KILL, KILL_THREAD, DENY | SECCOMP_RET_KILL |
KILL_PROCESS | SECCOMP_RET_KILL_PROCESS |
USER_NOTIF | SECCOMP_RET_USER_NOTIF |
ERRNO(number) | SECCOMP_RET_ERRNO+number |
TRAP(number) | SECCOMP_RET_TRAP+number |
TRACE(number) | SECCOMP_RET_TRACE+number |
Una regla consiste en el nombre de la llamada al sistema y una lista opcional de expresiones booleanas.
Lista de expresiones booleanas separadas por comas.
Una coma es semánticamente equivalente a || pero tiene la precedencia más baja, por lo tanto puede ser más fácil de leer.
Normalmente las llamadas al sistema se especifican por sus nombres según se definen en el kernel de Linux.
Sin embargo, también puede filtrar llamadas al sistema personalizadas que no están en la lista estándar de llamadas al sistema.
Puede definir una constante y usarla en lugar del nombre de la llamada al sistema o utilizar la palabra clave SYSCALL.
#define mysyscall -1
POLICY my_const {
ALLOW {
mysyscall
}
}
POLICY my_literal {
ALLOW {
SYSCALL[-1]
}
}
Puede restringir una regla de llamada al sistema a un subconjunto de arquitecturas usando el guardián ON.
El guardián acepta un solo nombre de arquitectura o una lista separada por comas entre llaves.
Los nombres corresponden a los destinos reconocidos por kafel_set_target_archs (por ejemplo x86_64, x86, arm, aarch64, mips64, riscv64, m68k).
ALLOW {
io_uring_setup ON x86_64,
arm_fadvise64_64 ON arm
}
Al compilar para una arquitectura fuera del guardián, la regla de llamada al sistema se ignora, por lo que las políticas entre arquitecturas ya no necesitan duplicar políticas completas solo para tener en cuenta las diferencias de disponibilidad de llamadas al sistema.
Las expresiones booleanas se utilizan para filtrar llamadas al sistema según sus argumentos. Una expresión se asemeja a la sintaxis del lenguaje C, excepto que no hay operadores aritméticos.
some_syscall(first_arg, my_arg_name) { first_arg == 42 && my_arg_name != 42 }
Los operadores Y bitwise (&) y O ('|') pueden usarse para probar flags.
mmap { (prot & PROT_EXEC) == 0 },
open { flags == O_RDONLY|O_CLOEXEC }
No tiene que declarar argumentos para llamadas al sistema conocidas, sino que puede usar sus nombres regulares según se especifica en el kernel de Linux y las páginas man.
write { fd == 1 }
Para simplificar la reutilización y composición de políticas, kafel proporciona soporte para include.
#include "some_other_file.policy"
Kafel busca archivos incluidos solo en los directorios agregados explícitamente a las rutas de búsqueda.
kafel_add_include_search_path(ctxt, "includes/path");
Agrega includes/path a las rutas de búsqueda - la directiva include de ejemplo se referirá entonces a includes/path/some_other_file.policy.
La directiva include se termina con una nueva línea o un punto y coma. Se pueden especificar múltiples archivos, separados por espacio en blanco, en una sola directiva.
#include "first.policy" "second.policy"; #include "third.policy"
Cuando se usa con nsjail, el siguiente comando permite crear un entorno bastante restringido para su shell
$ ./nsjail --chroot / --seccomp_string 'POLICY a { ALLOW { write, execve, brk, access, mmap, open, newfstat, close, read, mprotect, arch_prctl, munmap, getuid, getgid, getpid, rt_sigaction, geteuid, getppid, getcwd, getegid, ioctl, fcntl, newstat, clone, wait4, rt_sigreturn, exit_group } } USE a DEFAULT KILL' -- /bin/sh -i
[2017-01-15T21:53:08+0100] Mode: STANDALONE_ONCE
[2017-01-15T21:53:08+0100] Jail parameters: hostname:'NSJAIL', chroot:'/', process:'/bin/sh', bind:[::]:0, max_conns_per_ip:0, uid:(ns:1000, global:1000), gid:(ns:1000, global:1000), time_limit:0, personality:0, daemonize:false, clone_newnet:true, clone_newuser:true, clone_newns:true, clone_newpid:true, clone_newipc:true, clonew_newuts:true, clone_newcgroup:false, keep_caps:false, tmpfs_size:4194304, disable_no_new_privs:false, pivot_root_only:false
[2017-01-15T21:53:08+0100] Mount point: src:'/' dst:'/' type:'' flags:0x5001 options:''
[2017-01-15T21:53:08+0100] Mount point: src:'(null)' dst:'/proc' type:'proc' flags:0x0 options:''
[2017-01-15T21:53:08+0100] PID: 18873 about to execute '/bin/sh' for [STANDALONE_MODE]
/bin/sh: 0: can't access tty; job control turned off
$ set
IFS='
'
OPTIND='1'
PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
PPID='0'
PS1='$ '
PS2='> '
PS4='+ '
PWD='/'
$ id
Bad system call
$ exit
[2017-01-15T21:53:17+0100] PID: 18873 exited with status: 159, (PIDs left: 0)