Objective 5.6
Configure and verify access control lists
ACLs are the most heavily tested topic in this domain and one of the most tested on the whole exam. Expect to read ACLs, predict which packets they permit or deny, spot placement errors, and write them. Take your time with this section and work through every example by hand.
What an ACL is
An access control list (ACL) is an ordered list of rules, called access control entries (ACEs) or simply statements, each of which says permit or deny for packets matching certain criteria. ACLs are used for two main things:
- Packet filtering: applied to a router interface in a direction (in or out), the ACL decides which packets may pass. This is the classic security use.
- Traffic identification: many other features (NAT, route maps, VPN interesting traffic, QoS,
debugfilters, vtyaccess-class) use ACLs simply to describe “which packets,” withpermitmeaning “match.”
How an ACL is processed
The rules are evaluated top-down, in order, and the first statement that matches is applied; no further statements are checked. If the packet reaches the end without matching anything, it hits the implicit deny any at the bottom of every ACL. You never see this line in the config, but it is always there. Consequences:
- An ACL that contains only
denystatements denies everything. - Order matters. A broad
permitplaced before a narrowdenymakes the deny useless. - An empty ACL (a name applied to an interface but with no statements) permits everything, because an ACL with no entries is not really applied. Once one statement exists, the implicit deny is active.
- The router also applies ACLs only to traffic passing through it; packets the router itself generates (such as its own pings or routing updates) are not checked by an outbound ACL.
Standard ACLs
A standard ACL examines only the source IPv4 address of a packet. Numbered standard ACLs use numbers 1 to 99 and the expanded range 1300 to 1999.
R1(config)# access-list 10 permit 192.168.10.0 0.0.0.255
R1(config)# access-list 10 deny 192.168.20.5 0.0.0.0
R1(config)# access-list 10 permit any
Because a standard ACL cannot see the destination, it cannot say “allow this host to reach the web server but not the file server.” It just blocks or allows a source everywhere the ACL is applied. That is why the rule of thumb is to place a standard ACL as close to the destination as possible: if you put it near the source, you would block that source from reaching everything beyond that point, not just the intended destination.
Wildcard masks
ACLs do not use subnet masks; they use wildcard masks, which look similar but work in reverse. In a wildcard mask, a 0 bit means “this bit must match” and a 1 bit means “ignore this bit.” Written in dotted decimal:
0.0.0.0= every bit must match = exactly one host.0.0.0.255= first three octets must match, last octet is ignored = any address in a /24.0.0.255.255= first two octets must match = any address in a /16.255.255.255.255= nothing has to match = every address.
The quick way to convert a subnet mask into a wildcard mask is to subtract each octet from 255:
| Subnet mask | Prefix | Wildcard mask |
|---|---|---|
| 255.255.255.0 | /24 | 0.0.0.255 |
| 255.255.255.128 | /25 | 0.0.0.127 |
| 255.255.255.192 | /26 | 0.0.0.63 |
| 255.255.255.224 | /27 | 0.0.0.31 |
| 255.255.255.240 | /28 | 0.0.0.15 |
| 255.255.255.248 | /29 | 0.0.0.7 |
| 255.255.255.252 | /30 | 0.0.0.3 |
| 255.255.0.0 | /16 | 0.0.255.255 |
| 255.0.0.0 | /8 | 0.255.255.255 |
Two keywords save typing: host means wildcard 0.0.0.0 (host 10.1.1.1 is the same as 10.1.1.1 0.0.0.0) and any means 0.0.0.0 255.255.255.255. In a numbered standard ACL, if you omit the wildcard mask entirely, IOS assumes 0.0.0.0 (a single host). In an extended ACL you must supply a wildcard or use host/any.
Worked examples: what does a statement match?
Work through these; the exam will present them almost exactly like this.
-
permit 10.0.0.0 0.0.255.255— first two octets fixed at 10.0; last two ignored. Matches 10.0.0.0 through 10.0.255.255, the 10.0.0.0/16 block. It does not match 10.1.0.1. -
permit 172.16.32.0 0.0.31.255— third octet wildcard is 31 (binary 00011111), meaning the top three bits of the third octet must match 32 (00100000) and the low five bits are free. That is 32 through 63 in the third octet. Matches 172.16.32.0 through 172.16.63.255, equivalent to 172.16.32.0/19. -
permit 192.168.1.64 0.0.0.63— last octet wildcard 63 frees the low six bits; 64 to 127. Matches 192.168.1.64 through 192.168.1.127, the /26 subnet. Note that 192.168.1.128 is not matched. -
permit host 10.10.10.10— exactly one address. -
permit 10.1.1.0 0.0.0.255versuspermit 10.1.1.0 0.0.0.0— the first matches the whole /24; the second matches only the single address 10.1.1.0 (which, as a network address, would never appear as a real source). If you forget the wildcard on a numbered standard ACL you get the second behavior by accident. -
Matching two subnets with one line: 192.168.4.0/24 and 192.168.5.0/24 differ only in the lowest bit of the third octet, so
permit 192.168.4.0 0.0.1.255matches both (192.168.4.0 through 192.168.5.255). This only works when the ranges align to a power of two; 192.168.5.0 and 192.168.6.0 cannot be combined this way because 5 is 101 and 6 is 110 in binary, and the wildcard 0.0.3.255 would also include 4 and 7. -
Odd-looking but valid:
permit 10.0.0.0 0.255.0.255means the first and third octets must be 10 and 0 respectively while the second and fourth can be anything: 10.x.0.y. Wildcards are bit masks, not necessarily contiguous.
Extended ACLs
An extended ACL can match on protocol, source address, destination address, and source and destination port numbers (for TCP and UDP), plus ICMP types and some other fields. Numbered extended ACLs use 100 to 199 and the expanded range 2000 to 2699. Because they can identify traffic precisely, extended ACLs should be placed as close to the source as possible, so that unwanted traffic is dropped before it consumes bandwidth across the network.
The general syntax:
access-list NUMBER {permit|deny} PROTOCOL SOURCE WILDCARD [OPERATOR PORT]
DESTINATION WILDCARD [OPERATOR PORT] [established] [log]
PROTOCOLisip(any IP traffic),tcp,udp,icmp, or a protocol number (for examplegreorospfare also accepted as keywords).- The port operators are
eq(equal),neq(not equal),gt(greater than),lt(less than), andrange LOW HIGH. Well-known names such aswww,telnet,ssh,domain,ftp,smtpmay be used instead of numbers. - A source port operator comes right after the source; a destination port operator comes after the destination. Almost always you match the destination port, because the client’s source port is random.
Examples:
! Allow the 10.1.1.0/24 LAN to reach web server 10.2.2.10 on HTTP and HTTPS
R1(config)# access-list 100 permit tcp 10.1.1.0 0.0.0.255 host 10.2.2.10 eq 80
R1(config)# access-list 100 permit tcp 10.1.1.0 0.0.0.255 host 10.2.2.10 eq 443
! Block Telnet from anywhere to the 10.2.2.0/24 server subnet
R1(config)# access-list 100 deny tcp any 10.2.2.0 0.0.0.255 eq 23
! Allow DNS queries to the DNS server
R1(config)# access-list 100 permit udp any host 10.2.2.53 eq 53
! Allow ping replies and requests
R1(config)# access-list 100 permit icmp any any
! Allow everything else (otherwise the implicit deny drops it)
R1(config)# access-list 100 permit ip any any
Read a statement in plain English to check it: “permit TCP from any host in 10.1.1.0/24 to the host 10.2.2.10 when the destination port equals 80.”
The established keyword
Extended ACLs are stateless: they look at each packet on its own. The established keyword on a tcp statement matches only segments that have the ACK or RST flag set, which is every segment of a TCP session except the very first SYN. This gives a crude form of statefulness: apply an inbound ACL on the Internet-facing interface with permit tcp any any established, and replies to sessions that inside users started are allowed in, while new connections initiated from outside (a bare SYN) are dropped. It does not work for UDP, and a firewall does this far better, but the concept appears on the exam.
R1(config)# access-list 110 permit tcp any 10.1.1.0 0.0.0.255 established
R1(config)# access-list 110 deny ip any any log
Named ACLs
Numbers are hard to remember. A named ACL uses a descriptive name and, more importantly, puts you into a sub-configuration mode where each statement has a sequence number, which lets you insert and delete individual lines. Named ACLs are created with ip access-list standard NAME or ip access-list extended NAME.
R1(config)# ip access-list extended BLOCK-TELNET
R1(config-ext-nacl)# remark Block Telnet into servers, permit rest
R1(config-ext-nacl)# deny tcp any 10.2.2.0 0.0.0.255 eq 23
R1(config-ext-nacl)# permit ip any any
R1(config-ext-nacl)# exit
R1(config)# ip access-list standard MGMT-HOSTS
R1(config-std-nacl)# permit host 10.1.1.100
R1(config-std-nacl)# permit 10.1.1.0 0.0.0.255
R1(config-std-nacl)# exit
The remark statement is a comment stored in the configuration and shown in show running-config; it does not affect matching. Remarks also work in numbered ACLs as access-list 100 remark TEXT.
If you do not specify sequence numbers, IOS assigns 10, 20, 30, and so on. You can give one explicitly to insert a line between existing ones:
R1(config)# ip access-list extended BLOCK-TELNET
R1(config-ext-nacl)# 15 deny tcp any 10.2.2.0 0.0.0.255 eq 22
R1(config-ext-nacl)# no 10
R1(config-ext-nacl)# exit
R1# show access-lists BLOCK-TELNET
Extended IP access list BLOCK-TELNET
15 deny tcp any 10.2.2.0 0.0.0.255 eq 22
20 permit ip any any
no SEQUENCE-NUMBER removes exactly that line, something impossible in a classic numbered ACL where no access-list 100 ... deletes the entire list. In fact, since IOS 12.3 you can edit a numbered ACL in the same way by entering ip access-list extended 100 (using the number as the name). Renumber with ip access-list resequence NAME 10 10 (start at 10, increment by 10) when you run out of gaps.
Applying an ACL to an interface
An ACL does nothing until it is applied. On an interface, use ip access-group {NUMBER|NAME} {in|out}:
R1(config)# interface GigabitEthernet0/0
R1(config-if)# ip access-group BLOCK-TELNET in
R1(config-if)# exit
R1(config)# interface GigabitEthernet0/1
R1(config-if)# ip access-group 10 out
Direction is from the router’s point of view: in filters packets arriving on that interface from the wire, before routing; out filters packets the router is about to send out that interface, after routing. The rule: one ACL per interface, per direction, per protocol. An interface can have one inbound IPv4 ACL and one outbound IPv4 ACL (and separately an IPv6 ACL in each direction), but never two inbound IPv4 ACLs. Applying a second one replaces the first.
Placement summary, with reasoning:
- Standard ACL near the destination, because it matches source only and would otherwise block that source from too much.
- Extended ACL near the source, because it can precisely identify the unwanted traffic and drop it before it crosses the network.
Restricting management access with access-class
To control who may Telnet or SSH into the device itself, apply a standard ACL to the vty lines with access-class. This is far more efficient than putting interface ACLs everywhere, because it covers every interface at once:
R1(config)# access-list 5 permit 10.1.1.0 0.0.0.255
R1(config)# line vty 0 4
R1(config-line)# access-class 5 in
Only hosts in 10.1.1.0/24 may open a vty session; everyone else is refused by the implicit deny. access-class ... out limits where a user on this device may Telnet to, a rarely used feature.
The log keyword
Adding log to the end of a statement makes the router generate a syslog message (level 6, informational) each time the statement is matched, with counts summarized at intervals. log-input additionally records the input interface and source MAC. Logging is useful for seeing what an ACL is dropping, but it consumes CPU, so use it sparingly and typically only on a final deny ip any any log statement to see what is being blocked.
%SEC-6-IPACCESSLOGP: list BLOCK-TELNET denied tcp 203.0.113.7(51234)
-> 10.2.2.10(22), 1 packet
Complete configuration example
Scenario: R1 has LAN 10.1.1.0/24 on G0/0 and a server subnet 10.2.2.0/24 on G0/1. Requirements: the host 10.1.1.50 must not reach the server subnet at all; all other LAN hosts may reach the servers only on HTTP, HTTPS, and DNS; ping to the servers is allowed for troubleshooting; only 10.1.1.100 may SSH to R1.
R1(config)# ip access-list extended LAN-TO-SERVERS
R1(config-ext-nacl)# remark Block the quarantined host first
R1(config-ext-nacl)# deny ip host 10.1.1.50 10.2.2.0 0.0.0.255
R1(config-ext-nacl)# permit tcp 10.1.1.0 0.0.0.255 10.2.2.0 0.0.0.255 eq 80
R1(config-ext-nacl)# permit tcp 10.1.1.0 0.0.0.255 10.2.2.0 0.0.0.255 eq 443
R1(config-ext-nacl)# permit udp 10.1.1.0 0.0.0.255 10.2.2.0 0.0.0.255 eq 53
R1(config-ext-nacl)# permit icmp 10.1.1.0 0.0.0.255 10.2.2.0 0.0.0.255
R1(config-ext-nacl)# deny ip any 10.2.2.0 0.0.0.255 log
R1(config-ext-nacl)# permit ip any any
R1(config-ext-nacl)# exit
! Extended ACL: apply inbound, close to the source LAN
R1(config)# interface GigabitEthernet0/0
R1(config-if)# ip access-group LAN-TO-SERVERS in
R1(config-if)# exit
! Management access
R1(config)# ip access-list standard SSH-ADMIN
R1(config-std-nacl)# permit host 10.1.1.100
R1(config-std-nacl)# exit
R1(config)# line vty 0 4
R1(config-line)# access-class SSH-ADMIN in
R1(config-line)# transport input ssh
Note the ordering: the specific deny for 10.1.1.50 comes first; if it were placed after the permit tcp ... eq 80 line, that host would still reach the web servers. The final permit ip any any lets LAN traffic to destinations other than the server subnet (for example, the Internet) continue.
Verification commands
show access-lists (or show ip access-lists for IPv4 only) displays every ACL with sequence numbers and match counters, which are invaluable for confirming that traffic is hitting the line you expect:
R1# show access-lists
Standard IP access list SSH-ADMIN
10 permit 10.1.1.100 (4 matches)
Extended IP access list LAN-TO-SERVERS
10 deny ip host 10.1.1.50 10.2.2.0 0.0.0.255 (12 matches)
20 permit tcp 10.1.1.0 0.0.0.255 10.2.2.0 0.0.0.255 eq www (87 matches)
30 permit tcp 10.1.1.0 0.0.0.255 10.2.2.0 0.0.0.255 eq 443 (140 matches)
40 permit udp 10.1.1.0 0.0.0.255 10.2.2.0 0.0.0.255 eq domain
50 permit icmp 10.1.1.0 0.0.0.255 10.2.2.0 0.0.0.255 (6 matches)
60 deny ip any 10.2.2.0 0.0.0.255 log (3 matches)
70 permit ip any any (2231 matches)
Notice that IOS converts well-known port numbers to names (eq www, eq domain) in the output. show access-lists does not tell you where an ACL is applied. For that, use show ip interface:
R1# show ip interface GigabitEthernet0/0
GigabitEthernet0/0 is up, line protocol is up
Internet address is 10.1.1.1/24
Broadcast address is 255.255.255.255
Address determined by setup command
MTU is 1500 bytes
Helper address is not set
Directed broadcast forwarding is disabled
Outgoing Common access list is not set
Outgoing access list is not set
Inbound Common access list is not set
Inbound access list is LAN-TO-SERVERS
show running-config | section access-list shows the configured statements and remarks, and show running-config | include access-group|access-class quickly shows where ACLs are applied. clear access-list counters resets the match counts.
ACL logic drills
Given this ACL applied inbound on G0/0 (the LAN side):
10 permit tcp 10.1.1.0 0.0.0.255 any eq 22
20 deny ip 10.1.1.0 0.0.0.255 host 10.2.2.10
30 permit ip 10.1.1.0 0.0.0.255 any
- Host 10.1.1.7 opens SSH (TCP 22) to 10.2.2.10: matches line 10, permitted. Line 20 never sees it.
- Host 10.1.1.7 opens HTTP to 10.2.2.10: line 10 fails (port 80, not 22), line 20 matches, denied.
- Host 10.1.1.7 pings 10.2.2.11: lines 10 and 20 fail, line 30 matches, permitted.
- Host 10.1.2.7 (a different subnet) sends anything: no line matches, implicit deny, denied.
- A reply from 10.2.2.10 back to 10.1.1.7 enters on G0/1 and exits G0/0: this ACL is inbound on G0/0, so it is not checked at all for that packet.