Skip to content

Commit eb3cb11

Browse files
committed
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.
1 parent 2974af8 commit eb3cb11

3 files changed

Lines changed: 3 additions & 3 deletions

File tree

‎plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterDestroyWorker.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ private void destroyKubernetesClusterNetwork() throws ManagementServerException
141141
if (network != null && network.getRemoved() == null) {
142142
Account owner = accountManager.getAccount(network.getAccountId());
143143
User callerUser = accountManager.getActiveUser(CallContext.current().getCallingUserId());
144-
ReservationContext context = new ReservationContextImpl(null, null, callerUser, owner);
144+
ReservationContext context = new ReservationContextImpl(null, null, callerUser, CallContext.current().getCallingAccount());
145145
releaseASNumber(kubernetesCluster.getZoneId(), kubernetesCluster.getNetworkId());
146146
boolean networkDestroyed = networkMgr.destroyNetwork(kubernetesCluster.getNetworkId(), context, true);
147147
if (!networkDestroyed) {

‎server/src/main/java/com/cloud/network/NetworkServiceImpl.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2924,7 +2924,7 @@ public boolean deleteNetwork(long networkId, boolean forced) {
29242924
}
29252925

29262926
User callerUser = _accountMgr.getActiveUser(CallContext.current().getCallingUserId());
2927-
ReservationContext context = new ReservationContextImpl(null, null, callerUser, owner);
2927+
ReservationContext context = new ReservationContextImpl(null, null, callerUser, caller);
29282928

29292929
return _networkMgr.destroyNetwork(networkId, context, forced);
29302930
}

‎server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2851,7 +2851,7 @@ protected boolean deletePrivateGatewayFromTheDB(final PrivateGateway gateway) {
28512851
if (deleteNetworkFinal) {
28522852
final User callerUser = _accountMgr.getActiveUser(CallContext.current().getCallingUserId());
28532853
final Account owner = _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM);
2854-
final ReservationContext context = new ReservationContextImpl(null, null, callerUser, owner);
2854+
final ReservationContext context = new ReservationContextImpl(null, null, callerUser, CallContext.current().getCallingAccount());
28552855
_ntwkMgr.destroyNetwork(networkId, context, false);
28562856
logger.debug("Deleted private network {}", network);
28572857
}

0 commit comments

Comments
 (0)