diff --git a/setup/bindir/cloud-setup-encryption.in b/setup/bindir/cloud-setup-encryption.in new file mode 100755 index 00000000000..009b69470e6 --- /dev/null +++ b/setup/bindir/cloud-setup-encryption.in @@ -0,0 +1,249 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import os +import sys +import subprocess +import glob +from random import choice +import string +from optparse import OptionParser +import commands +import shutil + +# squelch mysqldb spurious warnings +import warnings +warnings.simplefilter('ignore') +# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ---- +# ---- We do this so cloud_utils can be looked up in the following order: +# ---- 1) Sources directory +# ---- 2) waf configured PYTHONDIR +# ---- 3) System Python path +for pythonpath in ( + "@PYTHONDIR@", + os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"), + ): + if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath) +# ---- End snippet of code ---- + +def runCmd(cmds): + process = subprocess.Popen(' '.join(cmds), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + if process.returncode != 0: + raise Exception(stderr) + return stdout + +class DBDeployer(object): + parser = None + options = None + args = None + isDebug = False + mgmtsecretkey = None + dbsecretkey = None + encryptiontype = None + dbConfPath = r"@MSCONF@" + dbDotProperties = {} + dbDotPropertiesIndex = 0 + encryptionKeyFile = '@MSCONF@/key' + encryptionJarPath = '@JAVADIR@/cloud-jasypt-1.8.jar' + success = False + magicString = 'This_is_a_magic_string_i_think_no_one_will_duplicate' + + def preRun(self): + def backUpDbDotProperties(): + dbpPath = os.path.join(self.dbConfPath, 'db.properties') + copyPath = os.path.join(self.dbConfPath, 'db.properties.origin') + + if os.path.isfile(dbpPath): + shutil.copy2(dbpPath, copyPath) + + backUpDbDotProperties() + + def postRun(self): + def cleanOrRecoverDbDotProperties(): + dbpPath = os.path.join(self.dbConfPath, 'db.properties') + copyPath = os.path.join(self.dbConfPath, 'db.properties.origin') + if os.path.isfile(copyPath): + if not self.success: + shutil.copy2(copyPath, dbpPath) + os.remove(copyPath) + + cleanOrRecoverDbDotProperties() + + + def info(self, msg, result=None): + output = "" + if msg is not None: + output = "%-80s"%msg + + if result is True: + output += "[ \033[92m%-2s\033[0m ]\n"%"OK" + elif result is False: + output += "[ \033[91m%-6s\033[0m ]\n"%"FAILED" + sys.stdout.write(output) + sys.stdout.flush() + + def debug(self, msg): + msg = "DEBUG:%s"%msg + sys.stdout.write(msg) + sys.stdout.flush() + + def putDbProperty(self, key, value): + if self.dbDotProperties.has_key(key): + (oldValue, index) = self.dbDotProperties[key] + self.dbDotProperties[key] = (value, index) + else: + self.dbDotProperties[key] = (value, self.dbDotPropertiesIndex) + self.dbDotPropertiesIndex += 1 + + def getDbProperty(self, key): + if not self.dbDotProperties.has_key(key): + return None + (value, index) = self.dbDotProperties[key] + return value + + def errorAndExit(self, msg): + self.postRun() + err = '''\n\nWe apologize for below error: +*************************************************************** +%s +*************************************************************** +Please run: + + cloud-setup-encryption -h + +for full help +''' % msg + sys.stderr.write(err) + sys.stderr.flush() + sys.exit(1) + + def prepareDBFiles(self): + def prepareDBDotProperties(): + dbpPath = os.path.join(self.dbConfPath, 'db.properties') + dbproperties = file(dbpPath).read().splitlines() + newdbp = [] + emptyLine = 0 + for line in dbproperties: + passed = False + line = line.strip() + if line.startswith("#"): key = line; value = ''; passed = True + if line == '' or line == '\n': key = self.magicString + str(emptyLine); value = ''; emptyLine += 1; passed = True + + try: + if not passed: + (key, value) = line.split('=', 1) + except Exception, e: + err = '''Wrong format in %s (%s): +Besides comments beginning "#" and empty line, all key-value pairs must be in formula of + key=value +for example: + db.cloud.username = cloud + ''' % (dbpPath, line) + self.errorAndExit(err) + self.putDbProperty(key, value) + self.info("Preparing %s"%dbpPath, True) + + prepareDBDotProperties() + + def finalize(self): + def finalizeDbProperties(): + entries = [] + for key in self.dbDotProperties.keys(): + (value, index) = self.dbDotProperties[key] + if key.startswith("#"): + entries.insert(index, key) + elif key.startswith(self.magicString): + entries.insert(index, '') + else: + entries.insert(index, "%s=%s"%(key, value)) + file(os.path.join(self.dbConfPath, 'db.properties'), 'w').write('\n'.join(entries)) + + self.info("Finalizing setup ...", None) + finalizeDbProperties() + self.info(None, True) + self.success = True # At here, we have done successfully and nothing more after this flag is set + + def processEncryptionStuff(self): + def encrypt(input): + cmd = ['java','-classpath',self.encryptionJarPath,'org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI', 'encrypt.sh', 'input=%s'%input, 'password=%s'%self.mgmtsecretkey,'verbose=false'] + return runCmd(cmd).strip('\n') + + def saveMgmtServerSecretKey(): + if self.encryptiontype == 'file': + file(self.encryptionKeyFile, 'w').write(self.mgmtsecretkey) + + def formatEncryptResult(value): + return 'ENC(%s)'%value + + def encryptDBSecretKey(): + self.putDbProperty('db.cloud.encrypt.secret', formatEncryptResult(encrypt(self.dbsecretkey))) + + def encryptDBPassword(): + dbPassword = self.getDbProperty('db.cloud.password') + if dbPassword == '': return # Don't encrypt empty password + if dbPassword == None: self.errorAndExit('Cannot find db.cloud.password in %s'%os.path.join(self.dbConfPath, 'db.properties')) + self.putDbProperty('db.cloud.password', formatEncryptResult(encrypt(dbPassword))) + + usagePassword = self.getDbProperty('db.usage.password') + if usagePassword == '': return # Don't encrypt empty password + if usagePassword == None: self.errorAndExit('Cannot find db.usage.password in %s'%os.path.join(self.dbConfPath, 'db.properties')) + self.putDbProperty('db.usage.password', formatEncryptResult(encrypt(usagePassword))) + + self.info("Processing encryption ...", None) + self.putDbProperty("db.cloud.encryption.type", self.encryptiontype) + saveMgmtServerSecretKey() + encryptDBSecretKey() + encryptDBPassword() + self.info(None, True) + + def parseOptions(self): + def parseOtherOptions(): + self.encryptiontype = self.options.encryptiontype + self.mgmtsecretkey = self.options.mgmtsecretkey + self.dbsecretkey = self.options.dbsecretkey + self.isDebug = self.options.debug + + + def validateParameters(): + if self.encryptiontype != 'file' and self.encryptiontype != 'web': + self.errorAndExit('Wrong encryption type %s, --encrypt-type can only be "file" or "web'%self.encryptiontype) + + #---------------------- option parsing and command line checks ------------------------ + usage = """%prog [-e ENCRYPTIONTYPE] [-m MGMTSECRETKEY] [-k DBSECRETKEY] [--debug] + + This command sets up the CloudStack Encryption. + + """ + self.parser = OptionParser(usage=usage) + self.parser.add_option("-v", "--debug", action="store_true", dest="debug", default=False, + help="If enabled, print the commands it will run as they run") + self.parser.add_option("-e", "--encrypt-type", action="store", type="string", dest="encryptiontype", default="file", + help="Encryption method used for db password encryption. Valid values are file, web. Default is file.") + self.parser.add_option("-m", "--managementserver-secretkey", action="store", type="string", dest="mgmtsecretkey", default="password", + help="Secret key used to encrypt confidential parameters in db.properties. A string, default is password") + self.parser.add_option("-k", "--database-secretkey", action="store", type="string", dest="dbsecretkey", default="password", + help="Secret key used to encrypt sensitive database values. A string, default is password") + + (self.options, self.args) = self.parser.parse_args() + parseOtherOptions() + validateParameters() + + def run(self): + try: + self.preRun() + self.parseOptions() + self.prepareDBFiles() + self.processEncryptionStuff() + self.finalize() + finally: + self.postRun() + + print '' + print "CloudStack has successfully setup Encryption" + print '' + +if __name__ == "__main__": + o = DBDeployer() + o.run() +