Git szerver backupját szeretném jól (hatékonyan és biztonságosan) megoldani online backup klienssel. A cél az, hogy a backupból tetszőleges múltbéli állapot visszaállítható legyen, és még egy szándékos szerver rongálás után is visszaállítható legyen a backup, még akkor is, ha már a szerver törölt változatára is lefutott a backup - mert mondjuk nem állítottuk a backup szervert le időben.
Azt gondoltam ki, hogy ha egy bare repóba klónozom a backupolandó repót (amit egy szerveren elérhető), akkor az máris egy backup.
(Utánaolvastam, ez egyedül a note-okat nem másolja le, de azt éppenséggel nem is használunk, tehát nem gond. Első kérdés: van más információs, amit egy sima klón nem vesz át?)
Ha a backupot git fetch-csel frissítgetem (tehát a backup kliens/szerver kezdeményezi a mentést, nem a főszerver pusholgatja), akkor mindig a szerver állapotát fogja tükrözni, és a mentéshez használt sávszélesség és diszkterület is optimális lesz.
A probléma csak az, hogy ha a szerveren forced push-sal törölnek néhány kommitot, akkor az a backupon is törlődni fog - mivel a fetch a ref és tag-eket az új kommitra fogja állítani, elveszik az információ, hogy hol voltak előtte.
Erre azt a megoldást találtam ki, hogy a fetch után minden branch-re és tag-re csinálok egy backup tag-et valami ilyes formátumban (kivéve persze a régebbi backup tageket, amiket már nem duplikálok újra meg újra):
backup-YYYYMMDDHHmm-salt-tag-eredetitag
backup-YYYYMMDDHHmm-salt-branch-eredetibranch
Ezzel a szerveren nem tud ütközni semmi - mivel a salt rész egy kellően hosszú random string (pl pwgen által adott). Ezután nyugodtan feccselgethetem a repómat a szerverről, akármi is történik a szerveren, ebből a repóból vissza tudom állítani bármelyik pillanatkép állapotot.
Ha a git szervert feltöri egy támadó, még ő sem tudja a backup múltját elrontani, mivel a salt-ot nem ismeri, tehát a backup tag-eket nem tudja felülírni. (Mivel a fetch csak újat létrehozni és meglévőt módosítani tud, de wildcard törölni nem. Jól tudom ezt?)
Visszaállítás: az adott dátumra eső tageket kivéve az összes taget és branchet töröljük, majd a backup tageket visszanevezzük az eredetire.
Sávszélességben mindig csak az új kommittok diffjei utaznak (ahogy a git fetch-csel általában), tárolásban pedig kiaknázható a git deduplikációja. Tehát mindkét szempontból optimális-közeli a megoldás.
Kipróbáltam az elrendezést, egy sima forced push esetet (pl branch rebase) simán lekezel, valóban megmarad a "régi" állapot a backup gépen az adott tag alatt.
A kérdéseim:
* Van-e hiba az okoskodásban? El tudja-e rontani a backup múltját egy támadó, ha azt tesz a szerverrel, amit akar?
* Van-e olyan információ egy git repóban, amit a módszer nem tárol el egyáltalán, vagy nem biztonságosan visszaállítható formában tárol?
* Máshol is így csinálják a git-ek backupját, vagy feltaláltam a spanyol viaszt? Nem találtam hasonló megoldást guglizással eddig.
Szerk.:
Az alap elképzelés maradt, de finomítottam rajta:
* A fetch-nek megadom paraméterként, hogy mely refs alatti névtereket frissítse. Pl: +refs/heads/* +refs/tags/* (ezzel lehet pull requestet, vagy note-ot is kezelni) Így a config file-tól függetlenül jól fog működni. A plusz jel is kell, hogy a távoli esetleges forced pushokat is jól kezelje.
* -p kapcsolóval fetch-cselek, ez a heads és a tags alól kigyepálja a szerverről eltűnteket. Így az adott dátumhoz tartozó backupban nem lesznek jelen az adott dátummal már nem létező ref-ek (pl egy feature branch, amit végül töröltek)
* git update-ref paranccsal a refs/backup névtér alá csinálom a backup referenciákat. Mivel erre nincs fetch, ez távolról láthatatlan marad szándékos támadástól is (nem kell salt sem), illetve a prune sem törli. Viszont az objektumokat megtartja, tetszőleges dátum visszaállítható. A gitk programban is vizualizálódik, hogy a helyükön vannak ezek a jelölők.
Egyelőre jónak tűnik, egy darabig futtatom és nézegetem, hogy jó-e amit csinál.
- 1226 megtekintés
Hozzászólások
"Azt gondoltam ki, hogy ha egy bare repóba klónozom a backupolandó repót (amit egy szerveren elérhető), akkor az máris egy backup.
(Utánaolvastam, ez egyedül a note-okat nem másolja le, de azt éppenséggel nem is használunk, tehát nem gond. Első kérdés: van más információs, amit egy sima klón nem vesz át?)"
Pontosabban fogalmazva: egy "sima"
git clone --bare
csak a branch-eket (
refs/heads/*
) és tageket (
refs/tags/*
) és az azokból elérhető objektumokat klónozza, ami azon kívül van, azt nem, így kimaradnak pl. a note-ok (
refs/notes/*
), github pull requestjei (
refs/pull/*
), meg ki tudja mi minden még.
Persze a klónozás után olyan fetch refspec-eket állítasz be, amilyeneket csak akarsz.
"Ha a backupot git fetch-csel frissítgetem (tehát a backup kliens/szerver kezdeményezi a mentést, nem a főszerver pusholgatja), akkor mindig a szerver állapotát fogja tükrözni"
A backupból jövő két fetch között a szerverrel akármi is történhet, így a backup nyilvánvalóan nem mindig tükrözi a szerver állapotát.
Ez gond lehet, ha a szerver fogad egy push-t, majd leég az egész szerverszoba, mert akkor annak a pushnak az anyaga nem lesz meg a backupban. Szintén gond lehet, ha egy push megváltoztat egy ref-et, majd egy rákövetkező push force update-eli ugyanazt a ref-et.
Talán jobb lenne, ha a szerver automatikusan pusholna a backupba minden push fogadása után, lásd post-receive hook.
"A probléma csak az, hogy ha a szerveren forced push-sal törölnek néhány kommitot, akkor az a backupon is törlődni fog"
Egy forced update nem töröl commitokat.
"mivel a fetch a ref és tag-eket az új kommitra fogja állítani, elveszik az információ, hogy hol voltak előtte."
Megfelelő beállítások mellett nem veszik el, lásd
git reflog
és
core.logAllRefUpdates
.
Ami gond lehet, hogy egy ref törlésével annak reflogja is törlődik, de a leírásodból számomra nem derül ki, hogy a törölt refekkel mit akarsz kezdeni.
"Erre azt a megoldást találtam ki, hogy a fetch után minden branch-re és tag-re csinálok egy backup tag-et valami ilyes formátumban (kivéve persze a régebbi backup tageket, amiket már nem duplikálok újra meg újra)"
Attól függően, hogy hány ref van a backupolandó repóban, és hogy milyen gyakran fetch-elsz belőle, a backup repó refjeinek száma előbb-utóbb elég nagy lehet. A tapasztalat azt mutatja, hogy a git nem skálázódik túl jól a refek számának növekedésével, ami majd a fetch futási idejére és a két repo között átküldött üzenetek és (meta)adat mennyiségére is hatással lesz, főleg ha gyakoriak a forced update-ek.
"(Mivel a fetch csak újat létrehozni és meglévőt módosítani tud, de wildcard törölni nem. Jól tudom ezt?)"
Ez a fetch parancssori paramétereitől (
--prune
) függ.
"Ha a git szervert feltöri egy támadó, még ő sem tudja a backup múltját elrontani,"
Úgy véled, hogy a szervert fel tudja törni, de a backupot nem? :)
- A hozzászóláshoz be kell jelentkezni
"Úgy véled, hogy a szervert fel tudja törni, de a backupot nem? :)"
Úgy képzelem, hogy van offline backup (mondjuk havonta), és online (mondjuk naponta). Az online-t nyilván elvben feltörhetik.
Egy nyilvánvaló előnye van a backup szervernek: nincs nyitott portja, ő csak fetch-csel, de rá nem lehet pusholni. Jó eséllyel nem fogják tudni a szerverrel _egyszerre_ feltörni. Ráadásul lehet belőle több példány, akár eltérő oprendszerrel is, mivel egyszerű kliensként kell telepíteni.
"Egy forced update nem töröl commitokat."
Úgy értem, hogy ha nem marad rá branch vagy tag, amire vissza akarok állni, akkor egy dolog, hogy nem törlik, de nem megyek vele sokra, mert nem találom meg.
"Attól függően, hogy hány ref van a backupolandó repóban, és hogy milyen gyakran fetch-elsz belőle, a backup repó refjeinek száma előbb-utóbb elég nagy lehet."
Igen, ez egy probléma. Ha sok lesz, akkor lehet törölgetni belőle hogy a régi backupokat ritkítom hetire, vagy havira.
"Megfelelő beállítások mellett nem veszik el, lásd git reflog és core.logAllRefUpdates."
Na, erről nem tudtam eddig. Ezért kellene manpage-t olvasgatni először. Ez lehet hogy pont azt tudja, amit ezzel a trükkel akartam megcsinálni.
"A backupból jövő két fetch között a szerverrel akármi is történhet, így a backup nyilvánvalóan nem mindig tükrözi a szerver állapotát."
Úgy értem, a frissítés (fetch) időponjában. A két frissítés közötti állapotok nyilván elveszhetnek.
- A hozzászóláshoz be kell jelentkezni