
Un langage et une bibliothèque pour spécifier des politiques de filtrage d'appels système.
Kafel est un langage et une bibliothèque pour spécifier des politiques de filtrage d'appels système. Les politiques sont compilées en code BPF pouvant être utilisé avec seccomp-filter.
Ceci n'est PAS un produit officiel 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);
Un langage simple est utilisé pour définir les politiques.
Le fichier de politique est constitué d'instructions.
Une instruction peut être :
Les instructions de définition de politique placées au niveau du fichier seront ajoutées à la politique implicite de niveau supérieur. Cette politique de niveau supérieur va être compilée.
DEFAULT the_action
Spécifie que l'action the_action doit être prise lorsqu'aucune règle ne correspond.
L'action par défaut doit être spécifiée une seule fois.
Si le fichier de politique ne spécifie aucune action par défaut, l'action par défaut sera KILL
Kafel prend en charge les notations numériques suivantes :
420xfa107770b10101Vous pouvez définir des constantes numériques pour rendre vos politiques plus lisibles. Les définitions de constantes peuvent être placées presque n'importe où dans le fichier de politique. Une définition de constante ne peut pas être placée à l'intérieur d'une définition de politique. Les constantes définies peuvent ensuite être utilisées partout où un nombre est attendu.
#define MYCONST 123
Une définition de politique est une liste de blocs d'action et d'instructions d'utilisation séparées par des virgules.
samples/ contient quelques exemples de politiques qui démontrent les fonctionnalités prises en charge.
Une instruction USE someOtherPolicy se comporte comme si le corps de someOtherPolicy était copié à sa place. Vous ne pouvez utiliser que des politiques définies avant l'instruction d'utilisation. Avec les instructions d'utilisation, vous pouvez créer des groupes significatifs de règles de filtrage qui sont des blocs de construction de politiques plus grandes.
Un bloc d'action consiste en une cible et une liste de règles de correspondance d'appels système séparées par des virgules. La cible de la première règle correspondante est la décision de politique.
Le tableau suivant liste les cibles Kafel et leurs valeurs de retour seccomp-filter correspondantes.
Une règle consiste en un nom d'appel système et une liste optionnelle d'expressions booléennes.
Liste d'expressions booléennes séparées par des virgules. Une virgule est sémantiquement équivalente à || mais a la précédence la plus basse, elle peut donc être plus facile à lire.
Normalement, les appels système sont spécifiés par leurs noms tels que définis dans le noyau Linux. Cependant, vous pouvez également filtrer des appels système personnalisés qui ne sont pas dans la liste standard des appels système. Vous pouvez soit définir une constante et l'utiliser à la place du nom de l'appel système, soit utiliser le mot-clé SYSCALL.
#define mysyscall -1
POLICY my_const {
ALLOW {
mysyscall
}
}
POLICY my_literal {
ALLOW {
SYSCALL[-1]
}
}
Vous pouvez restreindre une règle d'appel système à un sous-ensemble d'architectures en utilisant la garde ON. La garde accepte soit un nom d'architecture unique, soit une liste séparée par des virgules entourée d'accolades. Les noms correspondent aux cibles reconnues par kafel_set_target_archs (par exemple x86_64, x86, arm, aarch64, mips64, riscv64, m68k).
ALLOW {
io_uring_setup ON x86_64,
arm_fadvise64_64 ON arm
}
Lors de la compilation pour une architecture en dehors de la garde, la règle d'appel système est ignorée, donc les politiques multi-architectures n'ont plus besoin de dupliquer des politiques entières pour tenir compte des différences de disponibilité des appels système.
Les expressions booléennes sont utilisées pour filtrer les appels système en fonction de leurs arguments. Une expression ressemble à la syntaxe du langage C, sauf qu'il n'y a pas d'opérateurs arithmétiques.
some_syscall(first_arg, my_arg_name) { first_arg == 42 && my_arg_name != 42 }
Les opérateurs binaires ET (&) et OU (|) peuvent être utilisés pour tester les flags.
mmap { (prot & PROT_EXEC) == 0 },
open { flags == O_RDONLY|O_CLOEXEC }
Vous n'avez pas besoin de déclarer les arguments pour les appels système bien connus, vous pouvez simplement utiliser leurs noms habituels tels que spécifiés dans le noyau Linux et les pages man.
write { fd == 1 }
Afin de simplifier la réutilisation et la composition des politiques, kafel prend en charge l'inclusion.
#include "some_other_file.policy"
Kafel recherche les fichiers inclus uniquement dans les répertoires explicitement ajoutés aux chemins de recherche.
kafel_add_include_search_path(ctxt, "includes/path");
Ajoute includes/path aux chemins de recherche - l'exemple de directive d'inclusion fera alors référence à includes/path/some_other_file.policy.
La directive d'inclusion est terminée par un saut de ligne ou un point-virgule. Plusieurs fichiers, séparés par des espaces, peuvent être spécifiés dans une seule directive.
#include "first.policy" "second.policy"; #include "third.policy"
Lorsqu'il est utilisé avec nsjail, la commande suivante permet de créer un environnement assez restreint pour votre 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)
| 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 |