Ο δαίμονας καταγράφει αποτυχία chroot και το προφίλ φαίνεται ήδη γενναιόδωρο, οπότε η δελεαστική διόρθωση είναι να πλατύνεις έναν κανόνα διαδρομής και να επανεκκινήσεις. Συνήθως δεν αλλάζει τίποτα, γιατί το chroot φρουρείται από ένα προνόμιο που το προφίλ δεν μπορούσε ποτέ να παραχωρήσει. Το να αποφασίσεις ποιο επίπεδο να αγγίξεις απαιτεί τρεις ελέγχους, και είναι φθηνοί: τι επιτρέπεται να κρατά η διεργασία, τι επιτρέπει η πολιτική και τι αφαίρεσε ο εκκινητής πριν αποκτήσει σημασία οποιοδήποτε από τα δύο.

Δύο ξεχωριστές πύλες, και μόνο η μία ανήκει στο AppArmor

Το chroot απαιτεί CAP_SYS_CHROOT στον χώρο ονομάτων χρήστη όπου εκτελείται η διεργασία. Είναι συνηθισμένο προνόμιο του Linux, και ο πυρήνας το ελέγχει πριν η κλήση κάνει οτιδήποτε με διαδρομές. Ξεχωριστά, ένα προφίλ AppArmor σε λειτουργία επιβολής μπορεί να αρνηθεί τη δυνατότητα ακόμη και όταν η διεργασία την έχει, γιατί η υποχρεωτική πολιτική συμβουλεύεται επιπλέον των διαπιστευτηρίων και όχι στη θέση τους. Η διάκριση έχει πρακτική σημασία: τα δύο επίπεδα ζουν σε διαφορετικά αρχεία και συνήθως τα συντηρούν διαφορετικοί άνθρωποι.

Και οι δύο πύλες αποτυγχάνουν με το ίδιο errno. Και οι δύο δίνουν EPERM, και οι δύο εμφανίζονται ως γενικό παράπονο αδειών στο αρχείο καταγραφής του δαίμονα, γι' αυτό το μάντεμα για το περιεχόμενο του προφίλ χάνει τόσο χρόνο. Ο κανόνας διαδρομής που επρόκειτο να προσθέσεις ελέγχει την πρόσβαση σε αρχεία αφού πετύχει το chroot· δεν έχει καμία σχέση με το αν η πράξη επιτρέπεται καθόλου.

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

Διάβασε την κατάσταση προνομίων πριν επεξεργαστείς την πολιτική

Ξεκίνα από τη διεργασία που τρέχει και όχι από τη διαμόρφωση που θεωρείς ενεργή. Το αρχείο κατάστασης εκθέτει πέντε μάσκες δυνατοτήτων και τη σημαία no-new-privileges, και απαντούν σε διαφορετικά ερωτήματα:

grep -E '^(CapInh|CapPrm|CapEff|CapBnd|CapAmb|NoNewPrivs):' /proc/$(pidof openvpn)/status
sudo aa-status | grep -i openvpn

Το CapEff είναι το σύνολο που μετράει για την κλήση: μια δυνατότητα παρούσα στο CapBnd αλλά απούσα από το CapEff επιτρέπεται από το ανώτατο όριο και δεν κρατιέται αυτή τη στιγμή. Το CapAmb μετράει όταν το εκτελέσιμο δεν είναι προνομιούχο με τη συνήθη έννοια του setuid, γιατί οι περιβαλλόμενες δυνατότητες είναι ο τρόπος με τον οποίο μια υπηρεσία κρατά προνόμιο μέσω exec χωρίς δυνατότητες αρχείου. Το CapInh κρατά bits που θα μπορούσαν να κληρονομηθούν μέσω exec, αλλά μόνο όταν το κληρονομήσιμο σύνολο του εκτελούμενου αρχείου το επιτρέπει, οπότε ένα bit εκεί δεν δίνει τίποτα από μόνο του.

Έπειτα επιβεβαίωσε ποιο προφίλ είναι πράγματι συνδεδεμένο. Μια διεργασία μπορεί να περιορίζεται από όνομα προφίλ που δεν περίμενες, ειδικά όταν μια διανομή παραδίδει το δικό της αρχείο μονάδας και το προφίλ μαζί, και η ανάγνωση του /proc/PID/attr/current το ξεκαθαρίζει σε μία γραμμή. Το να συγκρίνεις το επεξεργασμένο αρχείο σου με το προφίλ που αναφέρει ο πυρήνας είναι η διαφορά ανάμεσα στη διόρθωση της αιτίας και στην επεξεργασία ενός αρχείου που κανείς δεν φορτώνει.

