Git
Chapters ▾ 2nd Edition

7.14 Εργαλεία του Git - Αποθήκευση διαπιστευτηρίων

Αποθήκευση διαπιστευτηρίων

Εάν χρησιμοποιούμε τη μεταφορά μέσω SSH για τη σύνδεση με απομακρυσμένα αποθετήρια, είναι πιθανό να έχουμε ένα κλειδί χωρίς κωδική φράση, η οποία μας επιτρέπει να μεταφέρουμε δεδομένα με ασφάλεια χωρίς να πληκτρολογούμε το όνομα χρήστη και τον κωδικό πρόσβασής μας. Ωστόσο, αυτό δεν είναι δυνατό με τα πρωτόκολλα HTTP —κάθε σύνδεση χρειάζεται ένα όνομα χρήστη και έναν κωδικό πρόσβασης. Αυτό γίνεται ακόμα πιο δύσκολο για συστήματα με ταυτοποίηση δύο παραγόντων, όπου το διακριτικό (token) που χρησιμοποιούμε για έναν κωδικό πρόσβασης παράγεται τυχαία και δεν μπορεί να ωθηθεί.

Ευτυχώς, το Git διαθέτει ένα σύστημα διαπιστευτηρίων που μπορεί να μας βοηθήσει σε αυτό το πρόβλημα με περισσότερες από μία επιλογές:

  • Η προεπιλογή είναι να μην αποθηκεύει καθόλου διαπιστευτήρια.   Κάθε σύνδεση θα μας ζητάει το όνομα χρήστη και τον κωδικό πρόσβασής μας.

  • Κατά τη λειτουργία cache διατηρεί τα διαπιστευτήρια στη μνήμη για ορισμένο χρονικό διάστημα.   Κανένας από τους κωδικούς πρόσβασης δεν αποθηκεύεται ποτέ στον δίσκο και οι κωδικοί πρόσβασης διαγράφονται από την προσωρινή μνήμη μετά από 15 λεπτά.

  • Κατά τη λειτουργία store τα διαπιστευτήρια αποθηκεύονται σε ένα αρχείο απλού κειμένου στον δίσκο και δεν εκπνέουν ποτέ.   Αυτό σημαίνει ότι μέχρι να αλλάξουμε τον κωδικό πρόσβασης για τον κεντρικό υπολογιστή Git, δεν θα χρειαστεί ποτέ να πληκτρολογήσουμε ξανά τα διαπιστευτήριά μας.   Το μειονέκτημα αυτής της προσέγγισης είναι ότι οι κωδικοί πρόσβασης αποθηκεύονται σε αρχείο απλού κειμένου στον προσωπικό μας κατάλογο.

  • Εάν χρησιμοποιούμε Mac, το Git έρχεται με τη λειτουργία osxkeychain, η οποία αποθηκεύει τα διαπιστευτήρια στην ασφαλή κλειδοθήκη (keychain) που είναι συνδεδεμένη με το λογαριασμό μας στο σύστημα.   Αυτή η μέθοδος αποθηκεύει τα διαπιστευτήριά μας στον δίσκο και δεν λήγει ποτέ, αλλά είναι κρυπτογραφημένα με το ίδιο σύστημα που αποθηκεύει τα πιστοποιητικά HTTPS και την αυτόματη συμπλήρωση του Safari.

  • Εάν χρησιμοποιούμε Windows, μπορούμε να εγκαταστήσουμε έναν βοηθό που ονομάζεται winstore.   Είναι παρόμοιος με τον βοηθό osxkeychain που περιγράφεται παραπάνω, αλλά χρησιμοποιεί το Windows Credential Store (Χώρος Αποθήκευσης Διαπιστευτηρίων) για τον έλεγχο ευαίσθητων πληροφοριών.   Διατίθεται στη διεύθυνση https://gitcredentialstore.codeplex.com.

Μπορούμε να επιλέξουμε μία από αυτές τις μεθόδους ρυθμίζοντας την τιμή μίας μεταβλητής διαμόρφωσης του Git:

$ git config --global credential.helper cache

Μερικοί από αυτούς τους βοηθούς έχουν επιλογές. Ο βοηθός store μπορεί να πάρει ένα όρισμα --file <διαδρομή>, το οποίο καθορίζει πού θα αποθηκεύεται το αρχείο απλού κειμένου (η προεπιλογή είναι το ~/.git-credentials). Ο βοηθός cache δέχεται την επιλογή --timeout <δευτερόλεπτα> ', η οποία αλλάζει το χρονικό διάστημα για το οποίο ο δαίμονάς του συνεχίζει να τρέχει (η προεπιλογή είναι `900, δηλαδή 15 λεπτά). Ακολουθεί ένα παράδειγμα του πώς θα ρυθμίζουμε τον βοηθό store με ένα όνομα προσαρμοσμένου αρχείου:

