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;
 

Reply via email to