267 lines
9.1 KiB
Ruby
267 lines
9.1 KiB
Ruby
|
# Core function for OSSEC
|
||
|
# Used by the server and agent recipes
|
||
|
|
||
|
module OssecCore
|
||
|
|
||
|
def ossec_hostname_search()
|
||
|
# resolve the hostname_search of a rule to a list of hosts
|
||
|
node["ossec"]["rules"].each do |id,params|
|
||
|
if not params.nil?
|
||
|
params[:body].each do |key, value|
|
||
|
if key.eql?('hostname_search')
|
||
|
hosts_list = search(:node,
|
||
|
"(#{value}) AND roles:ossec-agent "\
|
||
|
" AND chef_environment:#{node.chef_environment}"
|
||
|
).map {|n| n.hostname}
|
||
|
if hosts_list.empty?
|
||
|
# search didn't return anything
|
||
|
# store a dummy value in the attributes
|
||
|
Chef::Log.info("OSSEC: Hostname search returned empty result. " +
|
||
|
"'#{value}'")
|
||
|
params[:body][:hostname] = "invalid-search-returned-empty-result"
|
||
|
else
|
||
|
# store in the node params but discard the last char
|
||
|
params[:body][:hostname] = hosts_list.join('|')
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
def ossec_event_location_search()
|
||
|
# resolve the location search of an email_alert block to a hostname
|
||
|
node["ossec"]["email_alerts"].each do|recipient,params|
|
||
|
if params.has_key?('event_location_search')
|
||
|
if Chef::Config[:solo]
|
||
|
Chef::Log.warn('This recipe uses search. Chef Solo does not support search.')
|
||
|
else
|
||
|
dest = search(:node,
|
||
|
"(#{params[:event_location_search]}) " \
|
||
|
"AND chef_environment:#{node.chef_environment}"
|
||
|
).map {|n| n.hostname}
|
||
|
node.default["ossec"]["email_alerts"][recipient]["resolved_search"] = dest
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def ossec_set_syscheck_flags!(*args)
|
||
|
# go through the list of command/active-response and check the ones
|
||
|
# that apply to this node
|
||
|
args.each do |config|
|
||
|
unless node["ossec"]["syscheck"][config].nil?
|
||
|
node["ossec"]["syscheck"][config].each do |item, params|
|
||
|
unless params[:apply_to].nil?
|
||
|
locations = search(:node,
|
||
|
"(#{params[:apply_to]}) " \
|
||
|
"AND chef_environment:#{node.chef_environment}"
|
||
|
).map {|n| n.ipaddress}
|
||
|
if locations.include?(node["ipaddress"])
|
||
|
node.default["ossec"]["syscheck"][config][item]["use_here"] = true
|
||
|
else
|
||
|
node.default["ossec"]["syscheck"][config][item]["use_here"] = false
|
||
|
end
|
||
|
else
|
||
|
node.default["ossec"]["syscheck"][config][item]["use_here"] = true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def ossec_set_filtered_flags!(*args)
|
||
|
# go through the list of command/active-response and check the ones
|
||
|
# that apply to this node
|
||
|
args.each do |config|
|
||
|
unless node["ossec"][config].nil?
|
||
|
node["ossec"][config].each do |item, params|
|
||
|
unless params[:apply_to].nil?
|
||
|
locations = search(:node,
|
||
|
"(#{params[:apply_to]}) " \
|
||
|
"AND chef_environment:#{node.chef_environment}"
|
||
|
).map {|n| n.ipaddress}
|
||
|
if locations.include?(node["ipaddress"])
|
||
|
node.default["ossec"][config][item]["use_here"] = true
|
||
|
else
|
||
|
node.default["ossec"][config][item]["use_here"] = false
|
||
|
end
|
||
|
else
|
||
|
node.default["ossec"][config][item]["use_here"] = true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
def ossec_agent_create_parameters(agent, server)
|
||
|
# Returns a hash with the identiers for this agent
|
||
|
agent_hash = {}
|
||
|
# IP is defined by lanip, if available (ohai plugin network_addr)
|
||
|
# or by the default ipaddress otherwise
|
||
|
agent_hash[:ip] = agent[:network][:lanip] || agent.ipaddress
|
||
|
|
||
|
# Ossec limits the agents name length to 32 characters, so to avoid
|
||
|
# names collisions, we concatenate the agent_ip with the first characters
|
||
|
# of the hostname
|
||
|
name = agent_hash[:ip] + "_" + agent[:hostname]
|
||
|
agent_hash[:name] = name[0,31]
|
||
|
|
||
|
# ossec agent id is an integer used to identify an agent. we force that ID
|
||
|
# to be the IP address without the dots (10.1.2.3 becomes 10123)
|
||
|
agent_hash[:id] = agent_hash[:ip].gsub(".", "")
|
||
|
|
||
|
agent_hash[:key] = "undef"
|
||
|
if server["ossec"]["agents"].has_key?(agent_hash[:id])
|
||
|
if server["ossec"]["agents"][agent_hash[:id]].has_key?('key')
|
||
|
agent_hash[:key] = server["ossec"]["agents"][agent_hash[:id]]["key"]
|
||
|
end
|
||
|
end
|
||
|
agent_hash[:rid] = "none"
|
||
|
|
||
|
return agent_hash
|
||
|
end
|
||
|
|
||
|
|
||
|
def ossec_generate_agent_key(agent_hash)
|
||
|
# Returns a 64 characters double md5 hash, used as a symetric key
|
||
|
seed1 = rand(100000000000).to_s
|
||
|
seed2 = rand(100000000000).to_s
|
||
|
str1 = Digest::MD5.hexdigest(seed1 + \
|
||
|
agent_hash[:id] + \
|
||
|
agent_hash[:ip] + \
|
||
|
seed2)
|
||
|
str2 = Digest::MD5.hexdigest(seed2 + \
|
||
|
agent_hash[:name] + \
|
||
|
seed1 + \
|
||
|
agent_hash[:ip] + \
|
||
|
agent_hash[:id])
|
||
|
key = str1 + str2
|
||
|
return key
|
||
|
end
|
||
|
|
||
|
|
||
|
def ossec_agent_has_valid_key?(agent_hash, server)
|
||
|
# Does the server have a valid key for this agent ?
|
||
|
if server["ossec"]["agents"].key?(agent_hash[:id])
|
||
|
if server["ossec"]["agents"][agent_hash[:id]].key?('key')
|
||
|
if server["ossec"]["agents"][agent_hash[:id]]["key"].length == 64
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
|
||
|
def ossec_agent_knows_key?(agent_hash, agent)
|
||
|
# Does the agent have a key that matches the server ?
|
||
|
if agent.key?('ossec')
|
||
|
if agent["ossec"].key?('agents')
|
||
|
if agent["ossec"]["agents"].key?(agent_hash[:id])
|
||
|
if agent["ossec"]["agents"][agent_hash[:id]].key?('key')
|
||
|
if agent["ossec"]["agents"][agent_hash[:id]]["key"] == agent_hash[:key]
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
|
||
|
def ossec_verify_agent(agent_hash, server)
|
||
|
# check if this agent (id, name, ip) is defined on the server
|
||
|
if server["ossec"]["agents"].key?(agent_hash[:id])
|
||
|
agent_srv_data = server["ossec"]["agents"][agent_hash[:id]]
|
||
|
if agent_srv_data[:name].eql?(agent_hash[:name])
|
||
|
if agent_srv_data[:ip].eql?(agent_hash[:ip])
|
||
|
return true
|
||
|
else
|
||
|
Chef::Log.info("OSSEC: agent ip mismatch. " +
|
||
|
"server has '#{agent_srv_data[:ip]}' " +
|
||
|
"agent has '#{agent_hash[:ip]}'")
|
||
|
end
|
||
|
else
|
||
|
Chef::Log.info("OSSEC: agent name mismatch. " +
|
||
|
"server has '#{agent_srv_data[:name]}' " +
|
||
|
"agent has '#{agent_hash[:name]}'")
|
||
|
end
|
||
|
else
|
||
|
Chef::Log.info("OSSEC: agent name '#{agent_hash[:name]}' " +
|
||
|
" ip '#{agent_hash[:ip]}'" +
|
||
|
" configuration not found on server.")
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
|
||
|
def ossec_agent_is_active?(id)
|
||
|
if File.exists?("/var/ossec/bin/agent_control")
|
||
|
#cmd = Chef::ShellOut.new("/var/ossec/bin/agent_control -s -i #{id}")
|
||
|
cmd = Mixlib::ShellOut.new("/var/ossec/bin/agent_control -s -i #{id}")
|
||
|
cmd_ret = cmd.run_command
|
||
|
status = cmd.stdout.split(",")
|
||
|
if status[3] && status[3].eql?("Active")
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
|
||
|
def ossec_agent_is_zombie?(id)
|
||
|
if File.exists?("/var/ossec/bin/agent_control")
|
||
|
#cmd = Chef::ShellOut.new("/var/ossec/bin/agent_control -s -i #{id}")
|
||
|
cmd = Mixlib::ShellOut.new("/var/ossec/bin/agent_control -s -i #{id}")
|
||
|
cmd_ret = cmd.run_command
|
||
|
status = cmd.stdout.split(",")
|
||
|
if not status[6] || status[3] =~ /(Never connected|)/
|
||
|
return true
|
||
|
elsif status[6] !~ /Unknown/
|
||
|
last_keep_alive = Time.parse(status[6])
|
||
|
three_days_ago = (Time.now - (24*60*60*3))
|
||
|
if three_days_ago > last_keep_alive
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
|
||
|
def ossec_agent_should_be_removed?(id)
|
||
|
if File.exists?("/var/ossec/bin/agent_control")
|
||
|
#cmd = Chef::ShellOut.new("/var/ossec/bin/agent_control -s -i #{id}")
|
||
|
cmd = Mixlib::ShellOut.new("/var/ossec/bin/agent_control -s -i #{id}")
|
||
|
cmd_ret = cmd.run_command
|
||
|
status = cmd.stdout.split(",")
|
||
|
if not status[6] or status[6] =~ /Unknown/
|
||
|
return true
|
||
|
else
|
||
|
last_keep_alive = Time.parse(status[6])
|
||
|
seven_days_ago = (Time.now - (24*60*60*7))
|
||
|
if seven_days_ago > last_keep_alive
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
|
||
|
def ossec_agent_needs_rid?(id, agent)
|
||
|
# Check if the agent queue needs to be removed, either because the server
|
||
|
# said so, or because the agent asked for it
|
||
|
if agent["ossec"]["agents"][id]["rid"].eql?("todo") \
|
||
|
or node["ossec"]["agents"][id]["rid"].eql?("todo")
|
||
|
return true
|
||
|
else
|
||
|
return false
|
||
|
end
|
||
|
end
|
||
|
end
|