መጽሐፍ "BPF ለሊኑክስ ክትትል"

መጽሐፍ "BPF ለሊኑክስ ክትትል"ሰላም የካብሮ ነዋሪዎች! የቢፒኤፍ ቨርቹዋል ማሽን ከሊኑክስ ከርነል በጣም አስፈላጊ አካል አንዱ ነው። ትክክለኛው አጠቃቀም የስርዓት መሐንዲሶች ስህተቶችን እንዲያገኙ እና በጣም ውስብስብ ችግሮችን እንኳን እንዲፈቱ ያስችላቸዋል። የከርነል ባህሪን የሚቆጣጠሩ እና የሚያሻሽሉ ፕሮግራሞችን እንዴት እንደሚጽፉ፣ በከርነል ውስጥ ያሉ ክስተቶችን ለመከታተል ኮድን እንዴት መተግበር እንደሚችሉ እና ሌሎችንም ይማራሉ። ዴቪድ ካላቬራ እና ሎሬንዞ ፎንታና የ BPF ኃይልን ለመክፈት ይረዱዎታል። የአፈጻጸም ማመቻቸት፣ አውታረ መረብ፣ ደህንነት እውቀትዎን ያስፋፉ። - የሊኑክስ ከርነልን ባህሪ ለመቆጣጠር እና ለማሻሻል BPF ይጠቀሙ። - ኮርነሉን እንደገና ማጠናቀር ወይም ስርዓቱን እንደገና ማስጀመር ሳያስፈልግ የከርነል ክስተቶችን ደህንነቱ በተጠበቀ ሁኔታ ለመቆጣጠር ኮድ ያስገቡ። - በ C ፣ Go ወይም Python ውስጥ ምቹ የኮድ ምሳሌዎችን ይጠቀሙ። - የ BPF ፕሮግራም የህይወት ኡደትን በባለቤትነት ይቆጣጠሩ።

የሊኑክስ ከርነል ደህንነት፣ ባህሪያቱ እና ሴክኮምፕ

BPF መረጋጋትን፣ ደህንነትን እና ፍጥነትን ሳያስቀር ከርነሉን ለማራዘም ሃይለኛ መንገድ ይሰጣል። በዚህ ምክንያት፣ የከርነል ገንቢዎች በሴኮምፕ ውስጥ በBPF ፕሮግራሞች የሚደገፉትን ሴኮምፕ ማጣሪያዎችን በመተግበር በሴኮምፕ ውስጥ የሂደቱን ማግለል ለማሻሻል ሁለገብነቱን መጠቀም ጥሩ ሀሳብ ነው ብለው አስበው ነበር። በዚህ ምዕራፍ ውስጥ ሴኮምፕ ምን እንደሆነ እና እንዴት ጥቅም ላይ እንደሚውል እናብራራለን. ከዚያ BPF ፕሮግራሞችን በመጠቀም ሴኮምፕ ማጣሪያዎችን እንዴት እንደሚጽፉ ይማራሉ. ከዚያ በኋላ ለሊኑክስ ደህንነት ሞጁሎች በከርነል ውስጥ የተካተቱትን አብሮ የተሰሩ BPF መንጠቆዎችን እንመለከታለን።

የሊኑክስ ሴኩሪቲ ሞጁሎች (LSM) የተለያዩ የደህንነት ሞዴሎችን ደረጃውን በጠበቀ መልኩ ለመተግበር የሚያገለግሉ የተግባር ስብስቦችን የሚያቀርብ ማዕቀፍ ነው። LSM እንደ Apparmor፣ SELinux እና Tomoyo ባሉ የከርነል ምንጭ ዛፍ ላይ በቀጥታ መጠቀም ይቻላል።

የሊኑክስን አቅም በመወያየት እንጀምር።

ባህሪዎች

