This is an automated email from the ASF dual-hosted git repository. DaanHoogland pushed a commit to branch ghi14530-db-encrypt-userdata in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit a622a6ef901f84c1e54669d675b6a3123f1aa4b3 Author: Daan Hoogland <[email protected]> AuthorDate: Thu Nov 5 17:08:17 2026 +0100 db: encrypt userdata --- .../com/cloud/network/as/AutoScaleVmProfileVO.java | 2 ++ .../com/cloud/upgrade/dao/Upgrade42300to2400.java | 30 ++++++++++++++++++++++ .../src/main/java/com/cloud/user/UserDataVO.java | 2 ++ .../java/com/cloud/user/dao/UserDataDaoImpl.java | 7 ++--- .../src/main/java/com/cloud/vm/UserVmVO.java | 2 ++ .../java/com/cloud/api/query/vo/UserVmJoinVO.java | 2 ++ 6 files changed, 42 insertions(+), 3 deletions(-) diff --git a/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmProfileVO.java b/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmProfileVO.java index 562d908507e..b2e5ef55ee6 100644 --- a/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmProfileVO.java +++ b/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmProfileVO.java @@ -42,6 +42,7 @@ import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; import com.cloud.utils.Pair; +import com.cloud.utils.db.Encrypt; import com.cloud.utils.db.GenericDao; import com.cloud.utils.net.NetUtils; @@ -85,6 +86,7 @@ public class AutoScaleVmProfileVO implements AutoScaleVmProfile, Identity, Inter @Column(name = "counter_params", updatable = true) private String counterParams; + @Encrypt @Column(name = "user_data", updatable = true, nullable = true, length = 1048576) @Basic(fetch = FetchType.LAZY) private String userData; diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42300to2400.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42300to2400.java index ce217cef9e7..e4f14323693 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42300to2400.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42300to2400.java @@ -16,6 +16,14 @@ // under the License. package com.cloud.upgrade.dao; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import com.cloud.utils.crypt.DBEncryptionUtil; +import com.cloud.utils.exception.CloudRuntimeException; + public class Upgrade42300to2400 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { @Override @@ -27,4 +35,26 @@ public class Upgrade42300to2400 extends DbUpgradeAbstractImpl implements DbUpgra public String getUpgradedVersion() { return "24.0.0"; } + + @Override + public void performDataMigration(Connection conn) { + encryptUserData(conn, "user_vm"); + encryptUserData(conn, "user_data"); + encryptUserData(conn, "autoscale_vmprofiles"); + } + + private void encryptUserData(Connection conn, String table) { + try (PreparedStatement selectStatement = conn.prepareStatement(String.format("SELECT `id`, `user_data` FROM `cloud`.`%s` WHERE `user_data` IS NOT NULL", table)); + ResultSet resultSet = selectStatement.executeQuery(); + PreparedStatement updateStatement = conn.prepareStatement(String.format("UPDATE `cloud`.`%s` SET `user_data` = ? WHERE `id` = ?", table))) { + while (resultSet.next()) { + updateStatement.setString(1, DBEncryptionUtil.encrypt(resultSet.getString(2))); + updateStatement.setLong(2, resultSet.getLong(1)); + updateStatement.executeUpdate(); + } + } catch (SQLException e) { + throw new CloudRuntimeException(String.format("Failed to encrypt user_data in table %s", table), e); + } + logger.debug("Encrypted user_data in table {}", table); + } } diff --git a/engine/schema/src/main/java/com/cloud/user/UserDataVO.java b/engine/schema/src/main/java/com/cloud/user/UserDataVO.java index e8864976069..3359c2c1311 100644 --- a/engine/schema/src/main/java/com/cloud/user/UserDataVO.java +++ b/engine/schema/src/main/java/com/cloud/user/UserDataVO.java @@ -28,6 +28,7 @@ import javax.persistence.Table; import java.util.Date; import java.util.UUID; +import com.cloud.utils.db.Encrypt; import com.cloud.utils.db.GenericDao; @Entity @@ -55,6 +56,7 @@ public class UserDataVO implements UserData { @Column(name = "name") private String name; + @Encrypt @Column(name = "user_data", updatable = true, length = 1048576) @Basic(fetch = FetchType.LAZY) private String userData; diff --git a/engine/schema/src/main/java/com/cloud/user/dao/UserDataDaoImpl.java b/engine/schema/src/main/java/com/cloud/user/dao/UserDataDaoImpl.java index ded0cd172da..fc1527fe91b 100644 --- a/engine/schema/src/main/java/com/cloud/user/dao/UserDataDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/user/dao/UserDataDaoImpl.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.user.dao; +import java.util.Objects; + import com.cloud.user.UserDataVO; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; @@ -34,7 +36,6 @@ public class UserDataDaoImpl extends GenericDaoBase<UserDataVO, Long> implements userdataSearch = createSearchBuilder(); userdataSearch.and("accountId", userdataSearch.entity().getAccountId(), SearchCriteria.Op.EQ); userdataSearch.and("domainId", userdataSearch.entity().getDomainId(), SearchCriteria.Op.EQ); - userdataSearch.and("userData", userdataSearch.entity().getUserData(), SearchCriteria.Op.EQ); userdataSearch.done(); userdataByNameSearch = createSearchBuilder(); @@ -49,9 +50,9 @@ public class UserDataDaoImpl extends GenericDaoBase<UserDataVO, Long> implements SearchCriteria<UserDataVO> sc = userdataSearch.create(); sc.setParameters("accountId", accountId); sc.setParameters("domainId", domainId); - sc.setParameters("userData", userData); - return findOneBy(sc); + // user_data is encrypted non-deterministically, so compare the decrypted values + return listBy(sc).stream().filter(vo -> Objects.equals(vo.getUserData(), userData)).findFirst().orElse(null); } @Override diff --git a/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java b/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java index ce3a9a84a34..e6111c9d952 100644 --- a/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java @@ -28,6 +28,7 @@ import javax.persistence.Table; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.uservm.UserVm; +import com.cloud.utils.db.Encrypt; import org.apache.commons.lang3.StringUtils; @Entity @@ -39,6 +40,7 @@ public class UserVmVO extends VMInstanceVO implements UserVm { @Column(name = "iso_id", nullable = true, length = 17) private Long isoId = null; + @Encrypt @Column(name = "user_data", updatable = true, nullable = true, length = 1048576) @Basic(fetch = FetchType.LAZY) private String userData; diff --git a/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java index 6f84bd9b1e3..c099bc812ed 100644 --- a/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java @@ -45,6 +45,7 @@ import com.cloud.storage.Storage.TemplateType; import com.cloud.storage.Volume; import com.cloud.user.Account; import com.cloud.util.StoragePoolTypeConverter; +import com.cloud.utils.db.Encrypt; import com.cloud.utils.db.GenericDao; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; @@ -411,6 +412,7 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro @Column(name = "nic_dns_name") private String nicDnsName; + @Encrypt @Column(name = "user_data", updatable = true, nullable = true, length = 2048) private String userData;
