Китеп "Linux мониторинги үчүн BPF"

Китеп "Linux мониторинги үчүн BPF"Салам, Khabro тургундары! BPF виртуалдык машинасы Linux ядросунун эң маанилүү компоненттеринин бири. Аны туура колдонуу системалык инженерлерге каталарды табууга жана эң татаал маселелерди чечүүгө мүмкүндүк берет. Сиз ядронун жүрүм-турумун көзөмөлдөгөн жана өзгөрткөн программаларды кантип жазууну, ядродогу окуяларды көзөмөлдөө үчүн кодду кантип коопсуз ишке ашырууну жана башка көптөгөн нерселерди үйрөнөсүз. Дэвид Калавера жана Лоренцо Фонтана сизге BPFтин күчүн ачууга жардам берет. Ишти оптималдаштыруу, тармактык, коопсуздук боюнча билимиңизди кеңейтиңиз. - Linux ядросунун жүрүм-турумун көзөмөлдөө жана өзгөртүү үчүн BPF колдонуңуз. - Ядрону кайра компиляциялоосуз же системаны кайра жүктөөсүз өзөк окуяларын коопсуз көзөмөлдөө үчүн кодду киргизиңиз. — C, Go же Python тилдеринде ыңгайлуу код мисалдарын колдонуңуз. - BPF программасынын жашоо циклине ээ болуу менен көзөмөлдү алыңыз.

Linux ядросунун коопсуздугу, анын өзгөчөлүктөрү жана Seccomp

BPF туруктуулукту, коопсуздукту же ылдамдыкты жоготпостон ядрону кеңейтүүнүн күчтүү жолун камсыз кылат. Ушул себептен улам, ядрону иштеп чыгуучулар Seccomp BPF деп аталган BPF программалары тарабынан колдоого алынган Seccomp чыпкаларын ишке ашыруу аркылуу Seccomp процессинин изоляциясын жакшыртуу үчүн анын ар тараптуулугун колдонуу жакшы идея деп ойлошкон. Бул бөлүмдө биз Seccomp деген эмне жана ал кандайча колдонуларын түшүндүрөбүз. Андан кийин BPF программаларын колдонуп Seccomp чыпкаларын жазууну үйрөнөсүз. Андан кийин, биз Linux коопсуздук модулдары үчүн ядродо камтылган BPF илгичтерин карап чыгабыз.

Linux коопсуздук модулдары (LSM) ар кандай коопсуздук моделдерин стандартташтырылган түрдө ишке ашыруу үчүн колдонула турган функциялардын жыйындысын камсыз кылган негиз болуп саналат. LSM түздөн-түз өзөк булагы дарагында колдонулушу мүмкүн, мисалы, Apparmor, SELinux жана Tomoyo.

Келгиле, Linuxтун мүмкүнчүлүктөрүн талкуулоо менен баштайлы.

мүмкүнчүлүктөр

Linux'тун мүмкүнчүлүктөрүнүн маңызы - сиз артыкчылыксыз процесске белгилүү бир тапшырманы аткарууга уруксат беришиңиз керек, бирок бул максат үчүн suid колдонбостон, же болбосо процессти артыкчылыктуу кылып, чабуул жасоо мүмкүнчүлүгүн азайтып, процесске белгилүү бир тапшырмаларды аткарууга уруксат беришиңиз керек. Мисалы, эгер колдонмоңузга артыкчылыктуу порт ачуу керек болсо, айталы, 80, процессти тамыр катары иштетүүнүн ордуна, сиз ага CAP_NET_BIND_SERVICE мүмкүнчүлүгүн бере аласыз.

main.go деп аталган Go программасын карап көрөлү:

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

Бул программа 80 портунда HTTP серверин тейлейт (бул артыкчылыктуу порт). Адатта, биз аны компиляциядан кийин дароо иштетебиз:

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

Бирок, биз тамыр артыкчылыктарын бербегендиктен, бул код портту байлаганда ката кетирет:

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

