Op 24-07-2026 om 05:55 schreef Andrija Panic:
Hi all,
I'd like to propose changing the default egress policy for the built-in
Isolated guest network offerings f*rom "deny" to "allow", *made
configurable via a new global setting. A working PR is up for reference:
https://github.com/apache/cloudstack/pull/13684
I would argue that this is a good change. Similar I see this with
inbound security groups, it still confuses a lot of people who deploy a
VM and can't SSH to it, but that's a different discussion.
Egress traffic is something which should probably just work. VMs need to
download updates, sync their time, send out a ping somewhere.
Wido
*Background / current behaviour*
An Isolated network created from the shipped default offering
(DefaultIsolatedNetworkOfferingWithSourceNatService, and
DefaultIsolatedNetworkOffering) currently blocks all egress by default.
This comes from how these offerings are seeded in ConfigurationServerImpl:
they are built with a NetworkOfferingVO constructor that does not set
egressdefaultpolicy, so it falls back to the Java primitive default (false
= deny). The DB column default is also false.
Meanwhile:
- createNetworkOffering, when called without an explicit
egressdefaultpolicy, already defaults to ALLOW
(NetworkOfferingBaseCmd#getEgressDefaultPolicy returns true when null).
- *VPC tiers*, governed by NetworkACL, *use the default "allow*" ACL and
permit egress out of the box.
*So we ship two inconsistent defaults *(built-in offering = deny,
API-created offering = allow), and the flagship "simple Isolated network"
experience is the more surprising of the two.
Motivation
*(frustration mainly, of course, with zero actual security reasons to block
egress traffic)*
Deny-by-default egress on a NAT'd Isolated network provides very little
real security value: the network already has no inbound reachability
without explicit port-forwarding/static-NAT/LB + firewall rules. The egress
default mostly serves as a first-boot foot-gun: freshly deployed VMs cannot
reach package mirrors, NTP, metadata services, etc. until the operator
discovers they must add an allow-all egress rule. It's one of the most
common "why is my new network broken" support questions, and it differs from
what VPC tiers do.
*Proposal*
1. Add a global setting
network.isolated.default.egress.policy.allow (Boolean,
default = true).
2. Seed the two built-in default Isolated network offerings with an
egress default policy taken from that setting (allow by default).
3. Use the same setting as the fallback when createNetworkOffering is
called without an explicit egressdefaultpolicy, so the built-in and API
paths agree.
Operators who prefer deny-by-default set the flag to false before first
initialization (or simply create/keep a custom offering with
egressdefaultpolicy = deny — that path is unchanged).
*Backward compatibility*
This is deliberately scoped to be safe:
- Existing deployments are untouched. The default offerings are seeded
once, at first initialization; upgrades do not re-seed them. No data
migration runs against existing network_offerings rows or existing
networks, so every already-created offering and network keeps its current
policy.
- Only brand-new deployments (and offerings explicitly created without
the parameter) get allow-by-default.
- The egressdefaultpolicy parameter and the per-offering behaviour are
unchanged; this only changes a default.
The one genuine consideration is security posture: anyone standing up a
*new* cloud who relied on the historical deny-by-default of the built-in
offering would now get allow-by-default. That's exactly why it's a
documented, release-noted, operator-overridable setting rather than a
silent flip. I'd propose calling it out prominently in the release notes and
upgrade docs.
Note I am intentionally NOT changing the egress_default_policy DB column
default, nor adding an UPDATE against existing rows — the application
always sets the value explicitly on insert, and touching existing rows
would break the compatibility guarantee above.
*Questions for the list*
- Any objection to allow-by-default being the shipped default (vs.
keeping deny and only adding the knob)?
- Preferred scope for the setting — Global (as drafted) or Zone?
- Anything in Marvin / integration suites that asserts the current
deny-by-default that we should update alongside this?
-
The PR (5 files, ~25 lines) is linked above for concreteness — happy to hold
it until there's rough consensus on direction.
Thanks,
Andrija