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

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


The following commit(s) were added to refs/heads/4.20 by this push:
     new 376c1b40174 server: scope IPv6 security group member rules to the 
exact host (#14037)
376c1b40174 is described below

commit 376c1b40174ab4b8d7f9aced5401143cbc09d587
Author: Ramgopal Nagaboina <[email protected]>
AuthorDate: Wed Oct 7 02:18:41 2026 -0400

    server: scope IPv6 security group member rules to the exact host (#14037)
    
    * server: scope IPv6 security group member rules to the exact host
    
    When a security group rule references another security group, each member VM
    should be authorized as an exact host. The IPv4 address is correctly pinned 
to
    a /32, but the IPv6 address was expanded to /64, opening the whole subnet 
the
    member sits in rather than just that member. Pin the IPv6 member to /128 to
    match the IPv4 behaviour.
    
    * server: scope the Impl2 IPv6 security group member rule to the exact host
---
 .../network/security/SecurityGroupManagerImpl.java |  2 +-
 .../security/SecurityGroupManagerImpl2.java        |  2 +-
 .../SecurityGroupManagerImpl2Ipv6RuleTest.java     | 82 ++++++++++++++++++++
 .../SecurityGroupManagerImplIpv6RuleTest.java      | 90 ++++++++++++++++++++++
 4 files changed, 174 insertions(+), 2 deletions(-)

diff --git 
a/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl.java 
b/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl.java
index f0e3b32ebaa..8b938775265 100644
--- 
a/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl.java
+++ 
b/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl.java
@@ -356,7 +356,7 @@ public class SecurityGroupManagerImpl extends ManagerBase 
implements SecurityGro
                             cidr = cidr + "/32";
                             cidrs.add(cidr);
                             if (defaultNic.getIPv6Address() != null) {
-                                cidrs.add(defaultNic.getIPv6Address() + "/64");
+                                cidrs.add(defaultNic.getIPv6Address() + 
"/128");
                             }
                         }
                     }
diff --git 
a/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl2.java
 
