புத்தகம் "லினக்ஸ் கண்காணிப்புக்கான BPF"

புத்தகம் "லினக்ஸ் கண்காணிப்புக்கான BPF"வணக்கம், கப்ரோ குடியிருப்பாளர்களே! BPF மெய்நிகர் இயந்திரம் லினக்ஸ் கர்னலின் மிக முக்கியமான கூறுகளில் ஒன்றாகும். அதன் சரியான பயன்பாடு கணினி பொறியாளர்கள் தவறுகளைக் கண்டறிந்து மிகவும் சிக்கலான சிக்கல்களைக் கூட தீர்க்க அனுமதிக்கும். கர்னலின் நடத்தையை கண்காணிக்கும் மற்றும் மாற்றியமைக்கும் நிரல்களை எவ்வாறு எழுதுவது, கர்னலில் நிகழ்வுகளை கண்காணிக்க குறியீட்டை எவ்வாறு பாதுகாப்பாக செயல்படுத்துவது மற்றும் பலவற்றை நீங்கள் கற்றுக் கொள்வீர்கள். டேவிட் கலவேரா மற்றும் லோரென்சோ ஃபோன்டானா BPF இன் சக்தியைத் திறக்க உங்களுக்கு உதவுவார்கள். செயல்திறன் மேம்படுத்தல், நெட்வொர்க்கிங், பாதுகாப்பு பற்றிய உங்கள் அறிவை விரிவுபடுத்துங்கள். - லினக்ஸ் கர்னலின் நடத்தையை கண்காணிக்கவும் மாற்றவும் BPF ஐப் பயன்படுத்தவும். - கர்னலை மீண்டும் தொகுக்காமல் அல்லது கணினியை மறுதொடக்கம் செய்யாமல் கர்னல் நிகழ்வுகளைப் பாதுகாப்பாகக் கண்காணிக்க குறியீட்டை உட்செலுத்தவும். — C, Go அல்லது Python இல் வசதியான குறியீடு உதாரணங்களைப் பயன்படுத்தவும். - BPF திட்டத்தின் வாழ்க்கைச் சுழற்சியை வைத்திருப்பதன் மூலம் கட்டுப்பாட்டை எடுத்துக் கொள்ளுங்கள்.

லினக்ஸ் கர்னல் பாதுகாப்பு, அதன் அம்சங்கள் மற்றும் Seccomp

நிலைத்தன்மை, பாதுகாப்பு அல்லது வேகத்தை இழக்காமல் கர்னலை நீட்டிக்க BPF ஒரு சக்திவாய்ந்த வழியை வழங்குகிறது. இந்த காரணத்திற்காக, கர்னல் டெவலப்பர்கள் BPF நிரல்களால் ஆதரிக்கப்படும் Seccomp வடிப்பான்களை செயல்படுத்துவதன் மூலம், Seccomp BPF என்றும் அழைக்கப்படும் Seccomp இல் செயல்முறை தனிமைப்படுத்தலை மேம்படுத்த அதன் பன்முகத்தன்மையைப் பயன்படுத்துவது நல்லது என்று நினைத்தனர். இந்த அத்தியாயத்தில் Seccomp என்றால் என்ன, அது எவ்வாறு பயன்படுத்தப்படுகிறது என்பதை விளக்குவோம். BPF நிரல்களைப் பயன்படுத்தி Seccomp வடிப்பான்களை எவ்வாறு எழுதுவது என்பதை நீங்கள் கற்றுக் கொள்வீர்கள். அதன் பிறகு, லினக்ஸ் பாதுகாப்பு தொகுதிகளுக்கான கர்னலில் சேர்க்கப்பட்டுள்ள உள்ளமைக்கப்பட்ட பிபிஎஃப் ஹூக்குகளைப் பார்ப்போம்.

Linux Security Modules (LSM) என்பது பல்வேறு பாதுகாப்பு மாதிரிகளை தரப்படுத்தப்பட்ட முறையில் செயல்படுத்த பயன்படும் செயல்பாடுகளின் தொகுப்பை வழங்கும் ஒரு கட்டமைப்பாகும். Apparmor, SELinux மற்றும் Tomoyo போன்ற கர்னல் மூல மரத்தில் LSMஐ நேரடியாகப் பயன்படுத்தலாம்.

லினக்ஸின் திறன்களைப் பற்றி விவாதிப்பதன் மூலம் ஆரம்பிக்கலாம்.

வாய்ப்புகளை

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"

