root / snf-cyclades-app / synnefo / db / migrations / 0066_add_iv.py @ 8d5795b4
History | View | Annotate | Download (18.2 kB)
1 |
# encoding: utf-8
|
---|---|
2 |
import datetime |
3 |
from south.db import db |
4 |
from south.v2 import DataMigration |
5 |
from django.db import models |
6 |
from binascii import b2a_base64, a2b_base64 |
7 |
from Crypto.Cipher import AES |
8 |
from Crypto import Random |
9 |
from random import choice |
10 |
from string import letters, digits |
11 |
from synnefo.settings import SECRET_ENCRYPTION_KEY |
12 |
|
13 |
DB_ENCRYPTED_FIELD_PREFIX = 'encrypted'
|
14 |
SALT_LEN = 8
|
15 |
|
16 |
|
17 |
def _pad_secret(secret, blocksize=32, padding='}'): |
18 |
len_secret = len(secret)
|
19 |
if len_secret > 32: |
20 |
raise ValueError('Encryption key must be smaller than 32 bytes') |
21 |
if not len_secret in (16, 24, 32): |
22 |
return secret + (blocksize - len(secret)) * padding |
23 |
return secret
|
24 |
|
25 |
|
26 |
def encrypt(s, iv=None): |
27 |
if iv is None: |
28 |
obj = AES.new(_pad_secret(SECRET_ENCRYPTION_KEY), AES.MODE_CFB) |
29 |
else:
|
30 |
obj = AES.new(_pad_secret(SECRET_ENCRYPTION_KEY), AES.MODE_CFB, iv) |
31 |
return obj.encrypt(s)
|
32 |
|
33 |
|
34 |
def decrypt(s, iv=None): |
35 |
if iv is None: |
36 |
obj = AES.new(_pad_secret(SECRET_ENCRYPTION_KEY), AES.MODE_CFB) |
37 |
else:
|
38 |
obj = AES.new(_pad_secret(SECRET_ENCRYPTION_KEY), AES.MODE_CFB, iv) |
39 |
return obj.decrypt(s)
|
40 |
|
41 |
|
42 |
def encrypt_db_charfield_old(plaintext): |
43 |
if not plaintext: |
44 |
return plaintext
|
45 |
salt = "".join([choice(letters + digits) for i in xrange(SALT_LEN)]) |
46 |
|
47 |
plaintext = "%s%s" % (salt, plaintext)
|
48 |
# Encrypt and convert to binary
|
49 |
ciphertext = b2a_base64(encrypt(plaintext)) |
50 |
# Append prefix,salt and return encoded value
|
51 |
final = '%s:%s$%s' % (DB_ENCRYPTED_FIELD_PREFIX, salt, ciphertext)
|
52 |
return final.encode('utf8') |
53 |
|
54 |
|
55 |
def decrypt_db_charfield_old(ciphertext): |
56 |
if not ciphertext: |
57 |
return ciphertext
|
58 |
has_prefix = ciphertext.startswith(DB_ENCRYPTED_FIELD_PREFIX + ':')
|
59 |
if not has_prefix: # Non-encoded value |
60 |
return ciphertext
|
61 |
else:
|
62 |
_, ciphertext = ciphertext.split(':')
|
63 |
|
64 |
pure_salt, encrypted = ciphertext.split('$')
|
65 |
|
66 |
plaintext = decrypt(a2b_base64(encrypted)) |
67 |
|
68 |
salt = plaintext[:SALT_LEN] |
69 |
plaintext = plaintext[SALT_LEN:] |
70 |
|
71 |
if salt != pure_salt:
|
72 |
# Cannot decrtypt password
|
73 |
raise CorruptedPassword("Cannot decrypt password. Check the key") |
74 |
else:
|
75 |
return plaintext
|
76 |
|
77 |
|
78 |
def encrypt_db_charfield(plaintext): |
79 |
if not plaintext: |
80 |
return plaintext
|
81 |
salt = "".join([choice(letters + digits) for i in xrange(SALT_LEN)]) |
82 |
|
83 |
iv = Random.get_random_bytes(16)
|
84 |
plaintext = "%s%s" % (salt, plaintext)
|
85 |
# Encrypt and convert to binary
|
86 |
ciphertext = b2a_base64(encrypt(plaintext, iv)) |
87 |
iv = b2a_base64(iv) |
88 |
# Append prefix,salt and return encoded value
|
89 |
final = '%s:%s:%s$%s' % (DB_ENCRYPTED_FIELD_PREFIX, iv, salt, ciphertext)
|
90 |
return final.encode('utf8') |
91 |
|
92 |
|
93 |
def decrypt_db_charfield(ciphertext): |
94 |
if not ciphertext: |
95 |
return ciphertext
|
96 |
has_prefix = ciphertext.startswith(DB_ENCRYPTED_FIELD_PREFIX + ':')
|
97 |
if not has_prefix: # Non-encoded value |
98 |
return ciphertext
|
99 |
else:
|
100 |
_, iv, ciphertext = ciphertext.split(':')
|
101 |
|
102 |
pure_salt, encrypted = ciphertext.split('$')
|
103 |
iv = a2b_base64(iv) |
104 |
|
105 |
plaintext = decrypt(a2b_base64(encrypted), iv) |
106 |
|
107 |
salt = plaintext[:SALT_LEN] |
108 |
plaintext = plaintext[SALT_LEN:] |
109 |
|
110 |
if salt != pure_salt:
|
111 |
# Cannot decrtypt password
|
112 |
raise CorruptedPassword("Cannot decrypt password. Check the key") |
113 |
else:
|
114 |
return plaintext
|
115 |
|
116 |
|
117 |
class CorruptedPassword(Exception): |
118 |
pass
|
119 |
|
120 |
|
121 |
class Migration(DataMigration): |
122 |
|
123 |
def forwards(self, orm): |
124 |
"Write your forwards methods here."
|
125 |
for backend in orm.Backend.objects.all(): |
126 |
old_hash = backend.password_hash |
127 |
if len(old_hash.split(":")) == 2: |
128 |
old_pass = decrypt_db_charfield_old(old_hash) |
129 |
new_hash = encrypt_db_charfield(old_pass) |
130 |
# Bypass save method!
|
131 |
orm.Backend.objects.filter(id=backend.id).update(password_hash=new_hash) |
132 |
|
133 |
def backwards(self, orm): |
134 |
"Write your backwards methods here."
|
135 |
try:
|
136 |
for backend in orm.Backend.objects.all(): |
137 |
old_pass = decrypt_db_charfield(backend.password_hash) |
138 |
new_hash = encrypt_db_charfield_old(old_pass) |
139 |
orm.Backend.objects.filter(id=backend.id).update(password_hash=new_hash) |
140 |
except:
|
141 |
pass
|
142 |
|
143 |
models = { |
144 |
'db.backend': {
|
145 |
'Meta': {'object_name': 'Backend'}, |
146 |
'clustername': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), |
147 |
'ctotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), |
148 |
'dfree': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), |
149 |
'drained': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
150 |
'dtotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), |
151 |
'hash': ('django.db.models.fields.CharField', [], {'max_length': '40'}), |
152 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
153 |
'index': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'unique': 'True'}), |
154 |
'mfree': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), |
155 |
'mtotal': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), |
156 |
'offline': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
157 |
'password_hash': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), |
158 |
'pinst_cnt': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), |
159 |
'port': ('django.db.models.fields.PositiveIntegerField', [], {'default': '5080'}), |
160 |
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
161 |
'username': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}) |
162 |
}, |
163 |
'db.backendnetwork': {
|
164 |
'Meta': {'unique_together': "(('network', 'backend'),)", 'object_name': 'BackendNetwork'}, |
165 |
'backend': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'networks'", 'to': "orm['db.Backend']"}), |
166 |
'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), |
167 |
'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}), |
168 |
'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}), |
169 |
'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}), |
170 |
'backendtime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1, 1, 1, 0, 0)'}), |
171 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
172 |
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
173 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
174 |
'mac_prefix': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
175 |
'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'backend_networks'", 'to': "orm['db.Network']"}), |
176 |
'operstate': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '30'}), |
177 |
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) |
178 |
}, |
179 |
'db.bridgepooltable': {
|
180 |
'Meta': {'object_name': 'BridgePoolTable'}, |
181 |
'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}), |
182 |
'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), |
183 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
184 |
'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), |
185 |
'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}), |
186 |
'size': ('django.db.models.fields.IntegerField', [], {}) |
187 |
}, |
188 |
'db.flavor': {
|
189 |
'Meta': {'unique_together': "(('cpu', 'ram', 'disk', 'disk_template'),)", 'object_name': 'Flavor'}, |
190 |
'cpu': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
191 |
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
192 |
'disk': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
193 |
'disk_template': ('django.db.models.fields.CharField', [], {'default': "'plain'", 'max_length': '32'}), |
194 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
195 |
'ram': ('django.db.models.fields.IntegerField', [], {'default': '0'}) |
196 |
}, |
197 |
'db.ippooltable': {
|
198 |
'Meta': {'object_name': 'IPPoolTable'}, |
199 |
'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}), |
200 |
'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), |
201 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
202 |
'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), |
203 |
'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}), |
204 |
'size': ('django.db.models.fields.IntegerField', [], {}) |
205 |
}, |
206 |
'db.macprefixpooltable': {
|
207 |
'Meta': {'object_name': 'MacPrefixPoolTable'}, |
208 |
'available_map': ('django.db.models.fields.TextField', [], {'default': "''"}), |
209 |
'base': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), |
210 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
211 |
'offset': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), |
212 |
'reserved_map': ('django.db.models.fields.TextField', [], {'default': "''"}), |
213 |
'size': ('django.db.models.fields.IntegerField', [], {}) |
214 |
}, |
215 |
'db.network': {
|
216 |
'Meta': {'object_name': 'Network'}, |
217 |
'action': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '32', 'null': 'True'}), |
218 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
219 |
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}), |
220 |
'dhcp': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), |
221 |
'flavor': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
222 |
'gateway': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), |
223 |
'gateway6': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), |
224 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
225 |
'link': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), |
226 |
'mac_prefix': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
227 |
'machines': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['db.VirtualMachine']", 'through': "orm['db.NetworkInterface']", 'symmetrical': 'False'}), |
228 |
'mode': ('django.db.models.fields.CharField', [], {'max_length': '16', 'null': 'True'}), |
229 |
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
230 |
'pool': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'network'", 'unique': 'True', 'null': 'True', 'to': "orm['db.IPPoolTable']"}), |
231 |
'public': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}), |
232 |
'serial': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'network'", 'null': 'True', 'to': "orm['db.QuotaHolderSerial']"}), |
233 |
'state': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '32'}), |
234 |
'subnet': ('django.db.models.fields.CharField', [], {'default': "'10.0.0.0/24'", 'max_length': '32'}), |
235 |
'subnet6': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), |
236 |
'tags': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), |
237 |
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
238 |
'userid': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'db_index': 'True'}) |
239 |
}, |
240 |
'db.networkinterface': {
|
241 |
'Meta': {'object_name': 'NetworkInterface'}, |
242 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
243 |
'dirty': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
244 |
'firewall_profile': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}), |
245 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
246 |
'index': ('django.db.models.fields.IntegerField', [], {}), |
247 |
'ipv4': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True'}), |
248 |
'ipv6': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), |
249 |
'mac': ('django.db.models.fields.CharField', [], {'max_length': '32', 'unique': 'True', 'null': 'True'}), |
250 |
'machine': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.VirtualMachine']"}), |
251 |
'network': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nics'", 'to': "orm['db.Network']"}), |
252 |
'state': ('django.db.models.fields.CharField', [], {'default': "'ACTIVE'", 'max_length': '32'}), |
253 |
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) |
254 |
}, |
255 |
'db.quotaholderserial': {
|
256 |
'Meta': {'object_name': 'QuotaHolderSerial'}, |
257 |
'accept': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
258 |
'pending': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'}), |
259 |
'resolved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
260 |
'serial': ('django.db.models.fields.BigIntegerField', [], {'primary_key': 'True', 'db_index': 'True'}) |
261 |
}, |
262 |
'db.virtualmachine': {
|
263 |
'Meta': {'object_name': 'VirtualMachine'}, |
264 |
'action': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}), |
265 |
'backend': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'virtual_machines'", 'null': 'True', 'to': "orm['db.Backend']"}), |
266 |
'backend_hash': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), |
267 |
'backendjobid': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}), |
268 |
'backendjobstatus': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}), |
269 |
'backendlogmsg': ('django.db.models.fields.TextField', [], {'null': 'True'}), |
270 |
'backendopcode': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}), |
271 |
'backendtime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(1, 1, 1, 0, 0)'}), |
272 |
'buildpercentage': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
273 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
274 |
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}), |
275 |
'flavor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Flavor']"}), |
276 |
'hostid': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
277 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
278 |
'imageid': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
279 |
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
280 |
'operstate': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}), |
281 |
'serial': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'virtual_machine'", 'null': 'True', 'to': "orm['db.QuotaHolderSerial']"}), |
282 |
'suspended': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), |
283 |
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
284 |
'userid': ('django.db.models.fields.CharField', [], {'max_length': '100', 'db_index': 'True'}) |
285 |
}, |
286 |
'db.virtualmachinediagnostic': {
|
287 |
'Meta': {'object_name': 'VirtualMachineDiagnostic'}, |
288 |
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
289 |
'details': ('django.db.models.fields.TextField', [], {'null': 'True'}), |
290 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
291 |
'level': ('django.db.models.fields.CharField', [], {'max_length': '20'}), |
292 |
'machine': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'diagnostics'", 'to': "orm['db.VirtualMachine']"}), |
293 |
'message': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
294 |
'source': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
295 |
'source_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}) |
296 |
}, |
297 |
'db.virtualmachinemetadata': {
|
298 |
'Meta': {'unique_together': "(('meta_key', 'vm'),)", 'object_name': 'VirtualMachineMetadata'}, |
299 |
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
300 |
'meta_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}), |
301 |
'meta_value': ('django.db.models.fields.CharField', [], {'max_length': '500'}), |
302 |
'vm': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'metadata'", 'to': "orm['db.VirtualMachine']"}) |
303 |
} |
304 |
} |
305 |
|
306 |
complete_apps = ['db']
|