የሊኑክስ የችሎታዎች ይዘት አንድን የተወሰነ ተግባር ለማከናወን ልዩ ልዩ ሂደትን ፈቃድ መስጠት አለብዎት ፣ ግን ለዚያ ዓላማ ሱይድ ሳይጠቀሙ ፣ ወይም ሂደቱን ልዩ ለማድረግ ፣ የጥቃት እድልን የሚቀንስ እና ሂደቱን አንዳንድ ተግባራትን እንዲፈጽም መፍቀድ ነው። ለምሳሌ፣ ማመልከቻዎ ልዩ ወደብ መክፈት ካለበት፣ 80 ይበሉ፣ ሂደቱን እንደ root ከማሄድ ይልቅ፣ በቀላሉ የCAP_NET_BIND_SERVICE አቅም መስጠት ይችላሉ።

main.go የሚባል የ Go ፕሮግራምን አስቡበት፡-

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

ይህ ፕሮግራም በፖርት 80 ላይ የኤችቲቲፒ አገልጋይ ያገለግላል (ይህ ልዩ ወደብ ነው)። ብዙውን ጊዜ ከተጠናቀረ በኋላ ወዲያውኑ እንሰራዋለን-

$ 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_አገልግሎትን አቅም በፕሮግራሙ ውስጥ ካሉት ሁሉም ነገሮች ጋር በማቅረብ ልዩ ልዩ የወደብ ትስስርን ማንቃት ይችላሉ። ይህንን ለማድረግ ፕሮግራማችንን በካፕ ማያያዝ እንችላለን፡-

# 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' - ተጠቃሚውን መለወጥ ስላለብን (እንደ root መሮጥ ስለማንፈልግ)፣ cap_net_bind_serviceን እና የተጠቃሚ መታወቂያውን በትክክል የመቀየር ችሎታን እንገልፃለን። ሾር ለማንም ማለትም cap_setuid እና cap_setgid።
  • -keep=1 - ከስር መለያ ሲቀይሩ የተጫኑትን ችሎታዎች ማቆየት እንፈልጋለን።
  • —user=“nobody” — ፕሮግራሙን የሚያስኬደው የመጨረሻ ተጠቃሚ ማንም አይሆንም።
  • —addamb=cap_net_bind_service - ከስር ሁነታ ከቀየሩ በኋላ ተዛማጅ ችሎታዎችን ማጽዳትን ያዘጋጁ።
  • - -c "./capabilities" - ፕሮግራሙን ብቻ ያሂዱ.

የተገናኙ ችሎታዎች የአሁኑ ፕሮግራም execve () በመጠቀም ሲፈጽም በልጆች ፕሮግራሞች የሚወርሱ ልዩ ዓይነት ችሎታዎች ናቸው። ለማያያዝ የተፈቀደላቸው ችሎታዎች ብቻ ወይም በሌላ አነጋገር እንደ አካባቢ ችሎታዎች ሊወርሱ ይችላሉ.

በ --caps አማራጭ ውስጥ ያለውን አቅም ከገለጹ በኋላ +eip ምን ማለት እንደሆነ እያሰቡ ይሆናል። አቅሙን ለመወሰን እነዚህ ባንዲራዎች ጥቅም ላይ ይውላሉ፡-

- ማግበር አለበት (p);

- ለአጠቃቀም ይገኛል (ሠ);

- በልጆች ሂደቶች ሊወረስ ይችላል (i).

የኬፕ_ኔት_ቢንድ_አገልግሎትን መጠቀም ስለምንፈልግ፣ ይህንን በ e ባንዲራ ማድረግ አለብን። ከዚያም ዛጎሉን በትእዛዙ ውስጥ እንጀምራለን. ይህ የችሎታውን ሁለትዮሽ ያስኬዳል እና በ i ባንዲራ ምልክት ማድረግ አለብን። በመጨረሻም፣ ባህሪው እንዲነቃ እንፈልጋለን (ይህንን ዩአይዲውን ሳንቀይር አደረግን) በ p. የ 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

በዚህ ምሳሌ ውስጥ ካፕን እንጠቀማለን, ነገር ግን libcap በመጠቀም ሼል መፃፍ ይችላሉ. ለበለጠ መረጃ፣ man 3 libcap ይመልከቱ።

