Raspberry Pi a zabezpieczenie systemu plików
Raspberry Pi oraz platformy mu podobne często korzystają z kart pamięci jako głównego nośnika dla systemu operacyjnego. Jest to rozwiązanie, które posiada wiele zalet, jak chociażby niski koszt, dopasowanie nośnika do potrzeb (prędkość/pojemność) czy też możliwość łatwej podmiany karty w celu uruchomienia innego systemu na tym samym urządzeniu.
Nie jest to rozwiązanie pozbawione wad, a jedną z bardziej dokuczliwych, z którą przyszło mi się zetknąć już niejednokrotnie, jest wysoka podatność na uszkodzenie systemu plików np. podczas utraty zasilania przez urządzenie w trakcie dokonywania operacji zapisu.
Jako iż problem wystąpił kilkakrotnie na przestrzeni dłuższego czasu, postanowiłem poszukać na to jakiegoś remedium. Pierwszą opcją było cykliczne robienie dumpa karty na PC, tak, aby w razie wystąpienia problemu, móc szybko przywrócić malinkę do życia, jednak regularne robienie takiego zrzutu, poza oczywistą upierdliwością, powodowała, iż taka "odbudowa" systemu była obarczona utratą zmian z okresu od ostatniego dumpa.
Szukając innego rozwiązania, wpadłem na pomysł, aby rozwiązać problem na warstwie sprzętu. Najpierw pomyślałem o bootowaniu z dysku na USB, jednak w tamtym okresie, aby tego dokonać, nadal trzeba było mieć kartę SD, która służyła jako starter. Zatem szukając dalej, zwróciłem się o pomoc do pamięci RAM :)
Podstawowym problemem był fakt dokonywania operacji zapisu na systemie plików podczas gdy zanikało zasilanie. Najprościej więc będzie pozbyć się lub ograniczyć operacje zapisu na karcie SD (co też wydłuży jej życie z uwagi na limity zapisu związane z technologią, w jakiej karty SD są tworzone).
Sposobów na to jest kilka. Tutaj opiszę jeden z nich i powiem parę słów o dostępnych alternatywach. Pragnę też podkreślić, iż całość tego poradnika jest tak naprawdę moim opracowaniem, które powstało na bazie doświadczenia wynikającego z kilku wpisów o podobnej tematyce na przestrzeni czasu, podczas którego starałem się dostosować ich treść do mojego przypadku.
Koncept
Na początek koncept. Możemy co prawda całkowicie wyeliminować zapis na kartę SD, jednak w zależności od zastosowania naszego urządzenia, możemy czasem potrzebować dokonania jakichś zmian na systemie, dlatego też postanowiłem:
- Montować system plików w trybie read-only
- Mniej istotne elementy, w których dokonywany jest zapis, zmapować do tempa (RAM)
- Przy bardziej istotnych operacjach przemontowywać system plików w tryb write, dokonać operacji i ponownie przemontować na read-only
Takie podejście znacząco zniweluje prawdopodobieństwo wystąpienia awarii systemu plików, gdyż operacje zapisu będą zdarzać się niezmiernie rzadko.
Konfiguracja
Przejdźmy do mięska na przykładzie raspbiana na RPi.
1. W pierwszej kolejności najlepiej zrobić update wszystkich dostępnych pakietów (a nawet i kernela, ale to już pomijam. Każdy, kto będzie chciał tego dokonać, zapewne znajdzie potrzebne materiały w sieci).
apt-get update; apt-get upgrade
reboot
2. Teraz pozbądźmy się niechcianych pakietów, mogących stwarzać problem poprzez jakieś regularne akcje zapisu, które trzeba by osobno jeszcze dokonfigurować i obudować, by nie stwarzały problemu. Trzeba mieć na uwadze, iż w jakimś konkretnym zastosowaniu mogą być potrzebne, dlatego też jeśli trzeba by je zostawić, to należy je dokładniej przeanalizować i spróbować uodpornić na system plików tylko do odczytu.
apt-get remove --purge wolfram-engine triggerhappy dphys-swapfile dbus
apt-get autoremove --purge
3. Przyszła pora na wyłączenie SWAP-a, aktywację fastboota i poinformowanie systemu podczas wstawaniu o pracy w ReadOnly.
W /boot/cmdline.txt
dodajemy na końcu
fastboot noswap ro
4. Kolejnym krokiem będzie zmiana pliku fstab tak, by system plików był read-only oraz ustawienie tymczasowego systemu plików na pamięć RAM. W /etc/fstab
:
proc /proc proc defaults 0 0
PARTUUID=6cebe3ce-01 /boot vfat defaults,ro 0 2
PARTUUID=6cebe3ce-02 / ext4 defaults,noatime,ro 0 1
tmpfs /tmp tmpfs nosuid,nodev 0 0
tmpfs /var/log tmpfs nosuid,nodev 0 0
tmpfs /var/tmp tmpfs nosuid,nodev 0 0
5. Skoro mamy już przygotowany system plików, warto teraz poprzepinać istotne foldery, aby pracowały na pamięci RAM.
rm -rf /var/lib/dhcp/ /var/lib/dhcpcd5 /var/run /var/spool /var/lock /etc/resolv.conf
ln -s /tmp /var/lib/dhcp
ln -s /tmp /var/lib/dhcpcd5
ln -s /tmp /var/run
ln -s /tmp /var/spool
ln -s /tmp /var/lock
rm -rf /var/lib/systemd/timesync
ln -s /tmp /var/lib/systemd/timesync
rm -rf /var/lib/ntp
ln -s /tmp /var/lib/ntp
touch /tmp/dhcpcd.resolv.conf; ln -s /tmp/dhcpcd.resolv.conf /etc/resolv.conf
6. Do tego dochodzi kilka zmian, które są zależne od dystrybucji Raspbiana.
I. Zmiana lokacji pliku PID DHCP
W /etc/systemd/system/dhcpcd
konieczna jest zmiana z:
PIDFile=/run/dhcpcd.pid
Na:
PIDFile=/var/run/dhcpcd.pid
II. Przeniesienie random-seeda do lokacji z zapisem
rm /var/lib/systemd/random-seed
ln -s /tmp/random-seed /var/lib/systemd/random-seed
W pliku /lib/systemd/system/systemd-random-seed.service
dodanie wpisu w sekcji [Service]
ExecStartPre=/bin/echo "" > /tmp/random-seed
systemctl daemon-reload
7. Następnym etapem będzie dokonanie zmian mających na celu utrzymanie poprawnej daty i godziny w systemie.
apt-get install ntp ntpdate
W pliku /etc/cron.hourly/fake-hwclock
:
#!/bin/sh
#
# Simple cron script - save the current clock periodically in case of
# a power failure or other crash
if (command -v fake-hwclock >/dev/null 2>&1) ; then
mount -o remount,rw /
fake-hwclock save
mount -o remount,ro /
fi
W pliku /etc/ntp.conf
konieczna jest zmiana z
driftfile /var/lib/ntp/ntp.drift
Na
driftfile /var/tmp/ntp.drift
8. Na koniec pozostało już tylko dokonać rebootu maszyny i mieć nadzieję, iż wszystko poszło po naszej myśli :)
reboot
Na tym można by skończyć, jeśli chodzi o podstawową konfigurację, ale oczywiście trzeba sobie usprawniać życie, dlatego poniżej dorzucam jeszcze tipsy.
Tips
1. Ręczna zmiana trybu pracy z read-only na write i vice-versa.
W /etc/bash.bashrc
:
set_bash_prompt() {
fs_mode=$(mount | sed -n -e "s/^\/dev\/.* on \/ .*(\(r[w|o]\).*/\1/p")
PS1='\[\033[01;32m\]\u@\h${fs_mode:+($fs_mode)}\[\033[00m\]:\[\033[01;34m\]\w\
[\033[00m\]\$ '
}
alias ro='sudo mount -o remount,ro / ; sudo mount -o remount,ro /boot'
alias rw='sudo mount -o remount,rw / ; sudo mount -o remount,rw /boot'
PROMPT_COMMAND=set_bash_prompt
2. Automatyczne zapisywanie historii basha podczas wylogowania.
W /etc/bash.bash_logout
:
mount -o remount,ro /
mount -o remount,ro /boot
3. W przypadku problemów z datą proponuję najpierw spróbować zaktualizować ntp przy użyciu polecenia:
ntpdate -u pool.ntp.org
Jeśli to nie pomoże, to zapoznać się z wpisem na StackExchange, który to opisuje, jak można sobie z tym poradzić poprzez wykorzystanie prywatnego tempa.
4. Problem ze startem DHCP, który można zaobserwować w logach startowych systemu, da się rozwiązać poprzez zmianę lokalizacji pliku PID DHCP.
sudo ln -s /tmp /run-dhcpcd
A w pliku /etc/systemd/system/dhcpcd5.service
zamiana linijki z PIDFile
na:
PIDFile=/run-dhcpcd/dhcpcd.pid
Teraz tak naprawdę nasz Raspbian bardzo się uodpornił na zaniki prądu. Dzięki powyższym zabiegom nasz system plików od niemalże startu urządzenia do jego wyłączenia jest bezpieczny. A jeśli zechcemy dokonać w nim jakichś zmian, wystarczy przełączyć się w tryb zapisu, a po ich dokonaniu z powrotem w tryb bezpieczny.
Alternatywy
Na koniec chciałbym wspomnieć o kilku alternatywach. Log2Ram - aplikacja/skrypt umożliwiająca w szybki i prosty sposób ustawić folder logów, aby działał w pamięci operacyjnej. Zainteresowanych odsyłam do GitHuba.
HDD/SSD na USB - RPi 4 pozwala już na pracę z dyskami poprzez USB bez konieczności posiadania karty SD. Natomiast w starszych modelach potrzebna jest karta, aby zainicjować sam mechanizm rozruchu, który potem będzie przekierowany na dysk z USB.
RamDisk - zamiast przestawiać kartę SD w tryb odczytu, możemy pokusić się o "zasłonięcie" faktycznych części systemu ramdyskiem, gdzie podczas startu skopiujemy oryginalne dane na ramdysk, a następnie zasłonimy nim oryginalną lokalizację. Rozwiązanie to możemy stosować punktowo albo przy odpowiedniej ilości pamięci, możemy tak zasłonić znacznie większe obszary systemu.
Jednak trzeba liczyć się z tym, iż stosowanie takiego rozwiązania może wpłynąć na czas startowania systemu przez kopiowanie danych z karty do pamięci oraz wykorzysta pamięć RAM, która w tego typu urządzeniach często jest dosyć ograniczona.
Rozruch z sieci - tutaj nie mam doświadczenia, zatem nie będę się wymądrzał. Wiem, że się da przy wykorzystaniu PXE na RPi 3+ (wcześniejsze nie mają wsparcia na SoC).