[caclmgrd] Add some default ACCEPT rules and lastly drop all incoming packets (#4412)
Modified caclmgrd behavior to enhance control plane security as follows: Upon starting or receiving notification of ACL table/rule changes in Config DB: 1. Add iptables/ip6tables commands to allow all incoming packets from established TCP sessions or new TCP sessions which are related to established TCP sessions 2. Add iptables/ip6tables commands to allow bidirectional ICMPv4 ping and traceroute 3. Add iptables/ip6tables commands to allow bidirectional ICMPv6 ping and traceroute 4. Add iptables/ip6tables commands to allow all incoming Neighbor Discovery Protocol (NDP) NS/NA/RS/RA messages 5. Add iptables/ip6tables commands to allow all incoming IPv4 DHCP packets 6. Add iptables/ip6tables commands to allow all incoming IPv6 DHCP packets 7. Add iptables/ip6tables commands to allow all incoming BGP traffic 8. Add iptables/ip6tables commands for all ACL rules for recognized services (currently SSH, SNMP, NTP) 9. For all services which we did not find configured ACL rules, add iptables/ip6tables commands to allow all incoming packets for those services (allows the device to accept SSH connections before the device is configured) 10. Add iptables rules to drop all packets destined for loopback interface IP addresses 11. Add iptables rules to drop all packets destined for management interface IP addresses 12. Add iptables rules to drop all packets destined for point-to-point interface IP addresses 13. Add iptables rules to drop all packets destined for our VLAN interface gateway IP addresses 14. Add iptables/ip6tables commands to allow all incoming packets with TTL of 0 or 1 (This allows the device to respond to tools like tcptraceroute) 15. If we found control plane ACLs in the configuration and applied them, we lastly add iptables/ip6tables commands to drop all other incoming packets
This commit is contained in:
parent
c95db04f12
commit
5e8e0d76fc
@ -11,7 +11,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import ipaddr as ipaddress
|
import ipaddress
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@ -61,12 +61,21 @@ class ControlPlaneAclManager(object):
|
|||||||
|
|
||||||
ACL_TABLE_TYPE_CTRLPLANE = "CTRLPLANE"
|
ACL_TABLE_TYPE_CTRLPLANE = "CTRLPLANE"
|
||||||
|
|
||||||
# To specify a port range, use iptables format: separate start and end
|
# To specify a port range instead of a single port, use iptables format:
|
||||||
# ports with a colon, e.g., "1000:2000"
|
# separate start and end ports with a colon, e.g., "1000:2000"
|
||||||
ACL_SERVICES = {
|
ACL_SERVICES = {
|
||||||
"NTP": {"ip_protocols": ["udp"], "dst_ports": ["123"]},
|
"NTP": {
|
||||||
"SNMP": {"ip_protocols": ["tcp", "udp"], "dst_ports": ["161"]},
|
"ip_protocols": ["udp"],
|
||||||
"SSH": {"ip_protocols": ["tcp"], "dst_ports": ["22"]}
|
"dst_ports": ["123"]
|
||||||
|
},
|
||||||
|
"SNMP": {
|
||||||
|
"ip_protocols": ["tcp", "udp"],
|
||||||
|
"dst_ports": ["161"]
|
||||||
|
},
|
||||||
|
"SSH": {
|
||||||
|
"ip_protocols": ["tcp"],
|
||||||
|
"dst_ports": ["22"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -115,6 +124,78 @@ class ControlPlaneAclManager(object):
|
|||||||
tcp_flags_str = tcp_flags_str[:-1]
|
tcp_flags_str = tcp_flags_str[:-1]
|
||||||
return tcp_flags_str
|
return tcp_flags_str
|
||||||
|
|
||||||
|
def generate_block_ip2me_traffic_iptables_commands(self):
|
||||||
|
LOOPBACK_INTERFACE_TABLE_NAME = "LOOPBACK_INTERFACE"
|
||||||
|
MGMT_INTERFACE_TABLE_NAME = "MGMT_INTERFACE"
|
||||||
|
VLAN_INTERFACE_TABLE_NAME = "VLAN_INTERFACE"
|
||||||
|
PORTCHANNEL_INTERFACE_TABLE_NAME = "PORTCHANNEL_INTERFACE"
|
||||||
|
INTERFACE_TABLE_NAME = "INTERFACE"
|
||||||
|
|
||||||
|
block_ip2me_cmds = []
|
||||||
|
|
||||||
|
# Add iptables rules to drop all packets destined for loopback interface IP addresses
|
||||||
|
loopback_iface_table = self.config_db.get_table(LOOPBACK_INTERFACE_TABLE_NAME)
|
||||||
|
if loopback_iface_table:
|
||||||
|
for ((iface_name, iface_cidr), _) in loopback_iface_table.iteritems():
|
||||||
|
ip_ntwrk = ipaddress.ip_network(iface_cidr, strict=False)
|
||||||
|
if isinstance(ip_ntwrk, ipaddress.IPv4Network):
|
||||||
|
block_ip2me_cmds.append("iptables -A INPUT -d {}/{} -j DROP".format(ip_ntwrk.network_address, ip_ntwrk.max_prefixlen))
|
||||||
|
elif isinstance(ip_ntwrk, ipaddress.IPv6Network):
|
||||||
|
block_ip2me_cmds.append("ip6tables -A INPUT -d {}/{} -j DROP".format(ip_ntwrk.network_address, ip_ntwrk.max_prefixlen))
|
||||||
|
else:
|
||||||
|
log_warning("Unrecognized IP address type on interface '{}': {}".format(iface_name, ip_ntwrk))
|
||||||
|
|
||||||
|
# Add iptables rules to drop all packets destined for management interface IP addresses
|
||||||
|
mgmt_iface_table = self.config_db.get_table(MGMT_INTERFACE_TABLE_NAME)
|
||||||
|
if mgmt_iface_table:
|
||||||
|
for ((iface_name, iface_cidr), _) in mgmt_iface_table.iteritems():
|
||||||
|
ip_ntwrk = ipaddress.ip_network(iface_cidr, strict=False)
|
||||||
|
if isinstance(ip_ntwrk, ipaddress.IPv4Network):
|
||||||
|
block_ip2me_cmds.append("iptables -A INPUT -d {}/{} -j DROP".format(ip_ntwrk.network_address, ip_ntwrk.max_prefixlen))
|
||||||
|
elif isinstance(ip_ntwrk, ipaddress.IPv6Network):
|
||||||
|
block_ip2me_cmds.append("ip6tables -A INPUT -d {}/{} -j DROP".format(ip_ntwrk.network_address, ip_ntwrk.max_prefixlen))
|
||||||
|
else:
|
||||||
|
log_warning("Unrecognized IP address type on interface '{}': {}".format(iface_name, ip_ntwrk))
|
||||||
|
|
||||||
|
# Add iptables rules to drop all packets destined for our VLAN interface gateway IP addresses
|
||||||
|
vlan_iface_table = self.config_db.get_table(VLAN_INTERFACE_TABLE_NAME)
|
||||||
|
if vlan_iface_table:
|
||||||
|
for ((iface_name, iface_cidr), _) in vlan_iface_table.iteritems():
|
||||||
|
ip_ntwrk = ipaddress.ip_network(iface_cidr, strict=False)
|
||||||
|
if isinstance(ip_ntwrk, ipaddress.IPv4Network):
|
||||||
|
block_ip2me_cmds.append("iptables -A INPUT -d {}/{} -j DROP".format(list(ip_ntwrk.hosts())[0], ip_ntwrk.max_prefixlen))
|
||||||
|
elif isinstance(ip_ntwrk, ipaddress.IPv6Network):
|
||||||
|
block_ip2me_cmds.append("ip6tables -A INPUT -d {}/{} -j DROP".format(list(ip_ntwrk.hosts())[0], ip_ntwrk.max_prefixlen))
|
||||||
|
else:
|
||||||
|
log_warning("Unrecognized IP address type on interface '{}': {}".format(iface_name, ip_ntwrk))
|
||||||
|
|
||||||
|
# Add iptables rules to drop all packets destined for point-to-point interface IP addresses
|
||||||
|
# (All portchannel interfaces and configured front-panel interfaces)
|
||||||
|
portchannel_iface_table = self.config_db.get_table(PORTCHANNEL_INTERFACE_TABLE_NAME)
|
||||||
|
if portchannel_iface_table:
|
||||||
|
for ((iface_name, iface_cidr), _) in portchannel_iface_table.iteritems():
|
||||||
|
ip_ntwrk = ipaddress.ip_network(iface_cidr, strict=False)
|
||||||
|
if isinstance(ip_ntwrk, ipaddress.IPv4Network):
|
||||||
|
block_ip2me_cmds.append("iptables -A INPUT -d {}/{} -j DROP".format(ip_ntwrk.network_address, ip_ntwrk.max_prefixlen))
|
||||||
|
elif isinstance(ip_ntwrk, ipaddress.IPv6Network):
|
||||||
|
block_ip2me_cmds.append("ip6tables -A INPUT -d {}/{} -j DROP".format(ip_ntwrk.network_address, ip_ntwrk.max_prefixlen))
|
||||||
|
else:
|
||||||
|
log_warning("Unrecognized IP address type on interface '{}': {}".format(iface_name, ip_ntwrk))
|
||||||
|
|
||||||
|
iface_table = self.config_db.get_table(INTERFACE_TABLE_NAME)
|
||||||
|
if iface_table:
|
||||||
|
for ((iface_name, iface_cidr), _) in iface_table.iteritems():
|
||||||
|
ip_ntwrk = ipaddress.ip_network(iface_cidr, strict=False)
|
||||||
|
if isinstance(ip_ntwrk, ipaddress.IPv4Network):
|
||||||
|
block_ip2me_cmds.append("iptables -A INPUT -d {}/{} -j DROP".format(ip_ntwrk.network_address, ip_ntwrk.max_prefixlen))
|
||||||
|
elif isinstance(ip_ntwrk, ipaddress.IPv6Network):
|
||||||
|
block_ip2me_cmds.append("ip6tables -A INPUT -d {}/{} -j DROP".format(ip_ntwrk.network_address, ip_ntwrk.max_prefixlen))
|
||||||
|
else:
|
||||||
|
log_warning("Unrecognized IP address type on interface '{}': {}".format(iface_name, ip_ntwrk))
|
||||||
|
|
||||||
|
return block_ip2me_cmds
|
||||||
|
|
||||||
|
|
||||||
def get_acl_rules_and_translate_to_iptables_commands(self):
|
def get_acl_rules_and_translate_to_iptables_commands(self):
|
||||||
"""
|
"""
|
||||||
Retrieves current ACL tables and rules from Config DB, translates
|
Retrieves current ACL tables and rules from Config DB, translates
|
||||||
@ -147,14 +228,54 @@ class ControlPlaneAclManager(object):
|
|||||||
iptables_cmds.append("ip6tables -F")
|
iptables_cmds.append("ip6tables -F")
|
||||||
iptables_cmds.append("ip6tables -X")
|
iptables_cmds.append("ip6tables -X")
|
||||||
|
|
||||||
# Add iptables commands to allow all IPv4 and IPv6 traffic from localhost
|
# Add iptables/ip6tables commands to allow all traffic from localhost
|
||||||
iptables_cmds.append("iptables -A INPUT -s 127.0.0.1 -i lo -j ACCEPT")
|
iptables_cmds.append("iptables -A INPUT -s 127.0.0.1 -i lo -j ACCEPT")
|
||||||
iptables_cmds.append("ip6tables -A INPUT -s ::1 -i lo -j ACCEPT")
|
iptables_cmds.append("ip6tables -A INPUT -s ::1 -i lo -j ACCEPT")
|
||||||
|
|
||||||
|
# Add iptables/ip6tables commands to allow all incoming packets from established
|
||||||
|
# TCP sessions or new TCP sessions which are related to established TCP sessions
|
||||||
|
iptables_cmds.append("iptables -A INPUT -p tcp -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT")
|
||||||
|
iptables_cmds.append("ip6tables -A INPUT -p tcp -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT")
|
||||||
|
|
||||||
|
# Add iptables/ip6tables commands to allow bidirectional ICMPv4 ping and traceroute
|
||||||
|
# TODO: Support processing ICMPv4 service ACL rules, and remove this blanket acceptance
|
||||||
|
iptables_cmds.append("iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT")
|
||||||
|
iptables_cmds.append("iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT")
|
||||||
|
|
||||||
|
# Add iptables/ip6tables commands to allow bidirectional ICMPv6 ping and traceroute
|
||||||
|
# TODO: Support processing ICMPv6 service ACL rules, and remove this blanket acceptance
|
||||||
|
iptables_cmds.append("ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT")
|
||||||
|
iptables_cmds.append("ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-reply -j ACCEPT")
|
||||||
|
|
||||||
|
# Add iptables/ip6tables commands to allow all incoming Neighbor Discovery Protocol (NDP) NS/NA/RS/RA messages
|
||||||
|
# TODO: Support processing NDP service ACL rules, and remove this blanket acceptance
|
||||||
|
iptables_cmds.append("ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT")
|
||||||
|
iptables_cmds.append("ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT")
|
||||||
|
iptables_cmds.append("ip6tables -A INPUT -p icmpv6 --icmpv6-type router-solicitation -j ACCEPT")
|
||||||
|
iptables_cmds.append("ip6tables -A INPUT -p icmpv6 --icmpv6-type router-advertisement -j ACCEPT")
|
||||||
|
|
||||||
|
# Add iptables/ip6tables commands to allow all incoming IPv4 DHCP packets
|
||||||
|
iptables_cmds.append("iptables -A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT")
|
||||||
|
iptables_cmds.append("ip6tables -A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT")
|
||||||
|
|
||||||
|
# Add iptables/ip6tables commands to allow all incoming IPv6 DHCP packets
|
||||||
|
iptables_cmds.append("iptables -A INPUT -p udp --dport 546:547 --sport 546:547 -j ACCEPT")
|
||||||
|
iptables_cmds.append("ip6tables -A INPUT -p udp --dport 546:547 --sport 546:547 -j ACCEPT")
|
||||||
|
|
||||||
|
# Add iptables/ip6tables commands to allow all incoming BGP traffic
|
||||||
|
# TODO: Determine BGP ACLs based on configured device sessions, and remove this blanket acceptance
|
||||||
|
iptables_cmds.append("iptables -A INPUT -p tcp --dport 179 -j ACCEPT")
|
||||||
|
iptables_cmds.append("iptables -A INPUT -p tcp --sport 179 -j ACCEPT")
|
||||||
|
iptables_cmds.append("ip6tables -A INPUT -p tcp --dport 179 -j ACCEPT")
|
||||||
|
iptables_cmds.append("ip6tables -A INPUT -p tcp --sport 179 -j ACCEPT")
|
||||||
|
|
||||||
|
|
||||||
# Get current ACL tables and rules from Config DB
|
# Get current ACL tables and rules from Config DB
|
||||||
self._tables_db_info = self.config_db.get_table(self.ACL_TABLE)
|
self._tables_db_info = self.config_db.get_table(self.ACL_TABLE)
|
||||||
self._rules_db_info = self.config_db.get_table(self.ACL_RULE)
|
self._rules_db_info = self.config_db.get_table(self.ACL_RULE)
|
||||||
|
|
||||||
|
num_ctrl_plane_acl_rules = 0
|
||||||
|
|
||||||
# Walk the ACL tables
|
# Walk the ACL tables
|
||||||
for (table_name, table_data) in self._tables_db_info.iteritems():
|
for (table_name, table_data) in self._tables_db_info.iteritems():
|
||||||
|
|
||||||
@ -246,6 +367,23 @@ class ControlPlaneAclManager(object):
|
|||||||
rule_cmd += " -j {}".format(rule_props["PACKET_ACTION"])
|
rule_cmd += " -j {}".format(rule_props["PACKET_ACTION"])
|
||||||
|
|
||||||
iptables_cmds.append(rule_cmd)
|
iptables_cmds.append(rule_cmd)
|
||||||
|
num_ctrl_plane_acl_rules += 1
|
||||||
|
|
||||||
|
# Add iptables commands to block ip2me traffic
|
||||||
|
iptables_cmds += self.generate_block_ip2me_traffic_iptables_commands()
|
||||||
|
|
||||||
|
# Add iptables/ip6tables commands to allow all incoming packets with TTL of 0 or 1
|
||||||
|
# This allows the device to respond to tools like tcptraceroute
|
||||||
|
iptables_cmds.append("iptables -A INPUT -m ttl --ttl-lt 2 -j ACCEPT")
|
||||||
|
iptables_cmds.append("ip6tables -A INPUT -p tcp -m hl --hl-lt 2 -j ACCEPT")
|
||||||
|
|
||||||
|
# Finally, if the device has control plane ACLs configured,
|
||||||
|
# add iptables/ip6tables commands to drop all other incoming packets
|
||||||
|
if num_ctrl_plane_acl_rules > 0:
|
||||||
|
iptables_cmds.append("iptables -A INPUT -j DROP")
|
||||||
|
iptables_cmds.append("iptables -A FORWARD -j DROP")
|
||||||
|
iptables_cmds.append("ip6tables -A INPUT -j DROP")
|
||||||
|
iptables_cmds.append("ip6tables -A FORWARD -j DROP")
|
||||||
|
|
||||||
return iptables_cmds
|
return iptables_cmds
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user