ፕሮግራሞችን በሚጽፉበት ጊዜ ብዙውን ጊዜ ገንቢው ፕሮግራሙ በሚሠራበት ጊዜ የሚያስፈልጉትን ሁሉንም ባህሪዎች አስቀድሞ አያውቅም ። በተጨማሪም, እነዚህ ባህሪያት በአዲስ ስሪቶች ውስጥ ሊለወጡ ይችላሉ.

የፕሮግራማችንን አቅም የበለጠ ለመረዳት፣ Kprobeን ለ cap_capable kernel ተግባር የሚያዘጋጀውን 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 kernel ተግባር ውስጥ bpftraceን ባለአንድ መስመር kprobe በመጠቀም ተመሳሳይ ነገር ማሳካት እንችላለን፡-

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 ነው፣ እሱ በፋይሉ ውስጥ ባለው የከርነል ምንጭ ኮድ ውስጥ ቋሚ/uapi/linux/ability.h ከመለያ 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">

አቅም በሌለው ሁኔታ እንዲሰሩ ለማስቻል እንደ runC ወይም Docker ላሉ ኮንቴይነሮች በመሮጫ ሰአት ብዙ ጊዜ ይነቃሉ፣ነገር ግን የሚፈቀዱት ብዙ መተግበሪያዎችን ለማሄድ የሚያስፈልጉትን ችሎታዎች ብቻ ነው። አንድ መተግበሪያ የተወሰኑ ችሎታዎች ሲፈልግ፣ Docker --cap-add በመጠቀም ሊያቀርባቸው ይችላል፡

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

ይህ ትዕዛዝ መያዣውን የ CAP_NET_ADMIN አቅም ይሰጠዋል፣ ይህም የ dummy0 በይነገጽን ለመጨመር የአውታረ መረብ ማገናኛን እንዲያዋቅር ያስችለዋል።

የሚቀጥለው ክፍል እንደ ማጣራት ያሉ ባህሪያትን እንዴት መጠቀም እንዳለብን ያሳያል, ነገር ግን የተለየ ዘዴ በመጠቀም የራሳችንን ማጣሪያዎች በፕሮግራም ለመተግበር ያስችለናል.

ሴክኮምፕ

ሴኮምፕ ሴክዩር ኮምፒውቲንግን የሚያመለክት ሲሆን በሊኑክስ ከርነል ውስጥ የሚተገበር የደህንነት ሽፋን ሲሆን ገንቢዎች የተወሰኑ የስርዓት ጥሪዎችን እንዲያጣሩ ያስችላቸዋል። ምንም እንኳን ሴኮምፕ ከሊኑክስ ጋር የሚወዳደር ቢሆንም የተወሰኑ የስርዓት ጥሪዎችን የማስተዳደር ችሎታው ከእነሱ ጋር ሲወዳደር የበለጠ ተለዋዋጭ ያደርገዋል።

የሴክኮምፕ እና ሊኑክስ ባህሪያት እርስበርስ የማይነጣጠሉ አይደሉም እና ብዙውን ጊዜ ከሁለቱም አቀራረቦች ጥቅም ለማግኘት አንድ ላይ ይውላሉ። ለምሳሌ፣ ለሂደቱ የCAP_NET_ADMIN አቅም መስጠት ትፈልጉ ይሆናል ነገርግን የሶኬት ግንኙነቶችን እንዲቀበል አትፍቀዱለት፣ መቀበል እና መቀበል4 የስርዓት ጥሪዎች።

የሴክኮምፕ ማጣሪያ ዘዴ በSECOMP_MODE_FILTER ሁነታ በሚሰሩ የ BPF ማጣሪያዎች ላይ የተመሰረተ ነው, እና የስርዓት ጥሪ ማጣሪያ እንደ ፓኬቶች በተመሳሳይ መንገድ ይከናወናል.

ሴኮምፕ ማጣሪያዎች በPR_SET_SECOMP ክዋኔ በኩል prctl በመጠቀም ይጫናሉ። እነዚህ ማጣሪያዎች በሴክኮምፕ_ዳታ መዋቅር ለተወከለው ለእያንዳንዱ የሴክኮምፕ ፓኬት የሚፈጸም የBPF ፕሮግራም መልክ ይይዛሉ። ይህ መዋቅር የማመሳከሪያ አርክቴክቸር፣ በስርዓቱ ጥሪ ጊዜ የአቀነባባሪ መመሪያ ጠቋሚ እና ቢበዛ ስድስት የስርዓት ጥሪ ነጋሪ እሴቶችን ይዟል፣ እንደ uint64 ይገለጻል።