capsh (кабык менеджери) - бул белгилүү бир мүмкүнчүлүктөр топтому бар кабыкты иштеткен курал.

Бул учурда, мурда айтылгандай, толук тамыр укуктарын берүүнүн ордуна, сиз программада болгон бардык башка нерселер менен бирге cap_net_bind_service мүмкүнчүлүгүн камсыз кылуу менен артыкчылыктуу портту байланыштыра аласыз. Бул үчүн, биз программабызды баш тамга менен кошо алабыз:

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

Бул команданы бир аз түшүнүп алалы.

  • capsh - капшакты кабык катары колдонуу.
  • —caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' - колдонуучуну өзгөртүү керек болгондуктан (биз тамыр катары иштеткибиз келбейт), биз cap_net_bind_service жана колдонуучунун идентификаторун чындыгында өзгөртүү мүмкүнчүлүгүн көрсөтөбүз. эч кимге тамыр, атап айтканда cap_setuid жана cap_setgid.
  • —keep=1 — биз түпкү аккаунттан которулганда орнотулган мүмкүнчүлүктөрдү сактап калгыбыз келет.
  • —user=“эч ким” — программаны иштеткен акыркы колдонуучу эч ким болбойт.
  • —addamb=cap_net_bind_service — тамыр режиминен өткөндөн кийин тиешелүү мүмкүнчүлүктөрдү тазалоону орнотуңуз.
  • - -c "./capabilities" - жөн гана программаны иштетиңиз.

Байланышкан мүмкүнчүлүктөр - учурдагы программа execve() аркылуу аларды аткарганда, бала программалар тарабынан мураска алынган мүмкүнчүлүктөрдүн өзгөчө түрү. Байланыштырууга уруксат берилген мүмкүнчүлүктөр гана, же башкача айтканда, айлана-чөйрөнүн мүмкүнчүлүктөрү катары мураска алынышы мүмкүн.

Мүмкүнчүлүктү --caps опциясында көрсөткөндөн кийин +eip эмнени билдирерин ойлонуп жаткандырсыз. Бул желектер жөндөмдүүлүгүн аныктоо үчүн колдонулат:

- активдештирилиши керек (p);

- колдонууга жеткиликтүү (д);

-бала процесстери тарабынан тукум кууса болот (i).

Биз cap_net_bind_service колдонгубуз келгендиктен, муну e желек менен кылышыбыз керек. Андан кийин командадагы кабыкты баштайбыз. Бул экилик мүмкүнчүлүктөрдү иштетет жана биз аны i желеги менен белгилешибиз керек. Акырында, биз функциянын иштетилишин каалайбыз (биз муну UIDди өзгөртпөстөн жасадык) б менен. Бул cap_net_bind_service+eip окшойт.

Сиз ss аркылуу натыйжаны текшере аласыз. Баракка туура келүү үчүн чыгарууну бир аз кыскарталы, бирок ал байланышкан портту жана 0дөн башка колдонуучунун идентификаторун көрсөтөт, бул учурда 65:

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

Бул мисалда биз capsh колдондук, бирок сиз libcap аркылуу кабык жазсаңыз болот. Көбүрөөк маалымат алуу үчүн, man 3 libcap караңыз.

Программаларды жазууда, көбүнчө иштеп чыгуучу программанын иштөө учурундагы бардык функцияларын алдын ала билбейт; Мындан тышкары, бул функциялар жаңы версияларда өзгөрүшү мүмкүн.

Биздин программанын мүмкүнчүлүктөрүн жакшыраак түшүнүү үчүн, cap_capable ядро ​​функциясы үчүн kprobe орноткон BCC жөндөмдүү куралды алсак болот:

/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

Ушул эле нерсеге cap_capable өзөк функциясында bpftrace бир лайнердик кпробду колдонуу менен жетише алабыз:

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

Эгерде биздин программанын мүмкүнчүлүктөрү 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

