Definition#

Firewall filters in Juniper Junos are stateless packet filters that act pretty much like the access lists in Cisco IOS, IOS XE and NX-OS. Once configured, they must be assigned in the input or output direction of one or more interfaces to become effective.

Example scenario: preventing ping packets to be initiated from one direction while allowing the other direction#

Topology#

R1:ge-0/0/5(.1) <— 10.10.7.0/30 —> (.2)ge-0.0.1:R5

Configuration#

On R5:

root@vRouter5> show configuration interfaces ge-0/0/1 
unit 0 {
    family inet {
        filter {
            input Count_Pkts;
        }
        address 10.10.7.2/30;
    }
}

root@vRouter5> 
root@vRouter5> show configuration firewall family inet filter Count_Pkts 
interface-specific;
term TrafficFromR1WAN {
    from {
        source-address {
            10.10.7.1/32;
        }
        protocol icmp;
        icmp-type echo-request;
    }
    then {
        count CounterTerm1;
        reject;
    }
}
term TrafficFromLinux02 {
    from {
        source-address {
            172.17.99.70/32;
        }
        protocol icmp;
    }
    then {
        count Count_icmp_from_Linux02;
        accept;                         
    }
}
term ElseTraffic {
    then accept;
}

root@vRouter5> 

The firewall filter will reject ICMP Echo Request packets from 10.10.7.1, which is R5’s WAN interface. At the end of the firewall filter, a final term ensures that traffic, that has not matched the previous terms, is accepted. However, ICMP Echo Reply packets from 10.10.7.1 will match the final term in the firewall filter and thus get accepted to traverse the device interface. This explains why a ping initiated from R5 to R1’s WAN interface succeeds, while a ping initiated from R1 to R5’s WAN interface does not.

root@vRouter5> ping 10.10.7.1  
PING 10.10.7.1 (10.10.7.1): 56 data bytes
64 bytes from 10.10.7.1: icmp_seq=0 ttl=64 time=2.323 ms
64 bytes from 10.10.7.1: icmp_seq=1 ttl=64 time=2.321 ms
^C
--- 10.10.7.1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max/stddev = 2.321/2.322/2.323/0.001 ms

root@vRouter5>   
root@R1> ping 10.10.7.2 count 2    
PING 10.10.7.2 (10.10.7.2): 56 data bytes
36 bytes from 10.10.7.2: Communication prohibited by filter
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
 4  5  00 0054 520d   0 0000  40  01 0686 10.10.7.1  10.10.7.2 

36 bytes from 10.10.7.2: Communication prohibited by filter
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
 4  5  00 0054 522c   0 0000  40  01 0667 10.10.7.1  10.10.7.2 


--- 10.10.7.2 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss

root@R1>

Protecting the control plane#

Protecting the control plane in Junos comes down to creating firewall filters, creating adequate terms and applying the firewall filter to the lo0 interface in the input direction. A single firewall filter with multiple terms can be used to protect different control plane protocols like BGP, OSPF, NETCONF, SSH and NTP at the same time. A final term in the firewall filter must reject all traffic not explicitely defined in the terms before it.

This can be achieved by maching the OSPF multicast groups, 224.0.0.5/32 and 224.0.0.6/32, in addition to the IP segments where OSPF peering takes place, in the firewall filter terms and accepting the matched packets.

Example configuration:

lab@vSRX> show configuration firewall family inet filter protect-re term allow-Ospf                
from {
    destination-prefix-list {
        Ospf;
    }
    protocol ospf;
}
then accept;
lab@vSRX> show configuration firewall family inet filter protect-re term the/Rest
then {
    reject;
}

lab@vSRX>
lab@vSRX> show configuration policy-options prefix-list Ospf
192.168.11.0/24;
224.0.0.5/32;
224.0.0.6/32;

lab@vSRX>

lab@vSRX> show configuration interfaces lo0
unit 0 {
    family inet {
        filter {
            input protect-re;
        }
        address 192.168.31.1/32;
    }
}

lab@vSRX>

The subnet 192.168.11.0/24 is a direct subnet where a neighboring OSPF speaker is esblishing peering with the local router.

A method to achieve this goal is by matching on the BGP protocol and on specific neighbors, in the firewall filter term. The neighbors can be individually identified, or using a wildcard like with apply-path.

Example configuration:

lab@vSRX> show configuration firewall family inet filter protect-re term allow-bgp
from {
    source-prefix-list {
        bgp-Neighbors;
    }
    protocol tcp;
    port 179;
}
then accept;

lab@vSRX>
lab@vSRX> show configuration policy-options prefix-list bgp-Neighbors
apply-path "protocols bgp group <*> neighbor <*>";

lab@vSRX>

The apply-path argument will extend the prefix list to all configured neighbors:

lab@vSRX> show configuration protocols bgp
group ISP-1 {
    peer-as 65001;
    neighbor 172.18.1.1;
}
group ISP-2 {
    peer-as 65002;
    neighbor 172.18.2.1;
}

