Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pan: application-override rule conversion #6651

Merged
merged 36 commits into from Feb 23, 2021
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9059f66
add tests
sfraint Feb 11, 2021
25004e6
convert SR per zone pair
sfraint Feb 11, 2021
40cf42c
Merge branch 'master' into pan-sr-zone-pair
sfraint Feb 12, 2021
2be4bb2
initial app-override parsing
sfraint Feb 12, 2021
fc2cb8a
add application-override VS class
sfraint Feb 12, 2021
eb02c98
Merge branch 'master' into pan-app-override
sfraint Feb 12, 2021
6ba8256
Merge branch 'pan-app-override-vs' into pan-app-override
sfraint Feb 12, 2021
c149678
add app-override rules to rulebase
sfraint Feb 12, 2021
73f735b
Merge branch 'pan-app-override-vs' into pan-app-override
sfraint Feb 12, 2021
b8f3281
parsing and extraction
sfraint Feb 13, 2021
f3f2f77
Merge branch 'master' into pan-app-override
sfraint Feb 13, 2021
58d44bc
trailing newline
sfraint Feb 13, 2021
9c424bf
Merge branch 'master' into pan-sr-conversion
sfraint Feb 13, 2021
e38ef75
Merge branch 'pan-sr-conversion' into pan-sr-zone-pair
sfraint Feb 13, 2021
5602a19
rename test config
sfraint Feb 16, 2021
1f0b288
propagate zones, test skeleton
sfraint Feb 16, 2021
507c6c8
merge pan-app-override
sfraint Feb 16, 2021
b784ead
save work
sfraint Feb 16, 2021
cf6550c
merge master
sfraint Feb 16, 2021
84667fc
save work
sfraint Feb 19, 2021
f6d58ad
cleanup, add validation
sfraint Feb 19, 2021
05a238d
merge master
sfraint Feb 19, 2021
0e4024f
comment
sfraint Feb 19, 2021
4ecf021
cleanup, comments
sfraint Feb 19, 2021
47ef105
comments
sfraint Feb 19, 2021
468570e
better test
sfraint Feb 19, 2021
f114da9
fix test
sfraint Feb 19, 2021
ae405bf
fix merge issue
sfraint Feb 19, 2021
68d3ad8
merge pan-sr-zone-pair
sfraint Feb 19, 2021
5ce8d9b
rely on validation
sfraint Feb 19, 2021
14d0dad
fix tests
sfraint Feb 20, 2021
b147b02
merge master
sfraint Feb 20, 2021
e6d609d
pmd
sfraint Feb 20, 2021
26a581a
comments and cleanup
sfraint Feb 20, 2021
4877ced
review comments
sfraint Feb 22, 2021
5a539d9
backout unneeded test config change
sfraint Feb 23, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -11,6 +11,7 @@
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNullableByDefault;
import org.batfish.datamodel.IntegerSpace;
import org.batfish.datamodel.IpProtocol;
import org.batfish.datamodel.SubRange;

/** PAN datamodel component containing application-override rule configuration */
Expand Down Expand Up @@ -116,6 +117,13 @@ public Protocol getProtocol() {
}

@Nullable
public IpProtocol getIpProtocol() {
return _protocol == Protocol.TCP
? IpProtocol.TCP
: _protocol == Protocol.UDP ? IpProtocol.UDP : null;
}

@Nonnull
public IntegerSpace getPort() {
return _port;
}
Expand Down

Large diffs are not rendered by default.

Expand Up @@ -105,6 +105,20 @@ public static TraceElement matchApplicationObjectTraceElement(
.build();
}

@VisibleForTesting
public static TraceElement matchApplicationOverrideRuleTraceElement(
String name, String vsysName, String filename) {
return TraceElement.builder()
.add("Matched application-override rule ")
.add(
name,
new VendorStructureId(
filename,
PaloAltoStructureType.APPLICATION_OVERRIDE_RULE.getDescription(),
computeObjectName(vsysName, name)))
.build();
}

