mirror of https://github.com/apache/cloudstack.git
Bug 11823: Added cloud-setup-encryption script for encryption upgrade
Status 11823: resolved fixed
This commit is contained in:
parent
6afaf4ff69
commit
151bbf286d
|
|
@ -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()
|
||||
|
||||
Loading…
Reference in New Issue