b/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl2.java
index bd532f8162f..28ec7c0d384 100644
--- 
a/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl2.java
+++ 
b/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl2.java
@@ -250,7 +250,7 @@ public class SecurityGroupManagerImpl2 extends 
SecurityGroupManagerImpl {
                         String cidr = ngmapVO.getGuestIpAddress() + "/32";
                         cidrs.add(cidr);
                         if (ngmapVO.getGuestIpv6Address() != null) {
-                            cidrs.add(ngmapVO.getGuestIpv6Address() + "/64");
+                            cidrs.add(ngmapVO.getGuestIpv6Address() + "/128");
                         }
                     }
                 } else if (rule.getAllowedSourceIpCidr() != null) {
diff --git 
a/server/src/test/java/com/cloud/network/security/SecurityGroupManagerImpl2Ipv6RuleTest.java
 
b/server/src/test/java/com/cloud/network/security/SecurityGroupManagerImpl2Ipv6RuleTest.java
new file mode 100644
index 00000000000..b8cbd1534d3
--- /dev/null
+++ 
b/server/src/test/java/com/cloud/network/security/SecurityGroupManagerImpl2Ipv6RuleTest.java
@@ -0,0 +1,82 @@
+// 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.network.security;
+
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import com.cloud.network.security.SecurityGroupManagerImpl.PortAndProto;
+import com.cloud.network.security.SecurityRule.SecurityRuleType;
+import com.cloud.network.security.dao.SecurityGroupRuleDao;
+import com.cloud.network.security.dao.SecurityGroupVMMapDao;
+import com.cloud.vm.VirtualMachine.State;
+
+@RunWith(MockitoJUnitRunner.Silent.class)
+public class SecurityGroupManagerImpl2Ipv6RuleTest {
+
+    @Mock
+    SecurityGroupRuleDao _securityGroupRuleDao;
+    @Mock
+    SecurityGroupVMMapDao _securityGroupVMMapDao;
+
+    @InjectMocks
+    SecurityGroupManagerImpl2 manager = new SecurityGroupManagerImpl2();
+
+    @Test
+    public void securityGroupMemberRuleUsesExactIpv6HostCidr() {
+        Long vmId = 1L;
+        SecurityRuleType type = SecurityRuleType.IngressRule;
+
+        // The VM belongs to security group 10, which has one rule referencing 
another security group (20).
+        SecurityGroupVMMapVO groupMap = 
Mockito.mock(SecurityGroupVMMapVO.class);
+        when(groupMap.getSecurityGroupId()).thenReturn(10L);
+        
when(_securityGroupVMMapDao.listByInstanceId(vmId)).thenReturn(Collections.singletonList(groupMap));
+
+        SecurityGroupRuleVO rule = Mockito.mock(SecurityGroupRuleVO.class);
+        when(rule.getProtocol()).thenReturn("tcp");
+        when(rule.getStartPort()).thenReturn(80);
+        when(rule.getEndPort()).thenReturn(80);
+        when(rule.getAllowedNetworkId()).thenReturn(20L);
+        when(_securityGroupRuleDao.listBySecurityGroupId(10L, 
type)).thenReturn(Collections.singletonList(rule));
+
+        // Unlike the superclass, Impl2 reads the member addresses straight 
from the VO join, not the nics table.
+        SecurityGroupVMMapVO memberMap = 
Mockito.mock(SecurityGroupVMMapVO.class);
+        when(memberMap.getGuestIpAddress()).thenReturn("10.1.1.5");
+        when(memberMap.getGuestIpv6Address()).thenReturn("2001:db8::5");
+        when(_securityGroupVMMapDao.listBySecurityGroup(20L, 
State.Running)).thenReturn(Collections.singletonList(memberMap));
+
+        Map<PortAndProto, Set<String>> allowed = 
manager.generateRulesForVM(vmId, type);
+
+        Assert.assertEquals(1, allowed.size());
+        Set<String> cidrs = allowed.values().iterator().next();
+        // The member must be authorized as an exact host, matching the IPv4 
/32 behaviour.
+        Assert.assertTrue("IPv4 member should be pinned to /32", 
cidrs.contains("10.1.1.5/32"));
+        Assert.assertTrue("IPv6 member should be pinned to the exact /128 
host", cidrs.contains("2001:db8::5/128"));
+        Assert.assertFalse("IPv6 member must not open the whole /64 subnet", 
cidrs.contains("2001:db8::5/64"));
+    }
+}
diff --git 
a/server/src/test/java/com/cloud/network/security/SecurityGroupManagerImplIpv6RuleTest.java
 
b/server/src/test/java/com/cloud/network/security/SecurityGroupManagerImplIpv6RuleTest.java
new file mode 100644
index 00000000000..e2b7e96ff26
--- /dev/null
+++ 
b/server/src/test/java/com/cloud/network/security/SecurityGroupManagerImplIpv6RuleTest.java
@@ -0,0 +1,90 @@
+// 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.network.security;
+
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import com.cloud.network.NetworkModel;
+import com.cloud.network.security.SecurityGroupManagerImpl.PortAndProto;
+import com.cloud.network.security.SecurityRule.SecurityRuleType;
+import com.cloud.network.security.dao.SecurityGroupRuleDao;
+import com.cloud.network.security.dao.SecurityGroupVMMapDao;
+import com.cloud.vm.Nic;
+import com.cloud.vm.VirtualMachine.State;
+
+@RunWith(MockitoJUnitRunner.Silent.class)
+public class SecurityGroupManagerImplIpv6RuleTest {
+
+    @Mock
+    SecurityGroupRuleDao _securityGroupRuleDao;
+    @Mock
+    SecurityGroupVMMapDao _securityGroupVMMapDao;
+    @Mock
+    NetworkModel _networkModel;
+
+    @InjectMocks
+    SecurityGroupManagerImpl manager = new SecurityGroupManagerImpl();
+
+    @Test
+    public void securityGroupMemberRuleUsesExactIpv6HostCidr() {
+        Long vmId = 1L;
+        SecurityRuleType type = SecurityRuleType.IngressRule;
+
+        // The VM belongs to security group 10, which has one rule referencing 
another security group (20).
+        SecurityGroupVMMapVO groupMap = 
Mockito.mock(SecurityGroupVMMapVO.class);
+        when(groupMap.getSecurityGroupId()).thenReturn(10L);
+        
when(_securityGroupVMMapDao.listByInstanceId(vmId)).thenReturn(Collections.singletonList(groupMap));
+
+        SecurityGroupRuleVO rule = Mockito.mock(SecurityGroupRuleVO.class);
+        when(rule.getProtocol()).thenReturn("tcp");
+        when(rule.getStartPort()).thenReturn(80);
+        when(rule.getEndPort()).thenReturn(80);
+        when(rule.getAllowedNetworkId()).thenReturn(20L);
+        when(_securityGroupRuleDao.listBySecurityGroupId(10L, 
type)).thenReturn(Collections.singletonList(rule));
+
+        // Group 20 has one running member VM with both an IPv4 and an IPv6 
address.
+        SecurityGroupVMMapVO memberMap = 
Mockito.mock(SecurityGroupVMMapVO.class);
+        when(memberMap.getInstanceId()).thenReturn(2L);
+        when(_securityGroupVMMapDao.listBySecurityGroup(20L, 
State.Running)).thenReturn(Collections.singletonList(memberMap));
+
+        Nic nic = Mockito.mock(Nic.class);
+        when(nic.getIPv4Address()).thenReturn("10.1.1.5");
+        when(nic.getIPv6Address()).thenReturn("2001:db8::5");
+        when(_networkModel.getDefaultNic(2L)).thenReturn(nic);
+
+        Map<PortAndProto, Set<String>> allowed = 
manager.generateRulesForVM(vmId, type);
+
+        Assert.assertEquals(1, allowed.size());
+        Set<String> cidrs = allowed.values().iterator().next();
+        // The member must be authorized as an exact host, matching the IPv4 
/32 behaviour.
+        Assert.assertTrue("IPv4 member should be pinned to /32", 
cidrs.contains("10.1.1.5/32"));
+        Assert.assertTrue("IPv6 member should be pinned to the exact /128 
host", cidrs.contains("2001:db8::5/128"));
+        Assert.assertFalse("IPv6 member must not open the whole /64 subnet", 
cidrs.contains("2001:db8::5/64"));
+    }
+}

Reply via email to