Το κάρφωμα ενός προγράμματος σε VPN στο Linux είναι κυρίως πρόβλημα δρομολόγησης, και ο κανόνας του τείχους προστασίας αποφασίζει μόνο τι γίνεται αφού επιλεγεί η διαδρομή. Αν ταιριάξετε το UID του προγράμματος στο OUTPUT πριν η σήραγγα κατέχει την προεπιλεγμένη διαδρομή, ο κανόνας απορρίπτει ακριβώς την κίνηση που θέλατε να προστατεύσετε. Ο ίδιος κανόνας δεν βλέπει και τη διεργασία του resolver που απαντά στα DNS ερωτήματα του προγράμματος. Βάλτε τη σειρά σωστά και ένας λογαριασμός χωρίς προνόμια γίνεται αξιόπιστος επιλογέας για τη διαιρεμένη σήραγγα.

Δώστε στον λογαριασμό διαδρομή πριν την ετυμηγορία

Το ταίριασμα ιδιοκτήτη απαντά σε μία ερώτηση: έφτιαξε το πακέτο ένας τοπικός υποδοχέας που ανήκει σε αυτό το UID; Δεν επιλέγει διασύνδεση και δεν μπορεί να μετακινήσει κίνηση που ο κύριος πίνακας στέλνει ήδη έξω από τη φυσική NIC. Γι' αυτό ένας κανόνας UID χωρίς αντίστοιχη διαδρομή μπλοκάρει το πρόγραμμα αντί να το ανακατευθύνει.

Στείλτε τον λογαριασμό πρώτα μέσα από τον δικό του πίνακα και μετά αφήστε το τείχος προστασίας να επιβεβαιώσει το αποτέλεσμα. Αυτό δουλεύει σε έναν υπολογιστή του οποίου το iproute2 καταλαβαίνει το uidrange, κάτι που ip rule help αναφέρει σε μία γραμμή. Ο πίνακας 100 κρατά μια προεπιλεγμένη διαδρομή μέσα από τη σήραγγα και ο κανόνας στέλνει μόνο το UID 1000 σε αυτόν τον πίνακα. Όλα τα άλλα μένουν στον κύριο πίνακα και συνεχίζουν να χρησιμοποιούν τη φυσική ζεύξη.

ip rule add uidrange 1000-1000 table 100
ip route add default dev tun0 table 100

Ένας δεύτερος λογαριασμός που χρειάζεται άλλη σήραγγα παίρνει τον πίνακα 101 και δικό του κανόνα, γιατί δύο προφίλ δεν μπορούν να κατέχουν και τα δύο την προεπιλεγμένη διαδρομή στον ίδιο πίνακα. Τα προφίλ του NetworkManager είναι σε επίπεδο συστήματος, οπότε αυτό το μέρος μένει χειροκίνητο και οι κανόνες εξαφανίζονται στην επανεκκίνηση αν κάτι δεν τους επαναφέρει. Γράψτε τους πίνακες σε σενάριο εκκίνησης δίπλα στον ορισμό της σήραγγας, ώστε μια επανασύνδεση να μην αφήσει έναν λογαριασμό σε λάθος δρόμο.

Γιατί η εξαίρεση του loopback σκοτώνει τη σήραγγα

Οι περισσότερες αντιγραμμένες εκδοχές του κανόνα απορρίπτουν κάθε πακέτο που δεν βγαίνει από το loopback. Η σήραγγα δεν είναι loopback, άρα αυτή η μορφή απορρίπτει και την κίνηση της σήραγγας και το αποτέλεσμα μοιάζει με νεκρό VPN και όχι με λάθος του τείχους προστασίας.

Όταν τρέχει το OUTPUT, ο πυρήνας έχει ήδη επιλέξει διασύνδεση εξόδου, οπότε -o συγκρίνεται με την απάντηση της διαδρομής και όχι με την κεφαλίδα του πακέτου. Το ταίριασμα ιδιοκτήτη εξετάζεται μόνο στο OUTPUT και στο POSTROUTING και τα προωθούμενα πακέτα δεν φέρουν υποδοχέα για να διαβάσει. Αυτός ο συνδυασμός σημαίνει ότι η εξαίρεση πρέπει να ονομάσει τη διασύνδεση της σήραγγας και να αρνηθεί αυτό το ταίριασμα, αντί να αρνηθεί το loopback.

Το loopback εξακολουθεί να αξίζει το δικό του ACCEPT πάνω από το DROP, γιατί το πρώτο πακέτο DNS από το πρόγραμμα πηγαίνει στον τοπικό stub μέσω lo. Βάλτε τους δύο κανόνες σε αυτή τη σειρά και ο λογαριασμός φτάνει στο stub, μετά στη σήραγγα και πουθενά αλλού.

