Libro "BPF por Linuksa Monitorado"

Libro "BPF por Linuksa Monitorado"Saluton, loĝantoj de Khabro! La virtuala maŝino BPF estas unu el la plej gravaj komponantoj de la Linukso-kerno. Ĝia taŭga uzo permesos al sistemaj inĝenieroj trovi misfunkciojn kaj solvi eĉ la plej kompleksajn problemojn. Vi lernos kiel verki programojn, kiuj kontrolas kaj modifas la konduton de la kerno, kiel sekure efektivigi kodon por monitori eventojn en la kerno, kaj multe pli. David Calavera kaj Lorenzo Fontana helpos vin malŝlosi la potencon de BPF. Pligrandigu vian scion pri agado-optimumigo, interkonektado, sekureco. - Uzu BPF por kontroli kaj modifi la konduton de la Linukso-kerno. - Injektu kodon por sekure monitori kernajn eventojn sen devi rekompili la kernon aŭ rekomenci la sistemon. — Uzu oportunajn kodekzemplojn en C, Go aŭ Python. - Prenu kontrolon posedante la BPF-programan vivociklon.

Linukso Kerna Sekureco, Ĝiaj Trajtoj kaj Seccomp

BPF provizas potencan manieron etendi la kernon sen oferi stabilecon, sekurecon aŭ rapidecon. Tial, la kernaj programistoj opiniis, ke estus bona ideo uzi ĝian ĉiuflankecon por plibonigi procezan izolitecon en Seccomp per efektivigo de Seccomp-filtriloj subtenataj de BPF-programoj, ankaŭ konataj kiel Seccomp BPF. En ĉi tiu ĉapitro ni klarigos kio estas Seccomp kaj kiel ĝi estas uzata. Tiam vi lernos kiel skribi Seccomp-filtrilojn uzante BPF-programojn. Post tio, ni rigardos la enkonstruitajn BPF-hokojn, kiuj estas inkluzivitaj en la kerno por Linukso-sekurecaj moduloj.

Linuksaj Sekurecaj Moduloj (LSM) estas kadro kiu disponigas aron da funkcioj kiuj povas esti uzataj por efektivigi diversajn sekurecmodelojn en normigita maniero. LSM povas esti uzata rekte en la kerna fontarbo, kiel Apparmor, SELinux kaj Tomoyo.

Ni komencu diskutante la kapablojn de Linukso.

Trajtoj

La esenco de la kapabloj de Linukso estas, ke vi devas doni senprivilegian procezpermeson plenumi certan taskon, sed sen uzi suid tiucele, aŭ alie igi la procezon privilegiita, reduktante la eblecon de atako kaj permesante al la procezo plenumi certajn taskojn. Ekzemple, se via aplikaĵo bezonas malfermi privilegian havenon, ekzemple 80, anstataŭ ruli la procezon kiel radikon, vi povas simple doni al ĝi la kapablon CAP_NET_BIND_SERVICE.

Konsideru programon Go nomita main.go:

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

Ĉi tiu programo servas HTTP-servilon ĉe la haveno 80 (ĉi tio estas privilegiita haveno). Kutime ni rulas ĝin tuj post kompilo:

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

Tamen, ĉar ni ne donas radikajn privilegiojn, ĉi tiu kodo ĵetos eraron kiam ligos la havenon:

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

capsh (ŝelmanaĝero) estas ilo kiu funkciigas ŝelon kun specifa aro de kapabloj.

En ĉi tiu kazo, kiel jam menciite, anstataŭ doni plenajn radikrajtojn, vi povas ebligi privilegian pordoligadon provizante la kapablon cap_net_bind_service kune kun ĉio alia, kio jam estas en la programo. Por fari tion, ni povas enmeti nian programon en kapŝ:

# 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"

Ni iomete komprenu ĉi tiun teamon.

  • capsh - uzu capsh kiel ŝelon.
  • —caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' - ĉar ni devas ŝanĝi la uzanton (ni ne volas funkcii kiel radiko), ni specifos cap_net_bind_service kaj la eblon efektive ŝanĝi la uzanto-identigilon de radiko al neniu, nome kap_setuid kaj cap_setgid.
  • —keep=1 — ni volas konservi la instalitajn kapablojn kiam ŝanĝas de la radika konto.
  • —user=“nobody” — la finuzanto rulanta la programon estos neniu.
  • —addamb=cap_net_bind_service — agordu la malplenigon de rilataj kapabloj post ŝanĝado de radika reĝimo.
  • - -c "./capabilities" - nur rulu la programon.