በlinux/seccomp.h ፋይል ውስጥ ካለው የከርነል ምንጭ ኮድ የሴክኮምፕ_ዳታ መዋቅር ይህን ይመስላል፡-

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

ከዚህ መዋቅር ማየት እንደምትችለው፣ በስርዓት ጥሪ፣ ክርክሮቹ ወይም ሁለቱንም በማጣመር ማጣራት እንችላለን።

እያንዳንዱን የሴክኮም ፓኬት ከተቀበለ በኋላ ማጣሪያው የመጨረሻ ውሳኔ ለማድረግ እና ቀጥሎ ምን ማድረግ እንዳለበት ለከርነሉ መንገር አለበት። የመጨረሻው ውሳኔ በአንድ የመመለሻ ዋጋዎች (ሁኔታ ኮዶች) ይገለጻል.

- 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 መፈለጊያውን ለማሳወቅ ይጠቅማል። መከታተያ ካልተገናኘ, ስህተት ተመልሶ ይመጣል, errno ወደ -ENOSYS ተቀናብሯል, እና የስርዓት ጥሪው አልተሰራም.

- SECCOMP_RET_LOG - የስርዓት ጥሪው ተፈትቷል እና ተመዝግቧል።

- SECCOMP_RET_ALLOW - የስርዓት ጥሪ በቀላሉ ይፈቀዳል።

ptrace የሂደቱን አፈፃፀም የመከታተል እና የመቆጣጠር ችሎታ ባለው ሂደት ውስጥ የመከታተያ ዘዴዎችን ለመተግበር የስርዓት ጥሪ ነው። የክትትል ፕሮግራሙ ውጤታማ በሆነ መንገድ አፈፃፀሙ ላይ ተጽእኖ ሊያሳድር እና የዱካ ማህደረ ትውስታ መዝገቦችን ማሻሻል ይችላል። በሴክኮምፕ አውድ ውስጥ፣ ptrace ጥቅም ላይ የሚውለው በSECOMP_RET_TRACE ሁኔታ ኮድ ሲቀሰቀስ ነው፣ ስለዚህ መፈለጊያው የስርዓት ጥሪውን እንዳይሰራ እና የራሱን አመክንዮ እንዲተገብር ያደርጋል።

የሰከንድ ስህተቶች

ከጊዜ ወደ ጊዜ ከሴኮምፕ ጋር በሚሰሩበት ጊዜ የተለያዩ ስህተቶች ያጋጥሙዎታል, እነዚህም በ SECCOMP_RET_ERRNO አይነት የመመለሻ ዋጋ ተለይተው ይታወቃሉ. ስህተትን ሪፖርት ለማድረግ፣ የሴክኮምፕ ሲስተም ጥሪ ከ1 ይልቅ -0 ይመለሳል።

የሚከተሉት ስህተቶች ሊኖሩ ይችላሉ:

- EACCESS - ደዋዩ የስርዓት ጥሪ ማድረግ አይፈቀድለትም። ይህ ብዙውን ጊዜ የሚከሰተው የCAP_SYS_ADMIN ልዩ መብቶች ስለሌለው ወይም ምንም_new_privs prctl በመጠቀም ስላልተዘጋጀ ነው (ስለዚህ በኋላ እንነጋገራለን)።

- EFAULT - ያለፉ ክርክሮች (args በ seccomp_data መዋቅር) ትክክለኛ አድራሻ የላቸውም;

- EINVAL - እዚህ አራት ምክንያቶች ሊኖሩ ይችላሉ:

- የተጠየቀው ክዋኔ አይታወቅም ወይም አሁን ባለው ውቅር በከርነል አይደገፍም;

- የተገለጹት ባንዲራዎች ለተጠየቀው ክዋኔ ልክ አይደሉም;