Πρόσθεσε τον κανόνα δυνατότητας μόνο όταν το λέει το ίχνος ελέγχου

Οι εγγραφές του πυρήνα και του ελέγχου ονομάζουν το επίπεδο που αρνείται. Ψάξε για άρνηση που φέρει την πράξη capable και το όνομα sys_chroot, μαζί με το προφίλ που την παρήγαγε. Αν υπάρχει αυτή η εγγραφή, η διεργασία κρατούσε ήδη τη δυνατότητα και το AppArmor την αρνήθηκε, και η στοχευμένη αλλαγή είναι ένας κανόνας δυνατότητας μέσα στο πραγματικό προφίλ:

capability sys_chroot,

Κράτησέ τον ως τμήμα στο προφίλ που είναι πραγματικά συνδεδεμένο, και έλεγξε τη λέξη-κλειδί με την αναφορά προφίλ apparmor.d αντί να αντιγράψεις κανόνα από άσχετο οδηγό. Αν τον βάλεις σε κοινό include, τον δίνεις σε κάθε περιορισμένο πρόγραμμα που τραβά εκείνο το αρχείο, και η αλλαγή προφίλ σε λειτουργία παραπόνων κρύβει την επόμενη άρνηση αντί να λύσει αυτήν. Ο κανόνας του προφίλ αφαιρεί μια άρνηση πολιτικής· δεν κατασκευάζει προνόμιο, οπότε μια διεργασία που δεν είχε ποτέ CAP_SYS_CHROOT στο ενεργό σύνολό της θα αποτύχει ακόμη και με τον κανόνα στη θέση του.

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

Ο διαχειριστής υπηρεσιών κρατά το δικό του ανώτατο όριο

Το systemd ορίζει σύνολο ορίων δυνατοτήτων για κάθε υπηρεσία, και εξ ορισμού αυτό το σύνολο είναι στενό. Ό,τι εξαιρείται εκεί δεν είναι διαθέσιμο στη διεργασία ό,τι κι αν λέει το προφίλ, γι' αυτό οι σκληρυμένες μονάδες μπορούν να αποτύχουν σε πράξη που δούλευε όταν ο δαίμονας ξεκινούσε με το χέρι. Οι σχετικές οδηγίες διαβάζονται εύκολα στη μονάδα και παραβλέπονται εύκολα όταν η εφαρμογή αναφέρει το σφάλμα. Τα drop-in που παραδίδει ένα πακέτο διανομής είναι η συνήθης πηγή ενός ορίου που κανείς δεν θυμάται ότι διαμόρφωσε.

Το να ανεβάσεις το όριο και να δώσεις τη δυνατότητα είναι δύο αλλαγές, όχι μία. Μια εγγραφή στο σύνολο ορίων λέει ότι η διεργασία επιτρέπεται να κρατά τη δυνατότητα· δεν την προσθέτει στο ενεργό σύνολο, και το NoNewPrivs μπορεί να εμποδίσει μεταγενέστερη ανύψωση να πετύχει ακόμη και όταν όλα τα άλλα ταιριάζουν. Πρόσθεσε μόνο ό,τι χρειάζεται η πράξη, κράτησε την υπόλοιπη σκλήρυνση άθικτη, και αντιστάσου στη συντόμευση να δώσεις όλες τις δυνατότητες για να περάσει μία κλήση.

Η σειρά μετράει κι εδώ. Άλλαξε πρώτα το όριο της μονάδας, επαλήθευσε ότι το CapEff περιέχει τώρα το bit, και μόνο τότε αποφάσισε αν το AppArmor εξακολουθεί να αρνείται. Το να κάνεις και τις δύο αλλαγές μαζί σε αφήνει να μαντεύεις ξανά αν η αποτυχία επιμείνει.

Όταν η άρνηση καθαρίσει, έλεγξε το ίδιο το κελί

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

Βοηθά επίσης να θυμάσαι τι δεν κάνει το chroot. Είναι όριο διαδρομών, όχι sandbox: μια διεργασία που κρατά το προνόμιο μπορεί να δραπετεύσει από αυτό, γι' αυτό η δυνατότητα συνήθως πέφτει μετά την κλήση και γι' αυτό ο περιορισμός πρέπει να μένει γύρω από τον δαίμονα. Το να κρατήσεις το προφίλ AppArmor, το σύνολο ορίων δυνατοτήτων και τους περιορισμούς του συστήματος αρχείων μαζί είναι το νόημα της άσκησης, όχι ένα κατάλοιπο προς αφαίρεση μόλις ξεκινήσει η υπηρεσία.

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

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