బుక్ "Linux మానిటరింగ్ కోసం BPF"

బుక్ "Linux మానిటరింగ్ కోసం BPF"హలో, ఖబ్రో నివాసులారా! BPF వర్చువల్ మెషీన్ Linux కెర్నల్ యొక్క అతి ముఖ్యమైన భాగాలలో ఒకటి. దీని సరైన ఉపయోగం సిస్టమ్ ఇంజనీర్లు లోపాలను కనుగొనడానికి మరియు చాలా క్లిష్టమైన సమస్యలను కూడా పరిష్కరించడానికి అనుమతిస్తుంది. మీరు కెర్నల్ ప్రవర్తనను పర్యవేక్షించే మరియు సవరించే ప్రోగ్రామ్‌లను ఎలా వ్రాయాలి, కెర్నల్‌లోని ఈవెంట్‌లను పర్యవేక్షించడానికి కోడ్‌ని సురక్షితంగా ఎలా అమలు చేయాలి మరియు మరెన్నో నేర్చుకుంటారు. డేవిడ్ కాలవేరా మరియు లోరెంజో ఫోంటానా BPF యొక్క శక్తిని అన్‌లాక్ చేయడంలో మీకు సహాయం చేస్తారు. పనితీరు ఆప్టిమైజేషన్, నెట్‌వర్కింగ్, భద్రత గురించి మీ పరిజ్ఞానాన్ని విస్తరించండి. - Linux కెర్నల్ ప్రవర్తనను పర్యవేక్షించడానికి మరియు సవరించడానికి BPFని ఉపయోగించండి. - కెర్నల్‌ను మళ్లీ కంపైల్ చేయకుండా లేదా సిస్టమ్‌ను రీబూట్ చేయకుండా కెర్నల్ ఈవెంట్‌లను సురక్షితంగా పర్యవేక్షించడానికి కోడ్‌ను ఇంజెక్ట్ చేయండి. — C, Go లేదా Pythonలో అనుకూలమైన కోడ్ ఉదాహరణలను ఉపయోగించండి. - BPF ప్రోగ్రామ్ లైఫ్‌సైకిల్‌ను సొంతం చేసుకోవడం ద్వారా నియంత్రించండి.

Linux కెర్నల్ సెక్యూరిటీ, దాని ఫీచర్లు మరియు Seccomp

BPF స్థిరత్వం, భద్రత లేదా వేగాన్ని త్యాగం చేయకుండా కెర్నల్‌ను విస్తరించడానికి శక్తివంతమైన మార్గాన్ని అందిస్తుంది. ఈ కారణంగా, కెర్నల్ డెవలపర్లు BPF ప్రోగ్రామ్‌ల ద్వారా మద్దతు ఇచ్చే Seccomp ఫిల్టర్‌లను అమలు చేయడం ద్వారా Seccomp BPF అని కూడా పిలువబడే Seccomp లో ప్రాసెస్ ఐసోలేషన్‌ను మెరుగుపరచడానికి దాని బహుముఖ ప్రజ్ఞను ఉపయోగించడం మంచి ఆలోచన అని భావించారు. ఈ అధ్యాయంలో Seccomp అంటే ఏమిటి మరియు అది ఎలా ఉపయోగించబడుతుందో వివరిస్తాము. అప్పుడు మీరు BPF ప్రోగ్రామ్‌లను ఉపయోగించి Seccomp ఫిల్టర్‌లను ఎలా వ్రాయాలో నేర్చుకుంటారు. ఆ తర్వాత, మేము Linux సెక్యూరిటీ మాడ్యూల్స్ కోసం కెర్నల్‌లో చేర్చబడిన అంతర్నిర్మిత BPF హుక్స్‌లను పరిశీలిస్తాము.

Linux సెక్యూరిటీ మాడ్యూల్స్ (LSM) అనేది వివిధ భద్రతా నమూనాలను ప్రామాణిక పద్ధతిలో అమలు చేయడానికి ఉపయోగించే ఫంక్షన్ల సమితిని అందించే ఫ్రేమ్‌వర్క్. Apparmor, SELinux మరియు Tomoyo వంటి కెర్నల్ సోర్స్ ట్రీలో LSMని నేరుగా ఉపయోగించవచ్చు.

Linux సామర్థ్యాలను చర్చించడం ద్వారా ప్రారంభిద్దాం.

అవకాశాలు