Ligitaj kapabloj estas speciala speco de kapabloj kiuj estas hereditaj de infanprogramoj kiam la nuna programo ekzekutas ilin uzante execve(). Nur kapabloj kiuj rajtas esti asociitaj, aŭ alivorte, kiel mediokapabloj, povas esti hereditaj.

Vi verŝajne demandas, kion signifas +eip post specifi la kapablon en la opcio --caps. Ĉi tiuj flagoj estas uzataj por determini ke la kapableco:

-devas esti aktivigita (p);

-uzebla (e);

-povas esti heredita per infanaj procezoj (i).

Ĉar ni volas uzi cap_net_bind_service, ni devas fari tion per la e flago. Tiam ni komencos la ŝelon en la komando. Ĉi tio funkcios la binarajn kapablojn kaj ni devas marki ĝin per la i-flago. Fine, ni volas, ke la funkcio estu ebligita (ni faris tion sen ŝanĝi la UID) per p. Ĝi aspektas kiel cap_net_bind_service+eip.

Vi povas kontroli la rezulton uzante ss. Ni mallongigu la eliron iomete por konveni sur la paĝo, sed ĝi montros la rilatan havenon kaj uzantidentigilon krom 0, ĉi-kaze 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 ĉi tiu ekzemplo ni uzis capsh, sed vi povas skribi ŝelon uzante libcap. Por pliaj informoj, vidu man 3 libcap.

Verkante programojn, sufiĉe ofte la programisto ne anticipe scias ĉiujn funkciojn, kiujn la programo bezonas dum la rultempo; Krome, ĉi tiuj funkcioj povas ŝanĝiĝi en novaj versioj.

Por pli bone kompreni la kapablojn de nia programo, ni povas preni la BCC kapablan ilon, kiu agordas la kprobe por la cap_capable kernelfunkcio:

/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

Ni povas atingi la samon uzante bpftrace kun unu-linia kprobe en la kap_kapabla kernelfunkcio:

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

Ĉi tio eligos ion kiel jenon se la kapabloj de nia programo estas ebligitaj post 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 kvina kolumno estas la kapabloj, kiujn la procezo bezonas, kaj ĉar ĉi tiu eligo inkluzivas ne-reviziajn eventojn, ni vidas ĉiujn ne-reviziajn kontrolojn kaj finfine la bezonatan kapablon kun la revizia flago (lasta en la eligo) agordita al 1. Kapablo. unu pri kiu ni interesiĝas estas CAP_NET_BIND_SERVICE, ĝi estas difinita kiel konstanto en la kerna fontkodo en la dosiero include/uapi/linux/ability.h kun identigilo 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">

Kapabloj ofte estas ebligitaj ĉe rultempo por ujoj kiel ekzemple runC aŭ Docker por permesi al ili funkcii en senprivilegia reĝimo, sed ili estas permesitaj nur la kapabloj necesaj por ruli la plej multajn aplikojn. Kiam aplikaĵo postulas certajn kapablojn, Docker povas provizi ilin uzante --cap-add:

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

Ĉi tiu komando donos al la ujo la kapablon CAP_NET_ADMIN, permesante al ĝi agordi retan ligon por aldoni la dummy0-interfacon.

La sekva sekcio montras kiel uzi funkciojn kiel filtrado, sed uzante malsaman teknikon, kiu permesas al ni programi efektivigi niajn proprajn filtrilojn.

Seccomp

Seccomp signifas Sekura Komputado kaj estas sekureca tavolo efektivigita en la Linukso-kerno kiu permesas al programistoj filtri certajn sistemajn vokojn. Kvankam Seccomp estas komparebla en kapabloj al Linukso, ĝia kapablo administri certajn sistemvokojn igas ĝin multe pli fleksebla kompare al ili.

