Մեկ cgroup-ից եկող թրաֆիկը նշելը հեշտ մասն է։ Երեք առանձին որոշումներ պետք է համաձայնեցվեն նախքան մեկ ծրագիր դուրս կգա ֆիզիկական ինտերֆեյսով, մինչ մնացած ամեն ինչ մնում է թունելում՝ որ socket-ն է պատկանում բացառությանը, որ երթուղային որոնումն է գործարկում նշանը, և որ աղբյուրի հասցեն է կրում փաթեթը դուրս գալիս։ Անհաջող փորձերի մեծ մասը ճիշտ է անում առաջինը, կիսով չափ՝ երկրորդը, և երբեք չի նայում երրորդին։

Երեք որոշում, ոչ թե մեկ

Դասակարգումը պատասխանում է ով է ազատված։ Երթուղավորումը պատասխանում է ուր է գնում նշված փաթեթը փոխարենը։ Աղբյուրի ընտրությունը պատասխանում է ինչ կտեսնի հեռավոր կողմը և ուր պետք է վերադառնա պատասխանը։

Սրանք ձախողվում են անկախ։ Փաթեթը կարող է դասակարգվել, վերաերթուղավորվել ֆիզիկական ինտերֆեյս, և այնուամենայնիվ կրել մասնավոր հասցե, որը ոչ մի վերին երթուղիչ չի կարող վերադարձնել, ինչը ստեղծում է ժամկետի սպառում, որը նույնական է firewall-ի արգելափակմանը։ Ոչինչ դասակարգման քայլում չի զգուշացնում դրա մասին։ Ոչինչ նաև երթուղավորման քայլում։

Հերթականությունը նույնպես կարևոր է։ Դասակարգումը տեղի է ունենում փաթեթի ստեղծման պահին, երթուղավորումը՝ դրանից հետո, իսկ թարգմանությունը՝ ելքի կետում։

Եթե չես կարող անվանել, թե երեք փուլերից որն է կոտրված, դու չես վրիպազերծում, այլ վերադասավորում ես կանոնները և հույս դնում։

Համապատասխանեցրու այնպիսի cgroup-ի, որը կմնա նաև վաղը

nftables-ը կարող է ստուգել socket-ի cgroup v2 անդամակցությունը, ինչը իսկապես օգտակար է, որովհետև հետևում է գործընթացին, ոչ թե նպատակակետին։ Խոչընդոտը նույնացուցիչն է։ Ընտրացանկից գործարկված աշխատասեղանի ծրագիրը հաճախ ապրում է անցողիկ scope-ում, որի անունը փոխվում է հաջորդ գործարկման ժամանակ։

nft list ruleset
systemctl status session.slice 2>/dev/null
cat /proc/self/cgroup

Scope-ի անունը մեկ նստաշրջանից պատճենելը և կոշտ կոդավորելը ստեղծում է կանոն, որն այսօր աշխատում է, իսկ վաղը լուռ դադարում է համապատասխանել։ Փոխարենը տեղադրիր ծրագիրը դիտավորյալ կայուն կատարման համատեքստում, օրինակ՝ քո կառավարած առանձին scope կամ unit, և համապատասխանեցրու դրան։ Այն ժամանակ կանոնը վերապրում է վերագործարկումները և փաթեթների թարմացումները։ Այն նաև դառնում է մի բան, որի մասին գործընկերը կարող է մտածել առանց քո նստաշրջանների պատմության։

Եղիր նաև ճշգրիտ այն մեխանիզմի հարցում, որն օգտագործում ես։ cgroup v1-ի net_cls դասակարգումը և cgroup v2-ի socket համապատասխանեցումը տարբեր բաներ են, և փաստաթղթերում նման անունով դաշտը դրանք փոխարինելի չի դարձնում։

route տիպի շղթան է, որ գործարկում է երկրորդ որոնումը

Տեղում ստեղծված թրաֆիկի համար շղթայի տիպը որոշում է՝ ինչ-որ բան տեղի կունենա արդյոք նշանը դնելուց հետո։ filter տիպի ելքային շղթան կարող է նշել փաթեթը, և միջուկը կշարունակի օգտագործել արդեն ընտրված երթուղին։ route տիպի շղթան այն մեխանիզմն է, որ նոր որոնում է պահանջում։

