26.01.2025

Dystrybucja kluczy prywatnych przez git

Technodruid Maksymilian

Zawsze kiedy stawiam nową maszynę albo zbieram się do formatu, zastanawiam się czy będę miał do wszystkiego dostęp albo czy aby na pewno się od czegoś przypadkiem nie odetnę.

Temat haseł w zasadzie mi rozwiązał pass - terminalowy password manager zintegrowany z gitem i szyfrowany kluczem gpg, więc relatywnie bezpiecznie można to w remotowym repo trzymać. Same fajne rzeczy.

Ale skąd na nowej maszynie wezme ten klucz gpg do odszyfrowania haseł? Nie mówiąc już o kluczach ssh etc.

Proste tożsame rozwiązanie to po prostu szyfrowanie takich kluczy ręcznie i podobnie dystrybucja gitem. Wtedy wystarczy że pamiętamy albo jesteśmy w stanie odzyskać jedną kredke do gita i gdzieś mamy klucz. Może to być klucz który będziemy trzymać na pendrivie albo innym yubikeyu, być może skitramy ten klucz w hashicorp vaulcie, albo użyjemy cloudowego KMSa. Wszystko to są całkiem dobre opcje ale z tyłu głowy dla każdej z nich mam gotowy scenariusz, w którym (nie koniecznie bezpowrotnie) tracę dostęp do klucza (nie mam ze sobą pendrive'a albo wyzionął ducha, selfhostowany HCP Vault się wykrzaczył, nie mam internetu żeby dostać się do Cloud KMSa).

Najlepiej więc byłoby wszystkie te nasze klucze prywatne szyfrować na kilka metod żeby mieć w razie czego fallback, ale to brzmi trochę jak kupa roboty jeżeli robilibyśmy to ręcznie.

Na szczęście mądrzejsi ludzie już ten problem rozwiązali i dali nam SOPS.

Sops po prawdzie raczej ma na celu rozwiązanie problemu sekretów w konfiguracji, tak żebyśmy nie musieli robić zbyt dużo gimanstyki z jakimiś wtyczkami w zależności od softu który będzie tą konfiguracje trawił, albo dziergać jakieś inne rozwiązania na integrację z natywnymi dla jakiegoś danego CI secret storami. Sops pozwala nam niejako w locie rozszyfrować yamla i podać go gdzie tam jest potrzebny. Do VSCode'a nawet chyba jest wtyczka która powoduje że edycja takich plików jest totalnie przezroczysta dla użytkownika. Nas to tutaj szczególnie nie interesuje, ale gwoli ścisłości uważam że warto to tu nadmienić bo to dobry tech.

Sops pozwoli nam z minimalnym configiem szyfrować pliki wieloma metodami i równie prosto je rozszyfrowywać.

W tym przykładzie używam GCP KMS i age. Ale no stronie SOPSa znaleźć można dokumentacje dla innych technologii.

GCP KMS

Konfiguracja GCP KMS

Będziemy potrzebowali pełnej nazwy klucza (tj. całej ścieżki z projektem, lokacją, keyringiem etc.):

gcloud kms keys list --location global --keyring sops | awk '{print $1}' | grep -v NAME

age

Generowanie klucza age i zrzucenie go do defaultowej ścieżki dla sops:

age-keygen -o ~/.config/sops/age/keys.txt

W ścieżce będzie zarówno klucz publiczny (potrzebny potem) jak i prywatny. Zawartość tego pliku powinniśmy skitrać na jakimś nośniku fizycznym i wsadzić w jakieś bezpieczne miejsce.

Przygotowanie danych do zaszyfrowania

W moim przypadku interesują mnie konkretnie klucze SSH i gpg, ale z czasem pewnie dorzucę jakieś inne identity file'e.

SSH

Tu sprawa jest dosyć prosta

cp -r ~/.ssh/id_* .
# chyba że ktoś używa bardziej egzotycznych nazw kluczy

gpg

Export kluczy publicznych:

gpg -a --export > publickeys.asc

Export kluczy prywatnych (i ich kluczy publicznych):

gpg -a --export-secret-keys > privatekeys.asc

Export trustdb:

gpg --export-ownertrust > trustdb.txt

Szyfrowanie repozytorium

Tworzymy .sops.yaml do umieszczenia w repozytorium

creation_rules:
- age: age1*********************************************************
  gcp_kms: projects/******/locations/global/keyRings/sops/cryptoKeys/sops-key

i szyfrujemy wszystkie pliki:

for f in $(ls); do sops encrypt -i $f; done

Teraz możemy zcommitować wszystko w repo (.sops.yaml teoeretycznie nie musi być w repo bo dane na temat tego jakie klucze zostały użyte i tak są w wynikowych plikach sopsa, ale jeżeli będziemy chcieli dodać nowy plik do repo to oszczędzi nam to roboty).

Odszyfrowywanie repozytorium

Zobaczyć zawartość dowolnego pliku możemy przez sops decrypt plik, adekwatnie więc możemy to pipeować, redirectować i tak dalej. W grę wchodzą też takie rozwiązania jak sops exec-env który uruchomi nam proces z załadowaną rozszyfrowaną zawartością w env varze albo sops exec-file który, jak się można dymyślić, wrzuci to do tymczasowego pliku.

I tak np. nasze eksporty gpg możemy importować przez:

for f in publickeys.asc privatekeys.asc trustdb.txt; do
sops decrypt $f | gpg --import
done

Bezpieczeństwo

Co mówią ludzie od SOPS

Zasadniczo potencjalne problemy są takie:

  • Przypadkowo zshareujacie swój klucz
  • Ktoś niepowołany uzyska dostęp do waszego klucza w cloudzie
  • Pojawi się krytyczna podatność na AES256_GCM

Na pierwszy problem nie ma rady, trzeba się pilnować

Jeżeli idzie o dostęp do clouda to wiadomo, dobre zmieniane regularnie hasło, Two-Factor przez token (nie numer telefonu) etc.

Podatność w AES256_GCM bolałaby nas w sytuacji w której ktoś miałby dostęp do zaszyfrowanych plików. Najlepiej więc trzymać je w prywatnym repo.