Бешинчи тилке – процесске керектүү мүмкүнчүлүктөр жана бул чыгарылыш аудиттен тышкары окуяларды камтыгандыктан, биз бардык аудитордук эмес текшерүүлөрдү жана акырында талап кылынган мүмкүнчүлүктү аудиттин желеги менен (чыгарууда акыркы) 1. Мүмкүнчүлүк бизди кызыктырган нерсе - CAP_NET_BIND_SERVICE, ал 10 идентификатору менен include/uapi/linux/ability.h файлындагы ядронун баштапкы кодундагы константа катары аныкталат:

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

Мүмкүнчүлүктөр көбүнчө runC же Docker сыяктуу контейнерлер үчүн иштөө убагында иштетилет, алар артыкчылыксыз режимде иштөөгө мүмкүндүк берет, бирок аларга көпчүлүк колдонмолорду иштетүү үчүн зарыл болгон мүмкүнчүлүктөргө гана уруксат берилет. Тиркеме белгилүү бир мүмкүнчүлүктөрдү талап кылганда, Docker аларды --cap-add аркылуу камсыздай алат:

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

Бул буйрук контейнерге CAP_NET_ADMIN мүмкүнчүлүгүн берип, dummy0 интерфейсин кошуу үчүн тармак шилтемесин конфигурациялоого мүмкүндүк берет.

Кийинки бөлүмдө чыпкалоо сыяктуу функцияларды кантип колдонуу керек, бирок өзүбүздүн чыпкаларыбызды программалык түрдө ишке ашырууга мүмкүндүк берген башка ыкманы колдонуу керек.

Seccomp

Seccomp Коопсуз эсептөө дегенди билдирет жана иштеп чыгуучуларга белгилүү система чалууларын чыпкалоого мүмкүндүк берген Linux ядросунда ишке ашырылган коопсуздук катмары. Seccomp мүмкүнчүлүктөрү боюнча Linux менен салыштырууга болот, бирок анын айрым тутумдук чалууларды башкаруу жөндөмдүүлүгү аларга салыштырмалуу бир топ ийкемдүү кылат.

Seccomp жана Linux өзгөчөлүктөрү бири-бирин жокко чыгарбайт жана көбүнчө эки ыкмадан тең пайда алуу үчүн чогуу колдонулат. Мисалы, сиз процесске CAP_NET_ADMIN мүмкүнчүлүгүн бергиңиз келсе, бирок ага розетка туташууларын кабыл алууга уруксат бербесеңиз болот, системалык чалууларды кабыл алуу жана кабыл алуу4.

Seccomp чыпкалоо ыкмасы SECCOMP_MODE_FILTER режиминде иштеген BPF чыпкаларына негизделген жана системалык чалууларды чыпкалоо пакеттердегидей эле аткарылат.

Seccomp чыпкалары PR_SET_SECCOMP операциясы аркылуу prctl аркылуу жүктөлөт. Бул чыпкалар seccomp_data структурасы тарабынан берилген ар бир Seccomp пакети үчүн аткарылуучу BPF программасынын формасын алышат. Бул структура маалымдама архитектурасын, системалык чакыруу учурундагы процессордун инструкцияларына көрсөткүчтү жана uint64 катары туюнтулган эң көп алты тутумдук чалуу аргументин камтыйт.

Linux/seccomp.h файлындагы ядронун баштапкы кодунан seccomp_data структурасы ушундай көрүнөт:

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

Бул түзүмдөн көрүнүп тургандай, биз системанын чакырыгы, анын аргументтери же экөөнүн тең айкалышы боюнча чыпкалай алабыз.

Ар бир Seccomp пакетин алгандан кийин, чыпка акыркы чечимди кабыл алуу жана ядрого андан ары эмне кылуу керектигин айтуу үчүн иштетүүнү аткарышы керек. Акыркы чечим кайтаруу баалуулуктарынын бири (статус коддору) менен көрсөтүлөт.

- SECCOMP_RET_KILL_PROCESS - ушундан улам аткарылбаган системалык чалууларды чыпкалоодон кийин дароо бүт процессти өлтүрөт.

