CloudStack has used StandardPBEStringEncryptor from jasypt for more than 10 years.
The encryptor use algorithm "PBEWithMD5AndDes", which is considered as Insecure, because it uses MD5 and DES which has only 56-bits key.
After investigation, we decided to replace it with an implementation of AES-GCM algorithm. The key length of AES-GCM is 256-bit. Please refer to https://github.com/google/tink
Main changes of this feature:
Community PR to main/4.18: https://github.com/apache/cloudstack/pull/7003
There are 4 new classes introduced in this feature:
It uses "com.google.crypto.tink.subtle.AesGcmJce" to decrypt/encrypt values
LegacyBase64Encryptor encryptorV1: the instance of LegacyBase64Encryptor
AeadBase64Encryptor encryptorV2: the instance of AeadBase64Encryptor
Base64Encryptor encryptor: the actual encryptor used in encryption and decryption
DBEncryptionUtil
EncryptionCLI
EncryptionUtil
"/usr/bin/cloudstack-setup-databases" is used to setup
it has been updated to support
Please note: com.cloud.utils.crypt.EncryptionCLI in /usr/share/cloudstack-common/lib/cloudstack-utils.jar is used to replace org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI
# cloudstack-setup-databases -h
Usage: cloudstack-setup-databases user:[password]@mysqlhost:[port] [--deploy-as=rootuser:[rootpassword]] [--auto=/path/to/server-setup.xml] [-e ENCRYPTIONTYPE] [-m MGMTSECRETKEY] [-k DBSECRETKEY] [–debug]
Options:
-h, --help show this help message and exit
-v, --debug If enabled, print the commands it will run as they run
-d ROOTCREDS, --deploy-as=ROOTCREDS
Colon-separated user name and password of a MySQL user
with administrative privileges
-s, --schema-only Creates the db schema without having to pass root
credentials - Please note: The databases (cloud,
cloud_usage) and user (cloud) has to be configured
manually prior to running this script when using this
flag.
-a SERVERSETUP, --auto=SERVERSETUP
Path to an XML file describing an automated unattended
cloud setup
-e ENCRYPTIONTYPE, --encrypt-type=ENCRYPTIONTYPE
Encryption method used for db password encryption.
Valid values are file, web and env. Default is file.
-m MGMTSECRETKEY, --managementserver-secretkey=MGMTSECRETKEY
Secret key used to encrypt confidential parameters in
db.properties. A string, default is password
-k DBSECRETKEY, --database-secretkey=DBSECRETKEY
Secret key used to encrypt sensitive database values.
A string, default is password
-i MSHOSTIP, --mshost=MSHOSTIP
Cluster management server host IP. A string, by
default it will try to detect a local IP
-r REGIONID, --regionid=REGIONID
Region Id for the management server cluster
-c DBCONFPATH, --db-conf-path=DBCONFPATH
The path to find db.properties which hold db
properties
-f DBFILESPATH, --db-files-path=DBFILESPATH
The path to find sql files to create initial
database(s)
-j ENCRYPTIONJARPATH, --encryption-jar-path=ENCRYPTIONJARPATH
The cloudstack jar to be used to encrypt the values in
db.properties
-n ENCRYPTIONKEYFILE, --encryption-key-file=ENCRYPTIONKEYFILE
The name of the file in which encryption key to be
generated
-g ENCRYPTORVERSION, --encryptor-version=ENCRYPTORVERSION
The encryptor version to be used to encrypt the values
in db.properties
-b MYSQLBINPATH, --mysql-bin-path=MYSQLBINPATH
The mysql installed bin path
create /etc/cloudstack/management/key (content of the file is the management key = password)
create /etc/cloudstack/management/db.properties with default passwords (management key = password, db key = password)
in db.properties (1) db.cloud.encryptor.version is not set; (2) db.cloud.encryption.type=file; (3) db.cloud.password, db.cloud.encrypt.secret, db.usage.password are encrypted by default encryptor (currently V2)
create /etc/cloudstack/management/key (content of the file is the management key = mgmtkey)
create /etc/cloudstack/management/db.properties with keys (management key = mgmtkey, db key = dbkey)
in db.properties (1) db.cloud.encryptor.version=V1; (2) db.cloud.encryption.type=file; (3) db.cloud.password, db.cloud.encrypt.secret, db.usage.password are encrypted by encryptor V1.
recreate database using mysql user root:'Password'
create /etc/cloudstack/management/db.properties with keys (management key = $CLOUD_SECRET_KEY, db key = password)
in db.properties (1) db.cloud.encryptor.version=V2; (2) db.cloud.encryption.type=env; (3) db.cloud.password, db.cloud.encrypt.secret, db.usage.password are encrypted by encryptor V2;
recreate database using mysql user root:'Password'
"/usr/bin/cloudstack-migrate-databases" are used to migrate the following with new keys/encryptor.
It has been updated to support
# cloudstack-migrate-databases -h
usage: cloudstack-migrate-databases -d <oldDBKey> -m <oldMSKey> [-e <newDBKey>] [-n <newMSKey>] [-v
<version>] [-f] [-h] [-l] [-s]
Options:
-d,--oldDBKey <oldDBKey> (required) Current DB Secret Key
-m,--oldMSKey <oldMSKey> (required) Current Mgmt Secret Key
-e,--newDBKey <newDBKey> New DB Secret Key
-n,--newMSKey <newMSKey> New Mgmt Secret Key
-v,--version <version> New DB Encryptor Version. Options are V1, V2.
-f,--force-database-migration Force database migration even if DB Secret key is not
changed
-h,--help Show help message
-l,--load-new-management-key-from-env Load new management key from environment variable
CLOUD_SECRET_KEY_NEW
-s,--skip-database-migration Skip database migration even if DB Secret key is changed
Return codes:
0 - Succeed to change keys and/or migrate databases
1 - Fail to parse the command line arguments
2 - Fail to validate parameters
3 - Fail to migrate database
The following tables and columns will be migrated during database migration.
Table | Coiumn | Column type | Conditions | Note |
---|---|---|---|---|
configuration | value | varchar(8191) | category IN ('Hidden', 'Secure') | |
host_details | value | varchar(255) | name = "password" | |
cluster_details | value | varchar(255) | name = "password" | |
storage_pool_details | value | varchar(255) | name in ("password", "powerflex.gw.username", "powerflex.gw.password") | ScaleIO |
image_store_details | value | varchar(255) | name in ("key", "secretkey") | |
user_vm_details | value | varchar(5120) | name = "password" | |
user_vm_deploy_as_is_details | value | varchar(255) | (1) get OVFPropertyTO from template_deploy_as_is_details by detail name (2) migrate value if property is password. | |
storage_pool | path | varchar(255) | pool_type = 'SMB' | |
image_store | url | varchar(2048) | protocol = 'cifs' | |
vpn_users | password | varchar(255) | ||
sslcerts | password | varchar(255) | ||
sslcerts | key | text | ||
account_details | value | varchar(255) | if https://github.com/apache/cloudstack/pull/6812 is merged, extra methods need to be added to cloudstack-migrate-databases to migrate the values (1) in account_details and domain_details (2) the configs are Hidden/Secure global configurations. | |
domain_details | value | varchar(255) | ||
s2s_customer_gateway | ipsec_psk | varchar(256) | ||
virtual_supervisor_module | password | varchar(255) | ||
ucs_manager | password | varchar(255) | ||
vm_instance | vnc_password | varchar(255) | ||
keystore | key | text | ||
passphrase | passphrase | varchar(64) | Need test: volume encryption Modify to varchar(255) | |
external_stratosphere_ssp_credentials | password | varchar(255) | ||
vmware_data_center | password | varchar(255) | ||
storage_pool | user_info | varchar(255) | ||
remote_access_vpn | ipsec_psk | varchar(256) | ||
user | secret_key | varchar(255) | ||
oobm | password | varchar(255) |
A standalone jar package cloudstack-utils.jar is shipped with cloudstack-common. It will be placed as /usr/share/cloudstack-common/lib/cloudstack-utils.jar.
with the jar, we can encrypt and decrypt values using cloudstack encryptors.
# java -classpath /usr/share/cloudstack-common/lib/cloudstack-utils.jar com.cloud.utils.crypt.EncryptionCLI
Missing required options: i, p
usage: Usage:
-d,--decrypt Decrypt instead of encrypt
-e,--encryptorversion <encryptorversion> The encryptor version
-i,--input <input> The input string to process
-p,--password <password> The encryption password
-v,--verbose Verbose output
# java -classpath /usr/share/cloudstack-common/lib/cloudstack-utils.jar com.cloud.utils.crypt.EncryptionCLI -p mgmtkey -i dbkey
+X4wvxJcDzp2DhD8tUyyJa+4Ije5VAELOYC75QfEQBGI
# java -classpath /usr/share/cloudstack-common/lib/cloudstack-utils.jar com.cloud.utils.crypt.EncryptionCLI -p mgmtkey -i +X4wvxJcDzp2DhD8tUyyJa+4Ije5VAELOYC75QfEQBGI -d
dbkey
# java -classpath /usr/share/cloudstack-common/lib/cloudstack-utils.jar com.cloud.utils.crypt.EncryptionCLI -p mgmtkey -i dbkey -e V1
sjlu6z6CSYCKitpVqgG0rw==
# java -classpath /usr/share/cloudstack-common/lib/cloudstack-utils.jar com.cloud.utils.crypt.EncryptionCLI -p mgmtkey -i sjlu6z6CSYCKitpVqgG0rw== -d -e V2
Exception in thread "main" com.cloud.utils.exception.CloudRuntimeException: Error decrypting value: sjlu6*****
at com.cloud.utils.crypt.CloudStackEncryptor.decrypt(CloudStackEncryptor.java:107)
at com.cloud.utils.crypt.EncryptionCLI.main(EncryptionCLI.java:50)
Caused by: com.cloud.utils.crypt.EncryptionException: Failed to decrypt sjlu6z6CSYCKitpVqgG0rw==. Error: ciphertext too short
at com.cloud.utils.crypt.AeadBase64Encryptor.decrypt(AeadBase64Encryptor.java:40)
at com.cloud.utils.crypt.CloudStackEncryptor.decrypt(CloudStackEncryptor.java:105)
... 1 more
# java -classpath /usr/share/cloudstack-common/lib/cloudstack-utils.jar com.cloud.utils.crypt.EncryptionCLI -p mgmtkey -i sjlu6z6CSYCKitpVqgG0rw== -d
dbkey
# java -classpath /usr/share/cloudstack-common/lib/cloudstack-utils.jar com.cloud.utils.crypt.EncryptionCLI -p mgmtkey -i sjlu6z6CSYCKitpVqgG0rw== -d -e V1
dbkey