இந்த அணியை கொஞ்சம் புரிந்து கொள்வோம்.

  • கேப்ஷ் - கேப்ஷை ஷெல்லாகப் பயன்படுத்தவும்.
  • —caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' - நாம் பயனரை மாற்ற வேண்டியிருப்பதால் (நாங்கள் ரூட்டாக இயக்க விரும்பவில்லை), cap_net_bind_service மற்றும் பயனர் ஐடியை உண்மையில் மாற்றும் திறனைக் குறிப்பிடுவோம் யாருக்கும் ரூட், அதாவது cap_setuid மற்றும் cap_setgid.
  • —keep=1 — ரூட் கணக்கிலிருந்து மாறும்போது நிறுவப்பட்ட திறன்களை வைத்திருக்க வேண்டும்.
  • —பயனர்=“யாரும்” — நிரலை இயக்கும் இறுதிப் பயனர் யாரும் இல்லை.
  • —addamb=cap_net_bind_service — ரூட் பயன்முறையிலிருந்து மாறிய பிறகு தொடர்புடைய திறன்களை அழிக்கவும்.
  • - -c "./capabilities" - நிரலை இயக்கவும்.

இணைக்கப்பட்ட திறன்கள் என்பது ஒரு சிறப்பு வகையான திறன்களாகும், அவை தற்போதைய நிரல் execve() ஐப் பயன்படுத்தி செயல்படுத்தும்போது குழந்தை நிரல்களால் பெறப்படும். தொடர்புடையதாக அனுமதிக்கப்படும் திறன்கள் அல்லது வேறுவிதமாகக் கூறினால், சுற்றுச்சூழல் திறன்கள் மட்டுமே மரபுரிமையாக இருக்க முடியும்.

--caps விருப்பத்தில் திறனைக் குறிப்பிட்ட பிறகு +eip என்றால் என்ன என்று நீங்கள் யோசித்துக்கொண்டிருக்கலாம். திறனைத் தீர்மானிக்க இந்தக் கொடிகள் பயன்படுத்தப்படுகின்றன:

செயல்படுத்தப்பட வேண்டும் (p);

-பயன்பாட்டிற்கு கிடைக்கிறது (இ);

குழந்தை செயல்முறைகள் மூலம் மரபுரிமை பெற முடியும் (i).

