summaryrefslogtreecommitdiffhomepage
path: root/ServerBackup.hs
blob: 1c6b6a9c026834e1121ffda4159f825c086412af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
{- Copyright 2016 Joey Hess <id@joeyh.name>
 -
 - Licensed under the GNU AGPL version 3 or higher.
 -}

module ServerBackup where

import Storage
import Storage.Local
import HTTP.Server
import System.Process
import System.FilePath
import System.Directory
import Data.List
import Control.Monad
import Data.Time.Clock.POSIX

-- | Storing all shards in one gpg encrypted file avoids a problem with
-- modern incremental backup programs such as obnam: Access to an obnam
-- repository allows one to see the date when a chunk first enters the
-- repository, which can allow dating when objects were first stored
-- in keysafe, and so help in a correlation attack.
--
-- Of course, it's not at all efficient for offsite backups!
backupServer :: Maybe LocalStorageDirectory -> FilePath -> IO ()
backupServer lsd d = do
	let storage = serverStorage lsd
	_ <- obscureShares storage
	topdir <- storageTopDir lsd
	createDirectoryIfMissing True d
	dest <- backupFile d <$> getPOSIXTime
	callCommand ("tar -C " ++ topdir ++ " -c . | gpg --encrypt --default-recipient-self > " ++ dest)
	-- Keep the past 7 backup files, in case an object file somehow
	-- gets deleted, this avoids the backup losing it too.
	-- These backup files can be used to determine eg, what day
	-- chunks were uploaded to the server, which is why only a few 
	-- are kept.
	pruneOldBackups d 7

restoreServer :: Maybe LocalStorageDirectory -> FilePath -> IO ()
restoreServer lsd d = do
	topdir <- storageTopDir lsd
	bs <- findBackups d
	forM_ bs $ \b ->
		callCommand ("gpg --decrypt " ++ b ++ " | tar -C " ++ topdir ++ " -x")
	let storage = serverStorage lsd
	_ <- obscureShares storage
	return ()

findBackups :: FilePath -> IO [FilePath]
findBackups d = map (d </>) . filter isBackup <$> getDirectoryContents d

pruneOldBackups :: FilePath -> Int -> IO ()
pruneOldBackups d keep = do
	fs <- findBackups d
	mapM_ removeFile (drop keep (reverse (sort fs)))

isBackup :: FilePath -> Bool
isBackup f = "keysafe-backup" `isPrefixOf` f

backupFile :: FilePath -> POSIXTime -> FilePath
backupFile d t = d </> "keysafe-backup." ++ show t ++ ".gpg"