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

feat(anta.tests): Added testcase to verify IPv4 ACL sequence entries #544

Merged
merged 5 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 77 additions & 4 deletions anta/tests/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
from datetime import datetime
from typing import List, Union

from pydantic import BaseModel, conint, model_validator
from pydantic import BaseModel, Field, conint, model_validator

from anta.custom_types import EcdsaKeySize, EncryptionAlgorithm, RsaKeySize
from anta.models import AntaCommand, AntaTest
from anta.models import AntaCommand, AntaTemplate, AntaTest
from anta.tools.get_item import get_item
from anta.tools.get_value import get_value
from anta.tools.utils import get_failed_logs

Expand Down Expand Up @@ -381,7 +382,8 @@ def test(self) -> None:

class VerifyBannerLogin(AntaTest):
"""
This class verifies the login banner of a device.
Verifies the login banner of a device.

Expected results:
* success: The test will pass if the login banner matches the provided input.
* failure: The test will fail if the login banner does not match the provided input.
Expand Down Expand Up @@ -412,7 +414,8 @@ def test(self) -> None:

class VerifyBannerMotd(AntaTest):
"""
This class verifies the motd banner of a device.
Verifies the motd banner of a device.

Expected results:
* success: The test will pass if the motd banner matches the provided input.
* failure: The test will fail if the motd banner does not match the provided input.
Expand All @@ -439,3 +442,73 @@ def test(self) -> None:
self.result.is_failure(f"Expected `{cleaned_banner}` as the motd banner, but found `{motd_banner}` instead.")
else:
self.result.is_success()


class VerifyIPv4ACL(AntaTest):
"""
Verifies the configuration of IPv4 ACLs.

Expected results:
* success: The test will pass if an IPv4 ACL is configured with the correct sequence entries.
* failure: The test will fail if an IPv4 ACL is not configured or entries are not in sequence.
"""

name = "VerifyIPv4ACL"
description = "Verifies the configuration of IPv4 ACLs."
categories = ["security"]
commands = [AntaTemplate(template="show ip access-lists {acl}")]

class Input(AntaTest.Input):
"""Inputs for the VerifyIPv4ACL test."""

ipv4_access_lists: List[IPv4ACL]
"""List of IPv4 ACLs to verify"""

class IPv4ACL(BaseModel):
"""Detail of IPv4 ACL"""

name: str
"""Name of IPv4 ACL"""

entries: List[IPv4ACLEntries]
"""List of IPv4 ACL entries"""

class IPv4ACLEntries(BaseModel):
"""IPv4 ACL entries details"""

sequence: int = Field(ge=1, le=4294967295)
"""Sequence number of an ACL entry"""
action: str
"""Action of an ACL entry"""

def render(self, template: AntaTemplate) -> list[AntaCommand]:
return [template.render(acl=acl.name, entries=acl.entries) for acl in self.inputs.ipv4_access_lists]

@AntaTest.anta_test
def test(self) -> None:
self.result.is_success()
for command_output in self.instance_commands:
# Collecting input ACL details
acl_name = command_output.params["acl"]
acl_entries = command_output.params["entries"]

# Check if ACL is configured
ipv4_acl_list = command_output.json_output["aclList"]
if not ipv4_acl_list:
self.result.is_failure(f"{acl_name}: Not found")
continue

# Check if the sequence number is configured and has the correct action applied
failed_log = f"{acl_name}:\n"
for acl_entry in acl_entries:
acl_seq = acl_entry.sequence
acl_action = acl_entry.action
if (actual_entry := get_item(ipv4_acl_list[0]["sequence"], "sequenceNumber", acl_seq)) is None:
failed_log += f"Sequence number `{acl_seq}` is not found.\n"
continue

if actual_entry["text"] != acl_action:
failed_log += f"Expected `{acl_action}` as sequence number {acl_seq} action but found `{actual_entry['text']}` instead.\n"

