You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by da...@apache.org on 2023/01/20 08:25:16 UTC

[cloudstack] branch main updated: Handle console session in multiple management servers (#7094)

This is an automated email from the ASF dual-hosted git repository.

dahn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new 911f951e2a Handle console session in multiple management servers (#7094)
911f951e2a is described below

commit 911f951e2afb29709c1eaeb2b6c83b2a5e465389
Author: Daniel Augusto Veronezi Salvador <38...@users.noreply.github.com>
AuthorDate: Fri Jan 20 05:25:07 2023 -0300

    Handle console session in multiple management servers (#7094)
---
 .../consoleproxy/ConsoleAccessManager.java         |   2 +
 .../main/java/com/cloud/vm/ConsoleSessionVO.java   | 123 +++++++++++++++++++++
 .../java/com/cloud/vm/dao/ConsoleSessionDao.java   |  14 ++-
 .../com/cloud/vm/dao/ConsoleSessionDaoImpl.java    |  22 ++--
 .../spring-engine-schema-core-daos-context.xml     |   1 +
 .../resources/META-INF/db/schema-41720to41800.sql  |  18 +++
 .../java/com/cloud/consoleproxy/AgentHookBase.java |   5 +-
 .../consoleproxy/ConsoleAccessManagerImpl.java     |  35 ++++--
 8 files changed, 197 insertions(+), 23 deletions(-)

diff --git a/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java b/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java
index b1bd198309..c763730a95 100644
--- a/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java
+++ b/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java
@@ -26,4 +26,6 @@ public interface ConsoleAccessManager extends Manager {
     boolean isSessionAllowed(String sessionUuid);
 
     void removeSessions(String[] sessionUuids);
+
+    void removeSession(String sessionUuid);
 }
diff --git a/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java b/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java
new file mode 100644
index 0000000000..2373896b72
--- /dev/null
+++ b/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java
@@ -0,0 +1,123 @@
+//
+// 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.
+//
+
+package com.cloud.vm;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import java.util.Date;
+
+@Entity
+@Table(name = "console_session")
+public class ConsoleSessionVO {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private long id;
+
+    @Column(name = "uuid")
+    private String uuid;
+
+    @Column(name = "created")
+    private Date created;
+
+    @Column(name = "account_id")
+    private long accountId;
+
+    @Column(name = "user_id")
+    private long userId;
+
+    @Column(name = "instance_id")
+    private long instanceId;
+
+    @Column(name = "host_id")
+    private long hostId;
+
+    @Column(name = "removed")
+    private Date removed;
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public String getUuid() {
+        return uuid;
+    }
+
+    public void setUuid(String uuid) {
+        this.uuid = uuid;
+    }
+
+    public Date getCreated() {
+        return created;
+    }
+
+    public void setCreated(Date created) {
+        this.created = created;
+    }
+
+    public long getAccountId() {
+        return accountId;
+    }
+
+    public void setAccountId(long accountId) {
+        this.accountId = accountId;
+    }
+
+    public long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(long userId) {
+        this.userId = userId;
+    }
+
+    public long getInstanceId() {
+        return instanceId;
+    }
+
+    public void setInstanceId(long instanceId) {
+        this.instanceId = instanceId;
+    }
+
+    public long getHostId() {
+        return hostId;
+    }
+
+    public void setHostId(long hostId) {
+        this.hostId = hostId;
+    }
+
+    public Date getRemoved() {
+        return removed;
+    }
+
+    public void setRemoved(Date removed) {
+        this.removed = removed;
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDao.java
similarity index 69%
copy from api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java
copy to engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDao.java
index b1bd198309..43f30970be 100644
--- a/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDao.java
@@ -1,3 +1,4 @@
+//
 // 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
@@ -14,16 +15,17 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package org.apache.cloudstack.consoleproxy;
+//
+
+package com.cloud.vm.dao;
 
-import com.cloud.utils.component.Manager;
-import org.apache.cloudstack.api.command.user.consoleproxy.ConsoleEndpoint;
+import com.cloud.vm.ConsoleSessionVO;
+import com.cloud.utils.db.GenericDao;
 
-public interface ConsoleAccessManager extends Manager {
+public interface ConsoleSessionDao extends GenericDao<ConsoleSessionVO, Long> {
 
-    ConsoleEndpoint generateConsoleEndpoint(Long vmId, String extraSecurityToken, String clientAddress);
+    void removeSession(String sessionUuid);
 
     boolean isSessionAllowed(String sessionUuid);
 
-    void removeSessions(String[] sessionUuids);
 }
diff --git a/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDaoImpl.java
similarity index 61%
copy from api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java
copy to engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDaoImpl.java
index b1bd198309..2ac41918c2 100644
--- a/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDaoImpl.java
@@ -1,3 +1,4 @@
+//
 // 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
@@ -14,16 +15,23 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package org.apache.cloudstack.consoleproxy;
+//
 
-import com.cloud.utils.component.Manager;
-import org.apache.cloudstack.api.command.user.consoleproxy.ConsoleEndpoint;
+package com.cloud.vm.dao;
 
-public interface ConsoleAccessManager extends Manager {
+import com.cloud.vm.ConsoleSessionVO;
+import com.cloud.utils.db.GenericDaoBase;
 
-    ConsoleEndpoint generateConsoleEndpoint(Long vmId, String extraSecurityToken, String clientAddress);
+public class ConsoleSessionDaoImpl extends GenericDaoBase<ConsoleSessionVO, Long> implements ConsoleSessionDao {
 
-    boolean isSessionAllowed(String sessionUuid);
+    @Override
+    public void removeSession(String sessionUuid) {
+        ConsoleSessionVO session = findByUuid(sessionUuid);
+        remove(session.getId());
+    }
 
-    void removeSessions(String[] sessionUuids);
+    @Override
+    public boolean isSessionAllowed(String sessionUuid) {
+        return findByUuid(sessionUuid) != null;
+    }
 }
diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
index 511dccd008..cec86d2d7b 100644
--- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
+++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
@@ -45,6 +45,7 @@
   <bean id="accountJoinDaoImpl" class="com.cloud.api.query.dao.AccountJoinDaoImpl" />
   <bean id="accountVlanMapDaoImpl" class="com.cloud.dc.dao.AccountVlanMapDaoImpl" />
   <bean id="alertDaoImpl" class="com.cloud.alert.dao.AlertDaoImpl" />
+  <bean id="consoleSessionDaoImpl" class="com.cloud.vm.dao.ConsoleSessionDaoImpl" />
   <bean id="asyncJobJoinDaoImpl" class="com.cloud.api.query.dao.AsyncJobJoinDaoImpl" />
   <bean id="autoScalePolicyConditionMapDaoImpl" class="com.cloud.network.as.dao.AutoScalePolicyConditionMapDaoImpl" />
   <bean id="autoScalePolicyDaoImpl" class="com.cloud.network.as.dao.AutoScalePolicyDaoImpl" />
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql b/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql
index 89698ffede..726d36caea 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql
@@ -1039,3 +1039,21 @@ WHERE   role_id = (SELECT id FROM `cloud`.`roles` WHERE name = 'Read-Only User -
 INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`)
 SELECT UUID(), `roles`.`id`, 'isAccountAllowedToCreateOfferingsWithTags', 'ALLOW'
 FROM `cloud`.`roles` WHERE `role_type` = 'DomainAdmin';
+
+--- Create table for handling console sessions #7094
+
+CREATE TABLE IF NOT EXISTS `cloud`.`console_session` (
+    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
+    `uuid` varchar(40) NOT NULL COMMENT 'UUID generated for the session',
+    `created` datetime NOT NULL COMMENT 'When the session was created',
+    `account_id` bigint(20) unsigned NOT NULL COMMENT 'Account who generated the session',
+    `user_id` bigint(20) unsigned NOT NULL COMMENT 'User who generated the session',
+    `instance_id` bigint(20) unsigned NOT NULL COMMENT 'VM for which the session was generated',
+    `host_id` bigint(20) unsigned NOT NULL COMMENT 'Host where the VM was when the session was generated',
+    `removed` datetime COMMENT 'When the session was removed/used',
+    CONSTRAINT `fk_consolesession__account_id` FOREIGN KEY(`account_id`) REFERENCES `cloud`.`account` (`id`),
+    CONSTRAINT `fk_consolesession__user_id` FOREIGN KEY(`user_id`) REFERENCES `cloud`.`user`(`id`),
+    CONSTRAINT `fk_consolesession__instance_id` FOREIGN KEY(`instance_id`) REFERENCES `cloud`.`vm_instance`(`id`),
+    CONSTRAINT `fk_consolesession__host_id` FOREIGN KEY(`host_id`) REFERENCES `cloud`.`host`(`id`),
+    CONSTRAINT `uc_consolesession__uuid` UNIQUE (`uuid`)
+);
diff --git a/server/src/main/java/com/cloud/consoleproxy/AgentHookBase.java b/server/src/main/java/com/cloud/consoleproxy/AgentHookBase.java
index 619825ecf4..0bac76923c 100644
--- a/server/src/main/java/com/cloud/consoleproxy/AgentHookBase.java
+++ b/server/src/main/java/com/cloud/consoleproxy/AgentHookBase.java
@@ -106,10 +106,13 @@ public abstract class AgentHookBase implements AgentHook {
             }
 
             if (!consoleAccessManager.isSessionAllowed(sessionUuid)) {
-                s_logger.error("Invalid session, only one session allowed per token");
+                s_logger.error(String.format("Session [%s] has been already used or does not exist.", sessionUuid));
                 return new ConsoleAccessAuthenticationAnswer(cmd, false);
             }
 
+            s_logger.debug(String.format("Removing session [%s] as it was just used.", sessionUuid));
+            consoleAccessManager.removeSession(sessionUuid);
+
             if (!ticket.equals(ticketInUrl)) {
                 Date now = new Date();
                 // considering of minute round-up
diff --git a/server/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManagerImpl.java b/server/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManagerImpl.java
index 559ceb43e0..0e3a0f822f 100644
--- a/server/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManagerImpl.java
@@ -38,10 +38,12 @@ import com.cloud.utils.Ternary;
 import com.cloud.utils.component.ManagerBase;
 import com.cloud.utils.db.EntityManager;
 import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.ConsoleSessionVO;
 import com.cloud.vm.UserVmDetailVO;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachineManager;
 import com.cloud.vm.VmDetailConstants;
+import com.cloud.vm.dao.ConsoleSessionDao;
 import com.cloud.vm.dao.UserVmDetailsDao;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
@@ -49,6 +51,7 @@ import org.apache.cloudstack.api.command.user.consoleproxy.ConsoleEndpoint;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.framework.security.keys.KeysManager;
 import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
@@ -60,10 +63,8 @@ import javax.naming.ConfigurationException;
 
 import java.util.Arrays;
 import java.util.Date;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.UUID;
 
 public class ConsoleAccessManagerImpl extends ManagerBase implements ConsoleAccessManager {
@@ -84,6 +85,8 @@ public class ConsoleAccessManagerImpl extends ManagerBase implements ConsoleAcce
     private AgentManager agentManager;
     @Inject
     private ConsoleProxyManager consoleProxyManager;
+    @Inject
+    private ConsoleSessionDao consoleSessionDao;
 
     private static KeysManager secretKeysManager;
     private final Gson gson = new GsonBuilder().create();
@@ -94,12 +97,9 @@ public class ConsoleAccessManagerImpl extends ManagerBase implements ConsoleAcce
             VirtualMachine.State.Stopped, VirtualMachine.State.Error, VirtualMachine.State.Destroyed
     );
 
-    private static Set<String> allowedSessions;
-
     @Override
     public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
         ConsoleAccessManagerImpl.secretKeysManager = keysManager;
-        ConsoleAccessManagerImpl.allowedSessions = new HashSet<>();
         return super.configure(name, params);
     }
 
@@ -146,16 +146,23 @@ public class ConsoleAccessManagerImpl extends ManagerBase implements ConsoleAcce
 
     @Override
     public boolean isSessionAllowed(String sessionUuid) {
-        return allowedSessions.contains(sessionUuid);
+        return consoleSessionDao.isSessionAllowed(sessionUuid);
     }
 
     @Override
     public void removeSessions(String[] sessionUuids) {
-        for (String r : sessionUuids) {
-            allowedSessions.remove(r);
+        if (ArrayUtils.isNotEmpty(sessionUuids)) {
+            for (String sessionUuid : sessionUuids) {
+                removeSession(sessionUuid);
+            }
         }
     }
 
+    @Override
+    public void removeSession(String sessionUuid) {
+        consoleSessionDao.removeSession(sessionUuid);
+    }
+
     protected boolean checkSessionPermission(VirtualMachine vm, Account account) {
         if (accountManager.isRootAdmin(account.getId())) {
             return true;
@@ -289,7 +296,7 @@ public class ConsoleAccessManagerImpl extends ManagerBase implements ConsoleAcce
         String url = generateConsoleAccessUrl(rootUrl, param, token, vncPort, vm);
 
         s_logger.debug("Adding allowed session: " + sessionUuid);
-        allowedSessions.add(sessionUuid);
+        persistConsoleSession(sessionUuid, vm.getId(), hostVo.getId());
         managementServer.setConsoleAccessForVm(vm.getId(), sessionUuid);
 
         ConsoleEndpoint consoleEndpoint = new ConsoleEndpoint(true, url);
@@ -303,6 +310,16 @@ public class ConsoleAccessManagerImpl extends ManagerBase implements ConsoleAcce
         return consoleEndpoint;
     }
 
+    protected void persistConsoleSession(String sessionUuid, long instanceId, long hostId) {
+        ConsoleSessionVO consoleSessionVo = new ConsoleSessionVO();
+        consoleSessionVo.setUuid(sessionUuid);
+        consoleSessionVo.setAccountId(CallContext.current().getCallingAccountId());
+        consoleSessionVo.setUserId(CallContext.current().getCallingUserId());
+        consoleSessionVo.setInstanceId(instanceId);
+        consoleSessionVo.setHostId(hostId);
+        consoleSessionDao.persist(consoleSessionVo);
+    }
+
     private String generateConsoleAccessUrl(String rootUrl, ConsoleProxyClientParam param, String token, int vncPort,
                                             VirtualMachine vm) {
         StringBuilder sb = new StringBuilder(rootUrl);