This repository has been archived by the owner on Jun 6, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
/
encrb
executable file
·137 lines (115 loc) · 4.53 KB
/
encrb
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/usr/bin/env python
# Copyright (c) 2012-2014 Heikki Hokkanen <hoxu at users.sf.net>
# License: GPLv3
# encrb - encrypted remote backup
#
# Backs up plaintext local directories to remote machine as encrypted, using
# encfs --reverse and rsync
import optparse
import os
import random
import string
import subprocess
import sys
import tempfile
import time
def path_to_string(path):
return path.strip('/').replace('/', '-')
class Encrb:
def backup_path(self, path):
print 'Backing up:', path
friendlyname = path_to_string(path)
tmpdir = tempfile.mkdtemp('-' + friendlyname)
# create encfs mount
print 'Making encfs reverse mount to "%s"...' % tmpdir
env = self.get_modified_env()
err = subprocess.call(['encfs', '--extpass', 'cat %s' % os.path.expanduser(self.options.passfile), '--reverse', path, tmpdir], env=env)
# rsync
if err == 0:
try:
destination = os.path.join(self.destination, friendlyname)
print 'Rsyncing encrypted "%s" to "%s"...' % (path, destination)
sys.stdout.flush()
subprocess.call(['rsync', '-avz', '--delete-delay', '--bwlimit', str(self.options.bwlimit), tmpdir + '/', destination])
except KeyboardInterrupt, e:
print 'KeyboardInterrupt, sleeping 1 sec, then unmounting'
time.sleep(1)
subprocess.call(['fusermount', '-u', tmpdir])
os.rmdir(tmpdir)
raise e
else:
print 'encfs returned error', err
# Unmount
print 'Unmounting encfs reverse mount...'
subprocess.call(['fusermount', '-u', tmpdir])
os.rmdir(tmpdir)
def get_modified_env(self):
env = dict(os.environ)
env['ENCFS6_CONFIG'] = os.path.expanduser(self.options.keyfile)
return env
def verify_path(self, path):
print 'Verifying:', path
# create sshfs mount
friendlyname = path_to_string(path)
mountpath = os.path.join(self.destination, friendlyname)
tmpdir = tempfile.mkdtemp('-' + friendlyname)
tmpdirplain = tempfile.mkdtemp('-%s-plain' % friendlyname)
print 'Mounting "%s" sshfs read-only to "%s"...' % (mountpath, tmpdir)
try:
subprocess.check_call(['sshfs', '-o', 'ro', mountpath, tmpdir])
# create encfs mount
print 'Mounting encfs on "%s"...' % tmpdirplain
try:
env = self.get_modified_env()
subprocess.call(['encfs', '--extpass', 'cat %s' % os.path.expanduser(self.options.passfile), tmpdir, tmpdirplain], env=env)
# compare files
print 'Running rsync --dry-run, this should not list any changes...'
subprocess.call(['rsync', '-a', '--dry-run', path, tmpdirplain])
finally:
# wait a bit, or unmounting fails
print 'Sleeping a bit...'
time.sleep(5)
# umount encfs
print 'Unmounting encfs "%s...' % tmpdirplain
subprocess.call(['fusermount', '-u', tmpdirplain])
finally:
# umount sshfs
print 'Unmounting sshfs "%s"...' % tmpdir
subprocess.call(['fusermount', '-u', tmpdir])
os.rmdir(tmpdirplain)
os.rmdir(tmpdir)
def run(self):
parser = optparse.OptionParser(usage='usage: %prog [options] dir-to-back-up1... remotepath')
parser.add_option('-k', '--keyfile', dest='keyfile', default='~/.encfs6-encrb.xml', help='encfs keyfile to use [%default]')
parser.add_option('-p', '--passfile', dest='passfile', default='~/.encfs-password', help='file containing encfs keyfile password [%default]')
parser.add_option('-b', '--bwlimit', dest='bwlimit', default=0, type='int', help='Bandwidth limit (KiB/s) for rsync [%default]')
parser.add_option('-v', '--verify', dest='verify', default=False, action="store_true", help="Instead of backing up, verify that the backups match sources")
(options, args) = parser.parse_args()
self.options = options
self.args = args
self.options.keyfile = os.path.expanduser(self.options.keyfile)
self.options.passfile = os.path.expanduser(self.options.passfile)
print 'Options:', options
if len(self.args) < 2:
parser.error('Need more than two arguments')
self.destination = self.args[-1]
# TODO check that running new enough encfs (old has bugs in --reverse mode)
print 'Paths to back up:', self.args[:-1]
print 'Destination:', self.destination
self.setup_once()
for p in self.args[:-1]:
if options.verify:
self.verify_path(p)
else:
self.backup_path(p)
def setup_once(self):
if os.path.exists(self.options.keyfile) or os.path.exists(self.options.passfile):
return
print '!!!!! Keyfile and passfile missing, running in setup mode. Press enter for default encfs settings.'
# generate and save password
password = ''.join(random.choice(string.letters + string.digits) for _ in range(12))
f = open(self.options.passfile, 'w')
f.write(password)
f.close()
e = Encrb()
e.run()