mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Bug 11990 - Add encryption properties to db.properties during install
status 11990: resolved fixed
This commit is contained in:
parent
f14e536cea
commit
a23e8af810
@ -10,6 +10,7 @@ import string
|
|||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
import commands
|
import commands
|
||||||
import MySQLdb
|
import MySQLdb
|
||||||
|
import shutil
|
||||||
|
|
||||||
# squelch mysqldb spurious warnings
|
# squelch mysqldb spurious warnings
|
||||||
import warnings
|
import warnings
|
||||||
@ -42,8 +43,38 @@ class DBDeployer(object):
|
|||||||
ip = None
|
ip = None
|
||||||
user,password,host,port,rootuser,rootpassword = [None,None,None,None,None,None]
|
user,password,host,port,rootuser,rootpassword = [None,None,None,None,None,None]
|
||||||
isDebug = False
|
isDebug = False
|
||||||
|
mgmtsecretkey = None
|
||||||
|
dbsecretkey = None
|
||||||
|
encryptiontype = None
|
||||||
dbConfPath = r"@MSCONF@"
|
dbConfPath = r"@MSCONF@"
|
||||||
dbFilesPath = r"@SETUPDATADIR@"
|
dbFilesPath = r"@SETUPDATADIR@"
|
||||||
|
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 not self.success:
|
||||||
|
shutil.copy2(copyPath, dbpPath)
|
||||||
|
os.remove(copyPath)
|
||||||
|
|
||||||
|
cleanOrRecoverDbDotProperties()
|
||||||
|
|
||||||
|
|
||||||
def info(self, msg, result=None):
|
def info(self, msg, result=None):
|
||||||
output = ""
|
output = ""
|
||||||
@ -62,14 +93,28 @@ class DBDeployer(object):
|
|||||||
sys.stdout.write(msg)
|
sys.stdout.write(msg)
|
||||||
sys.stdout.flush()
|
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 runMysql(self, text, table, isRoot=False):
|
def runMysql(self, text, table, isRoot=False):
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if not isRoot:
|
if not isRoot:
|
||||||
kwargs['user'] = self.user
|
kwargs['user'] = self.user
|
||||||
if self.password: kwargs['passwd'] = self.password
|
if self.password != '': kwargs['passwd'] = self.password
|
||||||
else:
|
else:
|
||||||
kwargs['user'] = self.rootuser
|
kwargs['user'] = self.rootuser
|
||||||
if self.password: kwargs['passwd'] = self.rootpassword
|
if self.rootpassword != '': kwargs['passwd'] = self.rootpassword
|
||||||
|
|
||||||
kwargs['port'] = self.port
|
kwargs['port'] = self.port
|
||||||
kwargs['host'] = self.host
|
kwargs['host'] = self.host
|
||||||
@ -133,6 +178,7 @@ Sql parameters:
|
|||||||
self.errorAndExit(err)
|
self.errorAndExit(err)
|
||||||
|
|
||||||
def errorAndExit(self, msg):
|
def errorAndExit(self, msg):
|
||||||
|
self.postRun()
|
||||||
if self.parser != None:
|
if self.parser != None:
|
||||||
self.parser.error(msg)
|
self.parser.error(msg)
|
||||||
else:
|
else:
|
||||||
@ -223,22 +269,56 @@ Sql parameters:
|
|||||||
dbpPath = os.path.join(self.dbConfPath, 'db.properties')
|
dbpPath = os.path.join(self.dbConfPath, 'db.properties')
|
||||||
dbproperties = file(dbpPath).read().splitlines()
|
dbproperties = file(dbpPath).read().splitlines()
|
||||||
newdbp = []
|
newdbp = []
|
||||||
|
emptyLine = 0
|
||||||
for line in dbproperties:
|
for line in dbproperties:
|
||||||
if line.startswith("cluster.node.IP="): line="cluster.node.IP=%s"%self.ip
|
passed = False
|
||||||
if line.startswith("db.cloud.username="): line="db.cloud.username=%s"%self.user
|
line = line.strip()
|
||||||
if line.startswith("db.cloud.password="): line="db.cloud.password=%s"%self.password
|
if line.startswith("#"): key = line; value = ''; passed = True
|
||||||
if line.startswith("db.cloud.host="): line="db.cloud.host=%s"%self.host
|
if line == '' or line == '\n': key = self.magicString + str(emptyLine); value = ''; emptyLine += 1; passed = True
|
||||||
if line.startswith("db.cloud.port="): line="db.cloud.port=%s"%self.port
|
|
||||||
if line.startswith("db.usage.username="): line="db.usage.username=%s"%self.user
|
try:
|
||||||
if line.startswith("db.usage.password="): line="db.usage.password=%s"%self.password
|
if not passed:
|
||||||
if line.startswith("db.usage.host="): line="db.usage.host=%s"%self.host
|
(key, value) = line.split('=', 1)
|
||||||
if line.startswith("db.usage.port="): line="db.usage.port=%s"%self.port
|
if key == "cluster.node.IP": value = self.ip
|
||||||
newdbp.append(line)
|
if key == "db.cloud.username": value = self.user
|
||||||
file(dbpPath,"w").write("\n".join(newdbp))
|
if key == "db.cloud.password": value = self.password
|
||||||
|
if key == "db.cloud.host": value = self.host
|
||||||
|
if key == "db.cloud.port": value = self.port
|
||||||
|
if key == "db.usage.username": value = self.user
|
||||||
|
if key == "db.usage.password": value = self.password
|
||||||
|
if key == "db.usage.host": value = self.host
|
||||||
|
if key == "db.usage.port": value = self.port
|
||||||
|
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)
|
self.info("Preparing %s"%dbpPath, True)
|
||||||
|
|
||||||
prepareDBDotProperties()
|
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 grabSystemInfo(self):
|
def grabSystemInfo(self):
|
||||||
def getIpAddr():
|
def getIpAddr():
|
||||||
try:
|
try:
|
||||||
@ -268,10 +348,11 @@ Sql parameters:
|
|||||||
if not self.serversetup:
|
if not self.serversetup:
|
||||||
coreSchemas.append('server-setup.sql')
|
coreSchemas.append('server-setup.sql')
|
||||||
|
|
||||||
coreSchemas = [os.path.join(self.dbFilesPath, x) for x in coreSchemas]
|
checkingList = [os.path.join(self.dbFilesPath, x) for x in coreSchemas]
|
||||||
for f in coreSchemas:
|
checkingList.append(self.encryptionJarPath)
|
||||||
|
for f in checkingList:
|
||||||
if not os.path.isfile(f):
|
if not os.path.isfile(f):
|
||||||
self.errorAndExit("Cloud DB schema %s was not found"%f)
|
self.errorAndExit("Cloud DB required file %s was not found"%f)
|
||||||
self.info(None, True)
|
self.info(None, True)
|
||||||
|
|
||||||
def checkDbserverHostname():
|
def checkDbserverHostname():
|
||||||
@ -312,6 +393,33 @@ Sql parameters:
|
|||||||
checkSELinux()
|
checkSELinux()
|
||||||
checkMysqlConnection()
|
checkMysqlConnection()
|
||||||
|
|
||||||
|
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 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 == 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 == 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)
|
||||||
|
file(self.encryptionKeyFile, 'w').write(self.encryptiontype)
|
||||||
|
self.putDbProperty("db.cloud.encryption.type", self.encryptiontype)
|
||||||
|
encryptDBSecretKey()
|
||||||
|
encryptDBPassword()
|
||||||
|
self.info(None, True)
|
||||||
|
|
||||||
def parseOptions(self):
|
def parseOptions(self):
|
||||||
def parseOtherOptions():
|
def parseOtherOptions():
|
||||||
if self.options.rootcreds:
|
if self.options.rootcreds:
|
||||||
@ -330,6 +438,9 @@ Sql parameters:
|
|||||||
self.serversetup = self.options.serversetup
|
self.serversetup = self.options.serversetup
|
||||||
self.info("User specified server-setup.sql file at %s"%self.serversetup, True)
|
self.info("User specified server-setup.sql file at %s"%self.serversetup, True)
|
||||||
|
|
||||||
|
self.encryptiontype = self.options.encryptiontype
|
||||||
|
self.mgmtsecretkey = self.options.mgmtsecretkey
|
||||||
|
self.dbsecretkey = self.options.dbsecretkey
|
||||||
self.isDebug = self.options.debug
|
self.isDebug = self.options.debug
|
||||||
|
|
||||||
def parseUserAndPassword(cred):
|
def parseUserAndPassword(cred):
|
||||||
@ -340,14 +451,14 @@ Sql parameters:
|
|||||||
if len(user) < 1:
|
if len(user) < 1:
|
||||||
self.errorAndExit("Invalid user name and password format, must be in format of user:password, user name can not be empty")
|
self.errorAndExit("Invalid user name and password format, must be in format of user:password, user name can not be empty")
|
||||||
if len(stuff) == 1:
|
if len(stuff) == 1:
|
||||||
password = None
|
password = ''
|
||||||
else:
|
else:
|
||||||
password = stuff[1]
|
password = stuff[1]
|
||||||
|
|
||||||
forbidden = "' \\`"
|
forbidden = "' \\`"
|
||||||
for f in forbidden:
|
for f in forbidden:
|
||||||
if f in user: self.errorAndExit("User name cannot have the %r characters"%f)
|
if f in user: self.errorAndExit("User name cannot have the %r characters"%f)
|
||||||
if password and f in password: self.errorAndExit("Password cannot have the %r characters"%f)
|
if f in password: self.errorAndExit("Password cannot have the %r characters"%f)
|
||||||
return user, password
|
return user, password
|
||||||
|
|
||||||
def parseCasualCredit():
|
def parseCasualCredit():
|
||||||
@ -371,7 +482,7 @@ Sql parameters:
|
|||||||
self.errorAndExit("There are more than one parameters for user:password@hostname (%s)"%self.args)
|
self.errorAndExit("There are more than one parameters for user:password@hostname (%s)"%self.args)
|
||||||
|
|
||||||
arg = self.args[0]
|
arg = self.args[0]
|
||||||
stuff = arg.split("@", 2)
|
stuff = arg.split("@", 1)
|
||||||
if len(stuff) == 1: stuff.append("localhost")
|
if len(stuff) == 1: stuff.append("localhost")
|
||||||
self.user,self.password = parseUserAndPassword(stuff[0])
|
self.user,self.password = parseUserAndPassword(stuff[0])
|
||||||
self.host,self.port = parseHostInfo(stuff[1])
|
self.host,self.port = parseHostInfo(stuff[1])
|
||||||
@ -423,17 +534,32 @@ Sql parameters:
|
|||||||
help="Colon-separated user name and password of a MySQL user with administrative privileges")
|
help="Colon-separated user name and password of a MySQL user with administrative privileges")
|
||||||
self.parser.add_option("-a", "--auto", action="store", type="string", dest="serversetup", default="",
|
self.parser.add_option("-a", "--auto", action="store", type="string", dest="serversetup", default="",
|
||||||
help="Path to an XML file describing an automated unattended cloud setup")
|
help="Path to an XML file describing an automated unattended cloud setup")
|
||||||
|
self.parser.add_option("-e", "--encrypt-type", action="store", type="string", dest="encryptiontype", default="file",
|
||||||
|
help="Encryption method used for db password encryption, could be file. Default is file")
|
||||||
|
self.parser.add_option("-m", "--managementserver-secretkey", action="store", type="string", dest="mgmtsecretkey", default="password",
|
||||||
|
help="Secret key used for encrypt. A string, default is password")
|
||||||
|
self.parser.add_option("-k", "--database-secretkey", action="store", type="string", dest="dbsecretkey", default="password",
|
||||||
|
help="Secret key used for encrypt. A string, default is password")
|
||||||
|
self.parser.add_option("-t", "--test-encryption", action="store_true", dest="testencryption", default=False,
|
||||||
|
help="If enabled, will process encryption")
|
||||||
|
|
||||||
(self.options, self.args) = self.parser.parse_args()
|
(self.options, self.args) = self.parser.parse_args()
|
||||||
parseCasualCredit()
|
parseCasualCredit()
|
||||||
parseOtherOptions()
|
parseOtherOptions()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
try:
|
||||||
|
self.preRun()
|
||||||
self.parseOptions()
|
self.parseOptions()
|
||||||
self.checkSystemSetup()
|
self.checkSystemSetup()
|
||||||
self.grabSystemInfo()
|
self.grabSystemInfo()
|
||||||
self.prepareDBFiles()
|
self.prepareDBFiles()
|
||||||
self.setupDBSchema()
|
self.setupDBSchema()
|
||||||
|
if self.options.testencryption:
|
||||||
|
self.processEncryptionStuff()
|
||||||
|
self.finalize()
|
||||||
|
finally:
|
||||||
|
self.postRun()
|
||||||
|
|
||||||
print ''
|
print ''
|
||||||
print "CloudStack has successfully initialized database, you can check your database configuration in %s"%os.path.join(self.dbConfPath, 'db.properties')
|
print "CloudStack has successfully initialized database, you can check your database configuration in %s"%os.path.join(self.dbConfPath, 'db.properties')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user