Commit 0bdae644 authored by Tim Smith's avatar Tim Smith
Browse files

Merge pull request #53 from yakara-ltd/attribute_config

Purely attribute-driven ossec.conf and agent.conf
parents b0de2452 b00b3963
......@@ -19,15 +19,12 @@ platforms:
suites:
- name: default
run_list: ["recipe[ossec]"]
attributes: {}
- name: wui
driver_config:
network:
- ["private_network", {ip: "192.168.33.33"}]
run_list:
- "recipe[ossec::server]"
- "recipe[ossec::wui]"
attributes:
dev_mode: true
- recipe[ossec]
- name: client
run_list:
- recipe[ossec::client]
- name: server
run_list:
- recipe[ossec::server]
data_bags_path: 'test/integration/default/data_bags'
......@@ -18,65 +18,102 @@ This cookbook doesn't configure Windows systems yet. For information on installi
- Chef 11+
#### Cookbooks
- build-essential
- apt
- apache2
- yum-atomic
Attributes
----------
Default values are based on the defaults from OSSEC's own install.sh installation script.
* `node['ossec']['dir']` - Installation directory for OSSEC, default `/var/ossec`. All existing packages use this directory so you should not change this.
* `node['ossec']['server_role']` - When using server/agent setup, this role is used to search for the OSSEC server, default `ossec_server`.
* `node['ossec']['server_env']` - When using server/agent setup, this value will scope the role search to the specified environment, default nil.
* `node['ossec']['checksum']` - SHA256 checksum of the source. Verified with SHA1 sum from OSSEC site.
* `node['ossec']['version']` - Version of OSSEC to download/install. Used in URL.
* `node['ossec']['url']` - URL to download the source.
* `node['ossec']['logs']` - Array of log files to analyze. Default is an empty array. These are in addition to the default logs in the ossec.conf.erb template.
* `node['ossec']['syscheck_freq']` - Frequency that syscheck is executed, default 22 hours (79200 seconds)
* `node['ossec']['agent_server_ip']` - The IP of the OSSEC server. The client recipe will attempt to determine this value via search. Default is nil, only required for agent installations.
* `node['ossec']['data_bag']['encrypted']` - Boolean value which indicates whether or not the OSSEC data bag is encrypted
* `node['ossec']['data_bag']['name']` - The name of the data bag to use
* `node['ossec']['data_bag']['ssh']` - The name of the data bag item which contains the OSSEC keys
* `node['ossec']['server']['maxagents']` - Maximum number of agents, default setting is 256, but will be set to 1024 in the ossec::server recipe if used. Add as an override attribute in the `ossec_server` role if more nodes are required.
* `node['ossec']['disable_config_generation']` - Boolean that dictates whether this cookbook should drop the ossec.conf template or not. This is useful if you're using a wrapper cookbook and would like to generate your own template.
The `user` attributes are used to populate the config file (ossec.conf) and preload values for the installation script.
* `node['ossec']['user']['language']` - Language to use for installation, default en.
* `node['ossec']['user']['install_type']` - What kind of installation to perform, default is local. Using the client or server recipe will set this to `agent` or `server`, respectively.
* `node['ossec']['user']['dir']` - Installation directory for OSSEC, default `/var/ossec`.
* `node['ossec']['user']['delete_dir']` - Whether to delete the existing OSSEC installation directory, default true.
* `node['ossec']['user']['active_response']` - Whether to enable active response feature of OSSEC, default true. It is safe and recommended to leave this enabled.
* `node['ossec']['user']['syscheck']` - Whether to enable the integrity checking process, syscheck. Default true. It is safe and recommended to leave this enabled.
* `node['ossec']['user']['rootcheck']` - Whether to enable the rootkit checking process, rootcheck. Default true. It is safe and recommended to leave this enabled.
* `node['ossec']['user']['update']` - Whether an update installation should be done, default false.
* `node['ossec']['user']['update_rules']` - Whether to update rules files, default true.
* `node['ossec']['user']['binary_install']` - If true, use the binaries in the bin directory rather than compiling. Default false. The cookbook doesn't yet support binary installations.
* `node['ossec']['user']['agent_server_ip']` - The IP of the OSSEC server. The client recipe will attempt to determine this value via search. Default is nil, only required for agent installations.
* `node['ossec']['user']['enable_email']` - Enable or disable email alerting. Default is true.
* `node['ossec']['user']['email']` - Destination email address for OSSEC alerts. Default is `ossec@example.com` and should be changed via a role attribute. Can take a string or an array of email addresses.
* `node['ossec']['user']['smtp']` - Sets the SMTP relay to send email out. Default is 127.0.0.1, which assumes that a local MTA is set up (e.g., postfix).
* `node['ossec']['user']['remote_syslog']` - Whether to enable the remote syslog server on the OSSEC server. Default false, not relevant for non-server.
* `node['ossec']['user']['firewall_response']` - Enable or disable the firewall response which sets up firewall rules for blocking. Default is true.
* `node['ossec']['user']['pf']` - Enable PF firewall on BSD, default is false.
* `node['ossec']['user']['pf_table']` - The PF table to use on BSD. Default is false, set this to the desired table if enabling `pf`.
* `node['ossec']['user']['white_list']` - Array of additional IP addresses to white list. Default is empty.
These attributes are used to setup the OSSEC Web UI.
* `node['ossec']['wui']['checksum']` - Defaults to "142febadfd4b0de5a13ebd93c13eedfbee5f1899b6ee71c248054c14f47b8089"
* `node['ossec']['wui']['version']` - Defaults to "0.3"
* `node['ossec']['wui']['url']` - Defaults to "http://www.ossec.net/files/ossec-wui-0.3.tar.gz"
* `node['ossec']['users_databag']` - Defaults to 'users'
* `node['ossec']['users_databag_group']` - Defaults to 'sysadmins'
###ossec.conf
OSSEC's configuration is mainly read from an XML file called `ossec.conf`. You can directly control the contents of this file using node attributes under `node['ossec']['conf']`. These attributes are mapped to XML using Gyoku. See the [Gyoku site](https://github.com/savonrb/gyoku) for details on how this works.
Chef applies attributes from all attribute files regardless of which recipes were executed. In order to make wrapper cookbooks easier to write, `node['ossec']['conf']` is divided into the three installation types mentioned below, `local`, `server`, and `agent`. You can also set attributes under `all` to apply settings across all installation types. The typed attributes are automatically deep merged over the `all` attributes in the normal Chef manner.
`true` and `false` values are automatically mapped to `"yes"` and `"no"` as OSSEC expects the latter.
`ossec.conf` makes little use of XML attributes so you can generally construct nested hashes in the usual fashion. Where an attribute is required, you can do it like this:
default['ossec']['conf']['all']['syscheck']['directories'] = [
{ '@check_all' => true, 'content!' => '/bin,/sbin' },
'/etc,/usr/bin,/usr/sbin'
]
This produces:
<syscheck>
<directories check_all="yes">/bin,/sbin</directories>
<directories>/etc,/usr/bin,/usr/sbin</directories>
</syscheck>
The default values are based on those given in the OSSEC manual. They do not include any specific rules, checks, outputs, or alerts as everyone has different requirements.
###agent.conf
OSSEC servers can also distribute configuration to agents through the centrally managed XM file called `agent.conf`. Since Chef is better at distributing configuration than OSSEC is, the cookbook leaves this file blank by default. Should you want to populate it, it is done in a similar manner to the above. Since this file is only used on servers, you can define the attributes directly under `node['ossec']['agent_conf']`. Unlike conventional XML files, `agent.conf` has multiple root nodes so `node['ossec']['agent_conf']` must be treated as an array like so.
default['ossec']['agent_conf'] = [
{
'syscheck' => { 'frequency' => 4321 },
'rootcheck' => { 'disabled' => true }
},
{
'@os' => 'Windows',
'content!' => {
'syscheck' => { 'frequency' => 1234 }
}
}
]
This produces:
<agent_config>
<syscheck>
<frequency>4321</frequency>
</syscheck>
<rootcheck>
<disabled>yes</disabled>
</rootcheck>
</agent_config>
<agent_config os="Windows">
<syscheck>
<frequency>1234</frequency>
</syscheck>
</agent_config>
Recipes
-------
###repository
Adds the OSSEC repository to the package manager. This recipe is included by others and should not be used directly. For highly customised setups, you should use `ossec::install_agent` or `ossec::install_server` instead.
###install_agent
Installs the agent packages but performs no explicit configuation.
###install_server
Install the server packages but performs no explicit configuation.
###common
Puts the configuration file in place and starts the (agent or server) service. This recipe is included by other recipes and generally should not be used directly.
Note that the service will not be started if the client.keys file is missing or empty. For agents, this results in an error. For servers, this prevents ossec-remoted from starting, resulting in agents being unable to connect. Once client.keys does exist with content, simply perform another chef-client run to start the service.
###default
The default recipe downloads and installs the OSSEC source and makes sure the configuration file is in place and the service is started. Use only this recipe if setting up local-only installation. The server and client recipes (below) will set their installation type and include this recipe.
Runs `ossec::install_server` and then configures for local-only use. Do not mix this recipe with the others below.
###agent
......@@ -84,11 +121,11 @@ OSSEC uses the term `agent` instead of client. The agent recipe includes the `os
###client
Configures the system as an OSSEC agent to the OSSEC server. This recipe will search for the server based on `node['ossec']['server_role']`. It will also set the `install_type` and `agent_server_ip` attributes. The ossecd user will be created with the SSH key so the server can distribute the agent key.
Configures the system as an OSSEC agent to the OSSEC server. This recipe will search for the server based on `node['ossec']['server_role']`. It will also set the `agent_server_ip` attribute. The ossec user will have an SSH key created so the server can distribute the agent key.
###server
Sets up a system to be an OSSEC server. This recipe will set the `node['ossec']['server']['maxagents']` value to 1024 if it is not set on the node (e.g., via a role). It will search for all nodes that have an `ossec` attribute and add them as an agent.
Sets up a system to be an OSSEC server. This recipe will search for all nodes that have an `ossec` attribute and add them as an agent.
To manage additional agents on the server that don't run chef, or for agentless OSSEC configuration (for example, routers), add a new node for them and create the `node['ossec']['agentless']` attribute as true. For example if we have a router named gw01.example.com with the IP `192.168.100.1`:
......@@ -117,10 +154,6 @@ To manage additional agents on the server that don't run chef, or for agentless
Enable agentless monitoring in OSSEC and register the hosts on the server. Automated configuration of agentless nodes is not yet supported by this cookbook. For more information on the commands and configuration directives required in `ossec.conf`, see the [OSSEC Documentation](http://www.ossec.net/doc/manual/agent/agentless-monitoring.html)
###wui
Installs and configures OSSEC Web UI. Requires users to be setup in a data bag (see __Data Bags__ section below).
Usage
-----
......@@ -158,9 +191,13 @@ For the OSSEC server, create a role, `ossec_server`. Add attributes per above as
run_list("recipe[ossec::server]")
override_attributes(
"ossec" => {
"user" => {
"email" => "ossec@yourdomain.com",
"smtp" => "smtp.yourdomain.com"
"conf" => {
"server" => {
"global" => {
"email_to" => "ossec@yourdomain.com",
"smtp_server" => "smtp.yourdomain.com"
}
}
}
}
)
......@@ -173,37 +210,16 @@ For OSSEC agents, create a role, `ossec_client`.
run_list("recipe[ossec::client]")
override_attributes(
"ossec" => {
"user" => {
"email" => "ossec@yourdomain.com",
"smtp" => "smtp.yourdomain.com"
"conf" => {
"agent" => {
"syscheck" => {
"frequency" => 321
}
}
}
}
)
DATA BAGS
---------
### Users
Create a `users` data bag that will contain the users that will be able to log into the OSSEC webui. Each user can use htauth with a specified password. Users that should be able to log in should be in the sysadmin group. Example user data bag item:
```javascript
{
"id": "osssecadmin",
"groups": "sysadmin",
"htpasswd": "hashed_htpassword"
}
```
The htpasswd must be the hashed value. Get this value with htpasswd:
% htpasswd -n -s ossec
New password:
Re-type new password:
ossec:{SHA}oCagzV4lMZyS7jl2Z0WlmLxEkt4=
For example use the `{SHA}oCagzV4lMZyS7jl2Z0WlmLxEkt4=` value in the data bag.
Customization
----
......
......@@ -13,5 +13,3 @@ Once the above are installed, you should be able to run Test Kitchen:
kitchen list
kitchen test
For testing the wui recipe, run the wui suite. The test data bag uses a password of 'pass'.
......@@ -18,47 +18,45 @@
#
# general settings
default['ossec']['server_role'] = 'ossec_server'
default['ossec']['server_env'] = nil
default['ossec']['checksum'] = '917989e23330d18b0d900e8722392cdbe4f17364a547508742c0fd005a1df7dd'
default['ossec']['version'] = '2.8.3'
default['ossec']['url'] = "http://www.ossec.net/files/ossec-hids-#{node['ossec']['version']}.tar.gz"
default['ossec']['logs'] = []
default['ossec']['syscheck_freq'] = 79_200
default['ossec']['disable_config_generation'] = false
default['ossec']['dir'] = '/var/ossec'
default['ossec']['server_role'] = 'ossec_server'
default['ossec']['server_env'] = nil
default['ossec']['agent_server_ip'] = nil
# data bag configuration
default['ossec']['data_bag']['encrypted'] = false
default['ossec']['data_bag']['name'] = 'ossec'
default['ossec']['data_bag']['ssh'] = 'ssh'
# server-only
default['ossec']['server']['maxagents'] = 256
# ossec-batch-manager.pl location varies
default['ossec']['agent_manager'] = value_for_platform_family(
%w( rhel fedora suse ) => '/usr/share/ossec/contrib/ossec-batch-manager.pl',
'default' => "#{node['ossec']['dir']}/contrib/ossec-batch-manager.pl"
)
# used to populate config files and preload values for install
default['ossec']['user']['language'] = 'en'
default['ossec']['user']['install_type'] = 'local'
default['ossec']['user']['dir'] = '/var/ossec'
default['ossec']['user']['delete_dir'] = true
default['ossec']['user']['active_response'] = true
default['ossec']['user']['syscheck'] = true
default['ossec']['user']['rootcheck'] = true
default['ossec']['user']['update'] = false
default['ossec']['user']['update_rules'] = true
default['ossec']['user']['binary_install'] = false
default['ossec']['user']['agent_server_ip'] = nil
default['ossec']['user']['enable_email'] = true
default['ossec']['user']['email'] = 'ossec@example.com'
default['ossec']['user']['smtp'] = '127.0.0.1'
default['ossec']['user']['remote_syslog'] = false
default['ossec']['user']['firewall_response'] = true
default['ossec']['user']['pf'] = false
default['ossec']['user']['pf_table'] = false
default['ossec']['user']['white_list'] = []
# The following attributes are mapped to XML for ossec.conf using
# Gyoku. See the README for details on how this works.
# web-ui only
default['ossec']['wui']['checksum'] = '142febadfd4b0de5a13ebd93c13eedfbee5f1899b6ee71c248054c14f47b8089'
default['ossec']['wui']['version'] = '0.3'
default['ossec']['wui']['url'] = "http://www.ossec.net/files/ossec-wui-#{node['ossec']['wui']['version']}.tar.gz"
default['ossec']['users_databag'] = 'users'
default['ossec']['users_databag_group'] = 'sysadmin'
default['ossec']['conf']['all']['syscheck']['frequency'] = 21_600
default['ossec']['conf']['all']['rootcheck']['disabled'] = false
default['ossec']['conf']['all']['rootcheck']['rootkit_files'] = "#{node['ossec']['dir']}/etc/shared/rootkit_files.txt"
default['ossec']['conf']['all']['rootcheck']['rootkit_trojans'] = "#{node['ossec']['dir']}/etc/shared/rootkit_trojans.txt"
%w( local server ).each do |type|
default['ossec']['conf'][type]['global']['email_notification'] = false
default['ossec']['conf'][type]['global']['email_from'] = "ossecm@#{node['fqdn']}"
default['ossec']['conf'][type]['global']['email_to'] = 'ossec@example.com'
default['ossec']['conf'][type]['global']['smtp_server'] = '127.0.0.1'
default['ossec']['conf'][type]['alerts']['email_alert_level'] = 7
default['ossec']['conf'][type]['alerts']['log_alert_level'] = 1
default['ossec']['conf'][type]['alerts']['use_geoip'] = false
end
default['ossec']['conf']['server']['remote']['connection'] = 'secure'
default['ossec']['conf']['agent']['client']['server-ip'] = node['ossec']['agent_server_ip']
# agent.conf is also populated with Gyoku but in a slightly different
# way. We leave this blank by default because Chef is better at
# distributing agent configuration than OSSEC is.
default['ossec']['agent_conf'] = []
#
# Cookbook Name:: ossec
# Library:: helpers
#
# Copyright 2015, Opscode, Inc.
#
# 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.
#
class Chef
module OSSEC
module Helpers
# Gyoku looks for a symbol called :content! but Chef attributes
# are always stringified. We can't just call symbolize_keys
# because we need to recurse through the hash structure. Doing
# this also gives us the opportunity to convert true/false to
# yes/no, which is handy.
def self.object_to_ossec(object)
case object
when Hash
object.keys.each do |k|
if k == 'content!'
object[:content!] = object_to_ossec(object.delete(k))
else
object[k] = object_to_ossec(object[k])
end
end
object
when Array
object.map! do |e|
object_to_ossec(e)
end
when TrueClass
'yes'
when FalseClass
'no'
when NilClass
''
else
object
end
end
def self.ossec_to_xml(hash)
require 'gyoku'
Gyoku.xml object_to_ossec(hash)
end
end
end
end
......@@ -6,11 +6,11 @@ description 'Installs and onfigures ossec'
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
version '1.0.5'
%w( build-essential apt apache2 ).each do |pkg|
%w( apt yum-atomic ).each do |pkg|
depends pkg
end
%w( debian ubuntu arch redhat centos fedora scientific oracle amazon ).each do |os|
%w( debian ubuntu redhat centos fedora ).each do |os|
supports os
end
......
......@@ -30,10 +30,9 @@ else
end
end
node.set['ossec']['user']['install_type'] = 'agent'
node.set['ossec']['user']['agent_server_ip'] = ossec_server.first
node.set['ossec']['agent_server_ip'] = ossec_server.first
include_recipe 'ossec'
include_recipe 'ossec::install_agent'
dbag_name = node['ossec']['data_bag']['name']
dbag_item = node['ossec']['data_bag']['ssh']
......@@ -43,30 +42,24 @@ else
ossec_key = data_bag_item(dbag_name, dbag_item)
end
user 'ossecd' do
comment 'OSSEC Distributor'
shell '/bin/bash'
system true
gid 'ossec'
home node['ossec']['user']['dir']
end
directory "#{node['ossec']['user']['dir']}/.ssh" do
owner 'ossecd'
directory "#{node['ossec']['dir']}/.ssh" do
owner 'ossec'
group 'ossec'
mode 0750
end
template "#{node['ossec']['user']['dir']}/.ssh/authorized_keys" do
template "#{node['ossec']['dir']}/.ssh/authorized_keys" do
source 'ssh_key.erb'
owner 'ossecd'
owner 'ossec'
group 'ossec'
mode 0600
variables(key: ossec_key['pubkey'])
end
file "#{node['ossec']['user']['dir']}/etc/client.keys" do
owner 'ossecd'
file "#{node['ossec']['dir']}/etc/client.keys" do
owner 'ossec'
group 'ossec'
mode 0660
end
include_recipe 'ossec::common'
#
# Cookbook Name:: ossec
# Recipe:: common
#
# Copyright 2010, Opscode, Inc.
#
# 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.
#
ruby_block 'ossec install_type' do
block do
if node.recipes.include?('ossec::default')
type = 'local'
else
type = nil
File.open('/etc/ossec-init.conf') do |file|
file.each_line do |line|
if line =~ /^TYPE="([^"]+)"/
type = Regexp.last_match(1)
break
end
end
end
end
node.set['ossec']['install_type'] = type
end
end
# Gyoku renders the XML.
chef_gem 'gyoku' do
compile_time false if respond_to?(:compile_time)
end
file "#{node['ossec']['dir']}/etc/ossec.conf" do
owner 'root'
group 'ossec'
mode 0440
manage_symlink_source true
notifies :restart, 'service[ossec]'
content lazy {
# Merge the "typed" attributes over the "all" attributes.
all_conf = node['ossec']['conf']['all'].to_hash
type_conf = node['ossec']['conf'][node['ossec']['install_type']].to_hash
conf = Chef::Mixin::DeepMerge.deep_merge(type_conf, all_conf)
Chef::OSSEC::Helpers.ossec_to_xml('ossec_config' => conf)
}
end
file "#{node['ossec']['dir']}/etc/shared/agent.conf" do
owner 'root'
group 'ossec'
mode 0440
notifies :restart, 'service[ossec]'
# Even if agent.cont is not appropriate for this kind of
# installation, we need to create an empty file instead of deleting
# for two reasons. Firstly, install_type is set at converge time
# while action can't be lazy. Secondly, a subsequent package update
# would just replace the file.
action :create
content lazy {
if node['ossec']['install_type'] == 'server'
conf = node['ossec']['agent_conf'].to_a
Chef::OSSEC::Helpers.ossec_to_xml('agent_config' => conf)
else
''
end
}
end
# Both the RPM and DEB packages enable and start the service
# immediately after installation, which isn't helpful. An empty
# client.keys file will cause a server not to listen and an agent to
# abort immediately. Explicitly stopping the service here after
# installation allows Chef to start it when client.keys has content.
service 'stop ossec' do
service_name 'ossec-hids' unless platform_family?('debian')
action :nothing
%w( disable stop ).each do |action|
subscribes action, 'package[ossec]', :immediately
end
end
service 'ossec' do
service_name 'ossec-hids' unless platform_family?('debian')
supports status: true, restart: true
action [:enable, :start]
not_if do
(node['ossec']['install_type'] != 'local' && !File.size?("#{node['ossec']['dir']}/etc/client.keys")) ||
(node['ossec']['install_type'] == 'agent' && node['ossec']['agent_server_ip'].nil?)
end
end
......@@ -17,63 +17,5 @@
# limitations under the License.
#
include_recipe 'build-essential'
ossec_dir = "ossec-hids-#{node['ossec']['version']}"
remote_file "#{Chef::Config[:file_cache_path]}/#{ossec_dir}.tar.gz" do
source node['ossec']['url']
checksum node['ossec']['checksum']
end
execute "tar zxvf #{ossec_dir}.tar.gz" do
cwd Chef::Config[:file_cache_path]
creates "#{Chef::Config[:file_cache_path]}/#{ossec_dir}"
end
template "#{Chef::Config[:file_cache_path]}/#{ossec_dir}/etc/preloaded-vars.conf" do
source 'preloaded-vars.conf.erb'
variables ossec: node['ossec']['user']
end
bash 'install-ossec' do
cwd "#{Chef::Config[:file_cache_path]}/#{ossec_dir}"
code <<-EOH
echo "HEXTRA=-DMAX_AGENTS=#{node['ossec']['server']['maxagents']}" >> src/Config.OS
./install.sh
EOH
creates "#{node['ossec']['user']['dir']}/bin/ossec-control"
end
# not really a template, dist by ossec, so we don't need a copy in the cookbook
template "#{node['ossec']['user']['dir']}/bin/ossec-batch-manager.pl" do
source "#{Chef::Config[:file_cache_path]}/#{ossec_dir}/contrib/ossec-batch-manager.pl"
local true
owner 'root'
group 'ossec'
mode 0755
end
template "#{node['ossec']['user']['dir']}/etc/ossec.conf" do
source 'ossec.conf.erb'
owner 'root'
group 'ossec'
mode 0440
variables(ossec: node['ossec']['user'])
notifies :restart, 'service[ossec]'
not_if { node['ossec']['disable_config_generation'] }
end
case node['platform']
when 'arch'