Προς το περιεχόμενο

Προτεινόμενες αναρτήσεις

Δημοσ. (επεξεργασμένο)

Καλησπέρα,

Ψάχνω τρόπο να περιορίσω την πρόσβαση ενός προγράμματος python σε έναν συγκεκριμένο κατάλογο - ίσως κάτι σαν chrooting. Πιο συγκεκριμένα θέλω να γράψω έναν tftp server και πρέπει να τον περιορίσω ώστε να μην μπορεί να δει κάτι έξω απ' τον tftp root directory, για λόγους ασφαλείας.

H os.chroot() δυστυχώς δεν μου δουλεύει. Δηλαδή δουλεύει, αλλά σε συνδυασμό με το threading module μου δημιουργεί προβλήματα. Συγκεκριμένα, όταν κάποιο απ' τα ενεργά threads που δημιουργώ τερματιστεί, τότε ολόκληρο το πρόγραμμα κρασάρει με το παρακάτω μήνυμα:

libgcc_s.so.1 must be installed for pthread_cancel to work

Αν δεν χρησιμοποιήσω την os.chroot(), δεν υπάρχει κανένα θέμα όταν τερματίζει κάποιο thread και όλα δουλεύουν as expected.

Υπάρχει κάποιος άλλος "στανταρτ" τρόπος για να κάνω αυτό που θέλω ή θα πρέπει να πάω με κάτι custom που πιθανόν να μην είναι και τόσο ασφαλές;

Ευχαριστώ.

Επεξ/σία από Ilias95
Δημοσ.

Αν είναι linux only τότε σε καλύπτει το systemd σε αυτό με τις directives του στο tftp.service που θα κάνεις.

Δες πως μπορείς μέσω python-systemd να το ενημερώνεις μεσω του systemd-notify.

Αν όμως είναι setuid root που μάλλον χρειάζεται για ένα tftp server ή έχει την CAP_SYS_ADMIN/CAP_DAC_OVERRIDE capability τότε θα μπορεί να ξεφύγει απο το chroot οπότε τζάμπα γράφεις κώδικα.

Aν σε ενδιαφέρει το systemd-mailify αλληλεπιδρά με το systemd-notify c daemon μπορείς να τσεκάρεις το κώδικα. Εσενα θα πρέπει να είναι type=forking εμένα είναι type=notify στο service file.

Ιδανικά αν μπορείς δωσε του CAP_SETUID κα βάλε ένα dedicated χρήστη να τρέχει το server αν είναι εφικτό.

Δημοσ.

Για αρχή linux only είναι καλά, αλλά δεν νομίζω ότι θέλω να εξαρτιέμαι απ' το systemd (θα δημιουργήσω όμως service files όταν έρθει η ώρα).

Τώρα, ο TFTP root είναι κατάλογος που ανήκει στον nobody και έτσι έχει πρόσβαση μέσα ο καθένας (το TFTP δεν προσφέρει κανενός είδους authentication). Οπότε δεν βλέπω κάποιο λόγο να τρέχει τον server ο root, ίσα-ίσα το αντίθετο.

Δεδομένου λοιπόν ότι ο server τρέχει με δικαιώματα απλού χρήστη, έχουμε να φοβόμαστε μόνο για reads (και όχι writes) έξω απ' τον TFTP root (τα οποία φυσικά δεν θέλουμε να επιτρέπουμε).

Edit:
Ψέμματα, έχουμε να φοβόμαστε και για writes σε φακέλους άλλων απλών χρηστών έξω απ' τον tftp root.

Δημοσ.

Ελπίζω να χρησιμοποιείς multiprocessing module για όταν κάνεις το fork και όχι το threading.

Στα reads τι να φοβάσαι θα διαβάζει ότι επιτρέπεται απο το filesystem εφοσον δε κάνεις κάτι μεμπτο εσυ με το κώδικα σου.

Το πιο καλό chroot είναι να γίνεται εγκάτασταση με virtualenv όσον αφορά στη python.

 

