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

weizhouapache pushed a commit to branch 4.22-fix-network-removal-with-firewall
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit eb3cb11ab64dfc8a103258698c8510149be03e85
Author: Wei Zhou <[email protected]>
AuthorDate: Wed Oct 7 17:06:24 2026 +0200

    network: use calling account instead of network owner for destroy context
    
    Symptom
    -------
    Deleting a network whose owning account is disabled fails with:
    
      com.cloud.exception.PermissionDeniedException: Account Account
      [{"accountName":"...","id":...}] is disabled.
          at com.cloud.acl.DomainChecker.checkAccess(DomainChecker.java:149)
          at 
com.cloud.user.AccountManagerImpl.checkAccess(AccountManagerImpl.java:831)
          at 
com.cloud.network.firewall.FirewallManagerImpl.revokeFirewallRule(FirewallManagerImpl.java:1169)
          at 
com.cloud.network.firewall.FirewallManagerImpl.revokeAllFirewallRulesForNetwork(FirewallManagerImpl.java:1356)
          at 
org.apache.cloudstack.engine.orchestration.NetworkOrchestrator.cleanupNetworkResources(NetworkOrchestrator.java:4192)
          at 
org.apache.cloudstack.engine.orchestration.NetworkOrchestrator.destroyNetwork(NetworkOrchestrator.java:3512)
    
    Root cause
    ----------
    NetworkServiceImpl.deleteNetwork, VpcManagerImpl (private network cleanup),
    and KubernetesClusterDestroyWorker each build the ReservationContext used
    for destroyNetwork() with the network's *owning* account as the acting
    account:
    
      ReservationContext context = new ReservationContextImpl(null, null, 
callerUser, owner);
    
    That "owner" account is then propagated all the way into
    FirewallManagerImpl.revokeFirewallRule() as the checkAccess() caller when
    cleaning up the network's firewall rules. DomainChecker.checkAccess()
    unconditionally rejects any caller whose account state isn't ENABLED, so
    if the network's owner account happens to be disabled (e.g. while it is
    being cleaned up, or simply disabled by an admin without removing it),
    network deletion is blocked entirely — even for a root admin performing
    the deletion.
    
    By contrast, AccountManagerImpl.cleanupAccount() (the cascade that runs
    during full account deletion) already builds this same ReservationContext
    with the actual calling account instead of the owner:
    
      ReservationContext context = new ReservationContextImpl(null, null, 
getActiveUser(callerUserId), caller);
    
    ...which is why account deletion itself was never affected — only a
    direct deleteNetwork/VPC-network-cleanup call against a disabled-but-not-
    yet-removed account.
    
    Fix
    ---
    Use the actual calling account (the account performing the operation)
    instead of the network's owner account when building the
    ReservationContext for destroyNetwork(), in all three call sites, matching
    the pattern already used by AccountManagerImpl.cleanupAccount().
    
    Reproduced and verified
    ------------------------
    Reproduced end-to-end against a running management server (pre-fix build)
    by: creating a test account, creating an isolated network with the
    Firewall service and implementing it (deploy a VM), adding firewall rules
    on its source-NAT IP, disabling the account, destroying/expunging the VM,
    then attempting to delete the network — which failed with the exact same
    stack trace as above. Re-enabling the account allowed the same
    deleteNetwork call to succeed immediately, confirming the account's
    disabled state (via the owner-as-caller path) as the sole cause.
---
 .../cluster/actionworkers/KubernetesClusterDestroyWorker.java           | 2 +-
 server/src/main/java/com/cloud/network/NetworkServiceImpl.java          | 2 +-
 server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java          | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterDestroyWorker.java
 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterDestroyWorker.java
index 62bd8b4576a..1327b6e45cc 100644
--- 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterDestroyWorker.java
+++ 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterDestroyWorker.java
@@ -141,7 +141,7 @@ public class KubernetesClusterDestroyWorker extends 
KubernetesClusterResourceMod
         if (network != null && network.getRemoved() == null) {
             Account owner = accountManager.getAccount(network.getAccountId());
             User callerUser = 
accountManager.getActiveUser(CallContext.current().getCallingUserId());
-            ReservationContext context = new ReservationContextImpl(null, 
null, callerUser, owner);
+            ReservationContext context = new ReservationContextImpl(null, 
null, callerUser, CallContext.current().getCallingAccount());
             releaseASNumber(kubernetesCluster.getZoneId(), 
kubernetesCluster.getNetworkId());
             boolean networkDestroyed = 
networkMgr.destroyNetwork(kubernetesCluster.getNetworkId(), context, true);
             if (!networkDestroyed) {
diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java 
b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
index 6575da47364..969c33fd53a 100644
--- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
@@ -2924,7 +2924,7 @@ public class NetworkServiceImpl extends ManagerBase 
implements NetworkService, C
         }
 
         User callerUser = 
_accountMgr.getActiveUser(CallContext.current().getCallingUserId());
-        ReservationContext context = new ReservationContextImpl(null, null, 
callerUser, owner);
+        ReservationContext context = new ReservationContextImpl(null, null, 
callerUser, caller);
 
         return _networkMgr.destroyNetwork(networkId, context, forced);
     }
diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java 
b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
index 5717f8745ac..93de0f729fd 100644
--- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
@@ -2851,7 +2851,7 @@ public class VpcManagerImpl extends ManagerBase 
implements VpcManager, VpcProvis
             if (deleteNetworkFinal) {
                 final User callerUser = 
_accountMgr.getActiveUser(CallContext.current().getCallingUserId());
                 final Account owner = 
_accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM);
-                final ReservationContext context = new 
ReservationContextImpl(null, null, callerUser, owner);
+                final ReservationContext context = new 
ReservationContextImpl(null, null, callerUser, 
CallContext.current().getCallingAccount());
                 _ntwkMgr.destroyNetwork(networkId, context, false);
                 logger.debug("Deleted private network {}", network);
             }

Reply via email to