-ክዋኔው BPF_ABSን ያካትታል፣ነገር ግን ከተጠቀሰው ማካካሻ ጋር ችግሮች አሉ፣ይህም ከሴክኮምፕ_ዳታ መዋቅር መጠን ሊበልጥ ይችላል።

- ወደ ማጣሪያው የተላለፉ መመሪያዎች ብዛት ከከፍተኛው ይበልጣል;

- ENOMEM - ፕሮግራሙን ለማስኬድ በቂ ማህደረ ትውስታ የለም;

- EOPNOTSUPP - ክዋኔው በ SECCOMP_GET_ACTION_AVAIL ድርጊቱ እንደሚገኝ አመልክቷል, ነገር ግን ከርነል በክርክር ውስጥ መመለስን አይደግፍም;

- ESRCH - ሌላ ዥረት በማመሳሰል ጊዜ ችግር ተፈጠረ;

- ENOSYS - ከSECOMP_RET_TRACE እርምጃ ጋር የተያያዘ ምንም መከታተያ የለም።

prctl የተጠቃሚ-ቦታ ፕሮግራም የተወሰኑ የሂደቱን ገፅታዎች እንደ ባይት ኢንዲያኒዝም፣ የክር ስም፣ ደህንነቱ የተጠበቀ ስሌት ሁነታ (ሴኮምፕ)፣ ልዩ መብቶች፣ የፐርፍ ዝግጅቶች፣ ወዘተ ያሉ የሂደቱን ገፅታዎች እንዲቆጣጠር (እንዲያዘጋጅ እና እንዲያገኝ) የሚያስችል የስርዓት ጥሪ ነው።

ሴኮምፕ ለአንተ የማጠሪያ ቴክኖሎጂ ሊመስልህ ይችላል፣ ግን አይደለም። ሴኮምፕ ተጠቃሚዎች የማጠሪያ ዘዴን እንዲያዳብሩ የሚያስችል መገልገያ ነው። አሁን በሴኮምፕ ሲስተም ጥሪ በቀጥታ የሚጠራ ማጣሪያ በመጠቀም የተጠቃሚ መስተጋብር ፕሮግራሞች እንዴት እንደሚፈጠሩ እንመልከት።

የBPF ሴክኮም ማጣሪያ ምሳሌ

ከዚህ ቀደም የተብራሩትን ሁለት ድርጊቶች እንዴት ማዋሃድ እንደሚቻል እናሳያለን-

— በውሳኔዎቹ ላይ በመመስረት ከተለያዩ የመመለሻ ኮዶች ጋር እንደ ማጣሪያ የሚያገለግል የሴክኮምፕ BPF ፕሮግራም እንጽፋለን ።

- prctl በመጠቀም ማጣሪያውን ይጫኑ.

በመጀመሪያ ደረጃ ራስጌዎችን ከመደበኛ ቤተ-መጽሐፍት እና ከሊኑክስ ከርነል ያስፈልግዎታል፡-

#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_SECOMP እና CONFIG_SECOMP_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፣ ቅስት፣ 0፣ 3) - BPF_JEQ ን በመጠቀም በBPF_K accumulator ቋሚ ውስጥ ያለው የአርክቴክቸር እሴት ከቅስት ጋር እኩል መሆኑን ያረጋግጣል። እንደዚያ ከሆነ፣በማካካሻ 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) - የስርዓት ጥሪ ቁጥርን ከተለዋዋጭ እሴት ጋር ያወዳድራል. እኩል ከሆኑ ወደሚቀጥለው መመሪያ ይሂዱ እና የስርዓት ጥሪውን ያሰናክላል፣ አለበለዚያ የስርዓት ጥሪውን በSECOMP_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ን በመጠቀም እንዲፈፀም ይፈቅዳል።

ሴኮምፕ CBPF ነው።
ከተቀናበረ ELF ነገር ወይም JIT ከተጠናቀረ C ፕሮግራም ይልቅ የመመሪያዎች ዝርዝር ለምን ጥቅም ላይ እንደሚውል እያሰቡ ሊሆን ይችላል።