@VisibleForTesting
public static TraceElement matchBuiltInApplicationTraceElement(String name) {
return TraceElement.of(String.format("Matched built-in application %s", name));
Expand Down
Expand Up @@ -85,6 +85,7 @@
import static org.batfish.representation.palo_alto.PaloAltoTraceElementCreators.intrazoneDefaultAcceptTraceElement;
import static org.batfish.representation.palo_alto.PaloAltoTraceElementCreators.matchAddressAnyTraceElement;
import static org.batfish.representation.palo_alto.PaloAltoTraceElementCreators.matchAddressValueTraceElement;
import static org.batfish.representation.palo_alto.PaloAltoTraceElementCreators.matchApplicationAnyTraceElement;
import static org.batfish.representation.palo_alto.PaloAltoTraceElementCreators.matchDestinationAddressTraceElement;
import static org.batfish.representation.palo_alto.PaloAltoTraceElementCreators.matchSecurityRuleTraceElement;
import static org.batfish.representation.palo_alto.PaloAltoTraceElementCreators.matchServiceAnyTraceElement;
Expand Down Expand Up @@ -2137,6 +2138,7 @@ public void testIfaceOutgoingFilterTraceElements() {
isTraceTree(
matchDestinationAddressTraceElement(),
isTraceTree(matchAddressAnyTraceElement())),
isTraceTree(matchApplicationAnyTraceElement()),
isTraceTree(matchServiceAnyTraceElement()))))));

// Flow matching PERMIT security rule should generate a trace pointing to that rule.
Expand Down Expand Up @@ -2164,6 +2166,7 @@ public void testIfaceOutgoingFilterTraceElements() {
isTraceTree(
matchDestinationAddressTraceElement(),
isTraceTree(matchAddressAnyTraceElement())),
isTraceTree(matchApplicationAnyTraceElement()),
isTraceTree(matchServiceAnyTraceElement()))))));
}

Expand Down
Expand Up @@ -291,6 +291,7 @@ public void testRulebaseTracing() {
Flow rule2bFlow = createFlow("1.1.4.10", "1.1.1.10", IpProtocol.TCP, 0, 179);
Flow rule3Flow = createFlow("1.1.4.10", "1.1.1.10", IpProtocol.TCP, 0, 1234);
Flow rule4Flow = createFlow("1.1.1.10", "1.1.4.10", IpProtocol.TCP, 0, 53);
Flow rule3pt5Flow = createFlow("1.1.1.10", "10.12.14.16", IpProtocol.TCP, 0, 80);

IpAccessList filter = c.getIpAccessLists().get(crossZoneFilterName);
BiFunction<String, Flow, List<TraceTree>> trace =
Expand All @@ -316,6 +317,7 @@ public void testRulebaseTracing() {
isTraceTree(
matchDestinationAddressTraceElement(),
isTraceTree(matchAddressObjectTraceElement("addr2", "vsys1", filename))),
isTraceTree(matchApplicationAnyTraceElement()),
isTraceTree(matchServiceTraceElement()))));
}
{
Expand All @@ -331,6 +333,7 @@ public void testRulebaseTracing() {
isTraceTree(
matchDestinationAddressTraceElement(),
isTraceTree(matchAddressObjectTraceElement("addr2", "vsys1", filename))),
isTraceTree(matchApplicationAnyTraceElement()),
isTraceTree(matchBuiltInServiceTraceElement()))));
}
{
Expand Down Expand Up @@ -385,6 +388,21 @@ public void testRulebaseTracing() {
matchServiceApplicationDefaultTraceElement(),
isTraceTree(matchApplicationAnyTraceElement())))));
}
{
List<TraceTree> traces = trace.apply(iface1, rule3pt5Flow);
assertThat(
traces,
contains(
isTraceTree(
matchSecurityRuleTraceElement("RULE3pt5", "vsys1", filename),
isTraceTree(
matchSourceAddressTraceElement(), isTraceTree(matchAddressAnyTraceElement())),
isTraceTree(
matchDestinationAddressTraceElement(),
isTraceTree(matchAddressValueTraceElement("10.12.14.16"))),
isTraceTree(matchBuiltInApplicationTraceElement("aol-messageboard-posting")),
isTraceTree(matchBuiltInServiceTraceElement()))));
}
{
List<TraceTree> traces = trace.apply(iface1, rule4Flow);
assertThat(
Expand All @@ -402,6 +420,7 @@ public void testRulebaseTracing() {
isTraceTree(
matchNegatedAddressTraceElement(),
isTraceTree(matchAddressValueTraceElement("10.11.11.0/24")))),
isTraceTree(matchApplicationAnyTraceElement()),
isTraceTree(matchServiceAnyTraceElement()))));
}
}
Expand Down Expand Up @@ -457,4 +476,264 @@ public void testSecurityRules() throws IOException {
assertEquals(
traces.get(flowPermitZ4).get(0).getDisposition(), FlowDisposition.DELIVERED_TO_SUBNET);
}

