diff --git a/attributes/sysctl.rb b/attributes/sysctl.rb index feac5839..1f43fbac 100644 --- a/attributes/sysctl.rb +++ b/attributes/sysctl.rb @@ -17,12 +17,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Only enable IP traffic forwarding, if required. -default['sysctl']['params']['net']['ipv4']['ip_forward'] = - node['os-hardening']['network']['forwarding'] ? 1 : 0 -default['sysctl']['params']['net']['ipv6']['conf']['all']['forwarding'] = - node['os-hardening']['network']['ipv6']['enable'] && node['os-hardening']['network']['forwarding'] ? 1 : 0 - # Enable RFC-recommended source validation feature. It should not be used for # routers on complex networks, but is helpful for end hosts and routers serving # small networks. @@ -44,56 +38,9 @@ # time exceed, param problem, timestamp reply, information reply default['sysctl']['params']['net']['ipv4']['icmp_ratemask'] = 88089 -# Disable or Enable IPv6 as it is needed. -default['sysctl']['params']['net']['ipv6']['conf']['all']['disable_ipv6'] = - node['os-hardening']['network']['ipv6']['enable'] ? 0 : 1 - # Protect against wrapping sequence numbers at gigabit speeds: default['sysctl']['params']['net']['ipv4']['tcp_timestamps'] = 0 -# arp_announce - INTEGER -# Define different restriction levels for announcing the local source IP -# address from IP packets in ARP requests sent on interface: -# -# * **0** - (default) Use any local address, configured on any interface -# * **1** - Try to avoid local addresses that are not in the target's subnet -# for this interface. This mode is useful when target hosts reachable -# via this interface require the source IP address in ARP requests to -# be part of their logical network configured on the receiving -# interface. When we generate the request we will check all our -# subnets that include the target IP and will preserve the source -# address if it is from such subnet. If there is no such subnet we -# select source address according to the rules for level 2. -# * **2** - Always use the best local address for this target. In this mode -# we ignore the source address in the IP packet and try to select -# local address that we prefer for talks with the target host. Such -# local address is selected by looking for primary IP addresses on -# all our subnets on the outgoing interface that include the target -# IP address. If no suitable local address is found we select the -# first local address we have on the outgoing interface or on all -# other interfaces, with the hope we will receive reply for our -# request and even sometimes no matter the source IP address we -# announce. -# -default['sysctl']['params']['net']['ipv4']['conf']['all']['arp_ignore'] = - node['os-hardening']['network']['arp']['restricted'] ? 1 : 0 - -# Define different modes for sending replies in response to received ARP requests that resolve local target IP addresses: -# -# * **0** - (default): reply for any local target IP address, configured on -# any interface -# * **1** - reply only if the target IP address is local address configured -# on the incoming interface -# * **2** - reply only if the target IP address is local address configured -# on the incoming interface and both with the sender's IP address are -# part from same subnet on this interface -# * **3** - do not reply for local addresses configured with scope host, only -# resolutions for global and link addresses are replied -# * **4-7** - reserved -# * **8** - do not reply for all local addresses -default['sysctl']['params']['net']['ipv4']['conf']['all']['arp_announce'] = - node['os-hardening']['network']['arp']['restricted'] ? 2 : 0 - # RFC 1337 fix F1 default['sysctl']['params']['net']['ipv4']['tcp_rfc1337'] = 1 @@ -143,40 +90,6 @@ default['sysctl']['params']['net']['ipv6']['conf']['all']['accept_ra'] = 0 default['sysctl']['params']['net']['ipv6']['conf']['default']['accept_ra'] = 0 -# System -# ------ - -# This settings controls how the kernel behaves towards module changes at -# runtime. Setting to 1 will disable module loading at runtime. -# Setting it to 0 is actually never supported. -unless node['os-hardening']['security']['kernel']['enable_module_loading'] - default['sysctl']['params']['kernel']['modules_disabled'] = 1 -end - -# Magic Sysrq should be disabled, but can also be set to a safe value if so -# desired for physical machines. It can allow a safe reboot if the system hangs -# and is a 'cleaner' alternative to hitting the reset button. -# The following values are permitted: -# -# * **0** - disable sysrq -# * **1** - enable sysrq completely -# * **>1** - bitmask of enabled sysrq functions: -# * **2** - control of console logging level -# * **4** - control of keyboard (SAK, unraw) -# * **8** - debugging dumps of processes etc. -# * **16** - sync command -# * **32** - remount read-only -# * **64** - signalling of processes (term, kill, oom-kill) -# * **128** - reboot/poweroff -# * **256** - nicing of all RT tasks -default['sysctl']['params']['kernel']['sysrq'] = - node['os-hardening']['security']['kernel']['enable_sysrq'] ? node['os-hardening']['security']['kernel']['secure_sysrq'] : 0 - -# Prevent core dumps with SUID. These are usually only needed by developers and -# may contain sensitive information. -default['sysctl']['params']['fs']['suid_dumpable'] = - node['os-hardening']['security']['kernel']['enable_core_dump'] ? 1 : 0 - # ExecShield protection against buffer overflows # unless node['platform'] == "ubuntu" # ["nx"].include?(node['cpu'][0]['flags']) or case node['platform_family'] diff --git a/recipes/sysctl.rb b/recipes/sysctl.rb index 7f6ddacc..e22f5203 100644 --- a/recipes/sysctl.rb +++ b/recipes/sysctl.rb @@ -19,6 +19,94 @@ # limitations under the License. # +# default attributes +# We can not set this kind of defaults in the attribute files +# as we react on value of other attributes +# https://github.com/dev-sec/chef-ssh-hardening/issues/140#issuecomment-267779720 + +# Only enable IP traffic forwarding, if required. +node.default['sysctl']['params']['net']['ipv4']['ip_forward'] = + node['os-hardening']['network']['forwarding'] ? 1 : 0 +node.default['sysctl']['params']['net']['ipv6']['conf']['all']['forwarding'] = + node['os-hardening']['network']['ipv6']['enable'] && node['os-hardening']['network']['forwarding'] ? 1 : 0 + +# Disable or Enable IPv6 as it is needed. +node.default['sysctl']['params']['net']['ipv6']['conf']['all']['disable_ipv6'] = + node['os-hardening']['network']['ipv6']['enable'] ? 0 : 1 + +# Define different modes for sending replies in response to received ARP requests that resolve local target IP addresses: +# +# * **0** - (default): reply for any local target IP address, configured on +# any interface +# * **1** - reply only if the target IP address is local address configured +# on the incoming interface +# * **2** - reply only if the target IP address is local address configured +# on the incoming interface and both with the sender's IP address are +# part from same subnet on this interface +# * **3** - do not reply for local addresses configured with scope host, only +# resolutions for global and link addresses are replied +# * **4-7** - reserved +# * **8** - do not reply for all local addresses +node.default['sysctl']['params']['net']['ipv4']['conf']['all']['arp_ignore'] = + node['os-hardening']['network']['arp']['restricted'] ? 1 : 0 + +# Define different restriction levels for announcing the local source IP +# address from IP packets in ARP requests sent on interface: +# +# * **0** - (default) Use any local address, configured on any interface +# * **1** - Try to avoid local addresses that are not in the target's subnet +# for this interface. This mode is useful when target hosts reachable +# via this interface require the source IP address in ARP requests to +# be part of their logical network configured on the receiving +# interface. When we generate the request we will check all our +# subnets that include the target IP and will preserve the source +# address if it is from such subnet. If there is no such subnet we +# select source address according to the rules for level 2. +# * **2** - Always use the best local address for this target. In this mode +# we ignore the source address in the IP packet and try to select +# local address that we prefer for talks with the target host. Such +# local address is selected by looking for primary IP addresses on +# all our subnets on the outgoing interface that include the target +# IP address. If no suitable local address is found we select the +# first local address we have on the outgoing interface or on all +# other interfaces, with the hope we will receive reply for our +# request and even sometimes no matter the source IP address we +# announce. +# +node.default['sysctl']['params']['net']['ipv4']['conf']['all']['arp_announce'] = + node['os-hardening']['network']['arp']['restricted'] ? 2 : 0 + +# This setting controls how the kernel behaves towards module changes at +# runtime. Setting to 1 will disable module loading at runtime. +# Setting it to 0 is actually never supported. +unless node['os-hardening']['security']['kernel']['enable_module_loading'] + node.default['sysctl']['params']['kernel']['modules_disabled'] = 1 +end + +# Magic Sysrq should be disabled, but can also be set to a safe value if so +# desired for physical machines. It can allow a safe reboot if the system hangs +# and is a 'cleaner' alternative to hitting the reset button. +# The following values are permitted: +# +# * **0** - disable sysrq +# * **1** - enable sysrq completely +# * **>1** - bitmask of enabled sysrq functions: +# * **2** - control of console logging level +# * **4** - control of keyboard (SAK, unraw) +# * **8** - debugging dumps of processes etc. +# * **16** - sync command +# * **32** - remount read-only +# * **64** - signalling of processes (term, kill, oom-kill) +# * **128** - reboot/poweroff +# * **256** - nicing of all RT tasks +node.default['sysctl']['params']['kernel']['sysrq'] = + node['os-hardening']['security']['kernel']['enable_sysrq'] ? node['os-hardening']['security']['kernel']['secure_sysrq'] : 0 + +# Prevent core dumps with SUID. These are usually only needed by developers and +# may contain sensitive information. +node.default['sysctl']['params']['fs']['suid_dumpable'] = + node['os-hardening']['security']['kernel']['enable_core_dump'] ? 2 : 0 + # include sysctl recipe and set /etc/sysctl.d/99-chef-attributes.conf include_recipe 'sysctl::apply' diff --git a/spec/recipes/sysctl_spec.rb b/spec/recipes/sysctl_spec.rb index eb0c3692..71d6adfd 100644 --- a/spec/recipes/sysctl_spec.rb +++ b/spec/recipes/sysctl_spec.rb @@ -128,4 +128,240 @@ end end end + + describe 'sysctl flags' do + let(:ipv6_enable) { false } + let(:network_forwarding) { false } + let(:arp_restricted) { true } + let(:enable_module_loading) { true } + let(:enable_sysrq) { true } + let(:enable_core_dump) { true } + let(:chef_run) do + ChefSpec::SoloRunner.new do |node| + node.normal['os-hardening']['network']['forwarding'] = + network_forwarding + node.normal['os-hardening']['network']['ipv6']['enable'] = ipv6_enable + node.normal['os-hardening']['network']['arp']['restricted'] = + arp_restricted + node.normal['os-hardening']['security']['kernel']['enable_module_loading'] = # rubocop:disable Metrics/LineLength + enable_module_loading + node.normal['os-hardening']['security']['kernel']['enable_sysrq'] = + enable_sysrq + node.normal['os-hardening']['security']['kernel']['enable_core_dump'] = + enable_core_dump + end.converge(described_recipe) + end + + describe 'IPv4 forwarding' do + subject do + chef_run.node['sysctl']['params']['net']['ipv4']['ip_forward'] + end + + context 'when forwarding is enabled' do + let(:network_forwarding) { true } + + it 'should enable IPv4 forwarding in sysctl attributes' do + is_expected.to eq(1) + end + end + + context 'when forwarding is disabled' do + let(:network_forwarding) { false } + + it 'should disable IPv4 forwarding in sysctl attributes' do + is_expected.to eq(0) + end + end + end + + describe 'IPv6 forwarding' do + RSpec.shared_examples 'IPv6 forwarding in sysctl attributes' do |state| + subject { chef_run.node['sysctl']['params']['net']['ipv6']['conf']['all']['forwarding'] } # rubocop:disable Metrics/LineLength + + it "should #{state == 1 ? 'enable' : 'disable'} IPv6 forwarding in sysctl attributes" do # rubocop:disable Metrics/LineLength + is_expected.to eq(state) + end + end + + context 'when IPv6 is enabled' do + let(:ipv6_enable) { true } + + context 'when forwarding is enabled' do + let(:network_forwarding) { true } + + include_examples 'IPv6 forwarding in sysctl attributes', 1 + end + context 'when forwarding is disabled' do + let(:network_forwarding) { false } + + include_examples 'IPv6 forwarding in sysctl attributes', 0 + end + end + + context 'when IPv6 is disabled' do + let(:ipv6_enable) { false } + + context 'when forwarding is enabled' do + let(:network_forwarding) { true } + + include_examples 'IPv6 forwarding in sysctl attributes', 0 + end + + context 'when forwarding is disabled' do + let(:network_forwarding) { false } + + include_examples 'IPv6 forwarding in sysctl attributes', 0 + end + end + end + + describe 'Control IPv6' do + subject do + chef_run. + node['sysctl']['params']['net']['ipv6']['conf']['all']['disable_ipv6'] + end + + context 'when IPv6 is enabled' do + let(:ipv6_enable) { true } + + it 'should not disable IPv6' do + is_expected.to eq(0) + end + end + + context 'when IPv6 is disabled' do + let(:ipv6_enable) { false } + + it 'should not disable IPv6' do + is_expected.to eq(1) + end + end + end + + describe 'ARP restrictions' do + RSpec.shared_examples 'ARP restrictions in sysctl attributes' do |arp_ignore, arp_announce| # rubocop:disable Metrics/LineLength + describe 'arp_ignore' do + subject do + chef_run.node['sysctl']['params']['net']['ipv4']['conf']['all']['arp_ignore'] # rubocop:disable Metrics/LineLength + end + + it "should be set to #{arp_ignore}" do + is_expected.to eq(arp_ignore) + end + end + + describe 'arp_announce' do + subject do + chef_run.node['sysctl']['params']['net']['ipv4']['conf']['all']['arp_announce'] # rubocop:disable Metrics/LineLength + end + + it "should be set to #{arp_announce}" do + is_expected.to eq(arp_announce) + end + end + end + + context 'when ARP is restricted' do + let(:arp_restricted) { true } + + include_examples 'ARP restrictions in sysctl attributes', 1, 2 + end + + context 'when ARP is not restricted' do + let(:arp_restricted) { false } + + include_examples 'ARP restrictions in sysctl attributes', 0, 0 + end + end + + describe 'Module loading' do + subject do + chef_run.node['sysctl']['params']['kernel']['modules_disabled'] + end + + context 'when module loading is enabled' do + let(:enable_module_loading) { true } + + it 'should not set the sysctl flag' do + is_expected.to eq(nil) + end + + describe 'rebuild of initramfs' do + subject { chef_run } + + it 'should not create initramfs module file' do + is_expected.not_to create_template('/etc/initramfs-tools/modules') + end + + it 'should not rebuild initramfs' do + is_expected.not_to run_execute('update-initramfs') + end + end + end + + context 'when module loading is disabled' do + let(:enable_module_loading) { false } + + it 'should disable module loading via sysctl flag' do + is_expected.to eq(1) + end + + describe 'rebuild of initramfs' do + subject { chef_run } + + it 'should create initramfs module file' do + is_expected.to create_template('/etc/initramfs-tools/modules') + end + + it 'should rebuild initramfs' do + is_expected.to run_execute('update-initramfs') + end + end + end + end + + describe 'Control magic SysRq' do + subject do + chef_run.node['sysctl']['params']['kernel']['sysrq'] + end + + context 'when sysrq is enabled' do + let(:enable_sysrq) { true } + + it 'should enable sysrq with safe value' do + is_expected.to eq(244) + end + end + + context 'when sysrq is disabled' do + let(:enable_sysrq) { false } + + it 'should disable sysrq' do + is_expected.to eq(0) + end + end + end + + describe 'Core dumps with SUID' do + subject do + chef_run.node['sysctl']['params']['fs']['suid_dumpable'] + end + + context 'when core dumps are enabled' do + let(:enable_core_dump) { true } + + it 'should set suid_dumpable to safe value' do + is_expected.to eq(2) + end + end + + context 'when core dumps are disabled' do + let(:enable_core_dump) { false } + + it 'should set suid_dumpable to default value' do + is_expected.to eq(0) + end + end + end + end end