Szyfrowanie plików za pomocą kluczy SSH
Załóżmy, że używam w domu Linuksa. Do różnych celów. Jednym z nich jest pisanie podręczników dla mojego Klienta. Wprawdzie nie są to wiekopomne dzieła i nawet w drobnym procencie nie dorównują dokumentacji, jaką dawno temu stworzył Novell dla swojego systemu Netware, jednakże jestem z nimi związany (zarówno emocjonalnie, jak i finansowo) i bardzo bym nie chciał ich stracić. Mój domowy komputer ma tylko jeden dysk twardy, a ja – jako doświadczony użytkownik – wiem, że dyski takie ulegają awariom, w wyniku których dane potrafią wyparować szybciej, niż setka wódki. Na szczęście mam dostęp do super-bezpiecznego serwera, który sam w sobie posiada macierz dyskową zabezpieczającą przed utratą danych wskutek awarii jednego dysku. Dodatkowo dane z tego super-serwera są regularnie kopiowane na tasiemki w ramach wykonywania okresowych kopii bezpieczeństwa. Niestety, nie jestem jedynym użytkownikiem tego systemu (ba, nie mam nawet uprawnień administratora), a mój Klient byłby bardzo niezadowolony, gdyby ktoś przeczytał moją twórczość bez jego wiedzy. Ponieważ bardzo chcę mieć kopie zapasowe i bardzo nie chcę, aby ktoś miał dostęp do moich danych, postanowiłem je przed wysłaniem zaszyfrować.
Nie chcę używać żadnych haseł, bo łatwo je zapomnieć, a zapisane tracą pewną część swojej tajemniczości. Postanowiłem użyć kryptografii asymetrycznej, a dokładniej swojej pary kluczy SSH. Klucz prywatny i tak staram się chronić na wszystkie możliwe sposoby, więc dlaczego nie miałbym go wykorzystać także do szyfrowania moich kopii bezpieczeństwa?
Założenie jest takie, że chcę wykonać kopię zapasową katalogu ~/dane w taki sposób, aby możliwe było jej odczytanie jedynie przy użyciu prywatnej części mojego klucza SSH. Niestety, klucz SSH, którego używam (a także większość, jeżeli nie wszystkie klucze asymetryczne) nadaje się do zaszyfrowania wiadomości krótszej, niż on sam, czyli 2048 bitów (a nawet jeszcze mniej, co wynika z budowy samego klucza). Przy okazji, długość klucza sprawdza się w taki oto sposób (pierwsza liczba odpowiada właśnie długości klucza w bitach):
[kbechler@s16931669 ~]$ ssh-keygen -l -f ~/.ssh/id_rsa.pub
2048 09:d7:31:b7:12:3b:d3:3d:ce:ff:02:c7:19:82:54:c2 /home/kbechler/.ssh/id_rsa.pub (RSA)
Sytuacja nie jest jednak taka zła. Możemy przecież za pomocą SSH zaszyfrować sobie jakieś zamotane hasło, które następnie posłuży to zaszyfrowania naszych (a dokładniej moich ;-) danych).
Do dzieła. Pierwsze, co musimy zrobić, to przerobić format klucza tak, żeby był zrozumiały przez polecenie openssl. Robi się to w taki oto sposób:
[kbechler@s16931669 ~]$ openssl rsa -in ~/.ssh/id_rsa -outform pem > backup.key.pem
writing RSA key
[kbechler@s16931669 ~]$ openssl rsa -in ~/.ssh/id_rsa -outform pem -pubout > backup.pub.pem
writing RSA key
W wyniku działania tych dwóch radosnych komend otrzymaliśmy dwa pliki, zawierające – odpowiednio – prywatną oraz publiczną część naszego klucza w formacie PEM. Z kronikarskiego obowiązku napiszę jeszcze, że dane szyfrujemy za pomocą części publicznej, a odszyfrowujemy za pomocą prywatnej.
Do zaszyfrowania i wysłania moich cennych danych używam takiego oto, niezbyt rozbudowanego, skryptu:
#!/bin/bash
# aktualna data przyda sie do utworzenia nazwy pliku na serwerze docelowym
data=`date +%Y-%m-%d_%H:%M:%S`
# stworz archiwum zawierajace nasze cenne dane (oczywiscie w formie skompresowanej)
tar -czf ./backup.tgz ./dane
# wygeneruj klucz sesji i za jego pomoca zaszyfruj (szyfrowanie symetryczne) dane
openssl rand 64 |
tee >(openssl enc -aes-256-cbc -pass stdin -in ./backup.tgz -out ./backup.enc) |
openssl rsautl -encrypt -pubin -inkey ./backup.pub.pem -out ./backup.key
# na wszelki wypadek wygeneruj sumy kontrolne plikow
md5sum ./backup.enc ./backup.key > ./backup.md5
# wrzuc wszystko do jednego pliku .tar (tym razem juz bez kompresji) i wyslij na serwer docelowy
tar -c ./backup.enc ./backup.key ./backup.md5 | ssh flame "cat > ~/backup-${data}.tar"
# trzeba jeszcze po sobie posprzatac
rm ./backup.{tgz,enc,key,md5,tar}
Skoro tak dobrze nam idzie i udało się wysłać dane do innego serwera, to warto by jeszcze było poznać metodę, za pomocą której możemy te dane przywrócić. Ponieważ jestem leniwy i mam kiepską pamięć, do tego zadania również napisałem sobie krótki skrypt:
#!/bin/bash
# jezeli wszystko sie uda, to tutaj znajda sie dane odzyskane z kopii zapasowej
KATALOG_DOCELOWY="/home/kbechler/dane_z_backupu"
# wylistujmy nazwy plikow przechowywanych na serwerze
echo "Lista plikow dostepnych na serwerze:"
ssh flame 'ls backup*.tar'
echo
# popros uzytkonika o nazwe pliku, ktora chce odtworzyc
echo "Podaj nazwe pliku do odtworzenia:"
read plik
echo
# pobierz plik z serwera
echo "Pobieram plik..."
ssh flame "cat ${plik}" > ./${plik}
if [ $? -ne 0 ]; then
echo "BLAD!"
exit 1
fi
echo "Wypakowuje zawartosc archiwum..."
tar -xf ./${plik}
if [ $? -ne 0 ]; then
echo "BLAD!"
exit 1
fi
echo "Sprawdzam sumy kontrolne..."
md5sum -c ./backup.md5 >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo "BLAD!"
exit 1
fi
echo "Odszyfrowuje dane..."
openssl rsautl -decrypt -inkey ./backup.key.pem -in ./backup.key |
openssl enc -aes-256-cbc -pass stdin -d -in ./backup.enc -out ./backup.tgz
if [ ! -d ${KATALOG_DOCELOWY} ]; then
echo "Tworze katalog ${KATALOG_DOCELOWY}..."
mkdir -p ${KATALOG_DOCELOWY}
fi
echo "Wypakowuje dane..."
tar xzf ./backup.tgz -C ${KATALOG_DOCELOWY}
# sprzatamy po sobie
rm ./${plik}
rm ./backup.{md5,enc,key,tgz}