@Test
public void testApplicationOverrideConditions() throws IOException {
String hostname = "application-override-conditions";
Configuration c = parseConfig(hostname);

int customApp1Port = 7653;
String if1name = "ethernet1/1"; // 10.0.1.1/24
String if3name = "ethernet1/3"; // 10.0.3.1/24

Batfish batfish = getBatfish(ImmutableSortedMap.of(c.getHostname(), c), _folder);
NetworkSnapshot snapshot = batfish.getSnapshot();
batfish.computeDataPlane(snapshot);

Flow flowPermit =
Flow.builder()
.setIngressNode(c.getHostname())
// Arbitrary source port
.setSrcPort(12345)
.setIpProtocol(IpProtocol.TCP)
.setIngressInterface(if1name)
.setSrcIp(Ip.parse("10.0.1.2"))
.setDstIp(Ip.parse("10.0.2.2"))
.setDstPort(customApp1Port)
.build();
Flow flowRejectPort =
flowPermit.toBuilder()
// Some dest port other than our custom app port
.setDstPort(customApp1Port - 1)
.build();
Flow flowRejectFromZone = flowPermit.toBuilder().setIngressInterface(if3name).build();
// Dest IP corresponding to a different zone (but still permitted by dest filter)
Flow flowRejectToZone = flowPermit.toBuilder().setDstIp(Ip.parse("10.0.1.3")).build();
Flow flowRejectSrc = flowPermit.toBuilder().setSrcIp(Ip.parse("10.0.1.3")).build();
Flow flowRejectDst = flowPermit.toBuilder().setSrcIp(Ip.parse("10.0.2.3")).build();
Flow flowRejectProtocol = flowPermit.toBuilder().setIpProtocol(IpProtocol.UDP).build();

SortedMap<Flow, List<Trace>> traces =
batfish
.getTracerouteEngine(snapshot)
.computeTraces(
ImmutableSet.of(
flowPermit,
flowRejectPort,
flowRejectFromZone,
flowRejectToZone,
flowRejectSrc,
flowRejectDst,
flowRejectProtocol),
false);

// Confirm flows not matching the rule are denied out
assertEquals(traces.get(flowRejectPort).get(0).getDisposition(), FlowDisposition.DENIED_OUT);
assertEquals(
traces.get(flowRejectFromZone).get(0).getDisposition(), FlowDisposition.DENIED_OUT);
assertEquals(traces.get(flowRejectToZone).get(0).getDisposition(), FlowDisposition.DENIED_OUT);
assertEquals(traces.get(flowRejectSrc).get(0).getDisposition(), FlowDisposition.DENIED_OUT);
assertEquals(traces.get(flowRejectDst).get(0).getDisposition(), FlowDisposition.DENIED_OUT);
assertEquals(
traces.get(flowRejectProtocol).get(0).getDisposition(), FlowDisposition.DENIED_OUT);

// Confirm flow matching rule is successful
assertTrue(traces.get(flowPermit).get(0).getDisposition().isSuccessful());
}

