Πώς να βρείτε ποιο sandbox του systemd απέρριψε το hook OpenVPN σας
Το hook λειτουργεί ως root και αποτυγχάνει κάτω από το systemd, επειδή η unit διατηρεί λιγότερες δυνατότητες, μικρότερο /dev και διαδρομές μόνο ανάγνωσης.
Το hook λειτουργεί όταν το εκτελείτε με το χέρι ως root και επιστρέφει Operation not permitted τη στιγμή που η υπηρεσία το ξεκινά. Το αρχείο δεν άλλαξε ανάμεσα σε αυτές τις εκτελέσεις· το περιβάλλον άλλαξε. Το systemd δίνει στη διεργασία έναν μικρότερο κόσμο: λιγότερες δυνατότητες, ένα διαφορετικό /dev, διαδρομές συστήματος αρχείων μόνο για ανάγνωση και ένα όριο εργασιών. Διαβάστε την αποτυχία ως ένα όριο που απέρριψε μία συγκεκριμένη πράξη, όχι ως ένα σενάριο που χάλασε.
Ξεκινήστε από την πράξη που απορρίφθηκε
Μια γραμμή αρχείου καταγραφής που λέει ότι η unit απέτυχε δεν σας λέει σχεδόν τίποτα. Η χρήσιμη γραμμή ονομάζει την κλήση που απορρίφθηκε: έναν κόμβο συσκευής που δεν άνοιξε, έναν κατάλογο όπου δεν γράφτηκε, έναν χώρο ονομάτων που δεν δημιουργήθηκε. Βρείτε αυτή την κλήση πριν αγγίξετε έστω μία οδηγία, γιατί η καθεμία αντιστοιχεί σε διαφορετικό περιορισμό. Έπειτα διαβάστε την unit που πράγματι εκτελέστηκε, μαζί με τα drop-in, αντί για το αρχείο που παρέδωσε το πακέτο.
Το ερώτημα παρακάτω εκτυπώνει τη διαδρομή του τμήματος, τους καταλόγους drop-in και τις ιδιότητες που μετρούν για μια απόρριψη συσκευής ή συστήματος αρχείων. Διαβάστε τις όλες πριν αποφασίσετε ποιο όριο είναι σχετικό:
systemctl show "[email protected]" -p FragmentPath -p DropInPaths -p PrivateDevices -p DevicePolicy -p DeviceAllow -p ProtectHome -p ProtectSystem -p TasksMax
journalctl -u "[email protected]" -b --no-pager | grep -iE 'operation not permitted|permission denied|cannot open|failed'Τίποτα σε αυτό το μπλοκ δεν γράφει. Αν η απορριφθείσα κλήση λείπει από το ημερολόγιο, ανεβάστε τη λεπτομέρεια του πελάτη για μία εκτέλεση αντί να μαντεύετε οδηγίες. Μια διεργασία που τρέχει αποδεικνύει ότι η unit ξεκίνησε, όχι ότι το hook έτρεξε με τα προνόμια που χρειαζόταν.
Ένας χώρος ονομάτων χρειάζεται δικαιώματα προσάρτησης που μια συσκευή TUN δεν χρησιμοποιεί ποτέ
Δύο πράξεις που μοιάζουν ίδιες μέσα σε ένα hook έχουν διαφορετικά προφίλ αδειών. Το άνοιγμα του /dev/net/tun και η δημιουργία της διεπαφής χρειάζεται διαχείριση δικτύου συν πρόσβαση σε αυτόν τον κόμβο. Η δημιουργία ενός ονομασμένου χώρου ονομάτων δικτύου χρειάζεται επίσης εργασία προσάρτησης, γιατί το εργαλείο χώρων ονομάτων προσαρτά μια νέα /sys όψη και δένει έναν κατάλογο κατάστασης στη θέση του. unit πελάτη από το upstream αφήνει σκόπιμα τη διαχείριση προσάρτησης έξω από το σύνολο ορίων της, γι' αυτό το βήμα του χώρου ονομάτων απορρίπτεται ενώ η ίδια η σήραγγα θα λειτουργούσε.
Γι' αυτό η παραχώρηση του κόμβου συσκευής δεν σώζει το σενάριο. Μια άδεια συσκευής απαντά σε μία ερώτηση, αν αυτή η διεργασία μπορεί να ανοίξει αυτόν τον κόμβο, και η ερώτηση του χώρου ονομάτων είναι άλλη. Αντιστοιχίστε την αποτυχημένη κλήση με τη δυνατότητά της πριν διευρύνετε οτιδήποτε.
Ένα τεστ στο κέλυφός σας κληρονομεί κάθε δυνατότητα που έχει ένας root χωρίς περιορισμούς, και η unit κληρονομεί ό,τι κράτησε το σύνολο ορίων της. Οι δύο εκτελέσεις είναι διαφορετικά πειράματα ακόμη και όταν το δυαδικό είναι ίδιο.
Συσκευές, προστατευμένες διαδρομές και όρια εργασιών
Όταν η απορριφθείσα πράξη είναι γνωστή, τρεις ομάδες οδηγιών εξηγούν τις περισσότερες αποτυχίες. Την πρόσβαση σε συσκευές τη ρυθμίζει PrivateDevices, που αντικαθιστά /dev με ένα ελάχιστο σύνολο ψευδοσυσκευών, και DevicePolicy με DeviceAllow, που αποφασίζουν τι επιτρέπει το cgroup. /dev/net/tun δεν είναι μέρος αυτού του ελάχιστου συνόλου, άρα μια unit με PrivateDevices=yes δεν μπορεί να το ανοίξει ό,τι κι αν λέει η πολιτική συσκευών.
Η προστασία του συστήματος αρχείων απαντά σε δεύτερη ερώτηση. ProtectHome=yes κάνει /home, /root και /run/user μη προσπελάσιμους, και ProtectSystem=full κάνει /usr, /boot και /etc μόνο για ανάγνωση. Γι' αυτό ένα hook που γράφει δίπλα στο δικό του αρχείο ρυθμίσεων λειτουργεί στο κέλυφός σας και αποτυγχάνει κάτω από την unit, με το ίδιο σχήμα άδειας όπως μια δυνατότητα που λείπει. Μετακινήστε την εγγράψιμη είσοδο σε μια διαδρομή που επιτρέπει η πολιτική, ή προσθέστε μια στενή ReadWritePaths= καταχώριση.
Τα όρια πόρων είναι η τρίτη ομάδα, και η λιγότερο προφανής. TasksMax= περιορίζει πόσες εργασίες επιτρέπεται να δημιουργήσει η unit, και ένα όριο περιγραφέων αρχείων συμπεριφέρεται το ίδιο για sockets και λαβές. Μια εγκατάσταση που ξεπέρασε το όριο αποτυγχάνει με σφάλμα πόρων αντί για σφάλμα άδειας, και γι' αυτό το ακριβές μήνυμα μετράει ακόμη.
Βγάλτε το βήμα του χώρου ονομάτων έξω από το hook
Η προετοιμασία που χρειάζεται ένα προνόμιο που ο πελάτης δεν πρέπει να κρατά ανήκει στη δική της unit. Ένα oneshot που τρέχει πριν από τον πελάτη, κρατά το αποτέλεσμά του και είναι διατεταγμένο πριν από τη σύνδεση δίνει στο βήμα του χώρου ονομάτων ό,τι χρειάζεται χωρίς να παραδώσει αυτά τα δικαιώματα στη διεργασία της σήραγγας. Έπειτα ο πελάτης αναφέρεται σε εκείνη την unit αντί να καλεί το σενάριο από το hook up του.
Το πρότυπο παρακάτω είναι αρχικό σχήμα, όχι κάτι που εφαρμόζεται αδιάβαστο. Κρατά την προετοιμασία εκεί όπου ζει το προνόμιο και αφήνει την unit της σήραγγας ανέγγιχτη:
# /etc/systemd/system/[email protected]
[Unit]
Description=Namespace preparation for %i
Before=openvpn-client@%i.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/netns-up.sh %i
CapabilityBoundingSet=CAP_NET_ADMIN CAP_SYS_ADMIN
[Install]
WantedBy=multi-user.targetΠροσθέστε τη σειρά με ένα drop-in αντί να επεξεργαστείτε τη συσκευασμένη unit, ώστε μια αναβάθμιση να μην μπορεί να ρίξει σιωπηλά την αλλαγή σας. Ο κατάλογος είναι κλειδωμένος στο όνομα της παρουσίας, άρα ένα δεύτερο προφίλ χρειάζεται το δικό του αρχείο:
sudo mkdir -p /etc/systemd/system/[email protected]
sudo tee /etc/systemd/system/[email protected]/10-namespace.conf >/dev/null <<'CONF'
[Unit]
[email protected]
[email protected]
CONF
sudo systemctl daemon-reloadΕπαναλάβετε την ίδια κλήση μετά την αλλαγή
Μια επισκευή επιβεβαιώνεται μόνο όταν η ίδια κλήση δίνει διαφορετικό αποτέλεσμα. Επανεκκινήστε και τις δύο unit, εκτελέστε την πράξη που απέτυχε και συγκρίνετε το αποτέλεσμά της με τη γραμμή ημερολογίου που κρατήσατε πριν από την αλλαγή. Μια unit που ξεκινά ενώ το hook δεν ξανατρέχει ποτέ δεν είναι επισκευή.
Η ακολουθία παρακάτω κρατά τη σύγκριση έντιμη, γιατί ελέγχει την απορριφθείσα κλήση και όχι το χρώμα της unit. Κρατήστε την έξοδό της δίπλα στη γραμμή ημερολογίου που πιάσατε πριν από την αλλαγή:
sudo systemctl restart "[email protected]" "[email protected]"
systemctl status "[email protected]" --no-pager
journalctl -u "[email protected]" -b --no-pager | grep -c 'Operation not permitted'
ip -brief addr show tun0Μετρήστε τις απορρίψεις αντί να διαβάσετε την τελευταία γραμμή, γιατί ένα hook μπορεί να αποτύχει, να ξαναπροσπαθήσει και να πετύχει, και μόνο το πλήθος δείχνει αν μετακινήθηκε το όριο. Αφήστε ένα σχόλιο στο drop-in που ονομάζει την οδηγία που χαλαρώσατε και τον λόγο, αφού η επόμενη αναβάθμιση συγκρίνει τη ρύθμισή σας με την προεπιλογή του πακέτου. Μια unit που ξεκινά μόνο αφού απενεργοποιηθεί κάθε προστασία δεν έχει διορθωθεί.