ለዚህ ሁለት ምክንያቶች አሉ.

• በመጀመሪያ፣ ሴክኮም ሲቢፒኤፍ (ክላሲክ BPF) ይጠቀማል እንጂ eBPF አይደለም፣ ይህ ማለት፡ ምንም መዝገቦች የሉትም፣ ነገር ግን የመጨረሻውን ስሌት ውጤት ለማከማቸት አሰባሳቢ ብቻ ነው፣ በምሳሌው ላይ እንደሚታየው።

• ሁለተኛ፣ ሴኮምፕ የ BPF መመሪያዎችን ድርድር ጠቋሚን በቀጥታ ይቀበላል እንጂ ሌላ የለም። የተጠቀምንባቸው ማክሮዎች እነዚህን መመሪያዎች በፕሮግራመር ተስማሚ በሆነ መንገድ እንዲገልጹ ያግዛሉ።

ይህን ስብሰባ ለመረዳት ተጨማሪ እገዛ ከፈለጉ፣ ተመሳሳይ ነገር የሚያደርገውን pseudocode ያስቡበት፡-

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_SECOMPን እንደ አማራጭ በመውሰድ prctl እንጠቀማለን። ከዚያም በ SECCOMP_MODE_FILTER አይነት የፕሮግ ተለዋዋጭ ውስጥ የሚገኘውን ማጣሪያውን እንዲጭን ሁነታውን እንነግረዋለን፡

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

እንጀምር. ፕሮግራማችንን ለማጠናቀር ክላንግ ወይም gcc ልንጠቀም እንችላለን በማንኛውም መንገድ ዋና.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 ተመሳሳይ ነገር ማሳካት አይፈልጉም?

ስለ ኢቢፒኤፍ ፕሮግራሞች ሲያስቡ፣ ብዙ ሰዎች በቀላሉ ጽፈው በአስተዳዳሪ ልዩ መብቶች እንደጫኑ ያስባሉ። ይህ መግለጫ በአጠቃላይ እውነት ቢሆንም፣ ከርነል የኢቢፒኤፍ እቃዎችን በተለያዩ ደረጃዎች ለመጠበቅ የተለያዩ ዘዴዎችን ይጠቀማል። እነዚህ ዘዴዎች BPF LSM ወጥመዶች ይባላሉ.

BPF LSM ወጥመዶች

የሥርዓት ክስተቶችን ከሥነ-ሕንፃ-ገለልተኛ ክትትል ለማቅረብ፣ LSM ወጥመዶችን ጽንሰ-ሀሳብ ተግባራዊ ያደርጋል። መንጠቆ ጥሪ በቴክኒካል ከስርዓት ጥሪ ጋር ይመሳሰላል፣ነገር ግን ስርዓቱ ራሱን የቻለ እና ከመሠረተ ልማት ጋር የተዋሃደ ነው። LSM የአብስትራክሽን ንብርብር በተለያዩ አርክቴክቸር ላይ ካሉ የስርዓት ጥሪዎች ጋር ሲገናኝ የሚያጋጥሙ ችግሮችን ለማስወገድ የሚረዳበት አዲስ ፅንሰ-ሀሳብ ይሰጣል።

ይህ ጽሑፍ በሚጻፍበት ጊዜ ከርነል ከ BPF ፕሮግራሞች ጋር የተቆራኘ ሰባት መንጠቆዎች አሉት፣ እና SELinux እነሱን የሚተገበረው አብሮገነብ LSM ብቻ ነው።

የወጥመዶቹ ምንጭ ኮድ በፋይሉ ውስጥ ባለው የከርነል ዛፍ ውስጥ ይገኛል/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 - ከርነል ለኢቢኤፍኤፍ ፕሮግራም የፋይል ገላጭ ሲመልስ ይፈትሻል;

— security_bpf_map_alloc — በ BPF ካርታዎች ውስጥ ያለው የደህንነት መስክ መጀመሩን ያረጋግጣል።

- security_bpf_map_free - የደህንነት መስኩ በBPF ካርታዎች ውስጥ መጸዳዱን ያረጋግጣል።