if failed_log != f"{acl_name}:\n":
self.result.is_failure(f"{failed_log}")
21 changes: 18 additions & 3 deletions examples/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,6 @@ anta.tests.security:
common_name: Arista Networks Internal IT Root Cert Authority
encryption_algorithm: RSA
key_size: 4096

anta.tests.services:
- VerifyBannerLogin:
login_banner: |
# Copyright (c) 2023-2024 Arista Networks, Inc.
Expand All @@ -241,6 +239,24 @@ anta.tests.services:
# Copyright (c) 2023-2024 Arista Networks, Inc.
# Use of this source code is governed by the Apache License 2.0
# that can be found in the LICENSE file.
- VerifyIPv4ACL:
ipv4_access_lists:
- name: default-control-plane-acl
entries:
- sequence: 10
action: permit icmp any any
- sequence: 20
action: permit ip any any tracked
- sequence: 30
action: permit udp any any eq bfd ttl eq 255
- name: LabTest
entries:
- sequence: 10
action: permit icmp any any
- sequence: 20
action: permit tcp any any range 5900 5910

anta.tests.services:
- VerifyHostname:
hostname: s1-spine1
- VerifyDNSLookup:
Expand Down Expand Up @@ -339,7 +355,6 @@ anta.tests.routing:
- VerifyRoutingTableSize:
minimum: 2
maximum: 20
- VerifyBFD:
- VerifyRoutingTableEntry:
vrf: default
routes:
Expand Down
220 changes: 220 additions & 0 deletions tests/units/anta_tests/test_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
VerifyAPISSLCertificate,
VerifyBannerLogin,
VerifyBannerMotd,
VerifyIPv4ACL,
VerifySSHIPv4Acl,
VerifySSHIPv6Acl,
VerifySSHStatus,
Expand Down Expand Up @@ -677,4 +678,223 @@
],
},
},
{
"name": "success",
"test": VerifyIPv4ACL,
"eos_data": [
{
"aclList": [
{
"sequence": [
{"text": "permit icmp any any", "sequenceNumber": 10},
{"text": "permit ip any any tracked", "sequenceNumber": 20},
{"text": "permit udp any any eq bfd ttl eq 255", "sequenceNumber": 30},
],
}
]
},
{
"aclList": [
{
"sequence": [
{"text": "permit icmp any any", "sequenceNumber": 10},
{"text": "permit tcp any any range 5900 5910", "sequenceNumber": 20},
],
}
]
},
],
"inputs": {
"ipv4_access_lists": [
{
"name": "default-control-plane-acl",
"entries": [
{"sequence": 10, "action": "permit icmp any any"},
{"sequence": 20, "action": "permit ip any any tracked"},
{"sequence": 30, "action": "permit udp any any eq bfd ttl eq 255"},
],
},
{
"name": "LabTest",
"entries": [{"sequence": 10, "action": "permit icmp any any"}, {"sequence": 20, "action": "permit tcp any any range 5900 5910"}],
},
]
},
"expected": {"result": "success"},
},
{
"name": "failure-acl-not-found",
"test": VerifyIPv4ACL,
"eos_data": [
{
"aclList": [
{
"sequence": [
{"text": "permit icmp any any", "sequenceNumber": 10},
{"text": "permit ip any any tracked", "sequenceNumber": 20},
{"text": "permit udp any any eq bfd ttl eq 255", "sequenceNumber": 30},
],
}
]
},
{"aclList": []},
],
"inputs": {
"ipv4_access_lists": [
{
"name": "default-control-plane-acl",
"entries": [
{"sequence": 10, "action": "permit icmp any any"},
{"sequence": 20, "action": "permit ip any any tracked"},
{"sequence": 30, "action": "permit udp any any eq bfd ttl eq 255"},
],
},
{
"name": "LabTest",
"entries": [{"sequence": 10, "action": "permit icmp any any"}, {"sequence": 20, "action": "permit tcp any any range 5900 5910"}],
},
]
},
"expected": {"result": "failure", "messages": ["LabTest: Not found"]},
},
{
"name": "failure-sequence-not-found",
"test": VerifyIPv4ACL,
"eos_data": [
{
"aclList": [
{
"sequence": [
{"text": "permit icmp any any", "sequenceNumber": 10},
{"text": "permit ip any any tracked", "sequenceNumber": 20},
{"text": "permit udp any any eq bfd ttl eq 255", "sequenceNumber": 40},
],
}
]
},
{
"aclList": [
{
"sequence": [
{"text": "permit icmp any any", "sequenceNumber": 10},
{"text": "permit tcp any any range 5900 5910", "sequenceNumber": 30},
],
}
]
},
],
"inputs": {
"ipv4_access_lists": [
{
"name": "default-control-plane-acl",
"entries": [
{"sequence": 10, "action": "permit icmp any any"},
{"sequence": 20, "action": "permit ip any any tracked"},
{"sequence": 30, "action": "permit udp any any eq bfd ttl eq 255"},
],
},
{
"name": "LabTest",
"entries": [{"sequence": 10, "action": "permit icmp any any"}, {"sequence": 20, "action": "permit tcp any any range 5900 5910"}],
},
]
},
"expected": {
"result": "failure",
"messages": ["default-control-plane-acl:\nSequence number `30` is not found.\n", "LabTest:\nSequence number `20` is not found.\n"],
},
},
{
"name": "failure-action-not-match",
"test": VerifyIPv4ACL,
"eos_data": [
{
"aclList": [
{
"sequence": [
{"text": "permit icmp any any", "sequenceNumber": 10},
{"text": "permit ip any any tracked", "sequenceNumber": 20},
{"text": "permit tcp any any range 5900 5910", "sequenceNumber": 30},
],
}
]
},
{
"aclList": [
{
"sequence": [
{"text": "permit icmp any any", "sequenceNumber": 10},
{"text": "permit udp any any eq bfd ttl eq 255", "sequenceNumber": 20},
],
}
]
},
],
"inputs": {
"ipv4_access_lists": [
{
"name": "default-control-plane-acl",
"entries": [
{"sequence": 10, "action": "permit icmp any any"},
{"sequence": 20, "action": "permit ip any any tracked"},
{"sequence": 30, "action": "permit udp any any eq bfd ttl eq 255"},
],
},
{
"name": "LabTest",
"entries": [{"sequence": 10, "action": "permit icmp any any"}, {"sequence": 20, "action": "permit tcp any any range 5900 5910"}],
},
]
},
"expected": {
"result": "failure",
"messages": [
"default-control-plane-acl:\n"
"Expected `permit udp any any eq bfd ttl eq 255` as sequence number 30 action but found `permit tcp any any range 5900 5910` instead.\n",
"LabTest:\nExpected `permit tcp any any range 5900 5910` as sequence number 20 action but found `permit udp any any eq bfd ttl eq 255` instead.\n",
],
},
},
{
"name": "failure-all-type",
"test": VerifyIPv4ACL,
"eos_data": [
{
"aclList": [
{
"sequence": [
{"text": "permit icmp any any", "sequenceNumber": 10},
{"text": "permit ip any any tracked", "sequenceNumber": 40},
{"text": "permit tcp any any range 5900 5910", "sequenceNumber": 30},
],
}
]
},
{"aclList": []},
],
"inputs": {
"ipv4_access_lists": [
{
"name": "default-control-plane-acl",
"entries": [
{"sequence": 10, "action": "permit icmp any any"},
{"sequence": 20, "action": "permit ip any any tracked"},
{"sequence": 30, "action": "permit udp any any eq bfd ttl eq 255"},
],
},
{
"name": "LabTest",
"entries": [{"sequence": 10, "action": "permit icmp any any"}, {"sequence": 20, "action": "permit tcp any any range 5900 5910"}],
},
]
},
"expected": {
"result": "failure",
"messages": [
"default-control-plane-acl:\nSequence number `20` is not found.\n"
"Expected `permit udp any any eq bfd ttl eq 255` as sequence number 30 action but found `permit tcp any any range 5900 5910` instead.\n",
"LabTest: Not found",
],
},
},
]
Loading