Seccomp kaj Linuksaj funkcioj ne estas reciproke ekskluzivaj kaj ofte estas uzataj kune por profiti el ambaŭ aliroj. Ekzemple, vi eble volas doni al procezo la kapablon CAP_NET_ADMIN sed ne permesi al ĝi akcepti socket-konektojn, blokante la akceptu kaj akceptu4 sistemajn vokojn.

La Seccomp-filtradmetodo estas bazita sur BPF-filtriloj funkciigantaj en la SECCOMP_MODE_FILTER-reĝimo, kaj sistemvoka filtrado estas farita en la sama maniero kiel por pakaĵetoj.

Seccomp-filtriloj estas ŝarĝitaj uzante prctl per la PR_SET_SECCOP-operacio. Tiuj filtriloj prenas la formon de BPF-programo kiu estas efektivigita por ĉiu Seccomp-pakaĵeto reprezentita per la seccomp_data strukturo. Ĉi tiu strukturo enhavas la referencan arkitekturon, montrilon al procesorinstrukcioj dum la sistemvoko, kaj maksimume ses sistemvoka argumentoj, esprimitaj kiel uint64.

Jen kiel aspektas la strukturo seccomp_data de la fontkodo de la kerno en la dosiero linux/seccomp.h:

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

Kiel vi povas vidi el ĉi tiu strukturo, ni povas filtri laŭ la sistemvoko, ĝiaj argumentoj aŭ kombinaĵo de ambaŭ.

Post ricevado de ĉiu Seccomp-pako, la filtrilo devas plenumi pretigon por fari finan decidon kaj diri al la kerno kion fari poste. La fina decido estas esprimita per unu el la revenaj valoroj (statuskodoj).

- SECCOMP_RET_KILL_PROCESS - mortigas la tutan procezon tuj post filtrado de sistemvoko kiu ne estas efektivigita pro tio.

- SECCOMP_RET_KILL_THREAD - finas la nunan fadenon tuj post filtrado de sistemvoko kiu ne estas efektivigita pro tio.

— SECCOMP_RET_KILL — kaŝnomo por SECCOMP_RET_KILL_THREAD, lasita por retrokongruo.

- SECCOMP_RET_TRAP - la sistemvoko estas malpermesita, kaj la signalo SIGSYS (Malbona Sistemvoko) estas sendita al la tasko kiu vokas ĝin.

- SECCOMP_RET_ERRNO - La sistemvoko ne estas efektivigita, kaj parto de la revenvaloro de la filtrilo SECCOMP_RET_DATA estas transdonita al uzantspaco kiel la errno-valoro. Depende de la kaŭzo de la eraro, malsamaj eraraj valoroj estas resenditaj. Listo de erarnombroj estas provizita en la sekva sekcio.

- SECCOMP_RET_TRACE - Uzita por sciigi la ptrace-spurilon uzante - PTRACE_O_TRACESECCOMP por kapti kiam sistemvoko estas efektivigita por vidi kaj kontroli tiun procezon. Se spurilo ne estas konektita, eraro estas resendita, errno estas agordita al -ENOSYS, kaj la sistemvoko ne estas efektivigita.

- SECCOMP_RET_LOG - la sistemvoko estas solvita kaj registrita.

- SECCOMP_RET_ALLOW - la sistemvoko estas simple permesita.

ptrace estas sistemvoko por efektivigi spurajn mekanismojn en procezo nomita tracee, kun la kapablo monitori kaj kontroli la plenumon de la procezo. La spurprogramo povas efike influi la ekzekuton kaj modifi la memorregistrojn de tracee. En la Seccomp-kunteksto, ptrace estas uzita kiam ekigita per la SECCOMP_RET_TRACE statuskodo, tiel ke la spurilo povas malhelpi la sistemvokon ekzekuti kaj efektivigi sian propran logikon.

Seccomp-eraroj

De tempo al tempo, laborante kun Seccomp, vi renkontos diversajn erarojn, kiuj estas identigitaj per revena valoro de tipo SECCOMP_RET_ERRNO. Por raporti eraron, la seccomp sistemvoko resendos -1 anstataŭ 0.

La jenaj eraroj eblas:

- EACCESS - La alvokanto ne rajtas fari sistemvokon. Ĉi tio kutime okazas ĉar ĝi ne havas CAP_SYS_ADMIN-privilegiojn aŭ no_new_privs ne estas agordita per prctl (pri tio ni parolos poste);

— EFAULT — la pasitaj argumentoj (argoj en la strukturo seccomp_data) ne havas validan adreson;

— EINVAL — povas esti kvar kialoj ĉi tie:

-la petita operacio estas nekonata aŭ ne subtenata de la kerno en la nuna agordo;

-la specifitaj flagoj ne validas por la petita operacio;

-operacio inkluzivas BPF_ABS, sed estas problemoj kun la specifita ofseto, kiu povas superi la grandecon de la strukturo seccomp_data;

-la nombro da instrukcioj pasitaj al la filtrilo superas la maksimumon;

— ENOMEM — ne sufiĉa memoro por ekzekuti la programon;

- EOPNOTSUPP - la operacio indikis ke kun SECCOMP_GET_ACTION_AVAIL la ago estis disponebla, sed la kerno ne subtenas revenojn en argumentoj;

— ESRCH — problemo okazis dum sinkronigado de alia fluo;

- ENOSYS - Ne estas spurilo ligita al la ago SECCOMP_RET_TRACE.

prctl estas sistemvoko kiu permesas al uzantspaca programo manipuli (agordi kaj akiri) specifajn aspektojn de procezo, kiel ekzemple bajta endianeco, fadennomoj, sekura komputadreĝimo (Seccomp), privilegioj, Perf-okazaĵoj, ktp.

Seccomp povas ŝajni al vi kiel sandbox-teknologio, sed ĝi ne estas. Seccomp estas ilo kiu permesas al uzantoj evoluigi sablokestan mekanismon. Nun ni rigardu kiel uzantinteragoprogramoj estas kreitaj uzante filtrilon nomitan rekte per la Seccomp sistemvoko.

BPF Seccomp Filtrila Ekzemplo

Ĉi tie ni montros kiel kombini la du agojn diskutitajn antaŭe, nome:

— ni skribos Seccomp BPF-programon, kiu estos uzata kiel filtrilo kun malsamaj revenkodoj depende de la decidoj faritaj;

— ŝarĝu la filtrilon per prctl.

Unue vi bezonas kapliniojn de la norma biblioteko kaj la Linukso-kerno:

#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>

Antaŭ provi ĉi tiun ekzemplon, ni devas certigi, ke la kerno estas kompilita kun CONFIG_SECCOMP kaj CONFIG_SECCOMP_FILTER agordita al y. Sur funkcianta maŝino vi povas kontroli ĉi tion jene:

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

La resto de la kodo estas duparta funkcio instal_filter. La unua parto enhavas nian liston de BPF-filtraj instrukcioj:

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),
  };

La instrukcioj estas fiksitaj uzante la BPF_STMT kaj BPF_JUMP makroojn difinitajn en la dosiero linux/filter.h.
Ni trarigardu la instrukciojn.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, arch))) - la sistemo ŝarĝas kaj akumuliĝas de BPF_LD en la formo de la vorto BPF_W, pakaj datumoj situas ĉe fiksa ofseto BPF_ABS.

- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ark, 0, 3) - kontrolas uzante BPF_JEQ ĉu la arkitekturvaloro en la akumulatorkonstanto BPF_K estas egala al ark. Se jes, saltas ĉe ofseto 0 al la sekva instrukcio, alie saltas ĉe ofseto 3 (ĉi-kaze) por ĵeti eraron ĉar arko ne kongruas.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, nr))) - Ŝarĝas kaj akumuliĝas de BPF_LD en la formo de la vorto BPF_W, kiu estas la sistema voknumero enhavita en la fiksa ofseto de BPF_ABS.

— BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1) — komparas la sisteman voknumeron kun la valoro de la nr-variablo. Se ili estas egalaj, pluiras al la sekva instrukcio kaj malŝaltas la sistemvokon, alie permesas la sistemvokon kun SECCOMP_RET_ALLOW.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (eraro & SECCOMP_RET_DATA)) - finas la programon per BPF_RET kaj rezulte produktas eraron SECCOMP_RET_ERRNO kun la nombro el la eraro variablo.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) - finas la programon per BPF_RET kaj permesas la sistemvokon esti efektivigita uzante SECCOMP_RET_ALLOW.