- SECCOMP_RET_KILL_THREAD - ушундан улам аткарылбай калган системалык чакырууну чыпкалоодон кийин дароо учурдагы жипти токтотот.

— SECCOMP_RET_KILL — SECCOMP_RET_KILL_THREAD үчүн лакап ат, артка шайкеш келүү үчүн калтырылган.

- SECCOMP_RET_TRAP - системалык чалууга тыюу салынып, аны чакырган тапшырмага SIGSYS (Начар система чалуу) сигналы жөнөтүлөт.

- SECCOMP_RET_ERRNO - Системалык чалуу аткарылган жок жана SECCOMP_RET_DATA чыпкасынын кайтаруу маанисинин бир бөлүгү колдонуучу мейкиндигине errno мааниси катары өткөрүлөт. Катанын себебине жараша ар кандай ката маанилери кайтарылат. Ката номерлеринин тизмеси кийинки бөлүмдө берилген.

- SECCOMP_RET_TRACE - Бул процессти көрүү жана көзөмөлдөө үчүн тутумдук чалуу аткарылганда кармап туруу үчүн - PTRACE_O_TRACESECCOMP аркылуу ptrace трекерине кабарлоо үчүн колдонулат. Эгерде трекер туташтырылбаса, ката кайтарылып, errno -ENOSYS деп коюлуп, системалык чалуу аткарылбайт.

- SECCOMP_RET_LOG - системалык чалуу чечилет жана журналга жазылат.

- SECCOMP_RET_ALLOW - системалык чалууга жөн гана уруксат берилет.

ptrace бул процесстин аткарылышын көзөмөлдөө жана көзөмөлдөө мүмкүнчүлүгү менен tracee деп аталган процессте көзөмөлдөө механизмдерин ишке ашыруу үчүн системалык чакыруу. Trace программасы аткарылууга эффективдүү таасир эте алат жана трасисттин эс регистрлерин өзгөртө алат. Seccomp контекстинде ptrace SECCOMP_RET_TRACE статус коду тарабынан иштетилгенде колдонулат, андыктан тресер системалык чалууларды аткарууга бөгөт коюп, өзүнүн логикасын ишке ашыра алат.

Seccomp каталары

Мезгил-мезгили менен Seccomp менен иштөөдө сиз SECCOMP_RET_ERRNO түрүндөгү кайтаруу мааниси менен аныкталган ар кандай каталарга туш болосуз. Ката жөнүндө кабарлоо үчүн, seccomp тутумунун чалуусу 1 ордуна -0 кайтарат.

Төмөнкү каталар болушу мүмкүн:

- EACCESS - Чалууга системалык чалууга уруксат берилбейт. Бул көбүнчө анын CAP_SYS_ADMIN артыкчылыктары жок же prctl аркылуу no_new_privs орнотулбагандыктан болот (бул тууралуу кийинчерээк сүйлөшөбүз);

— EFAULT — өтүп кеткен аргументтер (seccomp_data структурасындагы args) жарактуу дареги жок;

— EINVAL — бул жерде төрт себеп болушу мүмкүн:

-суралган операция белгисиз же учурдагы конфигурацияда ядро ​​тарабынан колдоого алынбайт;

- көрсөтүлгөн желектер суралган операция үчүн жарактуу эмес;

-операция BPF_ABS камтыйт, бирок seccomp_data структурасынын өлчөмүнөн ашып кетиши мүмкүн болгон көрсөтүлгөн офсет менен көйгөйлөр бар;

-фильтрге берилген инструкциялардын саны максимумдан ашса;

— ENOMEM — программаны аткаруу үчүн эстутум жетишсиз;

- EOPNOTSUPP - операция SECCOMP_GET_ACTION_AVAIL менен аракет жеткиликтүү болгонун көрсөттү, бирок ядро ​​аргументтерде кайтарууну колдобойт;

— ESRCH — башка агымды синхрондоштурууда көйгөй келип чыкты;

- ENOSYS - SECCOMP_RET_TRACE аракетине тиркелген тракер жок.