— security_bpf_prog_alloc — የደህንነት መስኩ በ BPF ፕሮግራሞች ውስጥ መጀመሩን ያረጋግጣል።

-security_bpf_prog_free - የደህንነት መስኩ በBPF ፕሮግራሞች ውስጥ መጸዳዱን ያረጋግጣል።

አሁን ፣ ይህንን ሁሉ ስናይ ፣ እኛ እንረዳለን-ከ LSM BPF interceptors በስተጀርባ ያለው ሀሳብ ለእያንዳንዱ eBPF ነገር ጥበቃ ሊሰጡ እንደሚችሉ ነው ፣ ይህም ተገቢ መብቶች ያላቸው ብቻ በካርዶች እና ፕሮግራሞች ላይ ስራዎችን ማከናወን እንደሚችሉ ያረጋግጣል።

ማጠቃለያ

ደህንነት ለመጠበቅ ለፈለጋችሁት ነገር ሁሉ በአንድ መጠን-የሚስማማ-ሁሉንም መንገድ ተግባራዊ ማድረግ የምትችሉት ነገር አይደለም። ስርዓቶችን በተለያዩ ደረጃዎች እና በተለያዩ መንገዶች መከላከል መቻል አስፈላጊ ነው. ብታምኑም ባታምኑም የስርአቱን ደህንነት ለመጠበቅ የሚበጀው መንገድ የተለያዩ የጥበቃ ደረጃዎችን ከተለያየ ቦታ ማደራጀት ስለሆነ የአንድ ደረጃ ደህንነትን መቀነስ አጠቃላይ ስርዓቱን ማግኘት አይፈቅድም። ዋናዎቹ ገንቢዎች የተለያዩ የንብርብሮች እና የመዳሰሻ ነጥቦችን ስብስብ በመስጠት ጥሩ ስራ ሰርተዋል። ንብርብሮች ምን እንደሆኑ እና ከእነሱ ጋር ለመስራት BPF ፕሮግራሞችን እንዴት እንደሚጠቀሙ ጥሩ ግንዛቤ እንደሰጠንዎት ተስፋ እናደርጋለን።

ስለ ደራሲያን

ዴቪድ ካላቬራ በ Netlify ላይ CTO ነው። በዶከር ድጋፍ ውስጥ ሰርቷል እና ለ Runc, Go እና BCC መሳሪያዎች እና ለሌሎች ክፍት ምንጭ ፕሮጀክቶች እድገት አስተዋጽኦ አድርጓል. በ Docker ፕሮጄክቶች እና በ Docker ፕለጊን ስነ-ምህዳር ልማት ላይ በሚሰራው ስራ ይታወቃል። ዴቪድ ስለ ነበልባል ግራፎች በጣም ይወዳል እና ሁልጊዜ አፈጻጸምን ለማመቻቸት ይፈልጋል።

ሎሬንዞ ፎንታና በSysdig ክፍት ምንጭ ቡድን ላይ ይሰራል፣ እሱ በዋነኝነት ያተኮረው ፋልኮ ላይ ነው፣ የክላውድ ቤተኛ ኮምፒውቲንግ ፋውንዴሽን ፕሮጀክት ኮንቴይነር Runtime ደህንነትን እና ያልተለመደ ማወቂያን በከርነል ሞጁል እና eBPF። እሱ ለተከፋፈሉ ስርዓቶች፣ ለሶፍትዌር የተገለጸ አውታረ መረብ፣ የሊኑክስ ከርነል እና የአፈጻጸም ትንተና በጣም ይወዳል።

» ስለ መጽሐፉ ተጨማሪ መረጃ እባክዎን ይጎብኙ የአሳታሚው ድር ጣቢያ
» ማውጫ
» የተቀነጨበ

ለ Khabrozhiteli በኩፖኑ ላይ 25% ቅናሽ - ሊኑክስ

የመጽሐፉን የወረቀት ስሪት ሲከፍሉ, ኢ-መጽሐፍ ወደ ኢሜል ይላካል.

ምንጭ: hab.com

አስተያየት ያክሉ