mirror of https://github.com/tLDP/LDP
508 lines
20 KiB
XML
508 lines
20 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||
<article lang="en-US">
|
||
<articleinfo>
|
||
<title>rp_filter</title>
|
||
</articleinfo>
|
||
|
||
<para><emphasis role="bold">What is it? What does it do?</emphasis></para>
|
||
|
||
<para>rp_filter stands for “reverse path filter” meaning the “reverse path”
|
||
(reply) should be the same as the “forward path” (incoming) – based on an
|
||
analysis of the available routing information. The goal in its use is to
|
||
mitigate the effect of malicious network traffic using spoofed source IP
|
||
addresses (RFCs 3704 and 2827 can be used as references for background on
|
||
the technology – search the web for rfc2827.txt and rfc3704.txt). What
|
||
rp_filter does is determined by the value it is set to and that in turn is
|
||
determined by the interactions of three settings, all located under
|
||
/proc/sys/net/ipv4/conf: an “all” setting, a “default” setting and a setting
|
||
per interface (network card – either real or virtual).</para>
|
||
|
||
<para>First of all, the value of the “effective” setting (see below and the
|
||
rp_filter section of
|
||
https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt)
|
||
means:</para>
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>0 - no validation, basically rp_filter is disabled</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>1 - Strict validation per rfc 3704 - “... if the packet is
|
||
received on the interface which would be used to forward the traffic to
|
||
the source of the packet, it passes the check”.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>2 - “loose” validation per rfc 3704 – “Each incoming packet's
|
||
source address is also tested against the FIB and if the source address
|
||
is not reachable via any interface the packet check will fail.” In other
|
||
words, if any route is found for replying to the source address
|
||
(including the default route) then that is acceptable. The rfc notes
|
||
that this is a misnomer because there is no “reverse path”.</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
|
||
<para>Looking at the three settings and their interaction:</para>
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>Per <ulink
|
||
url="https://www.theurbanpenguin.com/rp_filter-and-lpic-3-linux-security/"><emphasis
|
||
role="underline">https://www.theurbanpenguin.com/rp_filter-and-lpic-3-linux-security/</emphasis></ulink>
|
||
the “default” setting is used for any new new devices added after the
|
||
initial setup.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>The setting for each interface allows different interfaces to be
|
||
handled potentially differently than others depending on the interaction
|
||
between it and the “all” setting.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>The maximum value from either the “all” setting or the interface
|
||
setting is what is in effect. However, web posts indicate this may have
|
||
changed over time, test before using. An “all” setting of the following
|
||
values has the given implications:</para>
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>0 – the interface setting determines the effective value. This
|
||
“all” setting would be used to allow an interface's “0” setting to
|
||
be effective, otherwise the interface setting would be over-ridden.
|
||
If it is used, any interface needing rp_filter validation will need
|
||
to be set manually by other means.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>1 – an interface setting of “0” would be over-ridden but a
|
||
setting of “2” wouldn't. This setting forces strict mode unless the
|
||
interface is explicitly set for loose mode.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>2 - forces loose mode regardless of interface setting.</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</listitem>
|
||
</itemizedlist>
|
||
|
||
<para><emphasis role="bold">What can rp_filter can help
|
||
with.</emphasis></para>
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>Stopping further processing of traffic with “martian” source
|
||
addresses. Examples would be 127.?.?.? received on an external
|
||
interface, a private IP address range from the Internet, an IP address
|
||
in the experimental range. See the above RFCs for further detail.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>Preventing what could be called a “reflected” or “amplified”
|
||
attack (as discussed in RFC3704). An example would be an attacker
|
||
knowing that the target site used 10.10.10.0/24 internally and sending
|
||
traffic from the Internet with source IP addresses in the 10.10.10.0/24
|
||
range. If the network wasn't protected, not only would it have to
|
||
contend with the Internet traffic load but also internal replies to
|
||
assumed valid systems on the internal network.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>If the malicious traffic was requesting a tcp connection, the
|
||
potential exhaustion of tcp stack resources waiting for connection setup
|
||
replies from non-existent systems. See <ulink
|
||
url="https://www.theurbanpenguin.com/rp_filter-and-lpic-3-linux-security/"><emphasis
|
||
role="underline">https://www.theurbanpenguin.com/rp_filter-and-lpic-3-linux-security/</emphasis></ulink>
|
||
for further discussion.</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
|
||
<para><emphasis role="bold">What rp_filter can't help
|
||
with.</emphasis></para>
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>The load of receiving the malicious traffic.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>Spoofed traffic where the source IP address is at least
|
||
theoretically valid (is or could be valid if the system exists either on
|
||
the Internet or internally). For theoretically valid internal addresses,
|
||
the exhaustion of tcp stack resources is also not protected.</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
|
||
<para><emphasis role="bold">Is it Active?</emphasis></para>
|
||
|
||
<para>This is dependent on what the software source (Linux distribution for
|
||
a given version) has done (and this changes over time) or on previous manual
|
||
override of those settings. There are at least three ways of determining the
|
||
current configuration:</para>
|
||
|
||
<orderedlist>
|
||
<listitem>
|
||
<para><emphasis role="bold">ip netconf show all | grep
|
||
rp_filter</emphasis> (shows “off”, “loose” or “strict”)</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><emphasis role="bold">sysctl -ar '\.rp_filter'</emphasis> (shows
|
||
0, 1 or 2 – see above)</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><emphasis role="bold">find /proc/sys/net/ipv4/conf -name rp_filter
|
||
| while read a; do echo $a; cat $a; done</emphasis> (shows 0, 1 or 2 –
|
||
see above)</para>
|
||
</listitem>
|
||
</orderedlist>
|
||
|
||
<para><emphasis role="bold">Problems it can cause.</emphasis></para>
|
||
|
||
<para>As mentioned by RFC 3704, with multi-homed systems and asymmetric
|
||
routing - particularly for strict mode. The latter is more exotic so a
|
||
further examination of strict mode on multi-homed systems will be used to
|
||
illustrate the issue.</para>
|
||
|
||
<para>Assume a multi-homed system has two interfaces: eth<emphasis
|
||
role="bold">0</emphasis> (for 10.10.<emphasis role="bold">1</emphasis>.0/24
|
||
with node address 2) and eth<emphasis role="bold">1</emphasis> (for
|
||
10.10.<emphasis role="bold">2</emphasis>.0/24 with node address 2) and that
|
||
node 1 is the gateway for each subnet thus the following routing
|
||
table:</para>
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para><emphasis role="bold">default via 10.10.1.1 dev
|
||
eth0</emphasis></para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><emphasis role="bold">10.10.1.0/24 dev eth0 proto kernel scope
|
||
link src 10.10.1.2</emphasis></para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><emphasis role="bold">10.10.2.0/24 dev eth1 proto kernel scope
|
||
link src 10.10.2.2</emphasis></para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
|
||
<para>The following options are possible:</para>
|
||
|
||
<orderedlist>
|
||
<listitem>
|
||
<para>(inbound) traffic to 10.10.<emphasis role="bold">1</emphasis>.2
|
||
(eth<emphasis role="bold">0</emphasis>'s IP address) <emphasis
|
||
role="bold">from anywhere but </emphasis>10.10.<emphasis
|
||
role="bold">2</emphasis>.0/24: the default route is on the same
|
||
interface as the inbound traffic meeting rp_filter criteria.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>(inbound) traffic to 10.10.<emphasis role="bold">1</emphasis>.2
|
||
(eth<emphasis role="bold">0</emphasis>'s IP address) <emphasis
|
||
role="bold">from </emphasis>10.10.<emphasis
|
||
role="bold">2</emphasis>.0/24: comes in on eth<emphasis
|
||
role="bold">0</emphasis> but, because the system has a route to
|
||
10.10.<emphasis role="bold">2</emphasis>.0/24, the reply will attempt to
|
||
go out eth<emphasis role="bold">1</emphasis>. rp_filter will block this
|
||
because of the “reply on different interface” situation.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>traffic to 10.10.<emphasis role="bold">2</emphasis>.2
|
||
(eth<emphasis role="bold">1</emphasis>'s IP address) <emphasis
|
||
role="bold">f</emphasis><emphasis role="bold">rom anywhere but
|
||
</emphasis>10.10.<emphasis role="bold">2</emphasis>.0/24: comes in on
|
||
eth<emphasis role="bold">1</emphasis> but, because the system either
|
||
doesn't have a route to the source subnet <emphasis
|
||
role="bold"><emphasis>(</emphasis>or the source subnet is
|
||
10.10.1.0/24<emphasis>)</emphasis></emphasis> the default route and thus
|
||
eth<emphasis role="bold">0</emphasis> will attempt to be used. rp_filter
|
||
will block this because of the “reply on different interface”
|
||
situation.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>traffic to 10.10.<emphasis role="bold">2</emphasis>.2
|
||
(eth<emphasis role="bold">1</emphasis>'s IP address) <emphasis
|
||
role="bold">fr</emphasis><emphasis role="bold">om
|
||
10.10.2.0/24</emphasis>: comes in on eth<emphasis
|
||
role="bold">1</emphasis> and will go out on eth<emphasis
|
||
role="bold">1</emphasis> meeting rp_filter criteria.</para>
|
||
</listitem>
|
||
</orderedlist>
|
||
|
||
<para>For systems having additional interfaces the issues expand. Traffic
|
||
for any local interface other than the one having the default route (and not
|
||
having a specific route in the routing table) will have replies directed to
|
||
the default route thus violating rp_filter's strict mode criteria.</para>
|
||
|
||
<para><emphasis role="bold">Symptoms of the problem.</emphasis></para>
|
||
|
||
<para>These are possible symptoms, other issues such as destination offline
|
||
should also be considered.</para>
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>Can connect to an IP address from its subnet but not off subnet
|
||
and there are no firewall rules or, if there are firewall rules, nothing
|
||
is blocking the traffic.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>Traffic for the local system comes in but doesn't make it to the
|
||
local application.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>Traffic arrives but doesn't get routed. If there are firewall
|
||
rules, nothing is blocking the traffic.</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
|
||
<para><emphasis role="bold">Determining if rp_filter is the
|
||
problem.</emphasis></para>
|
||
|
||
<para><emphasis role="bold">Warning</emphasis>: An exception (see
|
||
“Exception” below) may cause the solutions listed here to fail to show an
|
||
rp_filter issue if:</para>
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>there's a default route on the target system</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>rp_filter on the target system is set to loose mode</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>the interfaces are connected to the same upstream device – if
|
||
rp_filter on the upstream device is using strict mode it will catch the
|
||
issue.</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para><emphasis role="bold">nstat -az
|
||
TcpExtIPReversePathFilter</emphasis> - shows a counter (first number)
|
||
which increments</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><emphasis role="bold">cat /proc/net/stat/rt_cache</emphasis> -
|
||
check the "in_martian_src" column (the eighth column with the first
|
||
column being 1) to see if it's incremented, <emphasis role="bold">man
|
||
lnstat</emphasis> gives the definitions for all columns) – note it could
|
||
be any row for that column since, per <emphasis role="bold">man
|
||
lnstat</emphasis>, the number of rows reflects the number of cpus in the
|
||
system. For a system (such as a hypervisor) with a large number of cpus
|
||
this may be unwieldy. The hexadecimal numbers here don't necessarily
|
||
agree with the other methods.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><emphasis role="bold">lnstat -k rt_cache:in_martian_src</emphasis>
|
||
will show changes real time but not the count.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><emphasis role="bold">netstat -s | grep Filter</emphasis> –
|
||
however, if forwarding (net.ipv4.ip_forward=1) is not enabled the entry
|
||
won't be found.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>Use <emphasis role="bold">ip route get <remote
|
||
IP></emphasis> to determine the interface which will be used, if it's
|
||
different than the entry interface then rp_filter in strict mode will
|
||
drop it.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>Enable martian logging, use <emphasis role="bold">sysctl -a | grep
|
||
martian</emphasis> to find the settings or <emphasis role="bold">echo 1
|
||
> /proc/sys/net/ipv4/conf/<interface>/log_martians</emphasis>.
|
||
If the goal is to document all such drops then replace <interface>
|
||
with “all” (without the quotes). <emphasis role="bold">grep martian
|
||
/var/log/syslog</emphasis>. any martians recorded will report: the date
|
||
and time, the source IP address of the martian, the IP address sending
|
||
the packet and what interface it arrived on.</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
|
||
<para><emphasis role="bold">Exception</emphasis></para>
|
||
|
||
<para>The following diagram shows a scenario where the rp_filter counters on
|
||
a system (hypervisor but could be any multi-homed system) exhibiting
|
||
problems will either be zero or not change in response to attempted access.
|
||
In order to conserve space in the diagram, all references to packet sources
|
||
and destinations should be understood to mean “IP address of specified
|
||
interface”. Situation:</para>
|
||
|
||
<orderedlist>
|
||
<listitem>
|
||
<para>A client sends a ping to the hypervisor's br1 IP address</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>The router forwards it</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>The hypervisor is using loose mode rp_filter and there is a return
|
||
path (the default route) for the packet therefore the rp_filter criteria
|
||
is satisfied, no counter is incremented. However, the hypervisor has no
|
||
specific route to the client and thus uses the default through br0. But,
|
||
when the hypervisor swaps the source and destination IP address/port as
|
||
a part of it's reply process, the br1 IP address becomes the
|
||
source.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>The packet is delivered to the router's eth1 port with source of
|
||
br1 and destination of the client.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>The router notes the br1 source on eth1 but the interface for
|
||
replying to that address should be eth2. Since rp_filter is using strict
|
||
mode this is a violation of the reverse path and the packet is dropped.
|
||
The client never gets a reply.</para>
|
||
</listitem>
|
||
</orderedlist>
|
||
|
||
<para><inlinemediaobject>
|
||
<imageobject>
|
||
<imagedata contentdepth="528" contentwidth="504"
|
||
fileref="LDP/rp_filterv2.jpg"/>
|
||
</imageobject>
|
||
</inlinemediaobject></para>
|
||
|
||
<para><emphasis role="bold">Solutions to a problem.</emphasis></para>
|
||
|
||
<para>Some possible solutions are:</para>
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>Disable rp_filter either for the interface with the problem or
|
||
system-wide. Use the second and third options above under “Is it active”
|
||
to find the solution appropriate to your issue. Either change
|
||
/etc/sysctrl.conf or use echo to change the value dynamically.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>Create policy-based routing tables - for the below assume
|
||
that:</para>
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>eth0 has IP address 1.2.3.4 and is the default route via
|
||
1.2.3.1in the main table.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>eth1 is the other interface with IP address 9.8.7.6 and
|
||
gateway 9.8.7.1.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>The alternate routing table to use was arbitrarily chosen to
|
||
be table 100. Terse examples are given, for more complex situations
|
||
an understanding of alternate routing tables and “ip rule”
|
||
configuration is needed. This is out of scope for this document,
|
||
search the web for “Linux policy based routing” to learn
|
||
more.</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>Find the “occupied” routing tables. All other tables in the
|
||
range 0-255 are empty and can be used as an alternate routing table.
|
||
Unfortunately, the proposed method is different for older and newer
|
||
systems.</para>
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>For older systems use: <emphasis role="bold">for i in `seq
|
||
0 255`; do if [[ `ip route sh ta $i | wc -m` -gt 0 ]]; then echo
|
||
"$i";fi;done</emphasis> – if this produces numerous “Error:
|
||
ipv4: FIB table does not exist.” messages then use the newer
|
||
system method.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>For newer systems use: <emphasis role="bold">for i in `seq
|
||
0 255`; do ip route sh ta $i 1>/dev/null 2>&1; if [[
|
||
$? -eq 0 ]]; then echo "$i";fi;done</emphasis></para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>Setting a default route for eth1</para>
|
||
|
||
<orderedlist>
|
||
<listitem>
|
||
<para><emphasis role="bold">ip route add table 100 default via
|
||
9.8.7.1 dev eth1</emphasis></para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><emphasis role="bold">ip rule add to 9.8.7.6 table
|
||
100</emphasis></para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><emphasis role="bold">ip rule from 9.8.7.6 table
|
||
100</emphasis></para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>This in effect disables rp_filter by setting a default
|
||
route through the interface for all traffic entering it.</para>
|
||
</listitem>
|
||
</orderedlist>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>Setting specific routes for eth1 – useful when all subnets
|
||
needing access via eth1 are known. Assume only various subnets in
|
||
the 10.0.0.0/8 and 192.168.0.0/16 range need access.</para>
|
||
|
||
<orderedlist>
|
||
<listitem>
|
||
<para><emphasis role="bold">ip route add table 100 10.0.0.0/8
|
||
dev eth1 via 9.8.7.1.</emphasis></para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><emphasis role="bold">ip route add table 100
|
||
192.168.0.0/16 dev eth1 via 9.8.7.1</emphasis>.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>When done, add steps 2 and 3 above.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>This has the advantage of allowing access while retaining
|
||
rp_filter protection for all other inbound access.</para>
|
||
</listitem>
|
||
</orderedlist>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</article>
|
||
|