Skip to content

Commit

Permalink
Add config parameter to disable loose RPF check.
Browse files Browse the repository at this point in the history
  • Loading branch information
fasaxc committed Aug 4, 2016
1 parent 3631bac commit 0c5969e
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 12 deletions.
9 changes: 9 additions & 0 deletions calico/felix/config.py
Expand Up @@ -240,6 +240,14 @@ def __init__(self, config_path):
"One of 'DROP', 'ACCEPT', 'LOG-and-DROP', "
"'LOG-and-ACCEPT'.",
"DROP")
self.add_parameter("IgnoreLooseRPF",
"If set to true, Felix will ignore the kernel's "
"RPF check setting. If set to false, Felix will "
"abort if the RPF setting is not 'strict'. Should "
"only be set to true if workloads are incapable of "
"spoofing their source IP. (For example, "
"unprivileged containers.)",
False, value_is_bool=True)
self.add_parameter("LogFilePath",
"Path to log file", "/var/log/calico/felix.log")
self.add_parameter("EtcdDriverLogFilePath",
Expand Down Expand Up @@ -419,6 +427,7 @@ def _finish_update(self, final=False):
self.FAILSAFE_OUTBOUND_PORTS = \
self.parameters["FailsafeOutboundHostPorts"].value
self.ACTION_ON_DROP = self.parameters["DropActionOverride"].value
self.IGNORE_LOOSE_RPF = self.parameters["IgnoreLooseRPF"].value

self._validate_cfg(final=final)

Expand Down
21 changes: 15 additions & 6 deletions calico/felix/devices.py
Expand Up @@ -36,7 +36,7 @@
_log = logging.getLogger(__name__)


def configure_global_kernel_config():
def configure_global_kernel_config(config):
"""
Configures the global kernel config. In particular, sets the flags
that we rely on to ensure security, such as the kernel's RPF check.
Expand Down Expand Up @@ -67,11 +67,20 @@ def configure_global_kernel_config():
ps_name = "/proc/sys/net/ipv4/conf/all/rp_filter"
rp_filter = int(_read_proc_sys(ps_name))
if rp_filter > 1:
_log.critical("Kernel's RPF check is set to 'loose'. This would "
"allow endpoints to spoof their IP address. Calico "
"requires net.ipv4.conf.all.rp_filter to be set to "
"0 or 1.")
raise BadKernelConfig("net.ipv4.conf.all.rp_filter set to 'loose'")
if config.IGNORE_LOOSE_RPF:
_log.warning(
"Kernel's RPF check is set to 'loose' and IgnoreLooseRPF "
"set to true. Calico will not be able to prevent workloads "
"from spoofing their source IP. Please ensure that some "
"other anti-spoofing mechanism is in place (such as running "
"only non-privileged containers)."
)
else:
_log.critical("Kernel's RPF check is set to 'loose'. This would "
"allow endpoints to spoof their IP address. Calico "
"requires net.ipv4.conf.all.rp_filter to be set to "
"0 or 1.")
raise BadKernelConfig("net.ipv4.conf.all.rp_filter set to 'loose'")

# Make sure the default for new interfaces is set to strict checking so
# that there's no race when a new interface is added and felix hasn't
Expand Down
2 changes: 1 addition & 1 deletion calico/felix/felix.py
Expand Up @@ -76,7 +76,7 @@ def _main_greenlet(config):

# Ensure the Kernel's global options are correctly configured for
# Calico.
devices.configure_global_kernel_config()
devices.configure_global_kernel_config(config)

# Check the commands we require are present.
futils.check_command_deps()
Expand Down
23 changes: 20 additions & 3 deletions calico/felix/test/test_devices.py
Expand Up @@ -59,7 +59,9 @@ def test_configure_global_kernel_config(self,
m_read_proc_sys,
m_write_proc_sys,
m_exists):
devices.configure_global_kernel_config()
m_config = mock.Mock()
m_config.IGNORE_LOOSE_RPF = True
devices.configure_global_kernel_config(m_config)
m_write_proc_sys.assert_called_once_with(
"/proc/sys/net/ipv4/conf/default/rp_filter", "1"
)
Expand All @@ -73,14 +75,29 @@ def test_configure_global_kernel_config_no_sysfs(self,
m_read_proc_sys,
m_write_proc_sys,
m_exists):
m_config = mock.Mock()
m_config.IGNORE_LOOSE_RPF = False
self.assertRaises(devices.BadKernelConfig,
devices.configure_global_kernel_config)
devices.configure_global_kernel_config, m_config)

def test_configure_global_kernel_config_bad_rp_filter(self):
m_config = mock.Mock()
m_config.IGNORE_LOOSE_RPF = False
with mock.patch("calico.felix.devices._read_proc_sys",
autospec=True, return_value="2") as m_read_proc_sys:
self.assertRaises(devices.BadKernelConfig,
devices.configure_global_kernel_config)
devices.configure_global_kernel_config,
m_config)

@mock.patch("calico.felix.devices._write_proc_sys",
autospec=True)
@mock.patch("calico.felix.devices._read_proc_sys",
autospec=True)
def test_configure_global_kernel_config_bad_rp_filter_ignored(self, m_rps, m_wps):
m_config = mock.Mock()
m_config.IGNORE_LOOSE_RPF = True
m_rps.return_value = "2"
devices.configure_global_kernel_config(m_config)

def test_read_proc_sys(self):
m_open = mock.mock_open(read_data="1\n")
Expand Down
4 changes: 2 additions & 2 deletions calico/felix/test/test_felix.py
Expand Up @@ -111,7 +111,7 @@ def test_main_greenlet(self, m_iwait, m_MasqueradeManager,
m_load.assert_called_once_with(async=False)
m_iface_exists.assert_called_once_with("tunl0")
m_iface_up.assert_called_once_with("tunl0")
m_configure_global_kernel_config.assert_called_once_with()
m_configure_global_kernel_config.assert_called_once_with(config)
m_conntrack.assert_called_once_with()
m_http_server.assert_called_once_with(("0.0.0.0", 9091),
felix.MetricsHandler)
Expand Down Expand Up @@ -170,7 +170,7 @@ def test_main_greenlet_no_ipv6(self, m_iwait, m_MasqueradeManager,
self.assertRaises(TestException,
felix._main_greenlet, config)
m_load.assert_called_once_with(async=False)
m_configure_global_kernel_config.assert_called_once_with()
m_configure_global_kernel_config.assert_called_once_with(config)
m_install_globals.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY,
ip_version=4)
m_conntrack.assert_called_once_with()
Expand Down

0 comments on commit 0c5969e

Please sign in to comment.