நாம் cap_net_bind_service ஐப் பயன்படுத்த விரும்புவதால், e கொடியுடன் இதைச் செய்ய வேண்டும். பின்னர் கட்டளையில் ஷெல் தொடங்குவோம். இது திறன்கள் பைனரியை இயக்கும் மற்றும் நாம் அதை i கொடியுடன் குறிக்க வேண்டும். இறுதியாக, p உடன் அம்சம் இயக்கப்பட வேண்டும் (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

இந்த எடுத்துக்காட்டில் நாங்கள் கேப்ஷைப் பயன்படுத்தினோம், ஆனால் நீங்கள் லிப்கேப்பைப் பயன்படுத்தி ஷெல்லை எழுதலாம். மேலும் தகவலுக்கு, 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 ஆகும், இது கோப்பில் உள்ள கர்னல் மூலக் குறியீட்டில் மாறிலியாக வரையறுக்கப்படுகிறது, இதில் அடங்கும்/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">

ரன்சி அல்லது டோக்கர் போன்ற கன்டெய்னர்களுக்கு இயக்க நேரத்தில் இயக்கப்படும் போது, ​​அவை சலுகை இல்லாத பயன்முறையில் இயங்க அனுமதிக்கப்படுகின்றன, ஆனால் பெரும்பாலான பயன்பாடுகளை இயக்கத் தேவையான திறன்கள் மட்டுமே அனுமதிக்கப்படுகின்றன. பயன்பாட்டிற்கு சில திறன்கள் தேவைப்படும்போது, ​​அவற்றை --cap-add ஐப் பயன்படுத்தி Docker வழங்க முடியும்:

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

இந்தக் கட்டளையானது கொள்கலனுக்கு CAP_NET_ADMIN திறனைக் கொடுக்கும், இது போலி0 இடைமுகத்தைச் சேர்க்க பிணைய இணைப்பை உள்ளமைக்க அனுமதிக்கிறது.

வடிகட்டுதல் போன்ற அம்சங்களை எவ்வாறு பயன்படுத்துவது என்பதை அடுத்த பகுதி காட்டுகிறது, ஆனால் எங்கள் சொந்த வடிப்பான்களை நிரல் ரீதியாக செயல்படுத்த அனுமதிக்கும் வேறுபட்ட நுட்பத்தைப் பயன்படுத்துகிறது.

Seccomp

Seccomp என்பது செக்யூர் கம்ப்யூட்டிங் மற்றும் லினக்ஸ் கர்னலில் செயல்படுத்தப்படும் ஒரு பாதுகாப்பு அடுக்கு ஆகும், இது டெவலப்பர்கள் குறிப்பிட்ட கணினி அழைப்புகளை வடிகட்ட அனுமதிக்கிறது. Seccomp ஆனது லினக்ஸுடன் ஒப்பிடக்கூடியதாக இருந்தாலும், சில கணினி அழைப்புகளை நிர்வகிக்கும் அதன் திறன் அவற்றுடன் ஒப்பிடும்போது அதை மிகவும் நெகிழ்வானதாக்குகிறது.

Seccomp மற்றும் Linux அம்சங்கள் ஒன்றுக்கொன்று பிரத்தியேகமானவை அல்ல, மேலும் இரண்டு அணுகுமுறைகளிலிருந்தும் பயனடைய பெரும்பாலும் ஒன்றாகப் பயன்படுத்தப்படுகின்றன. எடுத்துக்காட்டாக, நீங்கள் ஒரு செயல்முறைக்கு CAP_NET_ADMIN திறனைக் கொடுக்க விரும்பலாம், ஆனால் அது சாக்கெட் இணைப்புகளை ஏற்க அனுமதிக்காது, ஏற்றுக்கொள்ளும் மற்றும் ஏற்கும் 4 அமைப்பு அழைப்புகளைத் தடுக்கிறது.

Seccomp வடிகட்டுதல் முறையானது SECCOMP_MODE_FILTER பயன்முறையில் செயல்படும் BPF வடிப்பான்களை அடிப்படையாகக் கொண்டது, மேலும் கணினி அழைப்பு வடிகட்டுதல் பாக்கெட்டுகளைப் போலவே செய்யப்படுகிறது.

PR_SET_SECCOMP செயல்பாட்டின் மூலம் prctl ஐப் பயன்படுத்தி Seccomp வடிப்பான்கள் ஏற்றப்படுகின்றன. இந்த வடிப்பான்கள் BPF நிரலின் வடிவத்தை எடுக்கும், அவை seccomp_data கட்டமைப்பால் குறிப்பிடப்படும் ஒவ்வொரு Seccomp பாக்கெட்டிற்கும் செயல்படுத்தப்படும். இந்த அமைப்பில் குறிப்பு கட்டமைப்பு, கணினி அழைப்பின் போது செயலி வழிமுறைகளுக்கான ஒரு சுட்டிக்காட்டி மற்றும் அதிகபட்சமாக ஆறு கணினி அழைப்பு வாதங்கள், 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_O_TRACESECCOMP ஐப் பயன்படுத்தி ptrace ட்ரேசருக்குத் தெரிவிக்கப் பயன்படுகிறது, அந்தச் செயல்முறையைப் பார்க்கவும் கட்டுப்படுத்தவும் கணினி அழைப்பு செயல்படுத்தப்படும்போது இடைமறிக்க. ஒரு ட்ரேசர் இணைக்கப்படவில்லை என்றால், ஒரு பிழை திரும்பும், பிழையானது -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 கட்டமைப்பில் உள்ள 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 ஐப் பயன்படுத்தி வடிகட்டியை ஏற்றவும்.

முதலில் உங்களுக்கு நிலையான நூலகம் மற்றும் லினக்ஸ் கர்னலில் இருந்து தலைப்புகள் தேவை:

#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 உடன் தொகுக்கப்பட்டிருப்பதை உறுதி செய்ய வேண்டும். ஒரு வேலை செய்யும் இயந்திரத்தில் நீங்கள் இதை இப்படிச் சரிபார்க்கலாம்:

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_LD இலிருந்து கணினி ஏற்றப்பட்டு குவிகிறது, பாக்கெட் தரவு BPF_ABS நிலையான ஆஃப்செட்டில் அமைந்துள்ளது.

- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3) - BPF_JEQ ஐப் பயன்படுத்தி திரட்டி மாறிலி BPF_K இல் உள்ள கட்டமைப்பு மதிப்பு வளைவுக்குச் சமமாக உள்ளதா என்பதைச் சரிபார்க்கிறது. அப்படியானால், ஆஃப்செட் 0 இல் அடுத்த அறிவுறுத்தலுக்குத் தாவவும், இல்லையெனில் ஆஃப்செட் 3 இல் தாவவும் (இந்த விஷயத்தில்) வளைவு பொருந்தாததால் பிழையை ஏற்படுத்தவும்.