Linux యొక్క సామర్థ్యాల సారాంశం ఏమిటంటే, మీరు ఒక నిర్దిష్ట పనిని నిర్వహించడానికి అన్‌ప్రివిలేజ్డ్ ప్రాసెస్ అనుమతిని మంజూరు చేయాలి, కానీ ఆ ప్రయోజనం కోసం suidని ఉపయోగించకుండా, లేదా ప్రక్రియను విశేషమైనదిగా చేయడం, దాడి చేసే అవకాశాన్ని తగ్గించడం మరియు నిర్దిష్ట పనులను నిర్వహించడానికి ప్రక్రియను అనుమతించడం. ఉదాహరణకు, మీ అప్లికేషన్ ప్రత్యేక పోర్ట్‌ను తెరవాలంటే, ప్రాసెస్‌ను రూట్‌గా అమలు చేయడానికి బదులుగా 80 అని చెప్పండి, మీరు దానికి CAP_NET_BIND_SERVICE సామర్థ్యాన్ని అందించవచ్చు.

main.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"

ఈ బృందాన్ని కొంచెం అర్థం చేసుకుందాం.

  • క్యాప్ష్ - క్యాప్ష్‌ను షెల్‌గా ఉపయోగించండి.
  • —caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' - మేము వినియోగదారుని మార్చాల్సిన అవసరం ఉన్నందున (మేము రూట్‌గా అమలు చేయకూడదనుకుంటున్నాము), మేము cap_net_bind_serviceని మరియు వినియోగదారు IDని వాస్తవంగా మార్చగల సామర్థ్యాన్ని పేర్కొంటాము రూట్ ఎవరికీ కాదు, అవి 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ని మార్చకుండానే దీన్ని చేసాము) p తో. ఇది cap_net_bind_service+eip లాగా కనిపిస్తోంది.

మీరు ss ఉపయోగించి ఫలితాన్ని తనిఖీ చేయవచ్చు. పేజీకి సరిపోయేలా అవుట్‌పుట్‌ను కొంచెం కుదిద్దాం, అయితే ఇది అనుబంధిత పోర్ట్ మరియు వినియోగదారు IDని 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 చూడండి.

ప్రోగ్రామ్‌లను వ్రాసేటప్పుడు, చాలా తరచుగా డెవలపర్‌కు రన్ సమయంలో ప్రోగ్రామ్‌కు అవసరమైన అన్ని లక్షణాలను ముందుగానే తెలియదు; అంతేకాకుండా, ఈ ఫీచర్లు కొత్త వెర్షన్లలో మారవచ్చు.

మా ప్రోగ్రామ్ యొక్క సామర్థ్యాలను బాగా అర్థం చేసుకోవడానికి, మేము BCC సామర్థ్యం గల సాధనాన్ని తీసుకోవచ్చు, ఇది cap_capable కెర్నల్ ఫంక్షన్ కోసం kprobeని సెట్ చేస్తుంది:

/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 కెర్నల్ ఫంక్షన్‌లో ఒక-లైనర్ kprobeతో 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తో సహా/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">

రన్‌సి లేదా డాకర్ వంటి కంటైనర్‌లను అన్‌ప్రివిలేజ్డ్ మోడ్‌లో అమలు చేయడానికి అనుమతించడానికి రన్‌టైమ్‌లో సామర్థ్యాలు తరచుగా ప్రారంభించబడతాయి, అయితే అవి చాలా అప్లికేషన్‌లను అమలు చేయడానికి అవసరమైన సామర్థ్యాలు మాత్రమే అనుమతించబడతాయి. అనువర్తనానికి నిర్దిష్ట సామర్థ్యాలు అవసరమైనప్పుడు, డాకర్ వాటిని --cap-addని ఉపయోగించి అందించవచ్చు:

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

ఈ ఆదేశం కంటైనర్‌కు CAP_NET_ADMIN సామర్థ్యాన్ని ఇస్తుంది, ఇది నకిలీ0 ఇంటర్‌ఫేస్‌ను జోడించడానికి నెట్‌వర్క్ లింక్‌ను కాన్ఫిగర్ చేయడానికి అనుమతిస్తుంది.

తదుపరి విభాగం ఫిల్టరింగ్ వంటి లక్షణాలను ఎలా ఉపయోగించాలో చూపిస్తుంది, కానీ మన స్వంత ఫిల్టర్‌లను ప్రోగ్రామాటిక్‌గా అమలు చేయడానికి అనుమతించే వేరొక సాంకేతికతను ఉపయోగించడం.

సెకాంప్