prctl – бул колдонуучу-мейкиндик программасына процесстин спецификалык аспектилерин, мисалы, байт индиандуулугу, жиптердин аталыштары, коопсуз эсептөө режими (Seccomp), артыкчылыктар, Perf окуялары ж.

Seccomp сизге кумдук технология сыяктуу сезилиши мүмкүн, бирок андай эмес. Seccomp - бул колдонуучуларга кумкоргон механизмин иштеп чыгууга мүмкүндүк берүүчү программа. Эми келгиле, колдонуучунун өз ара аракеттенүү программалары Seccomp тутумунун чакыруусу аркылуу түздөн-түз чакырылган чыпка аркылуу кантип түзүлгөнүн карап көрөлү.

BPF Seccomp чыпкасынын мисалы

Бул жерде биз мурда талкууланган эки аракетти кантип айкалыштыруу керектигин көрсөтөбүз, атап айтканда:

— биз Seccomp BPF программасын жазабыз, ал кабыл алынган чечимдерге жараша ар кандай кайтаруу коддору менен фильтр катары колдонулат;

— prctl аркылуу чыпканы жүктөңүз.

Адегенде стандарттуу китепканадан жана 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>

Бул мисалга аракет кылуудан мурун, ядро ​​CONFIG_SECCOMP жана CONFIG_SECCOMP_FILTER y деп коюлган менен компиляцияланганын текшеришибиз керек. Иштеп жаткан машинада муну төмөнкүдөй текшере аласыз:

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

Калган код эки бөлүктөн турган install_filter функциясы. Биринчи бөлүгү биздин 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),
  };

Инструкциялар linux/filter.h файлында аныкталган BPF_STMT жана BPF_JUMP макросторунун жардамы менен коюлган.
Келгиле, көрсөтмөлөрдү карап көрөлү.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, arch))) - система BPF_LDден BPF_W сөзү түрүндө жүктөлөт жана топтолот, пакеттик маалыматтар BPF_ABS белгиленген жылышууда жайгашкан.

- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3) - BPF_JEQ жардамы менен BPF_K аккумулятордук константасынын архитектуралык мааниси archга барабар экендигин текшерет. Андай болсо, 0 офсетинен кийинки нускамага секирет, антпесе арка дал келбегендиктен ката кетирүү үчүн 3-офсетке (бул учурда) секирет.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, nr))) - BPF_LDден BPF_W сөзүнүн түрүндө жүктөлөт жана топтолот, бул BPF_ABSтин белгиленген офсетинде камтылган системалык чакыруу номери.

— BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1) — системанын чалуу номерин nr өзгөрмөнүн мааниси менен салыштырат. Эгерде алар бирдей болсо, кийинки нускамага өтөт жана системалык чалууларды өчүрөт, антпесе SECCOMP_RET_ALLOW менен системалык чалууга уруксат берет.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (ката & SECCOMP_RET_DATA)) - BPF_RET менен программаны токтотот жана натыйжада err өзгөрмөсүнүн номери менен SECCOMP_RET_ERRNO катасын чыгарат.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) - BPF_RET менен программаны токтотот жана SECCOMP_RET_ALLOW аркылуу системалык чалууларды аткарууга мүмкүндүк берет.

SECCOMP IS CBPF
Сиз эмне үчүн компиляцияланган ELF объектинин же JIT компиляцияланган C программасынын ордуна нускамалардын тизмеси колдонулат деп ойлонуп жатсаңыз болот.

Мунун эки себеби бар.

• Биринчиден, Seccomp eBPF эмес, cBPF (классикалык BPF) колдонот, бул: анын регистрлери жок, бирок мисалда көрүнүп тургандай, эсептөөнүн акыркы жыйынтыгын сактоо үчүн аккумулятор гана.

• Экинчиден, Seccomp BPF көрсөтмөлөрүнүн массивине көрсөткүчтү түздөн-түз кабыл алат жана башка эч нерсе жок. Биз колдонгон макростор бул нускамаларды программалоочуга ыңгайлуу жол менен көрсөтүүгө жардам берет.

