From d9ca0cc713faedaa270f040ee97c99e5cd232e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCnch?= Date: Thu, 9 Jun 2016 10:06:02 +0200 Subject: [PATCH 01/12] migrate to inspec testing framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick Münch --- controls/apache_spec.rb | 130 ++++++++++++++++++ default/puppet/Modulefile | 12 -- default/puppet/Puppetfile | 7 - default/puppet/manifests/site.pp | 30 ---- default/roles/apache.json | 16 --- default/serverspec/apache_spec.rb | 118 ---------------- default/serverspec/spec_helper.rb | 65 --------- default/serverspec/type/file_with_includes.rb | 67 --------- inspec.yml | 10 ++ libraries/apache_lib.rb | 92 +++++++++++++ 10 files changed, 232 insertions(+), 315 deletions(-) create mode 100644 controls/apache_spec.rb delete mode 100644 default/puppet/Modulefile delete mode 100644 default/puppet/Puppetfile delete mode 100644 default/puppet/manifests/site.pp delete mode 100644 default/roles/apache.json delete mode 100644 default/serverspec/apache_spec.rb delete mode 100644 default/serverspec/spec_helper.rb delete mode 100644 default/serverspec/type/file_with_includes.rb create mode 100644 inspec.yml create mode 100644 libraries/apache_lib.rb diff --git a/controls/apache_spec.rb b/controls/apache_spec.rb new file mode 100644 index 0000000..b0971b9 --- /dev/null +++ b/controls/apache_spec.rb @@ -0,0 +1,130 @@ +# encoding: utf-8 +# +# Copyright 2016, Patrick Muench +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# author: Christoph Hartmann +# author: Dominik Richter +# author: Patrick Muench + +title 'Apache server config' + +only_if do + command(apache.service).exist? +end + +title 'Apache server config' + +control 'apache-01' do + impact 1.0 + title 'Apache should be running' + desc 'Apache should be running.' + describe service(apache.service) do + it { should be_installed } + it { should be_enabled } + it { should be_running } + end +end + +control 'apache-02' do + title 'Apache should start max. 1 root-task' + desc 'The Apache service in its own non-privileged account. If the web server process runs with administrative privileges, an attack who obtains control over the apache process may control the entire system.' + total_tasks = command("ps aux | grep #{apache.service} | grep -v grep | grep root | wc -l | tr -d [:space:]").stdout.to_i + describe total_tasks do + it { should eq 1 } + end +end + + +control 'apache-03' do + impact 1.0 + title 'Check Apache config folder owner, group and permissions.' + desc 'The Apache config folder should owned and grouped by root, be writable, readable and executable by owner. It should be readable, executable by group and not readable, not writeable by others.' + describe file(apache.conf_dir) do + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + it { should be_readable.by('owner') } + it { should be_writable.by('owner') } + it { should be_executable.by('owner') } + it { should be_readable.by('group') } + it { should_not be_writable.by('group') } + it { should be_executable.by('group') } + it { should_not be_readable.by('others') } + it { should_not be_writable.by('others') } + it { should be_executable.by('others') } + end +end + +control 'apache-04' do + impact 1.0 + title 'Check Apache config file owner, group and permissions.' + desc 'The Apache config file should owned and grouped by root, only be writable and readable by owner and not write- and readable by others.' + describe file(apache.conf_path) do + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + it { should be_readable.by('owner') } + it { should be_writable.by('owner') } + it { should_not be_executable.by('owner') } + it { should be_readable.by('group') } + it { should_not be_writable.by('group') } + it { should_not be_executable.by('group') } + it { should_not be_readable.by('others') } + it { should_not be_writable.by('others') } + it { should_not be_executable.by('others') } + end + describe file(File.join(apache.conf_dir,'/conf-enabled/hardening.conf')) do + it { should be_owned_by 'root' } + it { should be_grouped_into 'root' } + it { should be_readable.by('owner') } + it { should be_writable.by('owner') } + it { should_not be_executable.by('owner') } + it { should be_readable.by('group') } + it { should_not be_writable.by('group') } + it { should_not be_executable.by('group') } + it { should_not be_readable.by('others') } + it { should_not be_writable.by('others') } + it { should_not be_executable.by('others') } + end +end + +control 'apache-05' do + impact 1.0 + title 'User and group should be set properly' + desc 'For security reasons it is recommended to run Apache in its own non-privileged account.' + describe apache_conf do + its('content') { should match(/^\s*?User\s+?#{apache.user}/) } + its('content') { should match(/^\s*?Group\s+?#{apache.user}/) } + end +end + +control 'apache-06' do + impact 1.0 + title 'Set the apache server token' + desc '\'ServerTokens Prod\' tells Apache to return only Apache as product in the server response header on the every page request' + describe apache_conf do + its('content') { should match(/^\s*ServerTokens Prod/) } + end +end + +control 'apache-07' do + impact 1.0 + title 'Should not load certain modules' + desc 'Apache HTTP should not load legacy modules' + describe apache_conf do + its(:content) { should_not match(/^\s*?LoadModule\s+?dav_module/) } + its(:content) { should_not match(/^\s*?LoadModule\s+?cgid_module/) } + its(:content) { should_not match(/^\s*?LoadModule\s+?cgi_module/) } + its(:content) { should_not match(/^\s*?LoadModule\s+?include_module/) } + end +end diff --git a/default/puppet/Modulefile b/default/puppet/Modulefile deleted file mode 100644 index 06feae6..0000000 --- a/default/puppet/Modulefile +++ /dev/null @@ -1,12 +0,0 @@ -name 'hardening/apache_hardening' -version '0.1.0' -source 'https://github.com/TelekomLabs/puppet-apache-hardening' -author 'Markus Schmall' -license 'Apache License, Version 2.0' -summary 'Configures Apache for security hardening' -description 'Configures Apache for security hardening' -project_page 'https://github.com/TelekomLabs/puppet-apache-hardening' - -dependency 'hardening/hardening_stdlib', '>=0.0.0 <1.0.0' -dependency 'puppetlabs/apache' - diff --git a/default/puppet/Puppetfile b/default/puppet/Puppetfile deleted file mode 100644 index fbf1b9c..0000000 --- a/default/puppet/Puppetfile +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env ruby -#^syntax detection - -forge "http://forge.puppetlabs.com" - -# use dependencies defined in Modulefile -modulefile \ No newline at end of file diff --git a/default/puppet/manifests/site.pp b/default/puppet/manifests/site.pp deleted file mode 100644 index 44e9509..0000000 --- a/default/puppet/manifests/site.pp +++ /dev/null @@ -1,30 +0,0 @@ -# Configure Apache Server as you normally would: - - -class { 'apache': - default_mods => false, - default_vhost => false, -} - -::apache::vhost { 'hardening-default': - port => 80, - docroot => $::apache::docroot, - scriptalias => $::apache::scriptalias, - serveradmin => $::apache::serveradmin, - access_log_file => $::apache::access_log_file, - priority => '25', - directories => [ - { 'path' => $::apache::docroot, - 'provider' => 'files', - 'allow' => 'from all', - 'order' => 'allow,deny', - 'options' => ['-Indexes','-FollowSymLinks','+MultiViews'], - 'allow_override' => ['None'], - - }, - ] -} - -class { 'apache_hardening': - provider => 'puppetlabs/apache' -} diff --git a/default/roles/apache.json b/default/roles/apache.json deleted file mode 100644 index 54137c5..0000000 --- a/default/roles/apache.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "apache", - "default_attributes": { }, - "override_attributes": { }, - "json_class": "Chef::Role", - "description": "Apache Hardened Server Test Role", - "chef_type": "role", - "default_attributes" : { - }, - "run_list": [ - "recipe[chef-solo-search]", - "recipe[apt]", - "recipe[apache2]", - "recipe[apache-hardening::default]" - ] -} \ No newline at end of file diff --git a/default/serverspec/apache_spec.rb b/default/serverspec/apache_spec.rb deleted file mode 100644 index 2860c6d..0000000 --- a/default/serverspec/apache_spec.rb +++ /dev/null @@ -1,118 +0,0 @@ -# encoding: utf-8 -# -# Copyright 2014, Deutsche Telekom AG -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -require 'spec_helper' - -RSpec::Matchers.define :match_key_value do |key, value| - match do |actual| - actual =~ /^\s*?#{key}\s*?=\s*?#{value}/ - end -end - -# set OS-dependent filenames and paths -case os[:family] -when 'ubuntu', 'debian' - apache_config_path = '/etc/apache2/' - apache_config = File.join(apache_config_path, 'apache2.conf') - service_name = 'apache2' - task_name = 'apache2' - user_name = 'www-data' -else - apache_config_path = '/etc/httpd/' - apache_config = File.join(apache_config_path, '/conf/httpd.conf') - service_name = 'httpd' - task_name = 'httpd' - user_name = 'apache' -end - -describe service("#{service_name}") do - it { should be_enabled } - it { should be_running } -end - -@max_servers = 0 - -describe 'Apache Service' do - it 'should start max. 1 root-tasks' do - total_tasks = command("ps aux | grep #{task_name} | grep -v grep | grep root | wc -l | tr -d [:space:]").stdout.to_i - expect(total_tasks).to eq 1 - end -end - -describe 'Apache Config' do - it 'config should not be worldwide read- or writeable' do - num_files = command("find #{apache_config_path} -perm -o+r -type f -o -perm -o+w -type f | wc -l").stdout.to_i - expect(num_files).to eq 0 - end - - describe "should have user and group set to #{user_name}" do - describe file_with_includes(apache_config, /^\s*Include.*$/) do - its(:content) { should match(/^\s*?User\s+?#{user_name}/) } - its(:content) { should match(/^\s*?Group\s+?#{user_name}/) } - end - end - - describe file_with_includes(apache_config, /^\s*Include.*$/) do - its(:content) { should match(/^ServerTokens Prod/) } - end - - describe 'should not load certain modules' do - describe file_with_includes(apache_config, /^\s*Include.*$/) do - its(:content) { should_not match(/^\s*?LoadModule\s+?dav_module/) } - its(:content) { should_not match(/^\s*?LoadModule\s+?cgid_module/) } - its(:content) { should_not match(/^\s*?LoadModule\s+?cgi_module/) } - its(:content) { should_not match(/^\s*?LoadModule\s+?include_module/) } - end - end - - describe 'should disable insecure HTTP-methods' do - describe file_with_includes(apache_config, /^\s*Include.*$/) do - its(:content) { should match(/^\s*?TraceEnable\s+?Off/) } - its(:content) { should match(/^\s*?/) } - end - end - - describe 'protect directories' do - - directories = file_with_includes(apache_config, %r{/^\s*Include.*$/}).content.gsub(/#.*$/, '').scan(%r{//im}).flatten - - it 'should include -FollowSymLinks or +SymLinksIfOwnerMatch for directories' do - expect(directories).to all(match(/-FollowSymLinks/i).or match(/\+SymLinksIfOwnerMatch/i)) - end - - it 'should include -Indexes for directories' do - expect(directories).to all(match(/-Indexes/i)) - end - end -end - -describe 'Virtualhosts' do - - # get all the non comment vhost tags - vhosts = file_with_includes(apache_config, %r{/^\s*Include.*$/}).content.gsub(/#.*$/, '').scan(%r{//im}).flatten - it 'should include Custom Log' do - expect(vhosts).to all(match(/CustomLog.*$/i)) - end - - ## get all ssl vhosts - vhosts = file_with_includes(apache_config, %r{/^\s*Include.*$/}).content.gsub(/#.*$/, '').scan(%r{//im}).flatten - - describe 'SSL Options' do - it 'should include SSLHonorCipherOrder On' do - expect(vhosts).to all(match(/SSLHonorCipherOrder.*On/i)) - end - end -end diff --git a/default/serverspec/spec_helper.rb b/default/serverspec/spec_helper.rb deleted file mode 100644 index 4c55516..0000000 --- a/default/serverspec/spec_helper.rb +++ /dev/null @@ -1,65 +0,0 @@ -# encoding: utf-8 -# -# Copyright 2014, Deutsche Telekom AG -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -if ENV['STANDALONE_SPEC'] - - require 'serverspec' - require 'pathname' - require 'net/ssh' - require 'highline/import' - - set :backend, :ssh - - RSpec.configure do |c| - - if ENV['ASK_SUDO_PASSWORD'] - c.sudo_password = ask('Enter sudo password: ') { |q| q.echo = false } - else - c.sudo_password = ENV['SUDO_PASSWORD'] - end - - options = {} - - if ENV['ASK_LOGIN_PASSWORD'] - options[:password] = ask("\nEnter login password: ") { |q| q.echo = false } - else - options[:password] = ENV['LOGIN_PASSWORD'] - end - - if ENV['ASK_LOGIN_USERNAME'] - options[:user] = ask("\nEnter login username: ") { |q| q.echo = false } - else - options[:user] = ENV['LOGIN_USERNAME'] || ENV['user'] || Etc.getlogin - end - - if options[:user].nil? - puts 'specify login user env LOGIN_USERNAME= or user=' - exit 1 - end - - c.host = ENV['TARGET_HOST'] - c.ssh_options = options.merge(Net::SSH::Config.for(c.host)) - - end - -else - require 'serverspec' - - set :backend, :exec -end - -require 'type/file_with_includes' diff --git a/default/serverspec/type/file_with_includes.rb b/default/serverspec/type/file_with_includes.rb deleted file mode 100644 index 5b7d2cb..0000000 --- a/default/serverspec/type/file_with_includes.rb +++ /dev/null @@ -1,67 +0,0 @@ -# encoding: utf-8 -# -# Copyright 2014, Deutsche Telekom AG -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -include Serverspec::Type - -module Serverspec - module Type - class FileWithIncludes < Base - def initialize(file_name, include_matcher) - @name = file_name - @runner = Specinfra::Runner - @file_name = file_name - @include_matcher = include_matcher - @content_files = {} - end - - def to_s - type = self.class.name.split(':')[-1] - type.gsub!(/([a-z\d])([A-Z])/, '\1 \2') - type.capitalize! - %(#{type} "#{@file_name}" with matcher "#{@include_matcher}") - end - - def content - if @content.nil? - file = get_file_content(@file_name) - - file.scan(@include_matcher).each do |include_match| - # We split at whitespace and strip quotes - Dir.glob(include_match.split(/\s/).last.gsub(/['"]/, '')).each do |file_name| - included_file = get_file_content(file_name) - file << included_file - end - - end - @content = file - end - @content - end - - def get_file_content(file) - if @content_files[file].nil? - @content_files[file] = @runner.get_file_content(file).stdout - end - @content_files[file] - end - end - - def file_with_includes(file_name, include_matcher) - FileWithIncludes.new(file_name, include_matcher) - end - end -end diff --git a/inspec.yml b/inspec.yml new file mode 100644 index 0000000..a1cba52 --- /dev/null +++ b/inspec.yml @@ -0,0 +1,10 @@ +name: apache-hardening +title: Hardening Framework Apache Hardening Test Suite +maintainer: Hardening Framework Team +copyright: Hardening Framework Team +copyright_email: hello@hardening.io +license: Apache 2 license +summary: Test-suite for best-practice apache hardening +version: 0.9.0 +supports: + - os-family: unix diff --git a/libraries/apache_lib.rb b/libraries/apache_lib.rb new file mode 100644 index 0000000..14179d1 --- /dev/null +++ b/libraries/apache_lib.rb @@ -0,0 +1,92 @@ +# encoding: utf-8 +# +# Copyright 2016, Patrick Muench +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# author: Christoph Hartmann +# author: Dominik Richter +# author: Patrick Muench + +class Apachelib < Inspec.resource(1) + name 'apache_lib' + + def valid_taskname + # define apache task/service name for different distros + + centos_taskname = 'httpd' + debian_taskname = 'apache2' + apache_taskname = debian_taskname + + case inspec.os[:family] + when 'ubuntu', 'debian' + apache_taskname + when 'redhat', 'centos' + apache_taskname = centos_taskname + end + + apache_taskname + end + + def valid_users + # define apache user for different distros + + centos_user = 'apache' + debian_user = 'www-data' + web_user = debian_user + + # adjust the nginx user based on OS + case inspec.os[:family] + when 'ubuntu', 'debian' + web_user + when 'redhat', 'centos' + web_user = centos_user + end + + web_user + end + + def valid_path + # define apache config path for different distros + centos_path = '/etc/httpd/' + debian_path = '/etc/apache2/' + apache_config_path = debian_path + + # adjust the nginx user based on OS + case inspec.os[:family] + when 'ubuntu', 'debian' + apache_config_path + when 'redhat', 'centos' + apache_config_path = centos_path + end + + apache_config_path + end + + def valid_config + # define apache config path for different distros + centos_config = '/conf/httpd.conf' + debian_config = 'apache2.conf' + apache_config = File.join(self.valid_path, debian_config) + + # adjust the nginx user based on OS + case inspec.os[:family] + when 'ubuntu', 'debian' + apache_config = File.join(self.valid_path, debian_config) + when 'redhat', 'centos' + apache_config = File.join(self.valid_path, centos_config) + end + + apache_config + end +end From ee8e5cbf674b6cad6be928544bc11a47074f63ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCnch?= Date: Thu, 9 Jun 2016 10:06:23 +0200 Subject: [PATCH 02/12] update travis.yml and Gemfile to use inspec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick Münch --- .travis.yml | 11 ++++++++--- Gemfile | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2c6aced..1da179c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,10 @@ +--- +language: ruby +cache: bundler rvm: - 1.9.3 - - 2.0.0 -language: ruby -script: bundle exec rake run_all_linters \ No newline at end of file + - 2.0 + - 2.2 + +bundler_args: --without integration +script: bundle exec rake diff --git a/Gemfile b/Gemfile index 76cf704..587005b 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,17 @@ source 'https://rubygems.org' +# pin dependency for Ruby 1.9.3 since bundler is not +# detecting that net-ssh 3 does not work with 1.9.3 +if Gem::Version.new(RUBY_VERSION) <= Gem::Version.new('1.9.3') + gem 'net-ssh', '~> 2.9' +end + gem 'rake' -gem 'serverspec', '~> 2.3.0' -gem 'rubocop', '~> 0.23' +gem 'inspec', '~> 0' +gem 'rubocop', '~> 0.36.0' gem 'highline', '~> 1.6.0' + +group :tools do + gem 'github_changelog_generator', '~> 1.12.0' +end From 9012a46868591014be20a8bd89ae2f5b85aee86b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCnch?= Date: Mon, 13 Jun 2016 13:37:13 +0200 Subject: [PATCH 03/12] complete inspec tests and use the inspec apache resource MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick Münch --- controls/apache_spec.rb | 129 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 9 deletions(-) diff --git a/controls/apache_spec.rb b/controls/apache_spec.rb index b0971b9..65ccdb1 100644 --- a/controls/apache_spec.rb +++ b/controls/apache_spec.rb @@ -103,8 +103,8 @@ title 'User and group should be set properly' desc 'For security reasons it is recommended to run Apache in its own non-privileged account.' describe apache_conf do - its('content') { should match(/^\s*?User\s+?#{apache.user}/) } - its('content') { should match(/^\s*?Group\s+?#{apache.user}/) } + its('User') { should eq [apache.user] } + its('Group') { should eq [apache.user] } end end @@ -112,19 +112,130 @@ impact 1.0 title 'Set the apache server token' desc '\'ServerTokens Prod\' tells Apache to return only Apache as product in the server response header on the every page request' - describe apache_conf do - its('content') { should match(/^\s*ServerTokens Prod/) } + + describe file(File.join(apache.conf_dir,'/conf-enabled/security.conf')) do + its('content') { should match(/^ServerTokens Prod/) } end + + # open bug https://github.com/chef/inspec/issues/786, if the bug solved use this test + # describe apache_conf do + # its('ServerTokens') { should eq 'Prod' } + # end end control 'apache-07' do impact 1.0 title 'Should not load certain modules' desc 'Apache HTTP should not load legacy modules' - describe apache_conf do - its(:content) { should_not match(/^\s*?LoadModule\s+?dav_module/) } - its(:content) { should_not match(/^\s*?LoadModule\s+?cgid_module/) } - its(:content) { should_not match(/^\s*?LoadModule\s+?cgi_module/) } - its(:content) { should_not match(/^\s*?LoadModule\s+?include_module/) } + + module_path = File.join(apache.conf_dir, '/mods-enabled/') + loaded_modules = command('ls ' << module_path).stdout.split.keep_if{|file_name| /.load/.match(file_name)} + + loaded_modules.each do |id| + describe file(File.join(module_path,id)) do + its('content') { should_not match(/^\s*?LoadModule\s+?dav_module/) } + its('content') { should_not match(/^\s*?LoadModule\s+?cgid_module/) } + its('content') { should_not match(/^\s*?LoadModule\s+?cgi_module/) } + its('content') { should_not match(/^\s*?LoadModule\s+?include_module/) } + end + end + + # open bug https://github.com/chef/inspec/issues/786, if the bug solved use this test + # describe apache_conf do + # its('LoadModule') { should_not eq 'dav_module' } + # its('LoadModule') { should_not eq 'cgid_module' } + # its('LoadModule') { should_not eq 'cgi_module' } + # its('LoadModule') { should_not eq 'include_module' } + # its('content') { should_not match(/^\s*?LoadModule\s+?dav_module/) } + # its('content') { should_not match(/^\s*?LoadModule\s+?cgid_module/) } + # its('content') { should_not match(/^\s*?LoadModule\s+?cgi_module/) } + # its('content') { should_not match(/^\s*?LoadModule\s+?include_module/) } + # end +end + +control 'apache-08' do + impact 1.0 + title 'Disable TRACE-methods' + desc 'The web server doesn’t allow TRACE request and help in blocking Cross Site Tracing attack.' + + describe file(File.join(apache.conf_dir,'/conf-enabled/security.conf')) do + its('content') { should match(/^\s*?TraceEnable\s+?Off/) } + end + + # open bug https://github.com/chef/inspec/issues/786, if the bug solved use this test + # describe apache_conf do + # its('TraceEnable') { should eq 'Off' } + # end +end + +control 'apache-09' do + impact 1.0 + title 'Disable insecure HTTP-methods' + desc 'Disable insecure HTTP-methods and allow only necessary methods.' + + describe file(File.join(apache.conf_dir,'/conf-enabled/hardening.conf')) do + its('content') { should match(/^\s*?/) } + end + + # open bug https://github.com/chef/inspec/issues/786, if the bug solved use this test + # describe apache_conf do + # its('LimitExcept') { should eq ['GET','POST'] } + # end +end + +control 'apache-10' do + impact 1.0 + title 'Disable Apache’s follows Symbolic Links for directories in alias.conf' + desc 'Should include -FollowSymLinks or +SymLinksIfOwnerMatch for directories in alias.conf' + + describe file(File.join(apache.conf_dir,'/mods-enabled/alias.conf')) do + its('content') { should match(/-FollowSymLinks/).or match(/\+SymLinksIfOwnerMatch/) } + end +end + +control 'apache-11' do + impact 1.0 + title 'Disable Directory Listing for directories in alias.conf' + desc 'Should include -Indexes for directories in alias.conf' + + describe file(File.join(apache.conf_dir,'/mods-enabled/alias.conf')) do + its('content') { should match(/-Indexes/) } + end +end + +control 'apache-12' do + impact 1.0 + title 'SSL honor cipher order' + desc 'When choosing a cipher during an SSLv3 or TLSv1 handshake, normally the client\'s preference is used. If this directive is enabled, the server\'s preference will be used instead.' + + describe file(File.join(apache.conf_dir, '/mods-enabled/ssl.conf')) do + its('content') { should match(/^\s*?SSLHonorCipherOrder\s+?On/i) } + end + + sites_enabled_path = File.join(apache.conf_dir, '/sites-enabled/') + loaded_sites = command('ls ' << sites_enabled_path).stdout.split.keep_if{|file_name| /.conf/.match(file_name)} + + loaded_sites.each do |id| + virtual_host = file(File.join(sites_enabled_path,id)).content.gsub(/#.*$/, '').scan(//im).flatten + if !virtual_host.empty? + describe virtual_host do + it { should include(/^\s*?SSLHonorCipherOrder\s+?On/i) } + end + end + end +end + +control 'apache-13' do + impact 1.0 + title 'Enable Apache Logging' + desc 'Apache allows you to logging independently of your OS logging. It is wise to enable Apache logging, because it provides more information, such as the commands entered by users that have interacted with your Web server.' + + sites_enabled_path = File.join(apache.conf_dir, '/sites-enabled/') + loaded_sites = command('ls ' << sites_enabled_path).stdout.split.keep_if{|file_name| /.conf/.match(file_name)} + + loaded_sites.each do |id| + describe file(File.join(sites_enabled_path,id)).content.gsub(/#.*$/, '').scan(//im).flatten do + it { should include(/CustomLog.*$/i) } + end end end From e32d6a199a9556324dc0a0f0dc1f71dabf64cb9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCnch?= Date: Mon, 13 Jun 2016 15:14:03 +0200 Subject: [PATCH 04/12] remove blank line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick Münch --- controls/apache_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/controls/apache_spec.rb b/controls/apache_spec.rb index 65ccdb1..0855f6e 100644 --- a/controls/apache_spec.rb +++ b/controls/apache_spec.rb @@ -46,7 +46,6 @@ end end - control 'apache-03' do impact 1.0 title 'Check Apache config folder owner, group and permissions.' From 70f8bbe1daacbeecfbfa28b25f88d716a8e4b3ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCnch?= Date: Mon, 13 Jun 2016 15:18:20 +0200 Subject: [PATCH 05/12] remove apache_lib.rb, because use native inspec resources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick Münch --- libraries/apache_lib.rb | 92 ----------------------------------------- 1 file changed, 92 deletions(-) delete mode 100644 libraries/apache_lib.rb diff --git a/libraries/apache_lib.rb b/libraries/apache_lib.rb deleted file mode 100644 index 14179d1..0000000 --- a/libraries/apache_lib.rb +++ /dev/null @@ -1,92 +0,0 @@ -# encoding: utf-8 -# -# Copyright 2016, Patrick Muench -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# author: Christoph Hartmann -# author: Dominik Richter -# author: Patrick Muench - -class Apachelib < Inspec.resource(1) - name 'apache_lib' - - def valid_taskname - # define apache task/service name for different distros - - centos_taskname = 'httpd' - debian_taskname = 'apache2' - apache_taskname = debian_taskname - - case inspec.os[:family] - when 'ubuntu', 'debian' - apache_taskname - when 'redhat', 'centos' - apache_taskname = centos_taskname - end - - apache_taskname - end - - def valid_users - # define apache user for different distros - - centos_user = 'apache' - debian_user = 'www-data' - web_user = debian_user - - # adjust the nginx user based on OS - case inspec.os[:family] - when 'ubuntu', 'debian' - web_user - when 'redhat', 'centos' - web_user = centos_user - end - - web_user - end - - def valid_path - # define apache config path for different distros - centos_path = '/etc/httpd/' - debian_path = '/etc/apache2/' - apache_config_path = debian_path - - # adjust the nginx user based on OS - case inspec.os[:family] - when 'ubuntu', 'debian' - apache_config_path - when 'redhat', 'centos' - apache_config_path = centos_path - end - - apache_config_path - end - - def valid_config - # define apache config path for different distros - centos_config = '/conf/httpd.conf' - debian_config = 'apache2.conf' - apache_config = File.join(self.valid_path, debian_config) - - # adjust the nginx user based on OS - case inspec.os[:family] - when 'ubuntu', 'debian' - apache_config = File.join(self.valid_path, debian_config) - when 'redhat', 'centos' - apache_config = File.join(self.valid_path, centos_config) - end - - apache_config - end -end From eb9c7666619cfc8bb17af0729d5d839ea8fe32ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCnch?= Date: Tue, 14 Jun 2016 08:25:36 +0200 Subject: [PATCH 06/12] fixing lint errors and adjust README.md, also update rakefile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick Münch --- README.md | 47 ++++++++++++++++++------------------- Rakefile | 51 ++++++++++++++++++----------------------- controls/apache_spec.rb | 28 +++++++++++----------- 3 files changed, 58 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index afca014..11cad9b 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,40 @@ tests-apache-hardening ===================== -This are the integration tests for the projects +This Compliance Profile ensures, that all hardening projects keep the same quality. -- https://github.com/hardening-io/puppet-apache-hardening -- https://github.com/hardening-io/chef-apache-hardening - -they start at `integration` level - -you can use the gem `kitchen-sharedtests` - -- https://github.com/ehaselwanter/kitchen-sharedtests/ - -to make them available to your project. Use `thor kitchen:fetch-remote-tests` to put the repo into `test/integration` +- https://github.com/dev-sec/puppet-apache-hardening +- https://github.com/dev-sec/chef-apache-hardening ## Standalone Usage -you can target the integration tests to any host were you have ssh access - -rake -T gives you a list of suites you can run (well ignore directories which are obviously not suites for now) +This Compliance Profile requires [InSpec](https://github.com/chef/inspec) for execution: ``` -± rake -T -rake serverspec:default # Run serverspec suite default +$ git clone https://github.com/dev-sec/tests-apache-hardening +$ inspec exec tests-apache-hardening ``` -run it with: +You can also execute the profile directly from Github: ``` -bundle install - -# default user and ssh-key +$ inspec exec https://github.com/dev-sec/tests-apache-hardening +``` -bundle exec rake serverspec:default target_host= +* Author:: Patrick Muench +* Author:: Dominik Richter +* Author:: Christoph Hartmann -# or with user, host, password +* Copyright 2014-2016, The Hardening Framework Team -ASK_LOGIN_PASSWORD=true bundle exec rake serverspec:default target_host=192.168.1.222 user=stack -``` +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at -add `format=html|json` to get a report.html or report.json document + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/Rakefile b/Rakefile index 3bc2908..1a77717 100644 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,7 @@ +#!/usr/bin/env rake # encoding: utf-8 -require 'rake' -require 'rspec/core/rake_task' +require 'rake/testtask' require 'rubocop/rake_task' # Rubocop @@ -10,37 +10,30 @@ task :rubocop do RuboCop::RakeTask.new end -# Lint the cookbook -desc 'Run linters' -task :run_all_linters => [:rubocop] # rubocop:disable Style/HashSyntax -task :default => :run_all_linters # rubocop:disable Style/HashSyntax +# lint the project +desc 'Run robocop linter' +task lint: [:rubocop] -# Serverspec tests -suites = Dir.glob('*').select { |entry| File.directory?(entry) } +# run tests +task default: [:lint, 'test:check'] -class ServerspecTask < RSpec::Core::RakeTask - attr_accessor :target - - def spec_command - if target.nil? - puts 'specify either env TARGET_HOST or target_host=' - exit 1 - end - - cmd = super - "env TARGET_HOST=#{target} STANDALONE_SPEC=true #{cmd} --format documentation --no-profile" +namespace :test do + # run inspec check to verify that the profile is properly configured + task :check do + dir = File.join(File.dirname(__FILE__)) + sh("bundle exec inspec check #{dir}") end end -namespace :serverspec do - suites.each do |suite| - desc "Run serverspec suite #{suite}" - ServerspecTask.new(suite.to_sym) do |t| - t.rspec_opts = '--no-color --format html --out report.html' if ENV['format'] == 'html' - t.rspec_opts = '--no-color --format json --out report.json' if ENV['format'] == 'json' - t.target = ENV['TARGET_HOST'] || ENV['target_host'] - t.ruby_opts = "-I #{suite}/serverspec" - t.pattern = "#{suite}/serverspec/*_spec.rb" - end +# Automatically generate a changelog for this project. Only loaded if +# the necessary gem is installed. +# use `rake changelog to=1.2.0` +begin + v = ENV['to'] + require 'github_changelog_generator/task' + GitHubChangelogGenerator::RakeTask.new :changelog do |config| + config.future_release = v end +rescue LoadError + puts '>>>>> GitHub Changelog Generator not loaded, omitting tasks' end diff --git a/controls/apache_spec.rb b/controls/apache_spec.rb index 0855f6e..d563fda 100644 --- a/controls/apache_spec.rb +++ b/controls/apache_spec.rb @@ -82,7 +82,7 @@ it { should_not be_writable.by('others') } it { should_not be_executable.by('others') } end - describe file(File.join(apache.conf_dir,'/conf-enabled/hardening.conf')) do + describe file(File.join(apache.conf_dir, '/conf-enabled/hardening.conf')) do it { should be_owned_by 'root' } it { should be_grouped_into 'root' } it { should be_readable.by('owner') } @@ -112,7 +112,7 @@ title 'Set the apache server token' desc '\'ServerTokens Prod\' tells Apache to return only Apache as product in the server response header on the every page request' - describe file(File.join(apache.conf_dir,'/conf-enabled/security.conf')) do + describe file(File.join(apache.conf_dir, '/conf-enabled/security.conf')) do its('content') { should match(/^ServerTokens Prod/) } end @@ -128,10 +128,10 @@ desc 'Apache HTTP should not load legacy modules' module_path = File.join(apache.conf_dir, '/mods-enabled/') - loaded_modules = command('ls ' << module_path).stdout.split.keep_if{|file_name| /.load/.match(file_name)} + loaded_modules = command('ls ' << module_path).stdout.split.keep_if{ |file_name| /.load/.match(file_name) } loaded_modules.each do |id| - describe file(File.join(module_path,id)) do + describe file(File.join(module_path, id)) do its('content') { should_not match(/^\s*?LoadModule\s+?dav_module/) } its('content') { should_not match(/^\s*?LoadModule\s+?cgid_module/) } its('content') { should_not match(/^\s*?LoadModule\s+?cgi_module/) } @@ -157,7 +157,7 @@ title 'Disable TRACE-methods' desc 'The web server doesn’t allow TRACE request and help in blocking Cross Site Tracing attack.' - describe file(File.join(apache.conf_dir,'/conf-enabled/security.conf')) do + describe file(File.join(apache.conf_dir, '/conf-enabled/security.conf')) do its('content') { should match(/^\s*?TraceEnable\s+?Off/) } end @@ -172,7 +172,7 @@ title 'Disable insecure HTTP-methods' desc 'Disable insecure HTTP-methods and allow only necessary methods.' - describe file(File.join(apache.conf_dir,'/conf-enabled/hardening.conf')) do + describe file(File.join(apache.conf_dir, '/conf-enabled/hardening.conf')) do its('content') { should match(/^\s*?/) } end @@ -187,7 +187,7 @@ title 'Disable Apache’s follows Symbolic Links for directories in alias.conf' desc 'Should include -FollowSymLinks or +SymLinksIfOwnerMatch for directories in alias.conf' - describe file(File.join(apache.conf_dir,'/mods-enabled/alias.conf')) do + describe file(File.join(apache.conf_dir, '/mods-enabled/alias.conf')) do its('content') { should match(/-FollowSymLinks/).or match(/\+SymLinksIfOwnerMatch/) } end end @@ -197,7 +197,7 @@ title 'Disable Directory Listing for directories in alias.conf' desc 'Should include -Indexes for directories in alias.conf' - describe file(File.join(apache.conf_dir,'/mods-enabled/alias.conf')) do + describe file(File.join(apache.conf_dir, '/mods-enabled/alias.conf')) do its('content') { should match(/-Indexes/) } end end @@ -212,11 +212,11 @@ end sites_enabled_path = File.join(apache.conf_dir, '/sites-enabled/') - loaded_sites = command('ls ' << sites_enabled_path).stdout.split.keep_if{|file_name| /.conf/.match(file_name)} + loaded_sites = command('ls ' << sites_enabled_path).stdout.split.keep_if{ |file_name| /.conf/.match(file_name) } loaded_sites.each do |id| - virtual_host = file(File.join(sites_enabled_path,id)).content.gsub(/#.*$/, '').scan(//im).flatten - if !virtual_host.empty? + virtual_host = file(File.join(sites_enabled_path, id)).content.gsub(/#.*$/, '').scan(%r{}im).flatten + unless virtual_host.empty? describe virtual_host do it { should include(/^\s*?SSLHonorCipherOrder\s+?On/i) } end @@ -230,11 +230,11 @@ desc 'Apache allows you to logging independently of your OS logging. It is wise to enable Apache logging, because it provides more information, such as the commands entered by users that have interacted with your Web server.' sites_enabled_path = File.join(apache.conf_dir, '/sites-enabled/') - loaded_sites = command('ls ' << sites_enabled_path).stdout.split.keep_if{|file_name| /.conf/.match(file_name)} + loaded_sites = command('ls ' << sites_enabled_path).stdout.split.keep_if{ |file_name| /.conf/.match(file_name) } loaded_sites.each do |id| - describe file(File.join(sites_enabled_path,id)).content.gsub(/#.*$/, '').scan(//im).flatten do + describe file(File.join(sites_enabled_path, id)).content.gsub(/#.*$/, '').scan(%r{}im).flatten do it { should include(/CustomLog.*$/i) } - end + end end end From af099d22392360b2411d4f9f42e6fc815d73eee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCnch?= Date: Tue, 14 Jun 2016 08:44:51 +0200 Subject: [PATCH 07/12] add build status to README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick Münch --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 11cad9b..b072d0f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ tests-apache-hardening ===================== +[![Build Status](http://img.shields.io/travis/dev-sec/tests-apache-hardening.svg)][1] + This Compliance Profile ensures, that all hardening projects keep the same quality. - https://github.com/dev-sec/puppet-apache-hardening From 899969652de81b04f7becf110346e933ec68840d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCnch?= Date: Tue, 14 Jun 2016 08:45:05 +0200 Subject: [PATCH 08/12] add link to gitter.im MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick Münch --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index b072d0f..44ce9da 100644 --- a/README.md +++ b/README.md @@ -40,3 +40,6 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +[1]: http://travis-ci.org/dev-sec/tests-apache-hardening +[2]: https://gitter.im/dev-sec/general From 97865e5a2051954a2dbe75794e101feb3b982ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCnch?= Date: Tue, 14 Jun 2016 08:58:18 +0200 Subject: [PATCH 09/12] fix lint errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick Münch --- controls/apache_spec.rb | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/controls/apache_spec.rb b/controls/apache_spec.rb index d563fda..f359534 100644 --- a/controls/apache_spec.rb +++ b/controls/apache_spec.rb @@ -128,7 +128,7 @@ desc 'Apache HTTP should not load legacy modules' module_path = File.join(apache.conf_dir, '/mods-enabled/') - loaded_modules = command('ls ' << module_path).stdout.split.keep_if{ |file_name| /.load/.match(file_name) } + loaded_modules = command('ls ' << module_path).stdout.split.keep_if { |file_name| /.load/.match(file_name) } loaded_modules.each do |id| describe file(File.join(module_path, id)) do @@ -212,14 +212,13 @@ end sites_enabled_path = File.join(apache.conf_dir, '/sites-enabled/') - loaded_sites = command('ls ' << sites_enabled_path).stdout.split.keep_if{ |file_name| /.conf/.match(file_name) } + loaded_sites = command('ls ' << sites_enabled_path).stdout.split.keep_if { |file_name| /.conf/.match(file_name) } loaded_sites.each do |id| virtual_host = file(File.join(sites_enabled_path, id)).content.gsub(/#.*$/, '').scan(%r{}im).flatten - unless virtual_host.empty? - describe virtual_host do - it { should include(/^\s*?SSLHonorCipherOrder\s+?On/i) } - end + next if virtual_host.empty? + describe virtual_host do + it { should include(/^\s*?SSLHonorCipherOrder\s+?On/i) } end end end @@ -230,11 +229,11 @@ desc 'Apache allows you to logging independently of your OS logging. It is wise to enable Apache logging, because it provides more information, such as the commands entered by users that have interacted with your Web server.' sites_enabled_path = File.join(apache.conf_dir, '/sites-enabled/') - loaded_sites = command('ls ' << sites_enabled_path).stdout.split.keep_if{ |file_name| /.conf/.match(file_name) } + loaded_sites = command('ls ' << sites_enabled_path).stdout.split.keep_if { |file_name| /.conf/.match(file_name) } loaded_sites.each do |id| describe file(File.join(sites_enabled_path, id)).content.gsub(/#.*$/, '').scan(%r{}im).flatten do - it { should include(/CustomLog.*$/i) } + it { should include(/CustomLog.*$/i) } end end end From 105701dfd43dae75214e19554359518b1a009cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCnch?= Date: Thu, 16 Jun 2016 16:49:14 +0200 Subject: [PATCH 10/12] comment the enabled test, because of systemd issues in debian-8 and ubuntu-16.04 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick Münch --- controls/apache_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controls/apache_spec.rb b/controls/apache_spec.rb index f359534..244c89e 100644 --- a/controls/apache_spec.rb +++ b/controls/apache_spec.rb @@ -32,7 +32,7 @@ desc 'Apache should be running.' describe service(apache.service) do it { should be_installed } - it { should be_enabled } + # it { should be_enabled } # comment the test because i got some trouble with the systemd in debian-8 and ubuntu-16.04 it { should be_running } end end From 778b5b9a72648ff8746f3ea8380e37ec730d3fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCnch?= Date: Fri, 17 Jun 2016 10:04:57 +0200 Subject: [PATCH 11/12] deactivate test service should be enabled only for ubuntu-16.04 and debian-8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick Münch --- controls/apache_spec.rb | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/controls/apache_spec.rb b/controls/apache_spec.rb index 244c89e..368e713 100644 --- a/controls/apache_spec.rb +++ b/controls/apache_spec.rb @@ -32,12 +32,21 @@ desc 'Apache should be running.' describe service(apache.service) do it { should be_installed } - # it { should be_enabled } # comment the test because i got some trouble with the systemd in debian-8 and ubuntu-16.04 it { should be_running } end end control 'apache-02' do + impact 1.0 + title 'Apache should be enabled' + desc 'Configure apache service to be automatically started at boot time' + only_if { os[:family] != 'ubuntu' && os[:release] != '16.04' } || only_if { os[:family] != 'debian' && os[:release] != '8' } + describe service(apache.service) do + it { should be_enabled } + end +end + +control 'apache-03' do title 'Apache should start max. 1 root-task' desc 'The Apache service in its own non-privileged account. If the web server process runs with administrative privileges, an attack who obtains control over the apache process may control the entire system.' total_tasks = command("ps aux | grep #{apache.service} | grep -v grep | grep root | wc -l | tr -d [:space:]").stdout.to_i @@ -46,7 +55,7 @@ end end -control 'apache-03' do +control 'apache-04' do impact 1.0 title 'Check Apache config folder owner, group and permissions.' desc 'The Apache config folder should owned and grouped by root, be writable, readable and executable by owner. It should be readable, executable by group and not readable, not writeable by others.' @@ -65,7 +74,7 @@ end end -control 'apache-04' do +control 'apache-05' do impact 1.0 title 'Check Apache config file owner, group and permissions.' desc 'The Apache config file should owned and grouped by root, only be writable and readable by owner and not write- and readable by others.' @@ -97,7 +106,7 @@ end end -control 'apache-05' do +control 'apache-06' do impact 1.0 title 'User and group should be set properly' desc 'For security reasons it is recommended to run Apache in its own non-privileged account.' @@ -107,7 +116,7 @@ end end -control 'apache-06' do +control 'apache-07' do impact 1.0 title 'Set the apache server token' desc '\'ServerTokens Prod\' tells Apache to return only Apache as product in the server response header on the every page request' @@ -122,7 +131,7 @@ # end end -control 'apache-07' do +control 'apache-08' do impact 1.0 title 'Should not load certain modules' desc 'Apache HTTP should not load legacy modules' @@ -152,7 +161,7 @@ # end end -control 'apache-08' do +control 'apache-09' do impact 1.0 title 'Disable TRACE-methods' desc 'The web server doesn’t allow TRACE request and help in blocking Cross Site Tracing attack.' @@ -167,7 +176,7 @@ # end end -control 'apache-09' do +control 'apache-10' do impact 1.0 title 'Disable insecure HTTP-methods' desc 'Disable insecure HTTP-methods and allow only necessary methods.' @@ -182,7 +191,7 @@ # end end -control 'apache-10' do +control 'apache-11' do impact 1.0 title 'Disable Apache’s follows Symbolic Links for directories in alias.conf' desc 'Should include -FollowSymLinks or +SymLinksIfOwnerMatch for directories in alias.conf' @@ -192,7 +201,7 @@ end end -control 'apache-11' do +control 'apache-12' do impact 1.0 title 'Disable Directory Listing for directories in alias.conf' desc 'Should include -Indexes for directories in alias.conf' @@ -202,7 +211,7 @@ end end -control 'apache-12' do +control 'apache-13' do impact 1.0 title 'SSL honor cipher order' desc 'When choosing a cipher during an SSLv3 or TLSv1 handshake, normally the client\'s preference is used. If this directive is enabled, the server\'s preference will be used instead.' @@ -223,7 +232,7 @@ end end -control 'apache-13' do +control 'apache-14' do impact 1.0 title 'Enable Apache Logging' desc 'Apache allows you to logging independently of your OS logging. It is wise to enable Apache logging, because it provides more information, such as the commands entered by users that have interacted with your Web server.' From 3bef004319ff3879e3ac6efbd7f66ea757f34415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCnch?= Date: Fri, 17 Jun 2016 10:34:06 +0200 Subject: [PATCH 12/12] tag version to 2.0.0 and change email address in inspec.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick Münch --- inspec.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inspec.yml b/inspec.yml index a1cba52..fc9fb5d 100644 --- a/inspec.yml +++ b/inspec.yml @@ -2,9 +2,9 @@ name: apache-hardening title: Hardening Framework Apache Hardening Test Suite maintainer: Hardening Framework Team copyright: Hardening Framework Team -copyright_email: hello@hardening.io +copyright_email: hello@dev-sec.io license: Apache 2 license summary: Test-suite for best-practice apache hardening -version: 0.9.0 +version: 2.0.0 supports: - os-family: unix