Edit:

Να ξέρεις πάντως οτι οτιδήποτε σε λίνουξ είναι μη systemd είναι καταδικάσμενο να έχει προβλήματα ασφαλείας.

Δεν είναι τυχαίο που όλοι απο τους συντηρητικούς της debian, τη canonical όλοι μα όλοι πήγαν σε systemd. Mείναν μόνο οι δεινόσαυροι του gentoo, slackware να τρέχουν κάτι απολιθώματα σαν σύγχρόνους service managers. Σίγουρα το systemd δεν είναι η λύση όλων των προβλημάτων ασφαλείας, είναι fragmented, μπορει να είναι και buggy, αλλά σίγουρα είναι μπρόστα απο οιτδήποτε άλλο σα service manager. Αυτα σα μια παρένθεση.

  • Like 1
Δημοσ.

Ελπίζω να χρησιμοποιείς multiprocessing module για όταν κάνεις το fork και όχι το threading.

Κάνω κάτι σαν αυτό: http://stackoverflow.com/questions/17453212/multi-threaded-tcp-server-in-python

Τρέχω κάθε νέα μεταφορά αρχείου σε νέο thread χρησιμοποιώντας το threading.Thread. Δεν κάνω fork ολόκληρη την διεργασία.

 

Δεν έχω ασχοληθεί ιδιαίτερα με multithreading στην python είναι η αλήθεια, υπάρχει θέμα με το threading module;

 

Στα reads τι να φοβάσαι θα διαβάζει ότι επιτρέπεται απο το filesystem εφοσον δε κάνεις κάτι μεμπτο εσυ με το κώδικα σου.

Το filesystem θα μου επιτρέπει να διαβάσω και τα αρχεία του Μπάμπη του Σουγιά που θα βρίσκονται στο /home/babis. Το θέμα είναι ότι δεν θέλω να υπάρχει περίπτωση να επιστρέψει ο server κάτι εκτός του tftp root directory ο οποίος θα είναι πχ. ο /srv/tftp/ (πόσο μάλλον να γράψει !).

Δημοσ.

Η μεταφορα αρχείων ναι θα γίνεται απο ένα νεό thread/κλάσση π.χ transferFiles(threading.Thread) αν θες όμως σωστό setuid ο server αφου διαβάσει το config του δημιουργεί ένα ίδιο process (fork) μέσω του multiprocessing module. Δες το systemd-mailify είναι λίγο χύμα ο κώδικας αλλά οκ.

 

Ένα σύγχρονο πρόγραμμα/server οφείλει αν μη τι άλλο να εκμεταλλεύεται όλους τους πυρήνες.


Πρέπει να υπάρχει ικανοποιητικό επίπεδο αφαίρεσης στη λογική του προγράμματος σου ώστε το forked setuid nobody process να έχει ικανοποιητικό επίπεδο ασφάλειας και παράλληλα να μπορεί να κάνει τη δουλειά χωρίς root. Λίγο δύσκολο οκ αλλά γίνεται με multiprocessing (όσα processes θες) που θα εκτελούν threads που θα μεταφέρουν αρχεία.

Δημοσ.

Από τη στιγμή που οι μεταφορές αρχείων θα είναι σίγουρα i/o bound δε βλέπω γιατί να χρειάζονται πολλά processes/threads. Σε linux απλά κανονίζεις να μην έχεις blocking operations.

Δημοσ.

Από τη στιγμή που οι μεταφορές αρχείων θα είναι σίγουρα i/o bound δε βλέπω γιατί να χρειάζονται πολλά processes/threads.

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

 

Δεν μπορεί το I/O να γίνεται "παράλληλα"; Να διαβάζω και να στέλνω δηλαδή πακέτα δεδομένων σε δύο hosts ταυτόχρονα; Τα αρχεία btw, διαβάζονται/γράφονται 512 bytes at a time.

Δημοσ.

