Skip to content

Commit 151bbf2

Browse files
author
kishan
committed
Bug 11823: Added cloud-setup-encryption script for encryption upgrade
Status 11823: resolved fixed
1 parent 6afaf4f commit 151bbf2

1 file changed

Lines changed: 249 additions & 0 deletions

File tree

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
import os
5+
import sys
6+
import subprocess
7+
import glob
8+
from random import choice
9+
import string
10+
from optparse import OptionParser
11+
import commands
12+
import shutil
13+
14+
# squelch mysqldb spurious warnings
15+
import warnings
16+
warnings.simplefilter('ignore')
17+
# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ----
18+
# ---- We do this so cloud_utils can be looked up in the following order:
19+
# ---- 1) Sources directory
20+
# ---- 2) waf configured PYTHONDIR
21+
# ---- 3) System Python path
22+
for pythonpath in (
23+
"@PYTHONDIR@",
24+
os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"),
25+
):
26+
if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath)
27+
# ---- End snippet of code ----
28+
29+
def runCmd(cmds):
30+
process = subprocess.Popen(' '.join(cmds), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
31+
stdout, stderr = process.communicate()
32+
if process.returncode != 0:
33+
raise Exception(stderr)
34+
return stdout
35+
36+
class DBDeployer(object):
37+
parser = None
38+
options = None
39+
args = None
40+
isDebug = False
41+
mgmtsecretkey = None
42+
dbsecretkey = None
43+
encryptiontype = None
44+
dbConfPath = r"@MSCONF@"
45+
dbDotProperties = {}
46+
dbDotPropertiesIndex = 0
47+
encryptionKeyFile = '@MSCONF@/key'
48+
encryptionJarPath = '@JAVADIR@/cloud-jasypt-1.8.jar'
49+
success = False
50+
magicString = 'This_is_a_magic_string_i_think_no_one_will_duplicate'
51+
52+
def preRun(self):
53+
def backUpDbDotProperties():
54+
dbpPath = os.path.join(self.dbConfPath, 'db.properties')
55+
copyPath = os.path.join(self.dbConfPath, 'db.properties.origin')
56+
57+
if os.path.isfile(dbpPath):
58+
shutil.copy2(dbpPath, copyPath)
59+
60+
backUpDbDotProperties()
61+
62+
def postRun(self):
63+
def cleanOrRecoverDbDotProperties():
64+
dbpPath = os.path.join(self.dbConfPath, 'db.properties')
65+
copyPath = os.path.join(self.dbConfPath, 'db.properties.origin')
66+
if os.path.isfile(copyPath):
67+
if not self.success:
68+
shutil.copy2(copyPath, dbpPath)
69+
os.remove(copyPath)
70+
71+
cleanOrRecoverDbDotProperties()
72+
73+
74+
def info(self, msg, result=None):
75+
output = ""
76+
if msg is not None:
77+
output = "%-80s"%msg
78+
79+
if result is True:
80+
output += "[ \033[92m%-2s\033[0m ]\n"%"OK"
81+
elif result is False:
82+
output += "[ \033[91m%-6s\033[0m ]\n"%"FAILED"
83+
sys.stdout.write(output)
84+
sys.stdout.flush()
85+
86+
def debug(self, msg):
87+
msg = "DEBUG:%s"%msg
88+
sys.stdout.write(msg)
89+
sys.stdout.flush()
90+
91+
def putDbProperty(self, key, value):
92+
if self.dbDotProperties.has_key(key):
93+
(oldValue, index) = self.dbDotProperties[key]
94+
self.dbDotProperties[key] = (value, index)
95+
else:
96+
self.dbDotProperties[key] = (value, self.dbDotPropertiesIndex)
97+
self.dbDotPropertiesIndex += 1
98+
99+
def getDbProperty(self, key):
100+
if not self.dbDotProperties.has_key(key):
101+
return None
102+
(value, index) = self.dbDotProperties[key]
103+
return value
104+
105+
def errorAndExit(self, msg):
106+
self.postRun()
107+
err = '''\n\nWe apologize for below error:
108+
***************************************************************
109+
%s
110+
***************************************************************
111+
Please run:
112+
113+
cloud-setup-encryption -h
114+
115+
for full help
116+
''' % msg
117+
sys.stderr.write(err)
118+
sys.stderr.flush()
119+
sys.exit(1)
120+
121+
def prepareDBFiles(self):
122+
def prepareDBDotProperties():
123+
dbpPath = os.path.join(self.dbConfPath, 'db.properties')
124+
dbproperties = file(dbpPath).read().splitlines()
125+
newdbp = []
126+
emptyLine = 0
127+
for line in dbproperties:
128+
passed = False
129+
line = line.strip()
130+
if line.startswith("#"): key = line; value = ''; passed = True
131+
if line == '' or line == '\n': key = self.magicString + str(emptyLine); value = ''; emptyLine += 1; passed = True
132+
133+
try:
134+
if not passed:
135+
(key, value) = line.split('=', 1)
136+
except Exception, e:
137+
err = '''Wrong format in %s (%s):
138+
Besides comments beginning "#" and empty line, all key-value pairs must be in formula of
139+
key=value
140+
for example:
141+
db.cloud.username = cloud
142+
''' % (dbpPath, line)
143+
self.errorAndExit(err)
144+
self.putDbProperty(key, value)
145+
self.info("Preparing %s"%dbpPath, True)
146+
147+
prepareDBDotProperties()
148+
149+
def finalize(self):
150+
def finalizeDbProperties():
151+
entries = []
152+
for key in self.dbDotProperties.keys():
153+
(value, index) = self.dbDotProperties[key]
154+
if key.startswith("#"):
155+
entries.insert(index, key)
156+
elif key.startswith(self.magicString):
157+
entries.insert(index, '')
158+
else:
159+
entries.insert(index, "%s=%s"%(key, value))
160+
file(os.path.join(self.dbConfPath, 'db.properties'), 'w').write('\n'.join(entries))
161+
162+
self.info("Finalizing setup ...", None)
163+
finalizeDbProperties()
164+
self.info(None, True)
165+
self.success = True # At here, we have done successfully and nothing more after this flag is set
166+
167+
def processEncryptionStuff(self):
168+
def encrypt(input):
169+
cmd = ['java','-classpath',self.encryptionJarPath,'org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI', 'encrypt.sh', 'input=%s'%input, 'password=%s'%self.mgmtsecretkey,'verbose=false']
170+
return runCmd(cmd).strip('\n')
171+
172+
def saveMgmtServerSecretKey():
173+
if self.encryptiontype == 'file':
174+
file(self.encryptionKeyFile, 'w').write(self.mgmtsecretkey)
175+
176+
def formatEncryptResult(value):
177+
return 'ENC(%s)'%value
178+
179+
def encryptDBSecretKey():
180+
self.putDbProperty('db.cloud.encrypt.secret', formatEncryptResult(encrypt(self.dbsecretkey)))
181+
182+
def encryptDBPassword():
183+
dbPassword = self.getDbProperty('db.cloud.password')
184+
if dbPassword == '': return # Don't encrypt empty password
185+
if dbPassword == None: self.errorAndExit('Cannot find db.cloud.password in %s'%os.path.join(self.dbConfPath, 'db.properties'))
186+
self.putDbProperty('db.cloud.password', formatEncryptResult(encrypt(dbPassword)))
187+
188+
usagePassword = self.getDbProperty('db.usage.password')
189+
if usagePassword == '': return # Don't encrypt empty password
190+
if usagePassword == None: self.errorAndExit('Cannot find db.usage.password in %s'%os.path.join(self.dbConfPath, 'db.properties'))
191+
self.putDbProperty('db.usage.password', formatEncryptResult(encrypt(usagePassword)))
192+
193+
self.info("Processing encryption ...", None)
194+
self.putDbProperty("db.cloud.encryption.type", self.encryptiontype)
195+
saveMgmtServerSecretKey()
196+
encryptDBSecretKey()
197+
encryptDBPassword()
198+
self.info(None, True)
199+
200+
def parseOptions(self):
201+
def parseOtherOptions():
202+
self.encryptiontype = self.options.encryptiontype
203+
self.mgmtsecretkey = self.options.mgmtsecretkey
204+
self.dbsecretkey = self.options.dbsecretkey
205+
self.isDebug = self.options.debug
206+
207+
208+
def validateParameters():
209+
if self.encryptiontype != 'file' and self.encryptiontype != 'web':
210+
self.errorAndExit('Wrong encryption type %s, --encrypt-type can only be "file" or "web'%self.encryptiontype)
211+
212+
#---------------------- option parsing and command line checks ------------------------
213+
usage = """%prog [-e ENCRYPTIONTYPE] [-m MGMTSECRETKEY] [-k DBSECRETKEY] [--debug]
214+
215+
This command sets up the CloudStack Encryption.
216+
217+
"""
218+
self.parser = OptionParser(usage=usage)
219+
self.parser.add_option("-v", "--debug", action="store_true", dest="debug", default=False,
220+
help="If enabled, print the commands it will run as they run")
221+
self.parser.add_option("-e", "--encrypt-type", action="store", type="string", dest="encryptiontype", default="file",
222+
help="Encryption method used for db password encryption. Valid values are file, web. Default is file.")
223+
self.parser.add_option("-m", "--managementserver-secretkey", action="store", type="string", dest="mgmtsecretkey", default="password",
224+
help="Secret key used to encrypt confidential parameters in db.properties. A string, default is password")
225+
self.parser.add_option("-k", "--database-secretkey", action="store", type="string", dest="dbsecretkey", default="password",
226+
help="Secret key used to encrypt sensitive database values. A string, default is password")
227+
228+
(self.options, self.args) = self.parser.parse_args()
229+
parseOtherOptions()
230+
validateParameters()
231+
232+
def run(self):
233+
try:
234+
self.preRun()
235+
self.parseOptions()
236+
self.prepareDBFiles()
237+
self.processEncryptionStuff()
238+
self.finalize()
239+
finally:
240+
self.postRun()
241+
242+
print ''
243+
print "CloudStack has successfully setup Encryption"
244+
print ''
245+
246+
if __name__ == "__main__":
247+
o = DBDeployer()
248+
o.run()
249+

0 commit comments

Comments
 (0)