@Test
public void testApplicationOverrideShadowing() throws IOException {
String hostname = "application-override-shadowing";
Configuration c = parseConfig(hostname);

int customApp1Port = 7653;
int customApp4Port = 6542;
int sshPort = 22;
String if1name = "ethernet1/1"; // 10.0.1.1/24

Batfish batfish = getBatfish(ImmutableSortedMap.of(c.getHostname(), c), _folder);
NetworkSnapshot snapshot = batfish.getSnapshot();
batfish.computeDataPlane(snapshot);

// Matches OVERRIDE_APP_RULE1 (app CUSTOM_APP1)
Flow flowCustomApp1 =
Flow.builder()
.setIngressNode(c.getHostname())
// Arbitrary source port
.setSrcPort(12345)
.setDstPort(customApp1Port)
.setIpProtocol(IpProtocol.TCP)
.setIngressInterface(if1name)
.setSrcIp(Ip.parse("10.0.1.2"))
.setDstIp(Ip.parse("10.0.2.2"))
.build();
// Matches OVERRIDE_APP_RULE2 (app CUSTOM_APP2)
Flow flowCustomApp2 = flowCustomApp1.toBuilder().setDstPort(sshPort).build();
// Similar to CUSTOM_APP1, but matches OVERRIDE_APP_RULE3 (app CUSTOM_APP3) due to source addr
Flow flowCustomApp3 = flowCustomApp1.toBuilder().setSrcIp(Ip.parse("1.0.1.2")).build();
// Similar to CUSTOM_APP2, but matches app SSH due to source addr
Flow flowSSH = flowCustomApp2.toBuilder().setSrcIp(Ip.parse("1.0.1.2")).build();

// Match one source address for CUSTOM_APP4
Flow flowCustomApp4a =
flowCustomApp1.toBuilder()
.setDstPort(customApp4Port)
.setSrcIp(Ip.parse("10.0.1.2"))
.build();
// Match another source address for CUSTOM_APP4
Flow flowCustomApp4b =
flowCustomApp1.toBuilder()
.setDstPort(customApp4Port)
.setSrcIp(Ip.parse("10.0.1.3"))
.build();

SortedMap<Flow, List<Trace>> traces =
batfish
.getTracerouteEngine(snapshot)
.computeTraces(
ImmutableSet.of(
flowCustomApp1,
flowCustomApp2,
flowCustomApp3,
flowSSH,
flowCustomApp4a,
flowCustomApp4b),
false);

// Test application-override rule shadowing another application-override rule
// Flow is flagged as CUSTOM_APP1
assertEquals(traces.get(flowCustomApp1).get(0).getDisposition(), FlowDisposition.DENIED_OUT);
// Similar to CUSTOM_APP1 but w/ different source addr, so flagged as CUSTOM_APP3
assertTrue(traces.get(flowCustomApp3).get(0).getDisposition().isSuccessful());

// Test application-override rule shadowing a built-in application
// Flow is flagged as CUSTOM_APP2
assertEquals(traces.get(flowCustomApp2).get(0).getDisposition(), FlowDisposition.DENIED_OUT);
// Similar to CUSTOM_APP2, but w/ different source addr, so flagged as SSH (not a custom app)
assertTrue(traces.get(flowSSH).get(0).getDisposition().isSuccessful());

// Confirm application definition can come from multiple application-override rules
// Flow matching OVERRIDE_APP_RULE4 (CUSTOM_APP4) is allowed
assertTrue(traces.get(flowCustomApp4a).get(0).getDisposition().isSuccessful());
// Flow matching OVERRIDE_APP_RULE5 (CUSTOM_APP4) is allowed
assertTrue(traces.get(flowCustomApp4b).get(0).getDisposition().isSuccessful());
}