$ git config --global credential.helper store --file ~/.my-credentials

Το Git ακόμη μας επιτρέπει να ρυθμίσουμε διάφορους βοηθούςς. Όταν αναζητάμε διαπιστευτήρια για έναν συγκεκριμένο κεντρικό υπολογιστή, το Git θα τα εξετάζει με τη σειρά και θα σταματήσει μετά την πρώτη απάντηση. Κατά την αποθήκευση διαπιστευτηρίων, το Git θα στείλει το όνομα χρήστη και τον κωδικό πρόσβασης σε όλους τους βοηθούς της λίστας και αυτοί μπορούν να επιλέξουν τι να τους κάνουν. Ακολουθεί ένα παράδειγμα του πώς θα έμοιαζε το .gitconfig αν είχαμε ένα αρχείο διαπιστευτηρίων σε ένα USB stick, αλλά θέλαμε να χρησιμοποιήσουμε την προσωρινή μνήμη για να γλιτώσουμε λίγη πληκτρολόγηση εάν η μονάδα δεν είναι συνδεδεμένη:

[credential]
    helper = store --file /mnt/thumbdrive/.git-credentials
    helper = cache --timeout 30000

Πώς λειτουργεί

Πώς λειτουργεί όλο αυτό; Η βασική εντολή του Git για το σύστημα βοηθών διαπιστευτηρίων είναι η git credential, η οποία παίρνει μια εντολή ως όρισμα και άλλες εισόδους από το stdin.

Αυτό είναι ίσως ευκολότερο να κατανοηθεί με ένα παράδειγμα. Ας υποθέσουμε ότι ένας βοηθός διαπιστευτηρίων έχει ρυθμιστεί και ο βοηθός έχει αποθηκεύσει τα διαπιστευτήρια για τον διακομιστή mygithost. Ακολουθεί μια συνεδρία που χρησιμοποιεί την εντολή fill, η οποία ενεργοποιείται όταν το Git προσπαθεί να βρει διαπιστευτήρια για έναν διακομιστή:

$ git credential fill  (1)
protocol=https         (2)
host=mygithost
                       (3)
protocol=https         (4)
host=mygithost
username=bob
password=s3cre7
$ git credential fill  (5)
protocol=https
host=unknownhost

Username for 'https://unknownhost': bob
Password for 'https://bob@unknownhost':
protocol=https
host=unknownhost
username=bob
password=s3cre7
  1. Αυτή είναι η γραμμή εντολών που ενεργοποιεί την αλληλεπίδραση.

  2. Η git credential στη συνέχεια αναμένει είσοδο από το stdin.     Το παρέχουμε με αυτά που γνωρίζουμε: το πρωτόκολλο και το όνομα του κεντρικού υπολογιστή.

  3. Μια κενή γραμμή υποδεικνύει ότι η είσοδος είναι πλήρης και το σύστημα διαπιστευτηρίων πρέπει να απαντήσει με αυτό που γνωρίζει.

  4. Η git credential γράφει στο stdout τις πληροφορίες που βρήκε.

  5. Εάν δεν εντοπιστούν διαπιστευτήρια, το Git ζητάει από τον χρήστη το όνομα χρήστη και τον κωδικό πρόσβασης και τα επαναφέρει στην κλήση του stdout (εδώ είναι συνδεδεμένα στην ίδια κονσόλα).

Το σύστημα διαπιστευτηρίων στην πραγματικότητα καλεί ένα πρόγραμμα που είναι ξεχωριστό από το ίδιο το Git· ποιο πρόγραμμα και πώς το καλεί εξαρτάται από την τιμή της μεταβλητής ρύθμισης credential.helper. Μπορεί να πάρει διάφορες μορφές:

Configuration Value Behavior

foo

Runs git-credential-foo

foo -a --opt=bcd

Runs git-credential-foo -a --opt=bcd

/absolute/path/foo -xyz

Runs /absolute/path/foo -xyz

!f() { echo "password=s3cre7"; }; f

Code after ! evaluated in shell