SECCOMP ESTAS CBPF
Vi eble scivolas kial listo de instrukcioj estas uzata anstataŭ kompilita ELF-objekto aŭ JIT-kompilita C-programo.

Estas du kialoj por ĉi tio.

• Unue, Seccomp uzas cBPF (klasika BPF) kaj ne eBPF, kio signifas: ĝi ne havas registrojn, sed nur akumulilon por konservi la lastan kalkulrezulton, kiel videblas en la ekzemplo.

• Due, Seccomp akceptas montrilon al tabelo de BPF-instrukcioj rekte kaj nenion alian. La makrooj, kiujn ni uzis, simple helpas specifi ĉi tiujn instrukciojn laŭ programista maniero.

Se vi bezonas pli da helpo por kompreni ĉi tiun asembleon, konsideru la pseŭdokodon, kiu faras la samon:

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

Post difini la filtrilkodon en la socket_filter strukturo, vi devas difini sock_fprog enhavantan la kodon kaj la kalkulitan longon de la filtrilo. Ĉi tiu datumstrukturo estas bezonata kiel argumento por deklari la procezon ruliĝi poste:

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

Restas nur unu afero por fari en la funkcio instal_filter - ŝargu la programon mem! Por fari tion, ni uzas prctl, prenante PR_SET_SECCOMP kiel opcion por eniri sekuran komputikreĝimon. Tiam ni diras al la reĝimo ŝargi la filtrilon per SECCOMP_MODE_FILTER, kiu estas enhavita en la prog-variablo de tipo sock_fprog:

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

Fine, ni povas uzi nian instal_filter-funkcion, sed antaŭ tio ni devas uzi prctl por agordi PR_SET_NO_NEW_PRIVS por la nuna ekzekuto kaj tiel eviti la situacion kie infanprocezoj ricevas pli da privilegioj ol siaj gepatroj. Kun ĉi tio, ni povas fari la sekvajn prctl-vokojn en la instal_filter-funkcio sen havi radikrajtojn.

Nun ni povas voki la funkcion instal_filter. Ni bloku ĉiujn skribajn sistemajn vokojn rilatajn al la arkitekturo X86-64 kaj simple donu permeson, kiu blokas ĉiujn provojn. Post instalo de la filtrilo, ni daŭrigas ekzekuton uzante la unuan argumenton:

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]);
 }

Ni komencu. Por kompili nian programon ni povas uzi ĉu clang aŭ gcc, ĉu ajn ĝi nur kompilas la main.c-dosieron sen specialaj opcioj:

clang main.c -o filter-write

Kiel notite, ni blokis ĉiujn enirojn en la programo. Por testi tion vi bezonas programon kiu eligas ion - ls ŝajnas bona kandidato. Jen kiel ŝi kutime kondutas:

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

Mirinda! Jen kiel aspektas uzi nian envolvaĵprogramon: Ni simple pasas la programon, kiun ni volas testi, kiel la unuan argumenton:

./filter-write "ls -la"

Kiam ekzekutita, ĉi tiu programo produktas tute malplenan produktaĵon. Tamen, ni povas uzi strace por vidi kio okazas:

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

La rezulto de la laboro estas multe mallongigita, sed la responda parto de ĝi montras, ke registroj estas blokitaj kun la EPERM-eraro - la sama, kiun ni agordis. Ĉi tio signifas, ke la programo eligas nenion ĉar ĝi ne povas aliri la skriban sistemvokon:

[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)

Nun vi komprenas kiel funkcias Seccop BPF kaj havas bonan ideon pri tio, kion vi povas fari per ĝi. Sed ĉu vi ne ŝatus atingi la samon per eBPF anstataŭ cBPF por utiligi ĝian plenan potencon?

