Gateway Peering ACLs
A Gateway Peering can carry an Access Control List
(ACL): an ordered list of rules that decides, for each new flow or packet,
whether the gateway admits it or drops it. ACLs are configured via the
optional, peering-scoped spec.acl field.
ACLs are opt-in: a peering with no spec.acl behaves exactly as described
in Gateway Peering.
The full list of fields is in the Fabric API reference.
Traffic filtering in Gateway Peerings
Several mechanisms restrict what traffic can flow between the two VPCs of a
Gateway Peering. The first mechanism is the definition of the peering itself,
via the prefixes in the expose blocks: if a packet does not match prefixes
from exposed blocks in a peering, it is dropped.
Some stateful NAT modes, such as masquerade and port-forwarding, add a second restriction: a flow may only be initiated in one direction. When masquerade is set-up, a flow can only be initiated from the VPC with the masqueraded prefixes; a packet sent towards a masqueraded host will be dropped if the gateway has no associated stateful flow entry in its table, even if the address is otherwise valid with regards to the peering definition. Conversely, flows can only be initiated towards, and never from, an endpoint using port-forwarding.
ACL rules come as a third mechanism, and add explicit allow/deny decisions based on 5-tuples. They come on top of the restrictions implied by the Gateway Peering definition, so an ACL rule will never allow any flow that is not permitted by the prefixes exposed, and NAT restrictions, in the peering.
ACL configuration
To configure an ACL, use field spec.acl next to spec.peering. The ACL
applies to the whole peering. The YAML definition for the expose blocks
remains unchanged. Here is an example:
This peering lets hosts in prefix 10.0.1.0/24 from vpc-app open TCP
connections on port 5432 to hosts with prefix 10.0.2.0/24 from vpc-db,
and nothing else (default: deny) in either direction.
Rule evaluation order
The gateway evaluates rules in spec.acl.rules top-down: the first rule
whose from/to and match pattern match the flow decides the verdict, and
evaluation stops immediately. If no rule matches, spec.acl.default applies.
Conflicts between rules are resolved purely by order, so a broad rule placed
early will shadow the more specific rules that follow it.
Rule direction: from and to
Every rule specifies a direction. Fields from and to must each
name one of the peering's two sides, using the same names as the keys under
spec.peering (externals included, prefixed with ext.). A rule applies to
packets emitted by from towards to.
Because a peering always has exactly two sides, one of from or to may be
omitted, and the other side is implied. Omitting both is not allowed.
Depending on the scope specified for the rule, rules may also apply to
packets of an established flow's reverse direction. Refer to the section on
rule scope for details.
Matching patterns
The match block defines what packets (or flows) an ACL rule applies to.
NAT considerations
The gateway evaluates ACL rules for a packet before any NAT is applied.
This means that the patterns in the match block are compared with the real
source address and the destination address the initiator dialed
(typically, the address as exposed as a service).
For example, this allows writing rules to let endpoint 10.0.1.1 reach a
server exposed with port-forwarding as 10.0.2.1, regardless of what address
the server uses in its own VPC.
Match block
The match block is a 5-tuple, expressed from the initiator's point of view:
srccontains thefromside's native address prefixes and portsdstcontains thetoside's advertised address prefixes and ports (from itsexpose'sas, oripswhere no NAT applies)protocan betcp,udp, or a quoted protocol number (for example:"132"for SCTP)
src and dst are lists of
match endpoints. Each
entry carries an IP prefix and an optional list of ports, where a port entry is
either a single port such as "80" (do not omit the quotes) or an inclusive
range such as "8000-8100". The prefix takes the same shape as an expose
entry: either a literal cidr, or a vpcSubnet reference naming a VPC subnet.
At most one of cidr and vpcSubnet can be set on a given entry.
Tip
Field proto supports values tcp, udp, or a quoted protocol number. An
icmp keyword should be added in the future, with related matching options
such as error types; until then, you can use protocol number "1" to match
ICMP packets.
Optional match fields
Everything in match is optional, and omitting a field widens the rule:
| Field | If ommitted, then... |
|---|---|
src, dst |
the rule matches any source or destination address and port within the peering configuration, respectively |
src.{cidr,vpcSubnet}, dst.{cidr,vpcSubnet} |
the rule matches any source or destination IP address within the peering configuration (respectively), but can still be restricted to specified port ranges |
src.ports, dst.ports |
the rule matches all source or destination ports within the peering configuration, respectively |
proto |
the rule matches any protocol |
If the match block is omitted altogether, the rule matches every flow in the
specified direction: this is how to write a generic allow or deny for one
direction.
Rule scope
The scope field in each rule selects whether the rule is stateful:
flow(the default) makes the rule bidirectional for established connections. Once a flow is admitted, its return traffic is matched by the flow table entry and allowed even if it does not match any other explicit allow rule. The connection can still only be initiated in the rule's direction.packetmakes the rule stateless. Return traffic is not implicitly allowed and must be permitted by a separate rule.
Note that under default: deny, a rule with scope: packet and no matching
reverse rule admits the forward direction while dropping every reply. Add
an explicit rule for the return direction whenever you use scope: packet and
need bidirectional traffic.
Also, note that reply traffic for allow rules with scope: flow may still be
dropped if it matches an explicit deny rule in the list: packets are evaluated
as potential "return traffic" only after going through a first direct lookup
against the ACL rules table.
The scope does not matter in the case of deny rules, given that no flow
matching this rule can be initiated in the first place.
Warning
Due to internal limitations for the gateway, scope: flow is currently
restricted to peerings for which all flows use masquerade or
port-forwarding. Flows relying on static NAT, or no NAT at all, do not
currently generate stateful entries in the gateway's table. This limitation
will be lifted in a future release.
Default action
When no rule matches, the default verdict from spec.acl.default is
applied. The field can take one of two values:
deny-unless-exposed(the default) allows packets to go through, even when no rule matched. The name of the option is a reminder that ACL rules apply on top of the peering configuration, and that packets only go through if they correspond to the peering setup, even if a wider ACL rule would otherwise authorize them.denymeans that packets not explicitly allowed by a rule (or corresponding to return traffic for an established flow allowed by a rule withscope: flow) are dropped.
Logging
Set the log field of a rule to true to log all packets matching that rule
(as well as corresponding reply traffic, for rules with scope: flow).
Examples
This section contains some example use cases and configuration for the peering ACLs.
Restricting an exposed service
VPC vpc-01 exposes a prefix masqueraded behind 100.64.10.0/24; on the other
side, vpc-02 publishes a backend as 100.64.20.10:443. Only vpc-01's
workload subnet may reach it.
A flow from 10.0.1.5 to 100.64.2.10:443 is evaluated before NAT, so the
rule matches vpc-01's real source address even though the packets leave the
gateway masqueraded behind 100.64.1.0/24. Based on the scope, reply traffic
is automatically allowed.
Egress allow-list towards an external
A private subnet reaches the Internet through masquerade, with a strict list of allowed destination ports, and one carve-out rule placed ahead of it.
Match field src is omitted throughout, so it matches any address
vpc-internal exposes through the peering. The deny rule come first because
the first match decides the verdict: moving allow-web above
block-blackholed-ranges would allow HTTP/HTTPS towards the blackholed ranges.
Bidirectional traffic with static NAT
A static one-to-one peering where each side may open its own connections. Each
side is named in the address space it dials: corp reaches the bench at its
advertised 198.51.100.10, while the bench reaches corp at its real address,
since corp applies no NAT.
In the following setup, vpc-01 exposes a prefix using static NAT, and uses an
ACL list to restrict communications to a few hosts within the prefix. Rules
applies per packet and not per flow (scope: packet), meaning that the return
traffic has to be explicitly allowed.
Endpoint 10.0.2.28 from vpc-02 can communicate with 10.0.1.10 from
vpc-01 both ways, thanks to the first rule allowing traffic from 10.0.1.10;
however, endpoint 10.0.2.12 from vpc-02 can emit towards 10.0.1.10, but
will not receive any return traffic.