Έτσι οι βοηθοί που περιγράφηκαν παραπάνω στην πραγματικότητα ονομάζονται git-credential-cache, git-credential-store κ.ο.κ. και μπορούμε να τους ρυθμίσουμε ώστε να λάβουν ορίσματα από τη γραμμή εντολών. Η γενική μορφή είναι git-credential-foo [args] <ενέργεια>. Το πρωτόκολλο stdin/stdout είναι το ίδιο με αυτό της git-credential, αλλά χρησιμοποιούν ένα ελαφρώς διαφορετικό σύνολο ενεργειών:

  • get είναι ένα αίτημα για ένα ζεύγος ονόματος χρήστη/κωδικού πρόσβασης.

  • store είναι ένα αίτημα για να αποθηκεύσουμε ένα σύνολο διαπιστευτηρίων στη μνήμη αυτού του βοηθού.

  • erase είναι αίτημα διαγραφής των διαπιστευτηρίων για τις δοσμένες ιδιότητες από τη μνήμη αυτού του βοηθού.

Για τις ενέργειες store και delete, δεν απαιτείται απάντηση (το Git τις αγνοεί ούτως ή άλλως). Για την ενέργεια get, ωστόσο, το Git ενδιαφέρεται πολύ για το τι έχει να πει ο βοηθός. Αν ο βοηθός δεν γνωρίζει τίποτα χρήσιμο, μπορεί απλά να τερματίσει χωρίς έξοδο, αλλά αν γνωρίζει, θα πρέπει να αυξήσει τις παρεχόμενες πληροφορίες με τις πληροφορίες που έχει αποθηκεύσει. Η έξοδος αντιμετωπίζεται σαν μια ακολουθία δηλώσεων εκχώρησης· ο,τιδήποτε παρέχεται θα αντικαταστήσει αυτό που ήδη γνωρίζει το Git.

Ακολουθεί το ίδιο παράδειγμα όπως προηγουμένως, αλλά παρακάμπτοντας την git-credential και πηγαίνοντας κατευθείαν στην git-credential-store:

$ git credential-store --file ~/git.store store  (1)
protocol=https
host=mygithost
username=bob
password=s3cre7
$ git credential-store --file ~/git.store get    (2)
protocol=https
host=mygithost