table inet bypass {
  chain mark {
    type route hook output priority mangle; policy accept;
    socket cgroupv2 level 2 "app.slice" meta mark set 0x1 comment "app bypass"
  }
}
ip rule add fwmark 0x1 lookup 200
ip route add default via 203.0.113.1 dev wlan0 table 200

Այնուհետև նշանին անհրաժեշտ է քաղաքականության կանոն, որն ուղարկում է այն աղյուսակ, և այդ աղյուսակը պետք է ամբողջական լինի՝ լռելյայն երթուղի, ցանկացած տեղական նպատակակետ, որ դեռ պետք է ծրագրին, և վերին դարպաս, որն իրոք պատասխանում է։

Ընտրիր նշանի արժեքը գիտակցաբար։ WireGuard-ը օգտագործում է իր fwmark-ը արտաքին տրանսպորտային թրաֆիկի համար, և պատահաբար նույն թվին հանդիպելը կարող է խանգարել թունելի սեփական երթուղային բացառությանը այնպիսի ձևերով, որ ախտորոշելը ցավոտ է։

Աղբյուրի հասցեն չի հետևում երթուղուն

Այն socket-ը, որն արդեն ընտրել է աղբյուրի հասցե թունելի ուղու համար, պահպանում է այդ հասցեն վերաերթուղավորումից հետո։ Ոչինչ երթուղու փոփոխության մեջ չի վերագրում այն, դրա համար էլ փաթեթը լքում է ֆիզիկական ինտերֆեյսը մասնավոր աղբյուրով, որը վերին ցանցը չի կարող վերադարձնել։

Տեսանելի ախտանիշը ծրագիր է, որը կարծես տվյալներ է ուղարկում և երբեք պատասխան չի ստանում, և firewall-ի մատյան, որը ոչինչ չի ցույց տալիս, որովհետև փաթեթը երբեք չվերադարձավ։

Նեղ ուղղումը աղբյուրի թարգմանության կանոն է՝ սահմանափակված այդ ծրագրի նշանով և նրա իրական ելքային ինտերֆեյսով, ինտեգրված գոյություն ունեցող NAT-ին՝ այն փոխարինելու փոխարեն։

table inet bypass {
  chain postrouting {
    type nat hook postrouting priority srcnat; policy accept;
    meta mark 0x1 oifname "wlan0" masquerade
  }
}

Թարգմանիր բացառությունը և ուրիշ ոչինչ։ Ընդհանուր masquerade կանոնը շտկում է ախտանիշը և ոչնչացնում է քո կարողությունը ասելու, թե որ թրաֆիկն որ ուղին գնաց։ Համապատասխանեցրու նշանին, իրական ելքային ինտերֆեյսին և, եթե կարող ես, ծրագրի սեփական հասցեին։ Լայն կանոնները հեշտ են գրվում և գրեթե անհնար է ավելի ուշ աուդիտ անել։

Ինչ դեռ դուրս է սահում բացառությունից

Անունների լուծումը կարող է գալ առանձին resolver գործընթացից այլ cgroup-ում, ուստի զննարկիչի socket-ի դասակարգումը չի դասակարգում նրա հարցումները։ Եթե resolver-ի հարցումները մնում են թունելում, մինչ կապը դուրս է գալիս դրանից, split-horizon պատասխանները կարող են չհամապատասխանել ուղուն։ Այդ անհամապատասխանությունը ստեղծում է խափանումներ, որոնք նման են DNS սխալների և այդպիսին չեն։

Երեխա գործընթացները ժառանգում են cgroup-ը շատ կարգավորումներում, բայց օգնականը, որն ինքն իրեն տեղափոխում է իր scope, չի ժառանգում։ Ստուգիր, ոչ թե ենթադրիր, հատկապես այն ամենի համար, ինչ նորից գործարկում է ինքն իրեն։

Վերջում որոշիր, թե ինչ պետք է լինի, երբ թունելն անջատված է։ Բացառությունը, որը լուռ դառնում է ամբողջական շրջանցում, այլ անվտանգության դիրքորոշում է, քան փակ վիճակում ձախողվողը, և դրանցից միայն մեկն է այն, ինչ նկատի ունեիր։

Առնչվող հոդվածներ