Бул ассамблеяны түшүнүүдө көбүрөөк жардам керек болсо, ошол эле нерсени аткарган псевдокодду карап көрүңүз:

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

socket_filter түзүмүндө чыпка кодун аныктагандан кийин, сиз кодду жана чыпканын эсептелген узундугун камтыган sock_fprog аныкташыңыз керек. Бул маалымат структурасы процессти кийинчерээк иштетүү үчүн аргумент катары керек:

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

Install_filter функциясында бир гана нерсе калды - программанын өзүн жүктөңүз! Бул үчүн, биз коопсуз эсептөө режимине кирүү үчүн параметр катары PR_SET_SECCOMP алып, prctl колдонобуз. Андан кийин режимге чыпканы SECCOMP_MODE_FILTER аркылуу жүктөөнү айтабыз, ал sock_fprog түрүндөгү prog өзгөрмөсүндө камтылган:

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

Акыр-аягы, биз install_filter функциябызды колдоно алабыз, бирок ага чейин PR_SET_NO_NEW_PRIVS учурдагы аткарууга коюу үчүн prctl колдонушубуз керек жана ошону менен бала процесстери ата-энелерине караганда көбүрөөк артыкчылыктарга ээ болгон кырдаалдан качышыбыз керек. Муну менен биз root укуктарына ээ болбостон install_filter функциясында төмөнкү prctl чалууларды жасай алабыз.

Эми биз install_filter функциясын чакыра алабыз. Келгиле, X86-64 архитектурасына тиешелүү бардык жазуу тутумдук чалууларды бөгөттөп, жөн гана бардык аракеттерди бөгөттөөчү уруксатты берели. Чыпканы орноткондон кийин, биз биринчи аргументти колдонуу менен аткарууну улантабыз:

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

Баштайлы. Программабызды компиляциялоо үчүн биз clang же gcc колдонсок болот, кандай болбосун, ал жөн гана main.c файлын атайын опцияларсыз компиляциялоодо:

clang main.c -o filter-write

Белгиленгендей, биз программадагы бардык жазууларды жаап салдык. Муну текшерүү үчүн сизге бир нерсени чыгарган программа керек - ls жакшы талапкер окшойт. Ал, адатта, мындай мамиле кылат:

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

Керемет! Бул жерде биздин орогуч программаны колдонуу кандай болот: Биз жөн гана биринчи аргумент катары сынагыбыз келген программаны тапшырабыз:

./filter-write "ls -la"

Аткарылганда, бул программа толугу менен бош жыйынтык чыгарат. Бирок, биз эмне болуп жатканын көрүү үчүн strace колдоно алабыз:

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

Иштин натыйжасы абдан кыскарган, бирок анын тиешелүү бөлүгү жазуулар EPERM катасы менен бөгөттөлгөндүгүн көрсөтүп турат - ошол эле биз конфигурациялаган. Бул программа эч нерсе чыгарбайт дегенди билдирет, анткени ал жазуу тутумунун чалуусуна кире албайт:

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

Эми сиз Seccomp BPF кантип иштээрин түшүнөсүз жана аны менен эмне кыла аларыңызды жакшы билесиз. Бирок анын толук күчүн колдонуу үчүн cBPFтин ордуна eBPF менен бир нерсеге жетүүнү каалабайсызбы?

eBPF программалары жөнүндө ойлонгондо, көпчүлүк адамдар аларды жөн эле жазып, администратор артыкчылыктары менен жүктөйт деп ойлошот. Бул билдирүү жалпысынан туура болсо да, ядро ​​ар кандай деңгээлдеги eBPF объекттерин коргоо үчүн механизмдердин комплексин ишке ашырат. Бул механизмдер BPF LSM тузактары деп аталат.

BPF LSM тузактары

Системалык окуяларга архитектурадан көз карандысыз мониторингди камсыз кылуу үчүн LSM тузак концепциясын ишке ашырат. Хок чалуу техникалык жактан системалык чалууга окшош, бирок системадан көз карандысыз жана инфраструктура менен интеграцияланган. LSM жаңы концепцияны сунуштайт, анда абстракция катмары ар түрдүү архитектураларда системалык чалууларды чечүүдө кездешкен көйгөйлөрдөн качууга жардам берет.