- BPF_STMT(BPF_LD + BPF_W + BPF_ABS (ஆஃப்செட்டாஃப்(struct seccomp_data, nr))) - BPF_LD என்ற வார்த்தையின் வடிவத்தில் BPF_LD இலிருந்து ஏற்றுகிறது மற்றும் குவிகிறது, இது 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;
}

இறுதியாக, எங்கள் install_filter செயல்பாட்டைப் பயன்படுத்தலாம், ஆனால் அதற்கு முன், தற்போதைய செயல்பாட்டிற்கு 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]);
 }

ஆரம்பிக்கலாம். எங்கள் நிரலைத் தொகுக்க, நாம் 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 -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 பொறிகளின் கருத்தை செயல்படுத்துகிறது. ஒரு ஹூக் அழைப்பு தொழில்நுட்ப ரீதியாக கணினி அழைப்பைப் போன்றது, ஆனால் அமைப்பு சுயாதீனமானது மற்றும் உள்கட்டமைப்புடன் ஒருங்கிணைக்கப்பட்டது. எல்எஸ்எம் ஒரு புதிய கருத்தை வழங்குகிறது, இதில் வெவ்வேறு கட்டமைப்புகளில் கணினி அழைப்புகளைக் கையாளும் போது ஏற்படும் சிக்கல்களைத் தவிர்க்க ஒரு சுருக்க அடுக்கு உதவுகிறது.

எழுதும் நேரத்தில், கர்னலில் 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 - கர்னல் 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 நிரல்களை எவ்வாறு பயன்படுத்துவது என்பது பற்றிய நல்ல புரிதலை நாங்கள் உங்களுக்கு வழங்கியுள்ளோம் என்று நம்புகிறோம்.

ஆசிரியர்கள் பற்றி

டேவிட் கலவேரா நெட்லிஃபையில் சி.டி.ஓ. அவர் டோக்கர் ஆதரவில் பணியாற்றினார் மற்றும் Runc, Go மற்றும் BCC கருவிகள் மற்றும் பிற திறந்த மூல திட்டங்களின் வளர்ச்சிக்கு பங்களித்தார். டோக்கர் திட்டப்பணிகள் மற்றும் டோக்கர் செருகுநிரல் சுற்றுச்சூழல் அமைப்பின் மேம்பாடு ஆகியவற்றில் அவரது பணிக்காக அறியப்பட்டவர். டேவிட் ஃபிளேம் கிராஃப்களில் மிகுந்த ஆர்வம் கொண்டவர் மற்றும் எப்போதும் செயல்திறனை மேம்படுத்த விரும்புகிறார்.

லோரென்சோ ஃபோண்டானா Sysdig இல் திறந்த மூலக் குழுவில் பணிபுரிகிறார், அங்கு அவர் முதன்மையாக ஃபால்கோவில் கவனம் செலுத்துகிறார், இது க்ளவுட் நேட்டிவ் கம்ப்யூட்டிங் ஃபவுண்டேஷன் திட்டமாகும், இது கர்னல் தொகுதி மற்றும் eBPF மூலம் கொள்கலன் இயக்க நேர பாதுகாப்பு மற்றும் ஒழுங்கின்மை கண்டறிதல் ஆகியவற்றை வழங்குகிறது. விநியோகிக்கப்பட்ட அமைப்புகள், மென்பொருள் வரையறுக்கப்பட்ட நெட்வொர்க்கிங், லினக்ஸ் கர்னல் மற்றும் செயல்திறன் பகுப்பாய்வு ஆகியவற்றில் அவர் ஆர்வமாக உள்ளார்.

» புத்தகத்தைப் பற்றிய கூடுதல் விவரங்களை இங்கே காணலாம் வெளியீட்டாளரின் இணையதளம்
» உள்ளடக்க அட்டவணை
» பகுதி

கூப்பனைப் பயன்படுத்தி Khabrozhiteleyக்கு 25% தள்ளுபடி - லினக்ஸ்

புத்தகத்தின் காகித பதிப்பை செலுத்தியவுடன், ஒரு மின்னணு புத்தகம் மின்னஞ்சல் மூலம் அனுப்பப்படும்.

ஆதாரம்: www.habr.com

கருத்தைச் சேர்