Στο πλαίσιο της αφαιρεσης που συζητάγαμε έγινε update στο mailify. Η λογική είναι έχεις μια κλάσση system functions και κάνεις τα μαγικά σου σα root, ok έχεις και μια helper class να διαβάζει config κτλ και τέλος όταν κάνεις το setuid δημιουργείς με multiprocessing ένα νεο fork του κύρίως σερβερ. Τσέκαρε κώδικα αν θες. Η κλασση  mailer είναι το αντιστοιχο δικό σου thread που θα μεταφέρει αρχεία.

Hope it helps :-)

Δημοσ.

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

 

Γενικά μιλώντας το σκεπτικό είναι ότι το network io δεν έχει ιδιαίτερες απαιτήσεις από τη CPU γιατί το ποσοστό του πραγματικού χρόνου που χρειάζεται να δουλέψει είναι πολύ μικρό και το υπόλοιπο είναι καθυστερήσεις δικτύου. Οπότε μπορείς με μία CPU να εξυπηρετήσεις πολύ μεγάλο αριθμό clients, οπότε πρακτικά το σημαντικότερο πράγμα για το throughput που θα έχεις είναι το overhead για το όποιο "context switch" κάνει η εφαρμογή για να περάσει από έναν client στον άλλο και όχι το CPU utilization (που μπορεί να είναι μεγάλο απλά γιατί είσαι inefficient χωρίς να κάνεις πολλή δουλειά).

 

Βασικά δηλαδή, μήπως κινείσαι προς λάθος κατεύθυνση; Γιατί να έχεις απαραίτητα έστω και έναν worker/core από τη στιγμή που ένας μόνο worker μπορεί να εξυπηρετήσει μεγάλο αριθμό clients (κλασικό παράδειγμα nginx)?

 

Δεν είναι ξεκάθαρο από το post σου πώς το σκέφτεσαι βέβαια, ίσως έβγαλα λάθος συμπέρασμα.

Δημοσ.

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

 

Δεν μπορεί το I/O να γίνεται "παράλληλα"; Να διαβάζω και να στέλνω δηλαδή πακέτα δεδομένων σε δύο hosts ταυτόχρονα; Τα αρχεία btw, διαβάζονται/γράφονται 512 bytes at a time.

 

 

Τα io ειναι αργα. Για αυτο ολα τα λειτουργικα εχουν δυο version. Την blocking και την non-blocking. Οι blocking ειναι αυτες που ξερεις, βαζεις ενα read και το thread σταματαει μεχρει να εκτελεστει το read. Ε οκ, υπαρχουν και οι non-blocking. Βαζεις ενα read και λες να σε ενημερωσει οταν εκτελεστει, εδω το thread δεν σταματαει. 

 

Εσυ θελεις να βαλεις thread για να μην σταματαει το main thread σου η read. Ενω ηδη υπαρχει μια read η οποια δεν θα σταματισει το main thread σου. Αρα εφοσον υπαρχει μια read η οποια δεν σταματαει το thread σου, για ποιο λογο θες αλλο thread;

Δημοσ.

@defacer
Τέλεια, να πω ότι πάλι δεν είμαι σίγουρος ότι κατάλαβα. :P Να πω επίσης ότι αυτή τη στιγμή ακόμα δεν έχω μελετήσει πως δουλεύουν οποιουδήποτε είδους servers γενικά και ειδικά πως χειρίζονται πολλαπλές αιτήσεις / πελάτες.

Καταλαβαίνω ότι το πρόγραμμα δεν θα είναι CPU-bound. Συμφωνώ επίσης ότι με μία CPU (και μία μόνο διεργασία) μπορώ να εξυπηρετήσω πολύ μεγάλο αριθμό clients. Το overhead του context switch δεν ξέρω πως μπορώ να το υπολογίσω. Για την ακρίβεια δεν ξέρω αν θεωρείται context switching αυτό που κάνω.