username=bob                                     (3)
password=s3cre7
  1. Εδώ λέμε στην git-credential-store να αποθηκεύσει κάποια διαπιστευτήρια: το όνομα χρήστη bob και ο κωδικός πρόσβασης s3cre7 πρέπει να χρησιμοποιούνται όταν προσπελασζουμε τη διεύθυνση https://mygithost.

  2. Τώρα ανακτούμε αυτά τα διαπιστευτήρια.     Παρέχουμε τα τμήματα της σύνδεσης που ήδη γνωρίζουμε (https://mygithost) και μια κενή γραμμή.

  3. Η git-credential-store απαντά με το όνομα χρήστη και τον κωδικό πρόσβασης που αποθηκεύσαμε παραπάνω.

Το αρχείο ~/git.store τώρα μοιάζει με αυτό:

https://bob:s3cre7@mygithost

Είναι μόνο για μια σειρά γραμμών, καθεμία από τις οποίες περιέχει μια διεύθυνση URL διακοσμημένη με διαπιστευτήρια. Οι βοηθοί osxkeychain και winstore χρησιμοποιούν την εγγενή μορφή των καταστημάτων υποστήριξης, ενώ η cache χρησιμοποιεί τη δική της μορφή μνήμης (η οποία δεν μπορεί να διαβάσει καμία άλλη διαδικασία).

Μία εξατομικευμένη προσωρινή μνήμη διαπιστευτηρίων

Δεδομένου ότι η git-credential-store και οι φίλοι της είναι προγράμματα ξεχωριστά από το Git, δεν είναι και μεγάλο διανοητικό άλμα να συνειδητοποιήσουμε ότι οποιοδήποτε πρόγραμμα μπορεί να είναι βοηθός διαπιστευτηρίων Git. Οι βοηθοί που παρέχονται από το Git καλύπτουν πολλές κοινές περιπτώσεις χρήσης αλλά όχι όλες. Για παράδειγμα, ας υποθέσουμε ότι η ομάδα μας έχει ορισμένα διαπιστευτήρια που είναι κοινά σε ολόκληρη την ομάδα, ίσως για ανάπτυξη. Αυτά αποθηκεύονται σε έναν κοινόχρηστο κατάλογο, αλλά δεν θέλουμε να τα αντιγράψουμε στο δικό μας κατάστημα διαπιστευτηρίων, επειδή αλλάζουν συχνά. Κανένας από τους υπάρχοντες βοηθούς δεν καλύπτει αυτήν την περίπτωση· ας δούμε τι θα χρειαζόταν για να γράψουμε το δικό μας. Υπάρχουν πολλά χαρακτηριστικά-κλειδά που πρέπει να έχει ένα τέτοιο πρόγραμμα:

  1. Η μόνη ενέργεια την οποία πρέπει να προσέξουμε πολύ είναι η get· Οι store και erase είναι λειτουργίες εγγραφής, επομένως απλά θα τερματίσουμε χωρίς έξοδο όταν τις λάβουμε.

  2. Η μορφή αρχείου του αρχείου κοινόχρηστων διαπιστευτηρίων είναι ίδια με αυτήν που χρησιμοποιείται από την git-credential-store.

  3. Η τοποθεσία αυτού του αρχείου είναι αρκετά τυπική, αλλά θα πρέπει να δώσουμε τη δυνατότητα στον χρήστη να δίνει άλλη διαδρομή στην περίπτωση που το θέλει.

Επαναλαμβάνουμε ότι θα γράψουμε αυτήν την επέκταση σε Ruby, αλλά οποιαδήποτε γλώσσα θα λειτουργήσει εφόσον το Git μπορεί να εκτελέσει το τελικό προϊόν. Εδώ είναι ο πλήρης πηγαίος κώδικας του νέου μας βοηθού διαπιστευτηρίων:

#!/usr/bin/env ruby

require 'optparse'

path = File.expand_path '~/.git-credentials' (1)
OptionParser.new do |opts|
    opts.banner = 'USAGE: git-credential-read-only [options] <action>'
    opts.on('-f', '--file PATH', 'Specify path for backing store') do |argpath|
        path = File.expand_path argpath
    end
end.parse!

exit(0) unless ARGV[0].downcase == 'get' (2)
exit(0) unless File.exists? path

known = {} (3)
while line = STDIN.gets
    break if line.strip == ''
    k,v = line.strip.split '=', 2
    known[k] = v
end

File.readlines(path).each do |fileline| (4)
    prot,user,pass,host = fileline.scan(/^(.*?):\/\/(.*?):(.*?)@(.*)$/).first
    if prot == known['protocol'] and host == known['host'] then
        puts "protocol=#{prot}"
        puts "host=#{host}"
        puts "username=#{user}"
        puts "password=#{pass}"
        exit(0)
    end
end
  1. Εδώ αναλύουμε τις επιλογές της γραμμής εντολών, επιτρέποντας στον χρήστη να καθορίσει το αρχείο εισόδου. Η προεπιλογή είναι ~/.git-credentials.

  2. Αυτό το πρόγραμμα αποκρίνεται μόνον εάν η ενέργεια είναι get και το αρχείο backing-store υπάρχει.

  3. Αυτός ο βρόχος διαβάζει από την stdin μέχρι να συναντήσει την πρώτη κενή γραμμή.     Οι είσοδοι αποθηκεύονται στο πίνακα αναζήτηση known για μεταγενέστερη αναφορά.

  4. Αυτός ο βρόχος διαβάζει τα περιεχόμενα του αρχείου αποθήκευσης αναζητώντας αντιστοιχίες.     Εάν το πρωτόκολλο και ο κεντρικός υπολογιστής από τη μεταβλητή known ταιριάζουν με αυτήν τη γραμμή, το πρόγραμμα εκτυπώνει τα αποτελέσματα στην stdout και τερματίζει.

Αποθηκεύουμε τον βοηθό μας ως git-credential-read-only, τον βάζουμε κάπου στο PATH μας και τον επισημόνουμε ως εκτελέσιμο. Ακολουθεί μια διαδραστική συνεδρία:

$ git credential-read-only --file=/mnt/shared/creds get
protocol=https
host=mygithost

protocol=https
host=mygithost
username=bob
password=s3cre7

Επειδή το όνομά του ξεκινάει με git-, μπορούμε να χρησιμοποιήσουμε την απλή σύνταξη για την τιμή της μεταβλητής ρύθμισης:

$ git config --global credential.helper read-only --file /mnt/shared/creds

Όπως μπορούμε να δούμε, η επέκταση αυτού του συστήματος είναι αρκετά απλή και μπορεί να λύσει μερικά κοινά προβλήματα για εμάς και την ομάδα μας.