Η παράκαμψη cgroup στο nftables χρειάζεται επίσης διόρθωση της διεύθυνσης προέλευσης
Η σήμανση ενός cgroup το στέλνει σε άλλον πίνακα, και το socket κρατά τη διεύθυνση που είχε ήδη. Ταξινόμηση, δρομολόγηση και μετάφραση ως τρία ξεχωριστά βήματα.
Η σήμανση της κίνησης από ένα cgroup είναι το εύκολο μέρος. Τρεις ξεχωριστές αποφάσεις πρέπει να ευθυγραμμιστούν πριν μία εφαρμογή βγει από τη φυσική διεπαφή ενώ όλα τα υπόλοιπα μένουν στη σήραγγα: ποιο socket ανήκει στην εξαίρεση, ποια αναζήτηση δρομολόγησης ενεργοποιεί η σήμανση και ποια διεύθυνση προέλευσης κουβαλά το πακέτο όταν φεύγει. Οι περισσότερες αποτυχημένες προσπάθειες πετυχαίνουν την πρώτη, πετυχαίνουν τη δεύτερη μισά και δεν κοιτάζουν ποτέ την τρίτη.
Τρεις αποφάσεις, όχι μία
Η ταξινόμηση απαντά ποιος εξαιρείται. Η δρομολόγηση απαντά πού πηγαίνει αντ' αυτού το σημασμένο πακέτο. Η επιλογή προέλευσης απαντά τι θα δει το απομακρυσμένο άκρο και πού πρέπει να επιστρέψει η απάντηση.
Αυτά αποτυγχάνουν ανεξάρτητα. Ένα πακέτο μπορεί να ταξινομηθεί, να επαναδρομολογηθεί στη φυσική διεπαφή και παρ' όλα αυτά να φέρει ιδιωτική διεύθυνση που κανένας ανάντη δρομολογητής δεν μπορεί να επιστρέψει, κάτι που παράγει λήξη χρόνου που μοιάζει ακριβώς με μπλοκάρισμα τείχους προστασίας. Τίποτα στο βήμα της ταξινόμησης δεν σε προειδοποιεί γι' αυτό. Τίποτα ούτε στο βήμα της δρομολόγησης.
Και η σειρά μετράει. Η ταξινόμηση συμβαίνει όταν δημιουργείται το πακέτο, η δρομολόγηση μετά από αυτό, και η μετάφραση στο σημείο εξόδου.
Αν δεν μπορείς να ονομάσεις ποιο από τα τρία στάδια είναι χαλασμένο, δεν κάνεις αποσφαλμάτωση, αναδιατάσσεις κανόνες και ελπίζεις.
Ταίριαξε ένα cgroup που θα υπάρχει και αύριο
Το nftables μπορεί να ελέγξει τη συμμετοχή ενός socket σε cgroup v2, κάτι πραγματικά χρήσιμο γιατί ακολουθεί τη διεργασία και όχι τον προορισμό. Η παγίδα είναι το αναγνωριστικό. Μια εφαρμογή επιφάνειας εργασίας που ξεκινά από μενού ζει συχνά σε ένα παροδικό scope του οποίου το όνομα αλλάζει στην επόμενη εκκίνηση.
nft list ruleset
systemctl status session.slice 2>/dev/null
cat /proc/self/cgroupΗ αντιγραφή ενός ονόματος scope από μία συνεδρία και η σκληρή κωδικοποίησή του παράγει έναν κανόνα που δουλεύει σήμερα και αύριο σταματά σιωπηλά να ταιριάζει. Βάλε την εφαρμογή σε ένα σκόπιμα σταθερό περιβάλλον εκτέλεσης, όπως ένα αποκλειστικό scope ή unit που ελέγχεις, και ταίριαξε με αυτό. Τότε ο κανόνας επιβιώνει από επανεκκινήσεις και ενημερώσεις πακέτων. Γίνεται επίσης κάτι για το οποίο ένας συνάδελφος μπορεί να συλλογιστεί χωρίς το ιστορικό των συνεδριών σου.
Να είσαι επίσης ακριβής ως προς το ποιον μηχανισμό χρησιμοποιείς. Η ταξινόμηση net_cls στο cgroup v1 και το ταίριασμα socket στο cgroup v2 είναι διαφορετικά πράγματα, και ένα πεδίο με παρόμοιο όνομα στην τεκμηρίωση δεν τα κάνει ανταλλάξιμα.
Μια αλυσίδα τύπου route ενεργοποιεί τη δεύτερη αναζήτηση
Για τοπικά παραγόμενη κίνηση, ο τύπος της αλυσίδας αποφασίζει αν θα συμβεί κάτι μετά τη σήμανση. Μια αλυσίδα output τύπου 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 που έχει ήδη επιλέξει διεύθυνση προέλευσης για τη διαδρομή της σήραγγας κρατά αυτή τη διεύθυνση μετά την επαναδρομολόγηση. Τίποτα στην αλλαγή δρομολόγησης δεν την ξαναγράφει, γι' αυτό το πακέτο φεύγει από τη φυσική διεπαφή με ιδιωτική προέλευση που το ανάντη δίκτυο δεν μπορεί να επιστρέψει.
Το ορατό σύμπτωμα είναι μια εφαρμογή που φαίνεται να στέλνει δεδομένα και δεν λαμβάνει ποτέ απάντηση, και ένα αρχείο καταγραφής τείχους προστασίας που δεν δείχνει τίποτα γιατί το πακέτο δεν γύρισε ποτέ.
Η στενή διόρθωση είναι ένας κανόνας μετάφρασης προέλευσης περιορισμένος στη σήμανση αυτής της εφαρμογής και στην πραγματική διεπαφή εξόδου της, ενσωματωμένος στο 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 δεν το κάνει. Έλεγξε αντί να υποθέσεις, ειδικά για οτιδήποτε επανεκτελεί τον εαυτό του.
Τέλος, αποφάσισε τι πρέπει να συμβεί όταν η σήραγγα είναι κάτω. Μια εξαίρεση που γίνεται σιωπηλά πλήρης παράκαμψη είναι διαφορετική στάση ασφαλείας από μία που αποτυγχάνει κλειστή, και μόνο μία από τις δύο είναι αυτό που εννοούσες.