Και επειδή κάτι μου λέει ότι ίσως μιλάμε για διαφορετικά πράγματα, θα γίνω πιο συγκεκριμένος και θα περιγράψω τι ακριβώς προσπαθώ να υλοποιήσω και γιατί πιστεύω ότι χρειάζομαι πολλά threads (και όχι πολλές διεργασίες).

Το TFTP δουλεύει πάνω από UDP και η φάση με την μεταφορά αρχείων πάει κάπως έτσι: c: r/w request, s: ack, c: 1st block of data, s: 1st ack, c: 2nd block of data, s 2nd ack, κτλ...

Για κάθε μεταφορά αρχείου πρέπει να ανοίγω ένα νέο udp socket το οποίο χρησιμοποιείται αποκλειστικά γι' αυτό το σκοπό. Οπότε δεδομένου ότι το να περιμένω να φτάσουν νέα δεδομένα στο socket είναι blocking operation, ο μόνος τρόπος που μπορώ να σκεφτώ για να γίνονται μεταφορές αρχείων παράλληλα είναι να τρέχω κάθε μεταφορά σε ξεχωριστό thread.

Τώρα για ξεχωριστά processes και forks δεν έκανα λόγο εγώ αλλά ο mad-proffessor.

Edit:
@παπι
Νομίζω ότι απαντάω σε αυτό που ρωτάς.
Θέλω νέα threads γιατί θέλω να ακούω ταυτόχρονα σε πολλά sockets και όχι για το I/O.

Edit2:
Και επειδή ίσως δεν έγινε σαφές απ' τα παραπάνω, πρέπει να ανοίγω νέα sockets γιατί έτσι δουλεύει το TFTP. Ο client αρχικά στέλνει ένα request στο main socket (port 69). Μετά για κάθε μεταφορά πρέπει να δημιουργώ μια νέα "σύνδεση" με unique TID (transfer ID), δηλαδή port και να ενημερώνω τον πελάτη πιο είναι το TID. Άρα πέρα απ' το αρχικό request του client στο main socket, όλη η υπόλοιπη επικοινωνία για την συγκεκριμένη μεταφορά πρέπει να γίνει στο νέο socket.

Δημοσ.

Για την ακρίβεια δεν ξέρω αν θεωρείται context switching αυτό που κάνω.

"Context switching" σε εισαγωγικά, aka "το σύνολο των CPU instructions που πρέπει να εκτελεστούν από τη στιγμή που θα τελειώσεις προσωρινά να εξυπηρετείς έναν client μέχρι τη στιγμή που θα είσαι έτοιμος να ξεκινήσεις να εξυπηρετείς τον επόμενο".

 

Για κάθε μεταφορά αρχείου πρέπει να ανοίγω ένα νέο udp socket το οποίο χρησιμοποιείται αποκλειστικά γι' αυτό το σκοπό. Οπότε δεδομένου ότι το να περιμένω να φτάσουν νέα δεδομένα στο socket είναι blocking operation, ο μόνος τρόπος που μπορώ να σκεφτώ για να γίνονται μεταφορές αρχείων παράλληλα είναι να τρέχω κάθε μεταφορά σε ξεχωριστό thread.

Εδώ είναι το θέμα, δεν είναι απαραίτητα blocking operation per socket. Θα κάνεις select σε ένα μάτσο sockets μαζί. Ακόμα και single threaded με ένα loop select/process/repeat μπορείς να εξυπηρετήσεις πολύ κόσμο. Και καθόλου τυχαία έτσι έχεις κάνει και πολλή δουλειά για την ελαχιστοποίηση του κόστους του "context switch".

Δημοσ.

 

Edit:

@παπι

Νομίζω ότι απαντάω σε αυτό που ρωτάς.

Θέλω νέα threads γιατί θέλω να ακούω ταυτόχρονα σε πολλά sockets και όχι για το I/O.

 

 

Και τα σοκετ τι ειναι; IO δεν ειναι;

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...