Жазуу учурунда ядродо BPF программалары менен байланышкан жети илгич бар жана SELinux аларды ишке ашырган жалгыз орнотулган LSM.

Тузактардын булак коду file/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);

Алардын ар бири аткаруунун ар кандай этаптарында чакырылат:

— security_bpf — аткарылган BPF тутумдук чалууларын баштапкы текшерүүнү жүргүзөт;

- security_bpf_map - ядро ​​карта үчүн файлдын дескрипторун кайтарганын текшерет;

- security_bpf_prog - ядро ​​eBPF программасы үчүн файлдын дескрипторун кайтарып бергенде текшерет;

— security_bpf_map_alloc — BPF карталарынын ичиндеги коопсуздук талаасы инициализацияланганын текшерет;

- security_bpf_map_free - коопсуздук талаасы BPF карталарынын ичинде тазаланганын текшерет;

— security_bpf_prog_alloc — коопсуздук талаасы BPF программаларында инициализацияланганын текшерет;

- security_bpf_prog_free - коопсуздук талаасы BPF программаларында тазаланганын текшерет.

Эми мунун баарын көрүп, биз түшүнөбүз: LSM BPF интерцепторлорунун идеясы - алар ар бир eBPF объектисин коргоону камсыз кылып, карталарда жана программаларда тиешелүү артыкчылыктарга ээ болгондор гана операцияларды аткара алышат.

на

Коопсуздук - бул сиз коргогуңуз келген нерселердин бардыгына бирдей ылайыктуу түрдө ишке ашыра турган нерсе эмес. Системаларды ар кандай деңгээлде жана ар кандай жолдор менен коргой билүү маанилүү. Ишенип коюңуз же ишенбеңиз, системаны коргоонун эң жакшы жолу - бул бир деңгээлдеги коопсуздукту азайтуу бүт системага кирүүгө мүмкүндүк бербеши үчүн, ар кандай позициялардан коргоонун ар кандай деңгээлин уюштуруу. Негизги иштеп чыгуучулар бизге ар кандай катмарлардын жана тийүү чекиттеринин топтомун берүү боюнча чоң жумуш жасашты. Биз сизге катмарлар деген эмнени жана алар менен иштөө үчүн BPF программаларын кантип колдонууну жакшы түшүндүк деп үмүттөнөбүз.

Авторлор жөнүндө

Дэвид Калавера Netlifyде CTO болуп саналат. Ал Docker колдоосунда иштеген жана Runc, Go жана BCC куралдарын, ошондой эле башка ачык булак долбоорлорун өнүктүрүүгө салым кошкон. Докер долбоорлорундагы иши жана Docker плагининин экосистемасынын өнүгүшү менен белгилүү. Дэвид жалын графиктерине абдан ынтызар жана ар дайым иштөөнү оптималдаштырууга умтулат.

Лоренцо Фонтана Sysdig компаниясынын ачык булактуу командасында иштейт, анда ал негизинен Falco, Cloud Native Computing Foundation долбооруна багытталган, ал контейнердин иштөө убактысынын коопсуздугун жана ядро ​​модулу жана eBPF аркылуу аномалияларды аныктоону камсыз кылат. Ал бөлүштүрүлгөн тутумдарга, программалык камсыздоону аныктоочу тармакка, Linux ядросуна жана өндүрүмдүүлүктүн анализине ынтызар.

» Китеп тууралуу кененирээк бул жерден тапса болот чыгаруучунун веб-сайты
» Мазмуну
» Үзүндү

Khabrozhiteley үчүн купонду колдонуу менен 25% арзандатуу - Linux

Китептин кагаз түрүндөгү версиясын төлөгөндөн кийин электрондук китеп электрондук почта аркылуу жөнөтүлөт.

Source: www.habr.com

Комментарий кошуу