Pensante pri eBPF-programoj, plej multaj homoj pensas, ke ili simple skribas ilin kaj ŝarĝas ilin per administraj privilegioj. Dum ĉi tiu deklaro estas ĝenerale vera, la kerno efektivigas aron de mekanismoj por protekti eBPF-objektojn sur diversaj niveloj. Tiuj mekanismoj estas nomitaj BPF LSM-kaptiloj.

BPF LSM kaptiloj

Por disponigi arkitektur-sendependan monitoradon de sistemakazaĵoj, LSM efektivigas la koncepton de kaptiloj. Hokvoko estas teknike simila al sistemvoko, sed estas sistemsendependa kaj integrita kun la infrastrukturo. LSM disponigas novan koncepton en kiu abstrakta tavolo povas helpi eviti problemojn renkontitajn dum traktado sistemvokojn pri malsamaj arkitekturoj.

En la momento de skribado, la kerno havas sep hokojn asociitajn kun BPF-programoj, kaj SELinux estas la nura enkonstruita LSM kiu efektivigas ilin.

La fontkodo por la kaptiloj troviĝas en la kerno-arbo en la dosiero 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);

Ĉiu el ili estos nomita en malsamaj stadioj de ekzekuto:

— security_bpf — faras komencan kontrolon de ekzekutitaj BPF-sistemvokoj;

- security_bpf_map - kontrolas kiam la kerno resendas dosierpriskribilon por la mapo;

- security_bpf_prog - kontrolas kiam la kerno resendas dosierpriskribilon por la programo eBPF;

— security_bpf_map_alloc — kontrolas ĉu la sekureca kampo ene de BPF-mapoj estas pravigita;

- security_bpf_map_free - kontrolas ĉu la sekureca kampo estas forigita ene de BPF-mapoj;

— security_bpf_prog_alloc — kontrolas ĉu la sekureca kampo estas pravigita ene de BPF-programoj;

- security_bpf_prog_free - kontrolas ĉu la sekureca kampo estas forigita ene de BPF-programoj.

Nun, vidante ĉion ĉi, ni komprenas: la ideo malantaŭ LSM BPF-interkaptistoj estas, ke ili povas provizi protekton al ĉiu eBPF-objekto, certigante ke nur tiuj kun la taŭgaj privilegioj povas fari operaciojn sur kartoj kaj programoj.

Resumo

Sekureco ne estas io, kion vi povas efektivigi en unu-granda maniero por ĉio, kion vi volas protekti. Gravas povi protekti sistemojn je malsamaj niveloj kaj diversmaniere. Kredu ĝin aŭ ne, la plej bona maniero por sekurigi sistemon estas organizi malsamajn nivelojn de protekto de malsamaj pozicioj, tiel ke redukti la sekurecon de unu nivelo ne ebligas aliron al la tuta sistemo. La kernaj programistoj faris bonegan laboron por doni al ni aron da malsamaj tavoloj kaj tuŝpunktoj. Ni esperas, ke ni donis al vi bonan komprenon pri kio tavoloj estas kaj kiel uzi BPF-programojn por labori kun ili.

Pri aŭtoroj

David Calavera estas la CTO ĉe Netlify. Li laboris en Docker-subteno kaj kontribuis al la evoluo de Runc, Go kaj BCC-iloj, same kiel aliaj malfermfontaj projektoj. Konata pro lia laboro pri Docker-projektoj kaj evoluo de la Docker-aldonaĵekosistemo. David estas tre pasia pri flamaj grafikaĵoj kaj ĉiam serĉas optimumigi rendimenton.

Lorenzo Fontana laboras pri la malfermfonta teamo ĉe Sysdig, kie li ĉefe koncentriĝas pri Falco, projekto de Cloud Native Computing Foundation, kiu provizas kontenan rultempan sekurecon kaj detekton de anomalioj per kernomodulo kaj eBPF. Li estas pasiigita pri distribuitaj sistemoj, programaro difinita retoj, la Linukso-kerno, kaj agado-analizo.

» Pliaj detaloj pri la libro troveblas ĉe retejo de la eldonisto
» Enhavtabelo
» Eltiraĵo

Por Khabrozhiteley 25% rabato uzante kuponon - linux

Post pago de la papera versio de la libro, elektronika libro estos sendita retpoŝte.

fonto: www.habr.com

Aldoni komenton