Seccomp అంటే సెక్యూర్ కంప్యూటింగ్ మరియు డెవలపర్‌లు నిర్దిష్ట సిస్టమ్ కాల్‌లను ఫిల్టర్ చేయడానికి అనుమతించే Linux కెర్నల్‌లో అమలు చేయబడిన భద్రతా పొర. Seccomp సామర్థ్యాలలో Linuxతో పోల్చదగినది అయినప్పటికీ, నిర్దిష్ట సిస్టమ్ కాల్‌లను నిర్వహించే దాని సామర్థ్యం వాటితో పోలిస్తే చాలా సరళమైనది.

Seccomp మరియు Linux లక్షణాలు పరస్పరం ప్రత్యేకమైనవి కావు మరియు రెండు విధానాల నుండి ప్రయోజనం పొందేందుకు తరచుగా కలిసి ఉపయోగించబడతాయి. ఉదాహరణకు, మీరు ఒక ప్రాసెస్‌కు CAP_NET_ADMIN సామర్థ్యాన్ని అందించాలనుకోవచ్చు కానీ సాకెట్ కనెక్షన్‌లను ఆమోదించడానికి అనుమతించకూడదు, అంగీకరించడం మరియు అంగీకరించడం4 సిస్టమ్ కాల్‌లను బ్లాక్ చేయడం.

SECCOMP వడపోత పద్ధతి SECCOMP_MODE_FILTER మోడ్‌లో పనిచేసే BPF ఫిల్టర్‌లపై ఆధారపడి ఉంటుంది మరియు సిస్టమ్ కాల్ ఫిల్టరింగ్ ప్యాకెట్‌ల మాదిరిగానే నిర్వహించబడుతుంది.

PR_SET_SECCOMP ఆపరేషన్ ద్వారా prctl ఉపయోగించి Seccomp ఫిల్టర్‌లు లోడ్ చేయబడతాయి. ఈ ఫిల్టర్‌లు 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 ఫిల్టర్ రిటర్న్ విలువలో కొంత భాగం వినియోగదారు స్పేస్‌కి ఎర్రనో విలువగా పంపబడుతుంది. లోపం యొక్క కారణాన్ని బట్టి, వివిధ ఎర్రర్నో విలువలు అందించబడతాయి. లోపం సంఖ్యల జాబితా తదుపరి విభాగంలో అందించబడింది.

- SECCOMP_RET_TRACE - ఉపయోగించి ptrace ట్రేసర్‌కు తెలియజేయడానికి ఉపయోగించబడుతుంది - PTRACE_O_TRACESECCOMP సిస్టమ్ కాల్ అమలు చేయబడినప్పుడు ఆ ప్రక్రియను చూడటానికి మరియు నియంత్రించడానికి అంతరాయం కలిగించడానికి. ట్రేసర్ కనెక్ట్ చేయబడకపోతే, లోపం తిరిగి వస్తుంది, ఎర్రనో -ENOSYSకి సెట్ చేయబడుతుంది మరియు సిస్టమ్ కాల్ అమలు చేయబడదు.

- SECCOMP_RET_LOG - సిస్టమ్ కాల్ పరిష్కరించబడింది మరియు లాగ్ చేయబడింది.

- SECCOMP_RET_ALLOW - సిస్టమ్ కాల్ కేవలం అనుమతించబడుతుంది.

ptrace అనేది ట్రేసీ అని పిలువబడే ప్రక్రియలో ట్రేసింగ్ మెకానిజమ్‌లను అమలు చేయడానికి సిస్టమ్ కాల్, ఇది ప్రక్రియ యొక్క అమలును పర్యవేక్షించే మరియు నియంత్రించే సామర్థ్యంతో ఉంటుంది. ట్రేస్ ప్రోగ్రామ్ అమలును సమర్థవంతంగా ప్రభావితం చేస్తుంది మరియు ట్రేసీ యొక్క మెమరీ రిజిస్టర్‌లను సవరించగలదు. Seccomp సందర్భంలో, SECCOMP_RET_TRACE స్థితి కోడ్ ద్వారా ప్రేరేపించబడినప్పుడు ptrace ఉపయోగించబడుతుంది, కాబట్టి ట్రేసర్ సిస్టమ్ కాల్‌ని అమలు చేయకుండా నిరోధించగలదు మరియు దాని స్వంత లాజిక్‌ను అమలు చేస్తుంది.

Seccomp లోపాలు

కాలానుగుణంగా, Seccompతో పని చేస్తున్నప్పుడు, మీరు SECCOMP_RET_ERRNO రకం రిటర్న్ విలువ ద్వారా గుర్తించబడే వివిధ లోపాలను ఎదుర్కొంటారు. లోపాన్ని నివేదించడానికి, seccomp సిస్టమ్ కాల్ 1కి బదులుగా -0ని అందిస్తుంది.

కింది లోపాలు సాధ్యమే:

- ACCESS - సిస్టమ్ కాల్ చేయడానికి కాలర్ అనుమతించబడడు. ఇది సాధారణంగా జరుగుతుంది ఎందుకంటే దీనికి CAP_SYS_ADMIN అధికారాలు లేవు లేదా prctlని ఉపయోగించి no_new_privs సెట్ చేయబడలేదు (దీని గురించి మేము తరువాత మాట్లాడుతాము);

— EFAULT — ఆమోదించబడిన ఆర్గ్యుమెంట్‌లు (seccomp_data స్ట్రక్చర్‌లోని ఆర్గ్‌లు) చెల్లుబాటు అయ్యే చిరునామాను కలిగి లేవు;

— EINVAL — ఇక్కడ నాలుగు కారణాలు ఉండవచ్చు:

-అభ్యర్థించిన ఆపరేషన్ తెలియదు లేదా ప్రస్తుత కాన్ఫిగరేషన్‌లోని కెర్నల్ ద్వారా మద్దతు లేదు;

అభ్యర్థించిన ఆపరేషన్ కోసం పేర్కొన్న ఫ్లాగ్‌లు చెల్లవు;

-ఆపరేషన్‌లో BPF_ABS ఉంటుంది, కానీ పేర్కొన్న ఆఫ్‌సెట్‌లో సమస్యలు ఉన్నాయి, ఇది seccomp_data స్ట్రక్చర్ పరిమాణాన్ని మించి ఉండవచ్చు;

-ఫిల్టర్‌కు పంపబడిన సూచనల సంఖ్య గరిష్టాన్ని మించిపోయింది;

- ENOMEM - ప్రోగ్రామ్‌ను అమలు చేయడానికి తగినంత మెమరీ లేదు;

- EOPNOTSUPP - SECCOMP_GET_ACTION_AVAILతో చర్య అందుబాటులో ఉందని ఆపరేషన్ సూచించింది, అయితే కెర్నల్ ఆర్గ్యుమెంట్‌లలో రిటర్న్‌లకు మద్దతు ఇవ్వదు;

- ESRCH - మరొక స్ట్రీమ్‌ను సమకాలీకరించేటప్పుడు సమస్య ఏర్పడింది;

- ENOSYS - SECCOMP_RET_TRACE చర్యకు ఎలాంటి ట్రేసర్ జోడించబడలేదు.

prctl అనేది సిస్టమ్ కాల్, ఇది బైట్ ఎండియన్‌నెస్, థ్రెడ్ పేర్లు, సురక్షిత గణన మోడ్ (Seccomp), అధికారాలు, పెర్ఫ్ ఈవెంట్‌లు మొదలైన ప్రక్రియ యొక్క నిర్దిష్ట అంశాలను మార్చటానికి (సెట్ మరియు పొందేందుకు) వినియోగదారు-స్పేస్ ప్రోగ్రామ్‌ని అనుమతిస్తుంది.

Seccomp మీకు శాండ్‌బాక్స్ టెక్నాలజీలా అనిపించవచ్చు, కానీ అది కాదు. Seccomp అనేది శాండ్‌బాక్స్ మెకానిజంను అభివృద్ధి చేయడానికి వినియోగదారులను అనుమతించే యుటిలిటీ. ఇప్పుడు Seccomp సిస్టమ్ కాల్ ద్వారా నేరుగా పిలువబడే ఫిల్టర్‌ని ఉపయోగించి వినియోగదారు పరస్పర చర్య ప్రోగ్రామ్‌లు ఎలా సృష్టించబడతాయో చూద్దాం.

BPF సెకాంప్ ఫిల్టర్ ఉదాహరణ

ఇంతకు ముందు చర్చించిన రెండు చర్యలను ఎలా కలపాలో ఇక్కడ మేము చూపుతాము, అవి:

— మేము 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_K అక్యుమ్యులేటర్ స్థిరాంకంలోని ఆర్కిటెక్చర్ విలువ ఆర్చ్‌కి సమానంగా ఉందో లేదో BPF_JEQని ఉపయోగించి తనిఖీ చేస్తుంది. అలా అయితే, ఆఫ్‌సెట్ 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తో ప్రోగ్రామ్‌ను ముగిస్తుంది మరియు ఫలితంగా ఎర్రర్ వేరియబుల్ నుండి నంబర్‌తో SECCOMP_RET_ERRNO లోపం ఏర్పడుతుంది.

- BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) - BPF_RETతో ప్రోగ్రామ్‌ను ముగించి, సిస్టమ్ కాల్‌ని SECCOMP_RET_ALLOW ఉపయోగించి అమలు చేయడానికి అనుమతిస్తుంది.

