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); }
