Skip to content

Commit 8358dfa

Browse files
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.
1 parent 3d70ce4 commit 8358dfa

2 files changed

Lines changed: 91 additions & 1 deletion

File tree

server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ protected Map<PortAndProto, Set<String>> generateRulesForVM(Long userVmId, Secur
355355
cidr = cidr + "/32";
356356
cidrs.add(cidr);
357357
if (defaultNic.getIPv6Address() != null) {
358-
cidrs.add(defaultNic.getIPv6Address() + "/64");
358+
cidrs.add(defaultNic.getIPv6Address() + "/128");
359359
}
360360
}
361361
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package com.cloud.network.security;
18+
19+
import static org.mockito.Mockito.when;
20+
21+
import java.util.Collections;
22+
import java.util.Map;
23+
import java.util.Set;
24+
25+
import org.junit.Assert;
26+
import org.junit.Test;
27+
import org.junit.runner.RunWith;
28+
import org.mockito.InjectMocks;
29+
import org.mockito.Mock;
30+
import org.mockito.Mockito;
31+
import org.mockito.junit.MockitoJUnitRunner;
32+
33+
import com.cloud.network.NetworkModel;
34+
import com.cloud.network.security.SecurityGroupManagerImpl.PortAndProto;
35+
import com.cloud.network.security.SecurityRule.SecurityRuleType;
36+
import com.cloud.network.security.dao.SecurityGroupRuleDao;
37+
import com.cloud.network.security.dao.SecurityGroupVMMapDao;
38+
import com.cloud.vm.Nic;
39+
import com.cloud.vm.VirtualMachine.State;
40+
41+
@RunWith(MockitoJUnitRunner.Silent.class)
42+
public class SecurityGroupManagerImplIpv6RuleTest {
43+
44+
@Mock
45+
SecurityGroupRuleDao _securityGroupRuleDao;
46+
@Mock
47+
SecurityGroupVMMapDao _securityGroupVMMapDao;
48+
@Mock
49+
NetworkModel _networkModel;
50+
51+
@InjectMocks
52+
SecurityGroupManagerImpl manager = new SecurityGroupManagerImpl();
53+
54+
@Test
55+
public void securityGroupMemberRuleUsesExactIpv6HostCidr() {
56+
Long vmId = 1L;
57+
SecurityRuleType type = SecurityRuleType.IngressRule;
58+
59+
// The VM belongs to security group 10, which has one rule referencing another security group (20).
60+
SecurityGroupVMMapVO groupMap = Mockito.mock(SecurityGroupVMMapVO.class);
61+
when(groupMap.getSecurityGroupId()).thenReturn(10L);
62+
when(_securityGroupVMMapDao.listByInstanceId(vmId)).thenReturn(Collections.singletonList(groupMap));
63+
64+
SecurityGroupRuleVO rule = Mockito.mock(SecurityGroupRuleVO.class);
65+
when(rule.getProtocol()).thenReturn("tcp");
66+
when(rule.getStartPort()).thenReturn(80);
67+
when(rule.getEndPort()).thenReturn(80);
68+
when(rule.getAllowedNetworkId()).thenReturn(20L);
69+
when(_securityGroupRuleDao.listBySecurityGroupId(10L, type)).thenReturn(Collections.singletonList(rule));
70+
71+
// Group 20 has one running member VM with both an IPv4 and an IPv6 address.
72+
SecurityGroupVMMapVO memberMap = Mockito.mock(SecurityGroupVMMapVO.class);
73+
when(memberMap.getInstanceId()).thenReturn(2L);
74+
when(_securityGroupVMMapDao.listBySecurityGroup(20L, State.Running)).thenReturn(Collections.singletonList(memberMap));
75+
76+
Nic nic = Mockito.mock(Nic.class);
77+
when(nic.getIPv4Address()).thenReturn("10.1.1.5");
78+
when(nic.getIPv6Address()).thenReturn("2001:db8::5");
79+
when(_networkModel.getDefaultNic(2L)).thenReturn(nic);
80+
81+
Map<PortAndProto, Set<String>> allowed = manager.generateRulesForVM(vmId, type);
82+
83+
Assert.assertEquals(1, allowed.size());
84+
Set<String> cidrs = allowed.values().iterator().next();
85+
// The member must be authorized as an exact host, matching the IPv4 /32 behaviour.
86+
Assert.assertTrue("IPv4 member should be pinned to /32", cidrs.contains("10.1.1.5/32"));
87+
Assert.assertTrue("IPv6 member should be pinned to the exact /128 host", cidrs.contains("2001:db8::5/128"));
88+
Assert.assertFalse("IPv6 member must not open the whole /64 subnet", cidrs.contains("2001:db8::5/64"));
89+
}
90+
}

0 commit comments

Comments
 (0)