mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com> (cherry picked from commit 43f39a1ec37de08e5b8e97dfd74a853c0aae76c7) Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
276 lines
11 KiB
Python
Executable File
276 lines
11 KiB
Python
Executable File
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Licensed to the Apache Software Foundation (ASF) under one
|
|
# or more contributor license agreements. See the NOTICE file
|
|
# distributed with this work for additional information
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
# to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance
|
|
# with the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing,
|
|
# software distributed under the License is distributed on an
|
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
# KIND, either express or implied. See the License for the
|
|
# specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
|
|
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
|
|
keyStorePassphrase = "vmops.com"
|
|
encryptiontype = None
|
|
dbConfPath = r"@MSCONF@"
|
|
dbDotProperties = {}
|
|
dbDotPropertiesIndex = 0
|
|
encryptionKeyFile = '@MSCONF@/key'
|
|
encryptionJarPath = '@COMMONLIBDIR@/lib/jasypt-1.9.2.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 encryptKeyStorePassphrase():
|
|
self.putDbProperty('db.cloud.keyStorePassphrase', formatEncryptResult(encrypt(self.keyStorePassphrase)))
|
|
|
|
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()
|
|
encryptKeyStorePassphrase()
|
|
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.keyStorePassphrase = self.options.keyStorePassphrase
|
|
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.parser.add_option("-p", "--keystore-passphrase", action="store", type="string", dest="keyStorePassphrase", default="vmops.com",
|
|
help="Passphrase used while generating jks file for ssl communication. A string, default is vmops.com")
|
|
|
|
(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()
|
|
|