@Test
public void testApplicationOverrideNatInteraction() throws IOException {
String hostname = "application-override-nat-interaction";
Configuration c = parseConfig(hostname);

int customAppDnatPort = 7653;
int customAppSnatPort = 6542;
String if1name = "ethernet1/1"; // 10.0.1.1/24

Batfish batfish = getBatfish(ImmutableSortedMap.of(c.getHostname(), c), _folder);
NetworkSnapshot snapshot = batfish.getSnapshot();
batfish.computeDataPlane(snapshot);

Flow flowCustomAppDnat =
Flow.builder()
.setIngressNode(c.getHostname())
// Arbitrary source port
.setSrcPort(12345)
.setDstPort(customAppDnatPort)
.setIpProtocol(IpProtocol.TCP)
.setIngressInterface(if1name)
.setSrcIp(Ip.parse("10.0.1.2"))
.setDstIp(Ip.parse("10.0.3.100"))
.build();

Flow flowCustomAppSnat =
Flow.builder()
.setIngressNode(c.getHostname())
// Arbitrary source port
.setSrcPort(12345)
.setDstPort(customAppSnatPort)
.setIpProtocol(IpProtocol.TCP)
.setIngressInterface(if1name)
.setSrcIp(Ip.parse("10.0.1.2"))
.setDstIp(Ip.parse("10.0.2.2"))
.build();

SortedMap<Flow, List<Trace>> traces =
batfish
.getTracerouteEngine(snapshot)
.computeTraces(ImmutableSet.of(flowCustomAppDnat, flowCustomAppSnat), false);

// Confirm CUSTOM_APP_DNAT is recognized, matching pre-DNAT addr and post-NAT zone
assertTrue(traces.get(flowCustomAppDnat).get(0).getDisposition().isSuccessful());

// Confirm CUSTOM_APP_SNAT flow is recognized, matching pre-SNAT addr & post-NAT zone
assertTrue(traces.get(flowCustomAppSnat).get(0).getDisposition().isSuccessful());
}

@Test
public void testApplicationOverrideServiceInteraction() throws IOException {
String hostname = "application-override-service-interaction";
Configuration c = parseConfig(hostname);

int customApp7652Port = 7652;
int customApp7653Port = 7653;
String if1name = "ethernet1/1"; // 10.0.1.1/24
String if3name = "ethernet1/3"; // 10.0.3.1/24
String if4name = "ethernet1/4"; // 10.0.4.1/24

Batfish batfish = getBatfish(ImmutableSortedMap.of(c.getHostname(), c), _folder);
NetworkSnapshot snapshot = batfish.getSnapshot();
batfish.computeDataPlane(snapshot);

// z1 -> z2 flow matching application and service
Flow flowZ1toZ2 =
Flow.builder()
.setIngressNode(c.getHostname())
// Arbitrary source port
.setSrcPort(12345)
.setDstPort(customApp7653Port)
.setIpProtocol(IpProtocol.TCP)
.setIngressInterface(if1name)
.setSrcIp(Ip.parse("10.0.1.2"))
.setDstIp(Ip.parse("10.0.2.2"))
.build();

// z3 -> z2 flow matching application, *not* service
Flow flowZ3toZ2MatchApp =
flowZ1toZ2.toBuilder()
.setIngressInterface(if3name)
.setSrcIp(Ip.parse("10.0.3.2"))
.setDstPort(customApp7652Port)
.build();
// z3 -> z2 flow matching service, *not* application
Flow flowZ3toZ2MatchService =
flowZ3toZ2MatchApp.toBuilder().setDstPort(customApp7653Port).build();

// z4 -> z2 flow matching service, *not* built-in application default port
Flow flowZ4toZ2MatchService =
flowZ3toZ2MatchService.toBuilder()
.setIngressInterface(if4name)
.setSrcIp(Ip.parse("10.0.4.2"))
.setDstPort(customApp7653Port)
.build();

SortedMap<Flow, List<Trace>> traces =
batfish
.getTracerouteEngine(snapshot)
.computeTraces(
ImmutableSet.of(
flowZ1toZ2, flowZ3toZ2MatchApp, flowZ3toZ2MatchService, flowZ4toZ2MatchService),
false);

// Flow matching service and application-override rule should be permitted
assertTrue(traces.get(flowZ1toZ2).get(0).getDisposition().isSuccessful());

// Flow matching service should be permitted
// Even if it doesn't match built-in application default port
assertTrue(traces.get(flowZ4toZ2MatchService).get(0).getDisposition().isSuccessful());

// Flow matching service or overridden application (but not both) should not be permitted
assertEquals(
traces.get(flowZ3toZ2MatchApp).get(0).getDisposition(), FlowDisposition.DENIED_OUT);
assertEquals(
traces.get(flowZ3toZ2MatchService).get(0).getDisposition(), FlowDisposition.DENIED_OUT);
}
}