Same Policy, Different Enforcement: Translating Firewall Rules Across Clouds Without Opening Holes
Every multi-cloud estate eventually copies a firewall rule from one provider to another and assumes the copy means the same thing. It rarely does. A rule that reads "allow the app tier to reach the database on 5432" can
Every multi-cloud estate eventually copies a firewall rule from one provider to another and assumes the copy means the same thing. It rarely does.
A rule that reads "allow the app tier to reach the database on 5432" can be tight in one cloud and surprisingly open in another — not because anyone typed the wrong port, but because the four major clouds disagree on defaults, statefulness, identity, and what happens when no rule matches. The translation error is silent. Tests from inside the allowed path pass, and the extra exposure only shows up in an audit, a scan, or an incident.
The same sentence means four different things
Start with the building blocks, because they do not line up one-to-one.
AWS security groups are stateful and allow-only. Return traffic for an allowed connection is permitted automatically, there is no explicit deny, and a workload with no matching allow simply cannot connect. Groups attach to interfaces, and rules can reference other security groups instead of addresses.
Azure network security groups are also stateful, but they include explicit deny rules and a set of built-in default rules with fixed priorities. Evaluation is by priority number, so a translated rule can lose to a default or to an earlier rule the team forgot existed. Application security groups add an identity layer that has no exact equivalent elsewhere.
Google Cloud VPC firewall rules are stateful for allowed connections, support allow and deny, and are evaluated by priority with an implied deny for ingress and an implied allow for egress at the lowest priority. That implied egress allow catches teams out: a policy described as "deny everything except these flows" is not what a literal translation produces unless egress is handled explicitly.
Oracle Cloud security lists and network security groups split the model differently again. Security lists apply to a whole subnet, are stateless unless marked otherwise, and need matching ingress and egress rules for return traffic. Network security groups are the more workload-scoped option, but a rule copied from a stateful system into a stateless list will break returning traffic in ways that look like intermittent application timeouts.
None of these models is wrong. They are just different machines, and a port number is the only part that translates cleanly.
Five translation traps that open holes
Assuming return traffic works the same way. Stateful in one cloud, stateless in another turns a working rule into a half-open conversation, and the usual fix under pressure — "just open the ephemeral range both ways" — is where the real exposure gets created.
Forgetting the implied rules. Defaults sit below or above the translated rules depending on the platform. An explicit deny written for one provider may be unnecessary there and essential in another, because the implied final action differs by direction.
Translating addresses instead of identities. A rule referencing a security group, a service tag, or a workload identity in the source cloud often becomes a broad CIDR range in the target cloud, because the target has no equivalent group handy. Each such substitution quietly widens the rule from "these instances" to "anything in this subnet, present and future."
Losing the logging difference. Some platforms log allowed and denied traffic by default once flow logs are on; others log only what a specific rule or setting captures. A translated policy can therefore enforce roughly correctly while producing no evidence in the new cloud — a problem that surfaces during an audit, not during testing.
Copying the exception, not the intent. Estates accumulate temporary rules: a vendor range for a weekend, a debugging allow from an office address. Copied forward cloud to cloud, the exception outlives its reason and becomes indistinguishable from designed policy.
Translate intent, not rules
The fix is to keep one step between the policy people mean and the rules each cloud enforces.
Write the intent once, in provider-neutral terms: which workload identity can reach which workload identity, on which protocol and port, in which direction, initiated by which side, and logged how. Keep that list short enough to review. A useful intent table names services, not subnets — "checkout can initiate to payments on 8443, both directions logged, no other east-west path" survives a migration; "10.4.0.0/16 to 10.7.3.0/24 on 8443" does not.
Then compile that intent separately for each cloud, deliberately, using each platform's native identity construct where it exists, and accepting that the resulting rule counts will differ. The Azure version may need explicit denies the AWS version does not. The Oracle version may need paired stateless rules. That divergence is correct — it is what "same policy" actually requires.
Finally, test the negative cases, not just the allowed path. For every intent, there are flows that must fail: the app tier reaching the database on any other port, a different workload in the same subnet reaching it at all, and an unrelated cloud reaching it over the interconnect. Automated checks that attempt those connections and expect refusal catch translation drift continuously, including drift introduced later by console edits in only one cloud.
A short drift check that pays for itself
Once a quarter, or on every network change, export the effective rules from each cloud and re-derive the intent table from them. Where the derived table has a flow the written intent does not, one of three things happened: a translation widened it, an exception never expired, or the intent changed and was never written down. All three are worth knowing before an auditor or an attacker finds them.
Pay particular attention to three rows in that comparison: any rule whose source is a whole VPC or VNet range rather than a workload identity, any egress rule broader than the ingress policy that supposedly matches it, and any rule with no corresponding log record in a test window. In practice those three patterns account for most of the silent widening in estates that started from a sound design.
The point of the exercise
Multi-cloud does not require identical tooling. It requires identical intent, enforced differently on purpose, with the differences written down. When the policy lives only as scattered provider-specific rules, every migration, audit, and incident response starts by reverse-engineering what the estate was supposed to allow.
Teams that run these estates day to day — including the work described at CloudVerg around VergShield zero-trust policy and VergMesh private connectivity — tend to treat firewall translation as a review step, never a copy-paste step. However the estate is built, the principle holds: if two clouds enforce the same sentence differently, the work is to name that difference before production does it for you.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.