Tn9hia opened a new issue, #14432:
URL: https://github.com/apache/cloudstack/issues/14432
### problem
Since #10594 ("security groups: Do not use conntrack when it is not
required"), a VM can reach **any port** of another VM that runs **on the same
KVM host and the same bridge**, regardless of the destination VM's ingress
rules. Traffic from outside the host is still filtered correctly.
This breaks tenant isolation. The source and destination VMs can belong to
different accounts.
**Root cause**
Each bridge chain evaluates the source VM first (`BF-<br>-IN`, `--physdev-in
<src vif>`) and the destination VM second (`BF-<br>-OUT`, `--physdev-out <dst
vif>`):
```
-A BF-br -m physdev --physdev-is-in --physdev-is-bridged -j BF-br-IN
-A BF-br -m physdev --physdev-is-out --physdev-is-bridged -j BF-br-OUT
```
Since #10594, the source VM's egress chain ends in a terminating `ACCEPT`
when the security group has no egress rules (and allowed egress rules also use
`ACCEPT`):
```python
# add_network_rules()
if egressrule_v4 == 0 :
execute('iptables -A ' + egress_vmchain + ' -j ACCEPT')
```
The packet is accepted inside `BF-<br>-IN`, so `BF-<br>-OUT` is never
reached and the destination VM's ingress chain is never evaluated. In 4.20 the
egress chain ended with `RETURN`, which let the packet go on to the destination
VM's chains.
| | 4.20 | 4.23 / main |
|---|---|---|
| Egress rule matched | `RETURN` | `ACCEPT` |
| No egress rules (default allow) | `RETURN` | `ACCEPT` |
| Egress rules present, no match | `DROP` | `RETURN` → `DROP` at the end of
`-def` |
| End of `<vm>-def` for `--physdev-in` | none (implicit return) | `DROP` |
| Destination ingress enforced for same-host traffic | yes | **no** |
When the two VMs run on different hosts, the destination host receives the
packet on the uplink and evaluates `BF-<br>-OUT`, so ingress is enforced there.
The bug only affects VMs on the same host and bridge.
### versions
CLOUDSTACK VERSION: 4.23.0.0 (cloudstack-agent 4.23.0.0). Also present on
current main.
COMPONENT: KVM agent – scripts/vm/network/security_group.py (Security Groups)
OS: KVM host: Ubuntu 24.04, iptables-nft
CONFIGURATION:
- Advanced zone with security groups enabled
- Shared guest network (VLAN), Linux bridge (brXXX-<vlan>)
- net.bridge.bridge-nf-call-iptables = 1
### The steps to reproduce the bug
#### STEPS TO REPRODUCE
1. In an advanced zone with security groups, create two security groups:
* `SG-A`: no rules at all (default egress allow).
* `SG-B`: one ingress rule, TCP 22 from `1.1.1.1/32` only.
2. Deploy `VM-A` with `SG-A` and `VM-B` with `SG-B` on the same shared
network, and make sure both run on the **same KVM host**. For example, use a
single host, or migrate one VM to the other's host.
3. From a machine outside the host (not `1.1.1.1`), run `ssh <VM-B IP>`. The
connection is blocked, as expected.
4. From inside `VM-A`, run `ssh <VM-B IP>`. **The connection succeeds.**
#### EXPECTED RESULTS
```
VM-B's ingress rules are enforced for all sources, including VMs on the same
host.
SSH from VM-A to VM-B is dropped.
```
#### ACTUAL RESULTS
```
SSH from VM-A to VM-B succeeds. Only VM-A's egress rules are evaluated;
VM-B's ingress chain is skipped for same-host, same-bridge traffic.
```
### What to do about it?
#### PROPOSED FIX
Use `RETURN` instead of `ACCEPT` for allowed egress, so the packet goes back
to `BF-<br>` and continues to `BF-<br>-OUT`. This is the same pattern the
system VM chains use (`-A r-XXX-VM -m physdev --physdev-in vnetX -j RETURN`).
Traffic to the uplink is still accepted by `-A BF-<br> -m physdev --physdev-out
<uplink> -j ACCEPT`.
Explicit egress denial moves into the egress chain. `ESTABLISHED,RELATED` is
kept there so replies to allowed inbound connections are not dropped by the
egress rules.
```diff
@@ default_network_rules()
- execute("iptables -A " + vmchain_default + " -m physdev
--physdev-is-bridged --physdev-in " + vif + " -j DROP")
+ execute("iptables -A " + vmchain_default + " -m physdev
--physdev-is-bridged --physdev-in " + vif + " -j RETURN")
@@
- execute('ip6tables -A ' + vmchain_default + ' -m physdev
--physdev-is-bridged --physdev-in ' + vif + ' -j DROP')
+ execute('ip6tables -A ' + vmchain_default + ' -m physdev
--physdev-is-bridged --physdev-in ' + vif + ' -j RETURN')
@@ add_network_rules()
if rule['ruletype'] == 'E':
vmchain = egress_vmchain
direction = "-d"
- action = "ACCEPT"
+ action = "RETURN"
@@
if egressrule_v4 == 0 :
- execute('iptables -A ' + egress_vmchain + ' -j ACCEPT')
+ execute('iptables -A ' + egress_vmchain + ' -j RETURN')
else:
- execute('iptables -A ' + egress_vmchain + ' -j RETURN')
+ execute('iptables -A ' + egress_vmchain + ' -m state --state
ESTABLISHED,RELATED -j RETURN')
+ execute('iptables -A ' + egress_vmchain + ' -j DROP')
if egressrule_v6 == 0 :
- execute('ip6tables -A ' + egress_vmchain + ' -j ACCEPT')
+ execute('ip6tables -A ' + egress_vmchain + ' -j RETURN')
else:
- execute('ip6tables -A ' + egress_vmchain + ' -j RETURN')
+ execute('ip6tables -A ' + egress_vmchain + ' -m state --state
ESTABLISHED,RELATED -j RETURN')
+ execute('ip6tables -A ' + egress_vmchain + ' -j DROP')
```
We tested the IPv4 part on 4.23.0.0. After a stop/start of the VMs so the
`-def` chains are rebuilt:
* Same-host traffic from VM-A to VM-B is dropped by VM-B's ingress chain.
* External access that VM-B's ingress rules allow still works.
* VM outbound and internet access is unaffected.
The IPv6 part mirrors the IPv4 change but has not been tested.
Note: VMs added to `cs_notrack` (any rule with `protocol=all` from
`0.0.0.0/0`) have no conntrack state, so the `ESTABLISHED,RELATED` rule does
not match for them. That behaviour is unchanged from 4.23. Also, the
`conntrack*_not_needed` check does not look at the rule type, so an *egress*
`all 0.0.0.0/0` rule also disables conntrack for the VM. This may deserve a
separate look.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]