SECCOMP CBPF
కంపైల్ చేయబడిన ELF ఆబ్జెక్ట్ లేదా JIT కంపైల్డ్ C ప్రోగ్రామ్‌కు బదులుగా సూచనల జాబితా ఎందుకు ఉపయోగించబడిందని మీరు ఆశ్చర్యపోవచ్చు.

దీనికి రెండు కారణాలున్నాయి.

• మొదటగా, Seccomp cBPF (క్లాసిక్ BPF)ని ఉపయోగిస్తుంది మరియు eBPF కాదు, అంటే: దీనికి రిజిస్టర్‌లు లేవు, కానీ ఉదాహరణలో చూడగలిగే విధంగా గణన యొక్క చివరి ఫలితాన్ని నిల్వ చేయడానికి ఒక సంచితం మాత్రమే.

• రెండవది, 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 రకం ప్రోగ్ వేరియబుల్‌లో ఉంటుంది:

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

చివరగా, మేము మా ఇన్‌స్టాల్_ఫిల్టర్ ఫంక్షన్‌ని ఉపయోగించవచ్చు, కానీ దానికంటే ముందు మేము ప్రస్తుత అమలు కోసం PR_SET_NO_NEW_PRIVSని సెట్ చేయడానికి prctlని ఉపయోగించాలి మరియు తద్వారా పిల్లల ప్రక్రియలు వారి తల్లిదండ్రుల కంటే ఎక్కువ అధికారాలను పొందే పరిస్థితిని నివారించాలి. దీనితో, మేము రూట్ హక్కులు లేకుండా 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]);
 }

ప్రారంభిద్దాం. మా ప్రోగ్రామ్‌ను కంపైల్ చేయడానికి మేము గణగణమని ద్వని చేయు లేదా 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 -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.

ట్రాప్‌ల యొక్క సోర్స్ కోడ్ ఫైల్‌లోని కెర్నల్ ట్రీలో ఉంది, 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);

వాటిలో ప్రతి ఒక్కటి అమలు యొక్క వివిధ దశలలో పిలువబడుతుంది:

— 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. అతను డాకర్ మద్దతులో పనిచేశాడు మరియు Runc, Go మరియు BCC టూల్స్, అలాగే ఇతర ఓపెన్ సోర్స్ ప్రాజెక్ట్‌ల అభివృద్ధికి సహకరించాడు. డాకర్ ప్రాజెక్ట్‌లు మరియు డాకర్ ప్లగ్ఇన్ ఎకోసిస్టమ్ అభివృద్ధిపై అతని పనికి ప్రసిద్ధి చెందింది. డేవిడ్ జ్వాల గ్రాఫ్‌ల పట్ల చాలా మక్కువ కలిగి ఉన్నాడు మరియు ఎల్లప్పుడూ పనితీరును ఆప్టిమైజ్ చేయడానికి చూస్తున్నాడు.

లోరెంజో ఫోంటానా సిస్‌డిగ్‌లోని ఓపెన్ సోర్స్ టీమ్‌లో పని చేస్తున్నాడు, అక్కడ అతను కెర్నల్ మాడ్యూల్ మరియు eBPF ద్వారా కంటైనర్ రన్‌టైమ్ సెక్యూరిటీ మరియు అనోమాలి డిటెక్షన్‌ను అందించే క్లౌడ్ నేటివ్ కంప్యూటింగ్ ఫౌండేషన్ ప్రాజెక్ట్ అయిన ఫాల్కోపై ప్రధానంగా దృష్టి సారించాడు. అతను డిస్ట్రిబ్యూటెడ్ సిస్టమ్స్, సాఫ్ట్‌వేర్ డిఫైన్డ్ నెట్‌వర్కింగ్, లైనక్స్ కెర్నల్ మరియు పనితీరు విశ్లేషణల పట్ల మక్కువ కలిగి ఉన్నాడు.

» పుస్తకం గురించి మరిన్ని వివరాలను ఇక్కడ చూడవచ్చు ప్రచురణకర్త యొక్క వెబ్‌సైట్
» విషయాల పట్టిక
» సారాంశం

Khabrozhiteley కోసం కూపన్ ఉపయోగించి 25% తగ్గింపు - linux

పుస్తకం యొక్క పేపర్ వెర్షన్ చెల్లించిన తర్వాత, ఎలక్ట్రానిక్ పుస్తకం ఇ-మెయిల్ ద్వారా పంపబడుతుంది.

మూలం: www.habr.com

ఒక వ్యాఖ్యను జోడించండి