iptables -I OUTPUT 1 -o lo -m owner --uid-owner vpnapp -j ACCEPT
iptables -A OUTPUT -m owner --uid-owner vpnapp ! -o tun0 -j DROP

Το IPv6 είναι ξεχωριστό σύνολο κανόνων και ένα ζεύγος μόνο για v4 αφήνει ip6tables ανέγγιχτο. Ένα πρόγραμμα που προτιμά εγγραφές AAAA θα έβγαινε τότε μέσα από τη φυσική ζεύξη χωρίς να φτάσει ποτέ στο DROP. Καθρεφτίστε τους δύο κανόνες στο ip6tables, ή απενεργοποιήστε το IPv6 στον υπολογιστή όταν η σήραγγα δεν μεταφέρει διεύθυνση v6. Οι κανόνες που προστέθηκαν με το χέρι εξαφανίζονται επίσης στην επανεκκίνηση, οπότε αποθηκεύστε τους με netfilter-persistent save ή ξαναχτίστε την ίδια λογική ως πίνακα nftables που φορτώνει η μονάδα εκκίνησης.

Οι απαντήσεις DNS έρχονται από άλλη διεργασία

Το ερώτημα του προγράμματος προς 127.0.0.53 περνά το ACCEPT του loopback και εκεί σταματά το ταίριασμα UID. Ο resolver του stub προωθεί προς τα πάνω από τον δικό του λογαριασμό υπηρεσίας, οπότε ένας κανόνας που κλειδώνει στο vpnapp δεν βλέπει ποτέ το δεύτερο ερώτημα. Αν ο resolver επιλέξει διακομιστή LAN για τη ζεύξη, οι απαντήσεις επιστρέφουν μέσα από τη φυσική NIC ενώ τα δικά του πακέτα του προγράμματος μένουν στη σήραγγα. Το ορατό αποτέλεσμα είναι μια σήραγγα που δουλεύει με σπασμένα ονόματα, ή ονόματα που απαντά ένας διακομιστής που δεν επιλέξατε.

Διορθώστε την επίλυση ονομάτων στη ζεύξη της σήραγγας και όχι στην εφαρμογή. Δώστε στο προφίλ της σήραγγας έναν διακομιστή DNS και έναν τομέα δρομολόγησης και μετά επιβεβαιώστε με resolvectl query και resolvectl status tun0 ότι ο resolver τον χρησιμοποιεί για τα ονόματα που μετράνε. Μέσα σε έναν χώρο ονομάτων δικτύου το πρόβλημα εξαφανίζεται, γιατί ο resolver του χώρου γνωρίζει μόνο τη σήραγγα.

Ελέγξτε τον κανόνα από τον ίδιο τον λογαριασμό

Ρωτήστε τον πυρήνα ποια διαδρομή παίρνει ένα UID, μετά κάντε ένα αίτημα ως αυτός ο λογαριασμός και συγκρίνετε τη διεύθυνση εξόδου με εκείνη του δικού σας. Αν και οι δύο αναφέρουν την ίδια διεύθυνση, ο κανόνας ταιριάζει κάτι άλλο από την κίνηση που δοκιμάσατε. Η πρώτη εντολή παρακάτω δείχνει την αναζήτηση διαδρομής χωρίς αποστολή πακέτου, κάτι που χωρίζει ένα λάθος δρομολόγησης από ένα λάθος τείχους προστασίας.

ip route get 1.1.1.1 uid 1000
sudo -u vpnapp curl -s https://ifconfig.co

Οι μετρητές δείχνουν αν ένας κανόνας ενεργοποιήθηκε ποτέ. Τρέξτε iptables -vnL OUTPUT, διαβάστε τον αριθμό πακέτων δίπλα σε κάθε κανόνα και θεωρήστε μηδέν δίπλα στο DROP μετά από πραγματική κίνηση ως λάθος ταιριάσματος.

Η προωθούμενη κίνηση μένει έξω από αυτόν τον σχεδιασμό. Ένα container ή VM πίσω από γέφυρα δεν έχει τοπικό υποδοχέα που ανήκει στο vpnapp, οπότε το ταίριασμα ιδιοκτήτη δεν ενεργοποιείται ποτέ και τα πακέτα αυτά ακολουθούν την πολιτική FORWARD. Αν αυτό το φορτίο πρέπει να καλυφθεί, δοκιμάστε το ξεχωριστά και γράψτε μια πολιτική κλειδωμένη στη γέφυρα ή στη διεύθυνση του container. Ο σχεδιασμός αντέχει όταν ο πίνακας, η εξαίρεση διασύνδεσης, ο resolver και οι κανόνες v6 δείχνουν όλοι την ίδια κατεύθυνση. Αφήστε έξω οποιοδήποτε από αυτά και ο λογαριασμός είτε διαρρέει είτε χάνει συνδεσιμότητα.

Σχετικά άρθρα