lab@vSRX>
lab@vSRX> ...all family inet filter protect-re term the/Rest            
then {
    reject;
}

lab@vSRX>

This mechanism restricts remote access per SSH to the local router, to a select list of source IP addresses.

Topology#

Rocky-Linux <—> Oob-Router <–> R1

Subnets#

Rocky-Linux: .21 <- 192.168.201.0/24 -> Oob-Router:.1 <- 172.17.81.0/24 -> R1:.42

Sample Configuration#

root@R1> show configuration policy-options prefix-list WassimRocky
192.168.201.0/24;
root@R1>
root@R1> show configuration firewall family inet filter Filter1
term AllowRocky {
    from {
        source-prefix-list {
            WassimRocky;
        }
        destination-port ssh;
    }
    then accept;
}
term PreventOthersSSH {
    from {
        destination-port ssh;
    }
    then {
        count CountSSHdiscards;
        discard;
    }
}
term AllowOthers {
    then accept;
}
root@R1> show configuration interfaces lo0 unit 0
family inet {
    filter {
        input Filter1;
    }
    address 1.1.1.1/32;
}

This mechanism ensures that NTP packet exchange occurs with only a known configured NTP server. Note: For some reason, the IP address of the loopback interface must be whitelisted in the firewall filter. Otherwise, no NTP packets are sent toward the server. #QA how does the packet exchange look like?

Example configuration:


lab@vSRX> ...all family inet filter protect-re term allow-NTP           
from {
    source-address {
        192.168.31.1/32;
    }
    source-prefix-list {
        NTP-srv;
    }
    port ntp;
}
then accept;

lab@vSRX> show configuration policy-options prefix-list NTP-srv
172.25.11.254/32;

lab@vSRX>

lab@vSRX> show system uptime
Current time: 2026-08-28 13:50:34 CEST
Time Source:  NTP CLOCK
System booted: 2026-08-27 05:26:19 CEST (1d 08:24 ago)
Protocols started: 2026-08-27 15:45:07 CEST (22:05:27 ago)
Last configured: 2026-08-28 13:47:54 CEST (00:02:40 ago) by lab
 1:50PM  up 1 day,  8:24, 1 users, load averages: 1.19, 1.64, 2.13

lab@vSRX>

Concluding note#

The Juniper JT-26A course has a lab exercise to practice the impact of firewall filters on ping capability between two routers. The issue with the official lab exercise is that the firewall filter is seen in the output of show interface detail but not in the configuration of the interface itself:

lab@vSRX> show interfaces detail ge-0/0/0 | match filter 
  Loopback: Disabled, Source filtering: Disabled, Flow control: Enabled
      Input Filters: filter-1

lab@vSRX> 

lab@vSRX> show configuration interfaces ge-0/0/0 
unit 0 {
    family inet {
        address 172.18.1.2/30;
    }
}

lab@vSRX> 

This was confusing. I tested the behavior in my own lab and had the same result that I expected. So I asked the instructor and he pointed me to the group configuration:


[edit]
lab@vSRX# show | match group1 | display set 
set groups group1 apply-flags omit
set groups group1 interfaces ge-0/0/2 mtu 1400
set groups group1 interfaces ge-0/0/2 unit 0 family inet no-neighbor-learn
set groups group1 interfaces ge-0/0/0 mtu 1300
set groups group1 interfaces ge-0/0/0 unit 0 family inet filter input filter-1
set apply-groups group1

[edit]
lab@vSRX#

[edit]
lab@vSRX# exit 
Exiting configuration mode

lab@vSRX> show configuration interfaces ge-0/0/0 | display inheritance 
##
## '1300' was inherited from group 'group1'
##
mtu 1300;
unit 0 {
    family inet {
        ##
        ## 'filter' was inherited from group 'group1'
        ##
        filter {
            ##
            ## 'input' was inherited from group 'group1'
            ## 'filter-1' was inherited from group 'group1'
            ##
            input filter-1;
        }
        address 172.18.1.2/30;
    }
}

lab@vSRX> 

So here is the difference between trusting what you see and verifying inheritance:


lab@vSRX> show configuration interfaces ge-0/0/0 
unit 0 {
    family inet {
        address 172.18.1.2/30;
    }
}

lab@vSRX> show configuration interfaces ge-0/0/0 | display inheritance 
##
## '1300' was inherited from group 'group1'
##
mtu 1300;
unit 0 {
    family inet {
        ##
        ## 'filter' was inherited from group 'group1'
        ##
        filter {
            ##
            ## 'input' was inherited from group 'group1'
            ## 'filter-1' was inherited from group 'group1'
            ##
            input filter-1;
        }
        address 172.18.1.2/30;
    }
}

lab@vSRX> 

#LessonLearned In Junos, what you see is not always what you get, i.e. interface inheritance is a real thing.