From dfbc993da94d646febc6885f1534b9550fd7ea7e Mon Sep 17 00:00:00 2001 From: Kibahop Date: Thu, 16 Feb 2023 13:15:55 +0200 Subject: [PATCH 01/27] WIP: add new manifest, type and a provider Signed-off-by: Kibahop --- lib/puppet/provider/remote_agent/default.rb | 91 +++++++++++++++++++++ lib/puppet/type/remote_agent.rb | 26 ++++++ manifests/remote_agent.pp | 24 ++++++ 3 files changed, 141 insertions(+) create mode 100644 lib/puppet/provider/remote_agent/default.rb create mode 100644 lib/puppet/type/remote_agent.rb create mode 100644 manifests/remote_agent.pp diff --git a/lib/puppet/provider/remote_agent/default.rb b/lib/puppet/provider/remote_agent/default.rb new file mode 100644 index 00000000..ea2238a8 --- /dev/null +++ b/lib/puppet/provider/remote_agent/default.rb @@ -0,0 +1,91 @@ +Puppet::Type.type(:remote_agent).provide(:ruby) do + + require 'base64' + require 'open3' + require 'net/http' + + def token_uri + Puppet.debug("In get_token") + URI("https://#{@resource[:api_host]}:#{@resource[:api_host_port]}/security/user/authenticate") + end + + def agent_id_uri(name) + URI("https://#{@resource[:api_host]}:#{@resource[:api_host_port]}/agents?name=#{name}") + end + + def delete_agent_uri(id) + URI("https://#{@resource[:api_host]}:#{@resource[:api_host_port]}/agents?agents_list=#{id}&status=all&older_than=0") + end + + def execute(command) + stdout, stderr, status = Open3.capture3(command) + raise "Command '#{command}' failed with exit code #{status.exitstatus}\nError message: #{stderr}" unless status.success? + stdout.chomp + end + + def get_token + Puppet.debug("In get_token") + uri = token_uri + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + + request = Net::HTTP::Get.new(uri) + request.basic_auth(resource[:api_username], resource[:api_password]) + + response = http.request(request) + token = JSON.parse(response.body)['data']['token'] + + end + + def get_agent_id_by_name(name, token) + Puppet.debug("In get_agent_id_by_name") + uri = agent_id_uri(name) + headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{token}" } + res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| + req = Net::HTTP::Get.new(uri, headers) + http.request(req) + end + id = JSON.parse(res.body)['data']['affected_items'][0]['id'] + return id + end + + def delete_agent(id, token) + uri = delete_agent_uri(id) + headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{token}" } + res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| + req = Net::HTTP::Delete.new(uri, headers) + http.request(req) + end + + res + end + + def token + @token ||= get_token.chomp + end + + def agent_id + Puppet.debug("In agent_id") + @agent_id ||= get_agent_id_by_name(resource[:name], token) + end + + def delete + delete_agent(agent_id, token) + end + + def exists? + # We assume the agent doesn't exist if we can't get the ID + Puppet.debug("In exist") + #!!agent_id rescue false + agent_id + end + + def destroy + Puppet.debug("In destroy") + if exists? + delete_agent(agent_id, token) + #@property_hash[:ensure] = :absent + end + end +end diff --git a/lib/puppet/type/remote_agent.rb b/lib/puppet/type/remote_agent.rb new file mode 100644 index 00000000..a8559b33 --- /dev/null +++ b/lib/puppet/type/remote_agent.rb @@ -0,0 +1,26 @@ +Puppet::Type.newtype(:remote_agent) do + ensurable do + + newvalue(:absent) do + provider.destroy + end + + newvalue(:present) do + pass + end + + defaultto :absent + end + + newparam :name, :namevar => true + newparam :api_username + newparam :api_password + newparam :api_host + newparam :api_host_port + + validate do + [:api_username, :api_password, :api_host, :api_host_port].each do |param| + raise ArgumentError, "The #{param} parameter is required." unless self[param] + end + end +end diff --git a/manifests/remote_agent.pp b/manifests/remote_agent.pp new file mode 100644 index 00000000..7035a2ae --- /dev/null +++ b/manifests/remote_agent.pp @@ -0,0 +1,24 @@ +class wazuh::remote_agent( + + String $agent_name, + String $api_username, + String $api_password, + Stdlib::Host $api_host, + Stdlib::Port $api_host_port, +) { + + notify { "agent: $agent_name": } + notify { "api_username: $api_username": } + notify { "api_password: $api_password": } + notify { "api_host: $api_host": } + notify { "api_host_port: $api_host_port": } + + remote_agent { $agent_name: + ensure => 'absent', + api_username => $api_username, + api_password => $api_password, + api_host => $api_host, + api_host_port => $api_host_port + } +} + From b8ee8099d4d7c142e11189c0c2ffb33c863a6152 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Sat, 18 Feb 2023 00:30:45 +0200 Subject: [PATCH 02/27] WIP: switch remote_agent from class to defined type for wider use cases Signed-off-by: Kibahop --- lib/puppet/provider/remote_agent/default.rb | 41 ++++++---------- lib/puppet/type/remote_agent.rb | 5 +- manifests/remote_agent.pp | 52 +++++++++++++++------ 3 files changed, 56 insertions(+), 42 deletions(-) diff --git a/lib/puppet/provider/remote_agent/default.rb b/lib/puppet/provider/remote_agent/default.rb index ea2238a8..fb64d200 100644 --- a/lib/puppet/provider/remote_agent/default.rb +++ b/lib/puppet/provider/remote_agent/default.rb @@ -1,11 +1,11 @@ +# frozen_string_literal: true + Puppet::Type.type(:remote_agent).provide(:ruby) do - require 'base64' require 'open3' require 'net/http' def token_uri - Puppet.debug("In get_token") URI("https://#{@resource[:api_host]}:#{@resource[:api_host_port]}/security/user/authenticate") end @@ -14,18 +14,12 @@ def agent_id_uri(name) end def delete_agent_uri(id) - URI("https://#{@resource[:api_host]}:#{@resource[:api_host_port]}/agents?agents_list=#{id}&status=all&older_than=0") - end - - def execute(command) - stdout, stderr, status = Open3.capture3(command) - raise "Command '#{command}' failed with exit code #{status.exitstatus}\nError message: #{stderr}" unless status.success? - stdout.chomp + URI("https://#{@resource[:api_host]}:#{@resource[:api_host_port]}/agents?agents_list=#{id}&status=#{@resource[:status]}&older_than=0") end def get_token - Puppet.debug("In get_token") uri = token_uri + http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE @@ -35,24 +29,25 @@ def get_token response = http.request(request) token = JSON.parse(response.body)['data']['token'] - end def get_agent_id_by_name(name, token) - Puppet.debug("In get_agent_id_by_name") uri = agent_id_uri(name) headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{token}" } + res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| req = Net::HTTP::Get.new(uri, headers) http.request(req) end + id = JSON.parse(res.body)['data']['affected_items'][0]['id'] - return id + return id unless id.nil? end def delete_agent(id, token) uri = delete_agent_uri(id) headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{token}" } + res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| req = Net::HTTP::Delete.new(uri, headers) http.request(req) @@ -66,26 +61,18 @@ def token end def agent_id - Puppet.debug("In agent_id") - @agent_id ||= get_agent_id_by_name(resource[:name], token) + @agent_id ||= get_agent_id_by_name(@resource[:name], token) end - def delete - delete_agent(agent_id, token) + def exists? + !!agent_id rescue false end - def exists? - # We assume the agent doesn't exist if we can't get the ID - Puppet.debug("In exist") - #!!agent_id rescue false - agent_id + def create + pass end def destroy - Puppet.debug("In destroy") - if exists? - delete_agent(agent_id, token) - #@property_hash[:ensure] = :absent - end + delete_agent(agent_id, token) end end diff --git a/lib/puppet/type/remote_agent.rb b/lib/puppet/type/remote_agent.rb index a8559b33..848a7b9a 100644 --- a/lib/puppet/type/remote_agent.rb +++ b/lib/puppet/type/remote_agent.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Puppet::Type.newtype(:remote_agent) do ensurable do @@ -17,9 +19,10 @@ newparam :api_password newparam :api_host newparam :api_host_port + newparam :status validate do - [:api_username, :api_password, :api_host, :api_host_port].each do |param| + [:api_username, :api_password, :api_host, :api_host_port, :status ].each do |param| raise ArgumentError, "The #{param} parameter is required." unless self[param] end end diff --git a/manifests/remote_agent.pp b/manifests/remote_agent.pp index 7035a2ae..ac98586e 100644 --- a/manifests/remote_agent.pp +++ b/manifests/remote_agent.pp @@ -1,23 +1,47 @@ -class wazuh::remote_agent( - - String $agent_name, +# +# @summary Removes an agent from the API host +# +# @example Basic usage +# class wazuh::remote_agent { 'myagent.example.com': +# api_username => $api_username, +# api_password => $api_password, +# api_host => $api_host, +# api_host_port => $api_host_port +# } +# +# @param agent_name +# Agent name to remove. +# +# @param status +# Limit removal to agents that have this +# status. Default 'all' +# +# @param api_username, +# username to acces the API. +# +# @param api_password +# password to acces the API. +# +# @param api_host +# The API host. +# +# @param api_host +# The API port. Default 55000. +# +define wazuh::remote_agent( String $api_username, String $api_password, Stdlib::Host $api_host, Stdlib::Port $api_host_port, + Enum['all', 'disconnected', 'active'] $status = 'all' ) { - notify { "agent: $agent_name": } - notify { "api_username: $api_username": } - notify { "api_password: $api_password": } - notify { "api_host: $api_host": } - notify { "api_host_port: $api_host_port": } - - remote_agent { $agent_name: - ensure => 'absent', - api_username => $api_username, - api_password => $api_password, - api_host => $api_host, + remote_agent { $title: + ensure => 'absent', + status => $status, + api_username => $api_username, + api_password => $api_password, + api_host => $api_host, api_host_port => $api_host_port } } From d1d8cdec983ab1011d03228dc0fe2635ed8189c9 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Mon, 27 Feb 2023 17:24:58 +0200 Subject: [PATCH 03/27] A set of utilities for managing wazuh agents Signed-off-by: Kibahop --- examples/supervise.pp | 16 ++++ examples/wazuh_actions.pp | 30 ++++++ examples/wazuh_agent_actions.pp | 27 ++++++ examples/wazuh_ensure_name.pp | 55 +++++++++++ examples/wazuh_nametest.pp | 14 +++ examples/wazuh_remove.pp | 16 ++++ lib/facter/ossec_conf_exists.rb | 13 +++ lib/facter/wazuh_facts.rb | 96 +++++++++++++++++++ .../functions/wazuh/api_remove_agent.rb | 87 +++++++++++++++++ lib/puppet/provider/agent_action/default.rb | 46 +++++++++ .../provider/local_agent_name/default.rb | 94 ++++++++++++++++++ lib/puppet/provider/remote_agent/default.rb | 78 --------------- lib/puppet/type/agent_action.rb | 28 ++++++ lib/puppet/type/local_agent_name.rb | 60 ++++++++++++ lib/puppet/type/remote_agent.rb | 29 ------ manifests/remote_agent.pp | 48 ---------- manifests/utils/agent_actions.pp | 13 +++ manifests/utils/agent_supervise.pp | 38 ++++++++ manifests/utils/api_remove_agent.pp | 54 +++++++++++ manifests/utils/local_agent_name.pp | 20 ++++ 20 files changed, 707 insertions(+), 155 deletions(-) create mode 100644 examples/supervise.pp create mode 100644 examples/wazuh_actions.pp create mode 100644 examples/wazuh_agent_actions.pp create mode 100644 examples/wazuh_ensure_name.pp create mode 100644 examples/wazuh_nametest.pp create mode 100644 examples/wazuh_remove.pp create mode 100644 lib/facter/ossec_conf_exists.rb create mode 100644 lib/facter/wazuh_facts.rb create mode 100644 lib/puppet/functions/wazuh/api_remove_agent.rb create mode 100644 lib/puppet/provider/agent_action/default.rb create mode 100644 lib/puppet/provider/local_agent_name/default.rb delete mode 100644 lib/puppet/provider/remote_agent/default.rb create mode 100644 lib/puppet/type/agent_action.rb create mode 100644 lib/puppet/type/local_agent_name.rb delete mode 100644 lib/puppet/type/remote_agent.rb delete mode 100644 manifests/remote_agent.pp create mode 100644 manifests/utils/agent_actions.pp create mode 100644 manifests/utils/agent_supervise.pp create mode 100644 manifests/utils/api_remove_agent.pp create mode 100644 manifests/utils/local_agent_name.pp diff --git a/examples/supervise.pp b/examples/supervise.pp new file mode 100644 index 00000000..3e3c7b1a --- /dev/null +++ b/examples/supervise.pp @@ -0,0 +1,16 @@ +# +# @summary Example to Restart agent based on limits in the hash +# +class local_profile::supervise { + + $when = { + "last_ack_since" => 300, + 'last_keepalive_since' => 300, + 'status' => 'disconnected', + } + + wazuh::utils::agent_supervisor { 'keep control': + when => $when, + } + +} diff --git a/examples/wazuh_actions.pp b/examples/wazuh_actions.pp new file mode 100644 index 00000000..adb8ed78 --- /dev/null +++ b/examples/wazuh_actions.pp @@ -0,0 +1,30 @@ +# +# @summmary Example wazuh_actions +# +class local_profile::wazuh_actions { + + wazuh::utils::agent_actions { 'start_wazuh_service': + action => start, + } + + wazuh::utils::agent_actions { 'stop_wazuh_service': + action => stop, + require => Wazuh::Utils::Agent_actions['start_wazuh_service'], + } + + wazuh::utils::agent_actions { 'disable_wazuh_service': + action => disable, + require => Wazuh::Utils::Agent_actions['start_wazuh_service'], + } + + wazuh::utils::agent_actions { 'enable_wazuh_service': + action => enable, + require => Wazuh::Utils::Agent_actions['disable_wazuh_service'], + } + + wazuh::utils::agent_actions { 'restart_wazuh_service': + action => restart, + require => Wazuh::Utils::Agent_actions['enable_wazuh_service'], + } +} + diff --git a/examples/wazuh_agent_actions.pp b/examples/wazuh_agent_actions.pp new file mode 100644 index 00000000..d7f64a4d --- /dev/null +++ b/examples/wazuh_agent_actions.pp @@ -0,0 +1,27 @@ +class local_profile::wazuh_actions { + + wazuh::utils::agent_actions { 'start_wazuh_service': + action => start, + } + + wazuh::utils::agent_actions { 'stop_wazuh_service': + action => stop, + require => Wazuh::Utils::Agent_actions['start_wazuh_service'], + } + + wazuh::utils::agent_actions { 'disable_wazuh_service': + action => disable, + require => Wazuh::Utils::Agent_actions['start_wazuh_service'], + } + + wazuh::utils::agent_actions { 'enable_wazuh_service': + action => enable, + require => Wazuh::Utils::Agent_actions['disable_wazuh_service'], + } + + wazuh::utils::agent_actions { 'restart_wazuh_service': + action => restart, + require => Wazuh::Utils::Agent_actions['enable_wazuh_service'], + } +} + diff --git a/examples/wazuh_ensure_name.pp b/examples/wazuh_ensure_name.pp new file mode 100644 index 00000000..7f135e6f --- /dev/null +++ b/examples/wazuh_ensure_name.pp @@ -0,0 +1,55 @@ +# +# @summary Example to change agent name +# +class local_profile::wazuh_ensure_name { + + $new_name = 'brand-new-agent.example.com' + $current_name = $facts['wazuh']['agent']['name'] + + notify { "current wazuh name fact: ${facts['wazuh']['agent']['name']}": + } + + notify { "new wazuh name: ${new_name}": + require => Notify["current wazuh name fact: ${facts['wazuh']['agent']['name']}"], + before => Notify["new wazuh name fact: ${facts['wazuh']['agent']['name']}"], + } + + # does the name differ from current name? + if $new_name != $current_name { + + # prevent current agent from reconnecting in the middle + wazuh::utils::agent_actions { "${current_name}_stop": + action => stop, + require => Notify["new wazuh name: ${new_name}"] + } + + # remove current name from the manager + # (custom function runs on puppet server) + wazuh::utils::api_remove_agent { $current_name: + api_username => 'wazuh', + api_password => 'wazuh', + api_host => 'wazuh.example.com', + api_host_port => 55000, + agent_name => $current_name, + require => Wazuh::Utils::Agent_actions["${current_name}_stop"], + } + + # reauth with a brand new name + wazuh::utils::local_agent_name { $new_name: + agent_name => $new_name, + auth_server_name => 'wazuh.example.com', + auth_password => 'changeme', + require => Wazuh::Utils::Api_remove_agent[$current_name], + } + + # start the service again + wazuh::utils::agent_actions { "${new_name}_start": + action => start, + require => Wazuh::Utils::Local_agent_name[$new_name], + notify => Notify["new wazuh name fact: ${facts['wazuh']['agent']['name']}"] + } + + } + + notify { "new wazuh name fact: ${facts['wazuh']['agent']['name']}": } +} diff --git a/examples/wazuh_nametest.pp b/examples/wazuh_nametest.pp new file mode 100644 index 00000000..092626ed --- /dev/null +++ b/examples/wazuh_nametest.pp @@ -0,0 +1,14 @@ +# +# @summary Agent name change example +# +class local_profile::wazuh_nametest { + + # $agent_name = 'my-brand-new-agent.example.com' + $agent_name = 'wazuh-agent.example.com' + + wazuh::utils::local_agent_name { $agent_name: + auth_server_name => 'wazuh.example.com', + auth_password => 'changeme', + agent_name => $agent_name, + } +} diff --git a/examples/wazuh_remove.pp b/examples/wazuh_remove.pp new file mode 100644 index 00000000..b1361945 --- /dev/null +++ b/examples/wazuh_remove.pp @@ -0,0 +1,16 @@ +# +# @summary Example to remove agent from manager +# +class local_profile::wazuh_remove { + + $agent_name = 'wazuh-agent.example.com' + # $agent_name = 'my-brand-new-agent.example.com' + + wazuh::utils::api_remove_agent { $agent_name: + agent_name => $agent_name, + api_username => 'wazuh', + api_password => 'wazuh', + api_host => 'wazuh.example.com', + api_host_port => 55000, + } +} diff --git a/lib/facter/ossec_conf_exists.rb b/lib/facter/ossec_conf_exists.rb new file mode 100644 index 00000000..3420a0fa --- /dev/null +++ b/lib/facter/ossec_conf_exists.rb @@ -0,0 +1,13 @@ +# +# @summary Check if Wazuh configuration file exist +# +# @author Petri Lammi petri.lammi@puppeteers.net +# +# @example +# +# notify { $facts['wazuh']['state']['status']: +Facter.add('ossec_conf_exists') do + setcode do + File.exist?('/var/ossec/etc/ossec.conf') + end +end diff --git a/lib/facter/wazuh_facts.rb b/lib/facter/wazuh_facts.rb new file mode 100644 index 00000000..5e1b3b40 --- /dev/null +++ b/lib/facter/wazuh_facts.rb @@ -0,0 +1,96 @@ +# +# @summary Produces a structured wazuh fact +# +# @author Petri Lammi petri.lammi@puppeteers.net +# +# @example +# +# notify { $facts['wazuh']['state']['status']: } +# +Facter.add(:wazuh) do + + confine :kernel => 'Linux' + confine :ossec_conf_exists => true + + setcode do + + @keyfile = String.new('/var/ossec/etc/client.keys') + + def wazuh_agent_data(index) + if File.exist?(@keyfile) + File.read(@keyfile).split[index] + else + nil + end + end + + wazuh_agent_id = wazuh_agent_data(0) + wazuh_agent_name = wazuh_agent_data(1) + wazuh_agent_ip = wazuh_agent_data(2) + wazuh_agent_key = wazuh_agent_data(3) + + def wazuh_agent_version + cmd = String.new + case Facter.value('osfamily') + when 'RedHat' + cmd = '/bin/rpm -q wazuh-agent --queryformat "%{VERSION}"' + when 'Debian' + cmd = '/usr/bin/dpkg-query -W -f="\\${Version}" wazuh-agent' + end + Facter::Core::Execution.execute(cmd) + end + + wazuh_agent_hash = { + id: wazuh_agent_id, + name: wazuh_agent_name, + ip_address: wazuh_agent_ip, + key: wazuh_agent_key, + version: wazuh_agent_version + } + + def get_ossec_conf_value(key) + if File.exist?(@keyfile) + IO.readlines('/var/ossec/etc/ossec.conf').grep(/^.*<#{key}>/).map { |line| + line.match(/^.*<#{key}>(.*)<\/#{key}>/)&.captures&.first&.strip + }.compact.first + else nil + end + end + + def wazuh_server_name + get_ossec_conf_value('address') + end + + wazuh_server_hash = { + name: wazuh_server_name, + } + + wazuh_state_hash = {} + state_file_path = '/var/ossec/var/run/wazuh-agentd.state' + if File.exist?(state_file_path) + File.foreach(state_file_path) do |line| + key, value = line.strip.split('=') + case key + when 'last_keepalive' + # calculate seconds since last keepalive + seconds_since_keepalive = (Time.now - Time.parse(value)).to_i + wazuh_state_hash['last_keepalive_since'] = seconds_since_keepalive + when 'last_ack' + # calculate seconds since last ack + seconds_since_ack = (Time.now - Time.parse(value)).to_i + wazuh_state_hash['last_ack_since'] = seconds_since_ack + when 'status' + wazuh_state_hash['status'] = value + end + end + end + + wazuh_hash = { + agent: wazuh_agent_hash, + server: wazuh_server_hash, + state: wazuh_state_hash + } + + wazuh_hash + end +end diff --git a/lib/puppet/functions/wazuh/api_remove_agent.rb b/lib/puppet/functions/wazuh/api_remove_agent.rb new file mode 100644 index 00000000..affcfe98 --- /dev/null +++ b/lib/puppet/functions/wazuh/api_remove_agent.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true +# +# @summary A custom fact to remove a Wazuh agent via API. Meant to be run on Puppet server. +# +Puppet::Functions.create_function(:'wazuh::api_remove_agent') do + + dispatch :remove_remote_agent do + param 'Hash', :config + end + + def token_uri(config) + URI("https://#{config['api_host']}:#{config['api_host_port']}/security/user/authenticate") + end + + def agent_id_uri(config) + URI("https://#{config['api_host']}:#{config['api_host_port']}/agents?name=#{config['agent_name']}") + end + + def delete_agent_uri(config, id) + URI("https://#{config['api_host']}:#{config['api_host_port']}/agents?agents_list=#{id}&status=#{config['agent_status']}&older_than=0") + end + + def get_token(config) + uri = token_uri(config) + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + + request = Net::HTTP::Get.new(uri) + request.basic_auth(config['api_username'], config['api_password']) + + begin + response = http.request(request) + rescue StandardError => e + Puppet.err("Failed to retrieve token: #{e.message}") + end + token = JSON.parse(response.body)&.dig('data','token') + end + + def get_agent_id_by_name(config, token) + uri = agent_id_uri(config) + headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{token}" } + + begin + res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| + req = Net::HTTP::Get.new(uri, headers) + http.request(req) + end + rescue StandardError => e + Puppet.err("Failed to retrieve agent id: #{e.message}") + end + + id = JSON.parse(res.body)&.dig('data', 'affected_items', 0, 'id') + if res.code == '200' + return id.nil? ? nil : id + end + end + + def remove_agent(config, id, token) + uri = delete_agent_uri(config, id) + headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{token}" } + + begin + res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| + req = Net::HTTP::Delete.new(uri, headers) + http.request(req) + end + rescue StandardError => e + Puppet.err("Error: #{e.message}") + end + + if res.code == '200' + Puppet.notice("Agent #{config['agent_name']}, id #{id} successfully removed from Wazuh server.") + else + Puppet.err("Failed to remove agent #{config['agent_name']}, id #{id} from Wazuh server. HTTP status code: #{res.code}") + end + end + + def remove_remote_agent(config) + token = get_token(config) + agent_id = get_agent_id_by_name(config, token) + unless agent_id.nil? + remove_agent(config, agent_id, token) + end + end +end diff --git a/lib/puppet/provider/agent_action/default.rb b/lib/puppet/provider/agent_action/default.rb new file mode 100644 index 00000000..a94f1689 --- /dev/null +++ b/lib/puppet/provider/agent_action/default.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true +# +# @summary A Puppet provider for performing actions on the Wazuh agent +# +# @author Petri Lammi petri.lammi@puppeteers.net +# +Puppet::Type.type(:agent_action).provide(:ruby) do + + desc "Provider for managing agent actions" + + mk_resource_methods + + def refresh=(value) + command = generate_command(value) + unless command.nil? + Puppet.debug("Executing command: #{command}") + system(command) + end + end + + def action=(value) + command = generate_command(value) + unless command.nil? + Puppet.debug("Executing command: #{command}") + system(command) + end + end + + private + + def generate_command(action) + actions = { + start: 'systemctl is-active --quiet wazuh-agent.service || systemctl start wazuh-agent.service', + stop: 'systemctl is-active --quiet wazuh-agent.service && systemctl stop wazuh-agent.service', + restart: 'systemctl is-active --quiet wazuh-agent.service && systemctl restart wazuh-agent.service', + disable: 'systemctl is-enabled --quiet wazuh-agent.service && systemctl stop wazuh-agent.service && systemctl disable wazuh-agent.service', + enable: 'systemctl is-enabled --quiet wazuh-agent.service || systemctl enable wazuh-agent.service && systemctl start wazuh-agent.service' + } + + command = actions[action.to_sym] + if command.nil? + raise Puppet::Error, "Invalid action specified: #{action}. Valid values are 'start', 'stop', 'restart', 'enable', and 'disable'." + end + command + end +end diff --git a/lib/puppet/provider/local_agent_name/default.rb b/lib/puppet/provider/local_agent_name/default.rb new file mode 100644 index 00000000..197e4d46 --- /dev/null +++ b/lib/puppet/provider/local_agent_name/default.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true +# +# @summary A Puppet provider for changing wazuh agent properties +# +# @author Petri Lammi petri.lammi@puppeteers.net +# +Puppet::Type.type(:local_agent_name).provide(:ruby) do + + desc 'Provider for managing agent name' + + mk_resource_methods + + def keyfile + @keyfile = '/var/ossec/etc/client.keys' if File.exists?('/var/ossec/etc/client.keys') + end + + def authdfile + @authdfile ||= '/var/ossec/etc/authd.pass' + end + + def ossec_conf_file + @ossec_conf_file ||= '/var/ossec/etc/ossec.conf' + end + + def current_auth_password + @current_password ||= File.read(authdfile).chomp if File.exist?(authdfile) + end + + def current_agent_name + @current_name ||= File.read(keyfile).split[1].chomp if File.exist?(keyfile) + end + + def get_ossec_conf_value(key) + if File.exist?(ossec_conf_file) + IO.readlines(ossec_conf_file).grep(/^.*<#{key}>/).map { |line| + line.match(/^.*<#{key}>(.*)<\/#{key}>/)&.captures&.first&.strip + }.compact.first + else nil + end + end + + def current_auth_server_name + @current_auth_server_name ||= get_ossec_conf_value('address').chomp + end + + def agent_name + @agent_name ||= resource[:agent_name].chomp + end + + def auth_server_name + @auth_server_name ||= resource[:auth_server_name].chomp + end + + def auth_password + @auth_password ||= resource[:auth_password].chomp + if @auth_password != current_auth_password + File.write(@auth_password, authdfile) + end + @auth_password + end + + def enrollment_port + @enrollment_port ||= resource[:enrollment_port] + end + + def remove_keys_file + File.delete(keyfile) + end + + def reauthenticate + wazuh_agent_auth = '/var/ossec/bin/agent-auth' + auth_password + agent = agent_name + server = auth_server_name + cmd = "#{wazuh_agent_auth} -A #{agent} -m #{server}" + cmd += " -p #{enrollment_port}" unless enrollment_port == 1515 + system(cmd) + end + + def exists? + agent_name == current_agent_name && + auth_server_name == current_auth_server_name && + auth_password == current_auth_password && + enrollment_port == 1515 + end + + def create + reauthenticate + end + + def destroy + pass + end +end diff --git a/lib/puppet/provider/remote_agent/default.rb b/lib/puppet/provider/remote_agent/default.rb deleted file mode 100644 index fb64d200..00000000 --- a/lib/puppet/provider/remote_agent/default.rb +++ /dev/null @@ -1,78 +0,0 @@ -# frozen_string_literal: true - -Puppet::Type.type(:remote_agent).provide(:ruby) do - - require 'open3' - require 'net/http' - - def token_uri - URI("https://#{@resource[:api_host]}:#{@resource[:api_host_port]}/security/user/authenticate") - end - - def agent_id_uri(name) - URI("https://#{@resource[:api_host]}:#{@resource[:api_host_port]}/agents?name=#{name}") - end - - def delete_agent_uri(id) - URI("https://#{@resource[:api_host]}:#{@resource[:api_host_port]}/agents?agents_list=#{id}&status=#{@resource[:status]}&older_than=0") - end - - def get_token - uri = token_uri - - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - - request = Net::HTTP::Get.new(uri) - request.basic_auth(resource[:api_username], resource[:api_password]) - - response = http.request(request) - token = JSON.parse(response.body)['data']['token'] - end - - def get_agent_id_by_name(name, token) - uri = agent_id_uri(name) - headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{token}" } - - res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| - req = Net::HTTP::Get.new(uri, headers) - http.request(req) - end - - id = JSON.parse(res.body)['data']['affected_items'][0]['id'] - return id unless id.nil? - end - - def delete_agent(id, token) - uri = delete_agent_uri(id) - headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{token}" } - - res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| - req = Net::HTTP::Delete.new(uri, headers) - http.request(req) - end - - res - end - - def token - @token ||= get_token.chomp - end - - def agent_id - @agent_id ||= get_agent_id_by_name(@resource[:name], token) - end - - def exists? - !!agent_id rescue false - end - - def create - pass - end - - def destroy - delete_agent(agent_id, token) - end -end diff --git a/lib/puppet/type/agent_action.rb b/lib/puppet/type/agent_action.rb new file mode 100644 index 00000000..9eeeadc9 --- /dev/null +++ b/lib/puppet/type/agent_action.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true +# +# @summary A Puppet type for performing actions on the Wazuh agent +# +# @author Petri Lammi petri.lammi@puppeteers.net +# +Puppet::Type.newtype(:agent_action) do + + @doc = 'A custom Puppet resource type for agent actions.' + + def pre_run_check + if !File.exist?('/var/ossec/etc/ossec.conf') + raise Puppet::Error, "No Wazuh, no catalog." + end + end + + newparam(:name) do + desc "Just here for itself" + isnamevar + end + + newproperty(:action) do + desc "The action to perform on the agent (service). Valid values are 'start', 'stop', 'restart', and 'clean'." + newvalues(:start, :stop, :restart, :disable, :enable) + end +end + + diff --git a/lib/puppet/type/local_agent_name.rb b/lib/puppet/type/local_agent_name.rb new file mode 100644 index 00000000..4aae9ae6 --- /dev/null +++ b/lib/puppet/type/local_agent_name.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true +# +# @summary A Puppet type for changing wazuh agent properties +# +# @author Petri Lammi petri.lammi@puppeteers.net +# +Puppet::Type.newtype(:local_agent_name) do + + @doc = 'Custom resource to ensure name on an agent.' + + ensurable do + + desc 'Create or remove the key and the cert' + + newvalue(:present) do + provider.create + end + + defaultto :present + end + + def pre_run_check + if !File.exist?('/var/ossec/etc/ossec.conf') + raise Puppet::ResourceError, "No configuration file, no agent name management." + end + end + + newparam(:name) do + desc 'Just here for the itself' + isnamevar + end + + newparam(:enrollment_port) do + desc 'The server enrollment port' + defaultto 1515 + end + + newproperty(:agent_name) do + desc 'The name of the agent' + validate do |value| + unless value =~ /^[\w\.\-]+$/ + raise ArgumentError, "#{agent_name} must contain only letters, digits, '_', '-', and '.'" + end + end + end + + newproperty(:auth_server_name) do + desc 'Auth server name' + validate do |value| + unless value =~ /^[\w\.\-]+$/ + raise ArgumentError, "#{auth_server_name} must contain only letters, digits, '_', '-', and '.'" + end + end + end + + newproperty(:auth_password) do + desc 'Auth password to authenticate to the server' + end +end + diff --git a/lib/puppet/type/remote_agent.rb b/lib/puppet/type/remote_agent.rb deleted file mode 100644 index 848a7b9a..00000000 --- a/lib/puppet/type/remote_agent.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -Puppet::Type.newtype(:remote_agent) do - ensurable do - - newvalue(:absent) do - provider.destroy - end - - newvalue(:present) do - pass - end - - defaultto :absent - end - - newparam :name, :namevar => true - newparam :api_username - newparam :api_password - newparam :api_host - newparam :api_host_port - newparam :status - - validate do - [:api_username, :api_password, :api_host, :api_host_port, :status ].each do |param| - raise ArgumentError, "The #{param} parameter is required." unless self[param] - end - end -end diff --git a/manifests/remote_agent.pp b/manifests/remote_agent.pp deleted file mode 100644 index ac98586e..00000000 --- a/manifests/remote_agent.pp +++ /dev/null @@ -1,48 +0,0 @@ -# -# @summary Removes an agent from the API host -# -# @example Basic usage -# class wazuh::remote_agent { 'myagent.example.com': -# api_username => $api_username, -# api_password => $api_password, -# api_host => $api_host, -# api_host_port => $api_host_port -# } -# -# @param agent_name -# Agent name to remove. -# -# @param status -# Limit removal to agents that have this -# status. Default 'all' -# -# @param api_username, -# username to acces the API. -# -# @param api_password -# password to acces the API. -# -# @param api_host -# The API host. -# -# @param api_host -# The API port. Default 55000. -# -define wazuh::remote_agent( - String $api_username, - String $api_password, - Stdlib::Host $api_host, - Stdlib::Port $api_host_port, - Enum['all', 'disconnected', 'active'] $status = 'all' -) { - - remote_agent { $title: - ensure => 'absent', - status => $status, - api_username => $api_username, - api_password => $api_password, - api_host => $api_host, - api_host_port => $api_host_port - } -} - diff --git a/manifests/utils/agent_actions.pp b/manifests/utils/agent_actions.pp new file mode 100644 index 00000000..27b7bb19 --- /dev/null +++ b/manifests/utils/agent_actions.pp @@ -0,0 +1,13 @@ +# +# @summary Perform an action for the Wazuh service +# +# @author Petri Lammi petri.lammi@puppeteers.net +# +define wazuh::utils::agent_actions( + Enum['start', 'stop', 'restart', 'disable', 'enable'] $action, +){ + + agent_action { $title: + action => $action, + } +} diff --git a/manifests/utils/agent_supervise.pp b/manifests/utils/agent_supervise.pp new file mode 100644 index 00000000..3410f6b8 --- /dev/null +++ b/manifests/utils/agent_supervise.pp @@ -0,0 +1,38 @@ +# +# @summary Restart agent on certain conditions +# +# @author Petri Lammi petri.lammi@puppeteers.net +# +# @example +# +# Define a hash $when to decide when +# +# $when = { +# "last_ack_since" => 300, +# 'last_keepalive_since' => 300, +# 'status' => 'disconnected', +# } +# +# wazuh::utils::agent_supervisor { 'keep control': +# when => $when, +# } +# +# @params +# +# $when +# A hash with three limits: +# last_ack_since, last_keepalive_since, status +# +define wazuh::utils::agent_supervise( + Hash $when, +) { + + if ($facts['wazuh']['state']['status'] == $when['status'] or + $facts['wazuh']['state']['last_keepalive_since'] > $when['last_keepalive_since'] or + $facts['wazuh']['state']['last_ack_since'] > $when['last_ack_since']) { + + wazuh::utils::agent_actions { 'Agent has gone too far, restarting...': + action => 'restart', + } + } +} diff --git a/manifests/utils/api_remove_agent.pp b/manifests/utils/api_remove_agent.pp new file mode 100644 index 00000000..c414a759 --- /dev/null +++ b/manifests/utils/api_remove_agent.pp @@ -0,0 +1,54 @@ +# +# @summary A define to remove an agent from the manager +# with API and a custom function +# +# @author Petri Lammi petri.lammi@puppeteers.net +# +# @example Basic usage +# +# wazuh::utils::api_remove_agent { 'myagent.example.com': +# api_username => $api_username, +# api_password => $api_password, +# api_host => $api_host, +# api_host_port => $api_host_port +# } +# +# @param api_username, +# username to acces the API. +# +# @param api_password +# password to acces the API. +# +# @param api_host +# The API host. +# +# @param api_host_port +# The API port. Default 55000. +# +# @param $agent_name +# Agent name to remove. +# +# @param agent_status +# Limit removal to agents that have this +# status. Default 'all' +# +define wazuh::utils::api_remove_agent( + String $api_username, + String $api_password, + Stdlib::Host $api_host, + Stdlib::Host $agent_name, + Stdlib::Port $api_host_port = 55000, + Enum['active', 'disconnected', 'never_connected', 'all'] $agent_status = 'all', +) { + + $config_hash = { + 'agent_name' => $agent_name, + 'agent_status' => $agent_status, + 'api_username' => $api_username, + 'api_password' => $api_password, + 'api_host' => $api_host, + 'api_host_port' => $api_host_port + } + + wazuh::api_remove_agent($config_hash) +} diff --git a/manifests/utils/local_agent_name.pp b/manifests/utils/local_agent_name.pp new file mode 100644 index 00000000..fd264180 --- /dev/null +++ b/manifests/utils/local_agent_name.pp @@ -0,0 +1,20 @@ +# +# @summary ensure local wazuh agent name +# +# @author Petri Lammi petri.lammi@puppeteers.net +# +# @example Basic usage +# +define wazuh::utils::local_agent_name( + Stdlib::Host $auth_server_name, + String $auth_password, + Stdlib::Host $agent_name = $title, + Stdlib::Port $enrollment_port = 1515, +) { + local_agent_name { $agent_name: + agent_name => $agent_name, + auth_password => $auth_password, + auth_server_name => $auth_server_name, + enrollment_port => $enrollment_port, + } +} From f0e9c5cf3a9634bfc6ab27789c3fa3df362a6591 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Thu, 2 Mar 2023 18:48:45 +0200 Subject: [PATCH 04/27] Updates to functions and utils. Signed-off-by: Kibahop --- .../functions/wazuh/api_agent_exists.rb | 66 +++++++++++++++++++ .../functions/wazuh/api_remove_agent.rb | 0 manifests/utils/agent_supervise.pp | 4 +- manifests/utils/api_agent_exists.pp | 48 ++++++++++++++ 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100755 lib/puppet/functions/wazuh/api_agent_exists.rb mode change 100644 => 100755 lib/puppet/functions/wazuh/api_remove_agent.rb create mode 100644 manifests/utils/api_agent_exists.pp diff --git a/lib/puppet/functions/wazuh/api_agent_exists.rb b/lib/puppet/functions/wazuh/api_agent_exists.rb new file mode 100755 index 00000000..4d4a54b0 --- /dev/null +++ b/lib/puppet/functions/wazuh/api_agent_exists.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true +# +# @summary A custom fact to check the existense of an agent +# +Puppet::Functions.create_function(:'wazuh::api_agent_exists') do + + dispatch :api_agent_exists? do + param 'Hash', :config + return_type 'Boolean' + end + +def token_uri(config) + URI("https://#{config['api_host']}:#{config['api_host_port']}/security/user/authenticate") + end + + def agent_id_uri(config) + URI("https://#{config['api_host']}:#{config['api_host_port']}/agents?name=#{config['agent_name']}") + end + + def agent_info_uri(config, id) + URI("https://#{config['api_host']}:#{config['api_host_port']}/agents?agents_list=#{id}&status=#{config['agent_status']}&older_than=0") + end + + def get_token(config) + uri = token_uri(config) + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + + request = Net::HTTP::Get.new(uri) + request.basic_auth(config['api_username'], config['api_password']) + + begin + response = http.request(request) + rescue StandardError => e + Puppet.err("Failed to retrieve token: #{e.message}") + end + token = JSON.parse(response.body)&.dig('data','token') + end + + def get_agent_id_by_name(config, token) + uri = agent_id_uri(config) + headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{token}" } + + begin + res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| + req = Net::HTTP::Get.new(uri, headers) + http.request(req) + end + rescue StandardError => e + Puppet.err("Failed to retrieve agent id: #{e.message}") + end + + id = JSON.parse(res.body)&.dig('data', 'affected_items', 0, 'id') + if res.code == '200' + return id.nil? ? false : true + end + end + + def api_agent_exists?(config) + token = get_token(config) + agent_info = get_agent_id_by_name(config, token) + end +end + diff --git a/lib/puppet/functions/wazuh/api_remove_agent.rb b/lib/puppet/functions/wazuh/api_remove_agent.rb old mode 100644 new mode 100755 diff --git a/manifests/utils/agent_supervise.pp b/manifests/utils/agent_supervise.pp index 3410f6b8..fadfb165 100644 --- a/manifests/utils/agent_supervise.pp +++ b/manifests/utils/agent_supervise.pp @@ -27,10 +27,12 @@ Hash $when, ) { + notify { "got: ${when}": } + if ($facts['wazuh']['state']['status'] == $when['status'] or $facts['wazuh']['state']['last_keepalive_since'] > $when['last_keepalive_since'] or $facts['wazuh']['state']['last_ack_since'] > $when['last_ack_since']) { - + wazuh::utils::agent_actions { 'Agent has gone too far, restarting...': action => 'restart', } diff --git a/manifests/utils/api_agent_exists.pp b/manifests/utils/api_agent_exists.pp new file mode 100644 index 00000000..77d4663c --- /dev/null +++ b/manifests/utils/api_agent_exists.pp @@ -0,0 +1,48 @@ +# +# @summary A define to check if and agent exists +# +# @author Petri Lammi petri.lammi@puppeteers.net +# +# @example Basic usage +# +# wazuh::utils::agent_exists { 'myagent.example.com': +# api_username => $api_username, +# api_password => $api_password, +# api_host => $api_host, +# api_host_port => $api_host_port +# } +# +# @param api_username, +# username to acces the API. +# +# @param api_password +# password to acces the API. +# +# @param api_host +# The API host. +# +# @param api_host_port +# The API port. Default 55000. +# +# @param $agent_name +# Agent name to remove. +# +define wazuh::utils::api_agent_exists( + String $api_username, + String $api_password, + Stdlib::Host $api_host, + Stdlib::Host $agent_name, + Stdlib::Port $api_host_port = 55000, +) { + + $config_hash = { + 'agent_name' => $agent_name, + 'agent_status' => $agent_status, + 'api_username' => $api_username, + 'api_password' => $api_password, + 'api_host' => $api_host, + 'api_host_port' => $api_host_port + } + + wazuh::api_agent_exists($config_hash) +} From d7ca08de05bef3a803bbeb149084d0409ea99399 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Sat, 4 Mar 2023 19:53:15 +0200 Subject: [PATCH 05/27] WIP: updates, adjustments and new features. Signed-off-by: Kibahop --- lib/facter/wazuh_facts.rb | 22 +++++- lib/puppet/functions/wazuh/api_agent_state.rb | 68 +++++++++++++++++++ .../provider/local_agent_name/default.rb | 19 +++++- lib/puppet/type/local_agent_name.rb | 5 ++ manifests/utils/agent_supervise.pp | 20 ++++++ manifests/utils/api_agent_exists.pp | 1 + manifests/utils/local_agent_name.pp | 35 +++++++--- 7 files changed, 158 insertions(+), 12 deletions(-) create mode 100755 lib/puppet/functions/wazuh/api_agent_state.rb diff --git a/lib/facter/wazuh_facts.rb b/lib/facter/wazuh_facts.rb index 5e1b3b40..5dfa1bd1 100644 --- a/lib/facter/wazuh_facts.rb +++ b/lib/facter/wazuh_facts.rb @@ -15,6 +15,7 @@ setcode do @keyfile = String.new('/var/ossec/etc/client.keys') + @authd_pass_file = String.new('/var/ossec/etc/authd.pass') def wazuh_agent_data(index) if File.exist?(@keyfile) @@ -39,13 +40,23 @@ def wazuh_agent_version end Facter::Core::Execution.execute(cmd) end + + def password_hash + if File.exists?(@authd_pass_file) + password = File.read(@authd_pass_file).chomp + hash = Digest::SHA256.hexdigest(password) + else + nil + end + end wazuh_agent_hash = { id: wazuh_agent_id, name: wazuh_agent_name, ip_address: wazuh_agent_ip, key: wazuh_agent_key, - version: wazuh_agent_version + version: wazuh_agent_version, + password_hash: password_hash } def get_ossec_conf_value(key) @@ -58,11 +69,16 @@ def get_ossec_conf_value(key) end def wazuh_server_name - get_ossec_conf_value('address') + get_ossec_conf_value('address') || nil + end + + def wazuh_server_port + get_ossec_conf_value('port').to_i || nil end wazuh_server_hash = { name: wazuh_server_name, + port: wazuh_server_port } wazuh_state_hash = {} @@ -80,7 +96,7 @@ def wazuh_server_name seconds_since_ack = (Time.now - Time.parse(value)).to_i wazuh_state_hash['last_ack_since'] = seconds_since_ack when 'status' - wazuh_state_hash['status'] = value + wazuh_state_hash['status'] = value.gsub("'", "") end end end diff --git a/lib/puppet/functions/wazuh/api_agent_state.rb b/lib/puppet/functions/wazuh/api_agent_state.rb new file mode 100755 index 00000000..fc94708d --- /dev/null +++ b/lib/puppet/functions/wazuh/api_agent_state.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true +# +# @summary A custom fact to check the status of an agent +# +Puppet::Functions.create_function(:'wazuh::api_agent_state') do + + dispatch :api_agent_state do + param 'Hash', :config + return_type 'String' + end + +def token_uri(config) + URI("https://#{config['api_host']}:#{config['api_host_port']}/security/user/authenticate") + end + + def agent_id_uri(config) + URI("https://#{config['api_host']}:#{config['api_host_port']}/agents?name=#{config['agent_name']}") + end + + def agent_info_uri(config, id) + URI("https://#{config['api_host']}:#{config['api_host_port']}/agents?agents_list=#{id}&status=#{config['agent_status']}&older_than=0") + end + + def get_token(config) + uri = token_uri(config) + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + + request = Net::HTTP::Get.new(uri) + request.basic_auth(config['api_username'], config['api_password']) + + begin + response = http.request(request) + rescue StandardError => e + Puppet.err("Failed to retrieve token: #{e.message}") + end + token = JSON.parse(response.body)&.dig('data','token') + end + + def get_agent_id_by_name(config, token) + uri = agent_id_uri(config) + headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{token}" } + + begin + res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| + req = Net::HTTP::Get.new(uri, headers) + http.request(req) + end + rescue StandardError => e + Puppet.err("Failed to retrieve agent id: #{e.message}") + end + + status = JSON.parse(res.body)&.dig('data', 'affected_items', 0, 'status') + if res.code == '200' + return status + else + return nil + end + end + + def api_agent_state(config) + token = get_token(config) + agent_state = get_agent_id_by_name(config, token) + end +end + diff --git a/lib/puppet/provider/local_agent_name/default.rb b/lib/puppet/provider/local_agent_name/default.rb index 197e4d46..bfd74e35 100644 --- a/lib/puppet/provider/local_agent_name/default.rb +++ b/lib/puppet/provider/local_agent_name/default.rb @@ -26,6 +26,10 @@ def current_auth_password @current_password ||= File.read(authdfile).chomp if File.exist?(authdfile) end + def current_auth_password_hash + @current_password_hash ||= Digest::SHA256.hexdigest(current_auth_password) + end + def current_agent_name @current_name ||= File.read(keyfile).split[1].chomp if File.exist?(keyfile) end @@ -59,10 +63,22 @@ def auth_password @auth_password end + def auth_password_hash + @auth_password_hash ||= Digest::SHA256.hexdigest(auth_password) + end + def enrollment_port @enrollment_port ||= resource[:enrollment_port] end + + def communication_port + @communication_port ||= resource[:communication_port] + end + def current_communication_port + @communication_port ||= get_ossec_conf_value('port') + end + def remove_keys_file File.delete(keyfile) end @@ -80,7 +96,8 @@ def reauthenticate def exists? agent_name == current_agent_name && auth_server_name == current_auth_server_name && - auth_password == current_auth_password && + auth_password_hash == current_auth_password_hash && + communication_port == current_communication_port && enrollment_port == 1515 end diff --git a/lib/puppet/type/local_agent_name.rb b/lib/puppet/type/local_agent_name.rb index 4aae9ae6..e29f880a 100644 --- a/lib/puppet/type/local_agent_name.rb +++ b/lib/puppet/type/local_agent_name.rb @@ -34,6 +34,11 @@ def pre_run_check desc 'The server enrollment port' defaultto 1515 end + + newparam(:communication_port) do + desc 'The server communication port' + defaultto 1514 + end newproperty(:agent_name) do desc 'The name of the agent' diff --git a/manifests/utils/agent_supervise.pp b/manifests/utils/agent_supervise.pp index fadfb165..78ccf9e5 100644 --- a/manifests/utils/agent_supervise.pp +++ b/manifests/utils/agent_supervise.pp @@ -29,6 +29,7 @@ notify { "got: ${when}": } + # Restart local agent if any of these conditions are true if ($facts['wazuh']['state']['status'] == $when['status'] or $facts['wazuh']['state']['last_keepalive_since'] > $when['last_keepalive_since'] or $facts['wazuh']['state']['last_ack_since'] > $when['last_ack_since']) { @@ -37,4 +38,23 @@ action => 'restart', } } + + + /* + # get remote status + # get local status + # if either is not connected + # loop 10 times and give up with a warning + if $out_of_sync['local_status'] && $out_of_sync['remote_status'] { + $range = range(0, 9) + $range.each |$index| { + warning('Still out of sync...') + if local_status == 'connected' and remote_status == 'connected' { + break + } + sleep(5) + } + } + */ } + diff --git a/manifests/utils/api_agent_exists.pp b/manifests/utils/api_agent_exists.pp index 77d4663c..4a1a2dd0 100644 --- a/manifests/utils/api_agent_exists.pp +++ b/manifests/utils/api_agent_exists.pp @@ -32,6 +32,7 @@ String $api_password, Stdlib::Host $api_host, Stdlib::Host $agent_name, + String $agent_status = 'all', Stdlib::Port $api_host_port = 55000, ) { diff --git a/manifests/utils/local_agent_name.pp b/manifests/utils/local_agent_name.pp index fd264180..37d5021b 100644 --- a/manifests/utils/local_agent_name.pp +++ b/manifests/utils/local_agent_name.pp @@ -6,15 +6,34 @@ # @example Basic usage # define wazuh::utils::local_agent_name( - Stdlib::Host $auth_server_name, - String $auth_password, + $wazuh_register_endpoint, + $wazuh_reporting_endpoint, + $wazuh_enrollment_auth_pass, + $wazuh_enrollment_enabled, + $wazuh_enrollment_port, + $agent_auth_password, + $ossec_port, + $agent_package_version, + + #Stdlib::Host $auth_server_name, + #String $agent_auth_password, Stdlib::Host $agent_name = $title, - Stdlib::Port $enrollment_port = 1515, + #Stdlib::Port $enrollment_port = 1515, + #Stdlib::Port $communication_port = 1514, ) { - local_agent_name { $agent_name: - agent_name => $agent_name, - auth_password => $auth_password, - auth_server_name => $auth_server_name, - enrollment_port => $enrollment_port, + + with($agent_auth_password, + $wazuh_register_endpoint, + $wazuh_enrollment_port, + $ossec_port) + |$auth_password, $auth_server_name, $enrollment_port, $communication_port | { + + local_agent_name { $agent_name: + agent_name => $agent_name, + auth_password => $auth_password, + auth_server_name => $auth_server_name, + enrollment_port => $enrollment_port, + communication_port => $communication_port, + } } } From 899e724764ddf533af090d0edd6756221e9c91f6 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Mon, 6 Mar 2023 16:03:42 +0200 Subject: [PATCH 06/27] Do forgotten linting for examples. Updates to functions, types and utils. Signed-off-by: Kibahop --- examples/supervise.pp | 8 ++-- examples/wazuh_actions.pp | 2 +- examples/wazuh_agent_actions.pp | 27 ----------- examples/wazuh_ensure_name.pp | 26 +++++----- examples/wazuh_nametest.pp | 2 +- examples/wazuh_remove.pp | 17 ++++--- .../functions/wazuh/api_agent_exists.rb | 3 ++ lib/puppet/functions/wazuh/api_agent_state.rb | 23 ++++++--- .../functions/wazuh/api_remove_agent.rb | 4 +- lib/puppet/type/local_agent_name.rb | 6 +-- manifests/utils/agent_supervise.pp | 47 ++++++++++++------- manifests/utils/local_agent_name.pp | 31 ++++++------ 12 files changed, 96 insertions(+), 100 deletions(-) delete mode 100644 examples/wazuh_agent_actions.pp diff --git a/examples/supervise.pp b/examples/supervise.pp index 3e3c7b1a..a4cc4dce 100644 --- a/examples/supervise.pp +++ b/examples/supervise.pp @@ -1,15 +1,15 @@ # # @summary Example to Restart agent based on limits in the hash # -class local_profile::supervise { +class wazuh::supervise { #lint:ignore:autoloader_layout $when = { - "last_ack_since" => 300, + 'last_ack_since' => 300, 'last_keepalive_since' => 300, 'status' => 'disconnected', } - - wazuh::utils::agent_supervisor { 'keep control': + + wazuh::utils::agent_supervise { 'keep control': when => $when, } diff --git a/examples/wazuh_actions.pp b/examples/wazuh_actions.pp index adb8ed78..d61fa5a6 100644 --- a/examples/wazuh_actions.pp +++ b/examples/wazuh_actions.pp @@ -1,7 +1,7 @@ # # @summmary Example wazuh_actions # -class local_profile::wazuh_actions { +class wazuh_actions { #lint:ignore:autoloader_layout wazuh::utils::agent_actions { 'start_wazuh_service': action => start, diff --git a/examples/wazuh_agent_actions.pp b/examples/wazuh_agent_actions.pp deleted file mode 100644 index d7f64a4d..00000000 --- a/examples/wazuh_agent_actions.pp +++ /dev/null @@ -1,27 +0,0 @@ -class local_profile::wazuh_actions { - - wazuh::utils::agent_actions { 'start_wazuh_service': - action => start, - } - - wazuh::utils::agent_actions { 'stop_wazuh_service': - action => stop, - require => Wazuh::Utils::Agent_actions['start_wazuh_service'], - } - - wazuh::utils::agent_actions { 'disable_wazuh_service': - action => disable, - require => Wazuh::Utils::Agent_actions['start_wazuh_service'], - } - - wazuh::utils::agent_actions { 'enable_wazuh_service': - action => enable, - require => Wazuh::Utils::Agent_actions['disable_wazuh_service'], - } - - wazuh::utils::agent_actions { 'restart_wazuh_service': - action => restart, - require => Wazuh::Utils::Agent_actions['enable_wazuh_service'], - } -} - diff --git a/examples/wazuh_ensure_name.pp b/examples/wazuh_ensure_name.pp index 7f135e6f..0834289b 100644 --- a/examples/wazuh_ensure_name.pp +++ b/examples/wazuh_ensure_name.pp @@ -1,14 +1,14 @@ # # @summary Example to change agent name # -class local_profile::wazuh_ensure_name { +class wazuh_ensure_name { #lint:ignore:autoloader_layout $new_name = 'brand-new-agent.example.com' $current_name = $facts['wazuh']['agent']['name'] notify { "current wazuh name fact: ${facts['wazuh']['agent']['name']}": } - + notify { "new wazuh name: ${new_name}": require => Notify["current wazuh name fact: ${facts['wazuh']['agent']['name']}"], before => Notify["new wazuh name fact: ${facts['wazuh']['agent']['name']}"], @@ -16,24 +16,24 @@ # does the name differ from current name? if $new_name != $current_name { - + # prevent current agent from reconnecting in the middle wazuh::utils::agent_actions { "${current_name}_stop": - action => stop, + action => stop, require => Notify["new wazuh name: ${new_name}"] } - + # remove current name from the manager # (custom function runs on puppet server) wazuh::utils::api_remove_agent { $current_name: - api_username => 'wazuh', - api_password => 'wazuh', - api_host => 'wazuh.example.com', - api_host_port => 55000, - agent_name => $current_name, - require => Wazuh::Utils::Agent_actions["${current_name}_stop"], + api_username => 'wazuh', + api_password => 'wazuh', + api_host => 'wazuh.example.com', + api_host_port => 55000, + agent_name => $current_name, + require => Wazuh::Utils::Agent_actions["${current_name}_stop"], } - + # reauth with a brand new name wazuh::utils::local_agent_name { $new_name: agent_name => $new_name, @@ -41,7 +41,7 @@ auth_password => 'changeme', require => Wazuh::Utils::Api_remove_agent[$current_name], } - + # start the service again wazuh::utils::agent_actions { "${new_name}_start": action => start, diff --git a/examples/wazuh_nametest.pp b/examples/wazuh_nametest.pp index 092626ed..95cb78c0 100644 --- a/examples/wazuh_nametest.pp +++ b/examples/wazuh_nametest.pp @@ -1,7 +1,7 @@ # # @summary Agent name change example # -class local_profile::wazuh_nametest { +class wazuh_nametest { #lint:ignore:autoloader_layout # $agent_name = 'my-brand-new-agent.example.com' $agent_name = 'wazuh-agent.example.com' diff --git a/examples/wazuh_remove.pp b/examples/wazuh_remove.pp index b1361945..5b4c56e3 100644 --- a/examples/wazuh_remove.pp +++ b/examples/wazuh_remove.pp @@ -1,16 +1,15 @@ # # @summary Example to remove agent from manager # -class local_profile::wazuh_remove { +class wazuh_remove { #lint:ignore:autoloader_layout $agent_name = 'wazuh-agent.example.com' - # $agent_name = 'my-brand-new-agent.example.com' - - wazuh::utils::api_remove_agent { $agent_name: - agent_name => $agent_name, - api_username => 'wazuh', - api_password => 'wazuh', - api_host => 'wazuh.example.com', - api_host_port => 55000, + + wazuh::utils::api_remove_agent { $agent_name: + agent_name => $agent_name, + api_username => 'wazuh', + api_password => 'wazuh', + api_host => 'wazuh.example.com', + api_host_port => 55000, } } diff --git a/lib/puppet/functions/wazuh/api_agent_exists.rb b/lib/puppet/functions/wazuh/api_agent_exists.rb index 4d4a54b0..8cf96810 100755 --- a/lib/puppet/functions/wazuh/api_agent_exists.rb +++ b/lib/puppet/functions/wazuh/api_agent_exists.rb @@ -2,6 +2,8 @@ # # @summary A custom fact to check the existense of an agent # +# @author Kibahop +# Puppet::Functions.create_function(:'wazuh::api_agent_exists') do dispatch :api_agent_exists? do @@ -61,6 +63,7 @@ def get_agent_id_by_name(config, token) def api_agent_exists?(config) token = get_token(config) agent_info = get_agent_id_by_name(config, token) + return agent_info end end diff --git a/lib/puppet/functions/wazuh/api_agent_state.rb b/lib/puppet/functions/wazuh/api_agent_state.rb index fc94708d..d489d64b 100755 --- a/lib/puppet/functions/wazuh/api_agent_state.rb +++ b/lib/puppet/functions/wazuh/api_agent_state.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true # -# @summary A custom fact to check the status of an agent +# @summary A custom fact to check the status of an agent on the manager side +# +# @author Kibahop # Puppet::Functions.create_function(:'wazuh::api_agent_state') do @@ -33,10 +35,12 @@ def get_token(config) begin response = http.request(request) + token = JSON.parse(response.body)&.dig('data','token') rescue StandardError => e - Puppet.err("Failed to retrieve token: #{e.message}") + error_message = "Failed to retrieve token: #{e.message}" + Puppet.err(error_message) + fail(error_message) end - token = JSON.parse(response.body)&.dig('data','token') end def get_agent_id_by_name(config, token) @@ -50,6 +54,7 @@ def get_agent_id_by_name(config, token) end rescue StandardError => e Puppet.err("Failed to retrieve agent id: #{e.message}") + raise e end status = JSON.parse(res.body)&.dig('data', 'affected_items', 0, 'status') @@ -59,10 +64,16 @@ def get_agent_id_by_name(config, token) return nil end end - + def api_agent_state(config) - token = get_token(config) - agent_state = get_agent_id_by_name(config, token) + begin + token = get_token(config) + agent_state = get_agent_id_by_name(config, token) + return agent_state + rescue StandardError => e + Puppet.err("Failed to get agent state: #{e.message}") + return nil + end end end diff --git a/lib/puppet/functions/wazuh/api_remove_agent.rb b/lib/puppet/functions/wazuh/api_remove_agent.rb index affcfe98..2fc0c929 100755 --- a/lib/puppet/functions/wazuh/api_remove_agent.rb +++ b/lib/puppet/functions/wazuh/api_remove_agent.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true # -# @summary A custom fact to remove a Wazuh agent via API. Meant to be run on Puppet server. +# @summary A custom fact to remove a Wazuh agent via API. Targeted to be run on a Puppet server. +# +# @author Kibahop # Puppet::Functions.create_function(:'wazuh::api_remove_agent') do diff --git a/lib/puppet/type/local_agent_name.rb b/lib/puppet/type/local_agent_name.rb index e29f880a..c5193459 100644 --- a/lib/puppet/type/local_agent_name.rb +++ b/lib/puppet/type/local_agent_name.rb @@ -6,7 +6,7 @@ # Puppet::Type.newtype(:local_agent_name) do - @doc = 'Custom resource to ensure name on an agent.' + @doc = 'Custom resource change wazuh agent properties.' ensurable do @@ -21,12 +21,12 @@ def pre_run_check if !File.exist?('/var/ossec/etc/ossec.conf') - raise Puppet::ResourceError, "No configuration file, no agent name management." + raise Puppet::ResourceError, 'No ossec configuration file, no changes' end end newparam(:name) do - desc 'Just here for the itself' + desc 'Just here for itself' isnamevar end diff --git a/manifests/utils/agent_supervise.pp b/manifests/utils/agent_supervise.pp index 78ccf9e5..160fa2ca 100644 --- a/manifests/utils/agent_supervise.pp +++ b/manifests/utils/agent_supervise.pp @@ -27,34 +27,45 @@ Hash $when, ) { - notify { "got: ${when}": } - # Restart local agent if any of these conditions are true if ($facts['wazuh']['state']['status'] == $when['status'] or $facts['wazuh']['state']['last_keepalive_since'] > $when['last_keepalive_since'] or $facts['wazuh']['state']['last_ack_since'] > $when['last_ack_since']) { - - wazuh::utils::agent_actions { 'Agent has gone too far, restarting...': + + warning("Agent ${profile::wazuh::agent::wazuh_agent_name} has lost touch with the server, restarting...") + + wazuh::utils::agent_actions { "Agent ${profile::wazuh::agent::wazuh_agent_name} has lost touch with the server, restarting...": action => 'restart', } } + # + # TODO: replace this with a loop + # + if $facts.dig('wazuh', 'state', 'status') != undef { + + if $facts.dig('wazuh', 'agent', 'name') == $profile::wazuh_agent::wazuh_agent_name { + + $agent_local_state = assert_type(String[1], $facts.dig('wazuh', 'state', 'status')) + $agent_remote_state = assert_type(String[1], wazuh::api_agent_state($profile::wazuh_agent::api_hash)) - /* - # get remote status - # get local status - # if either is not connected - # loop 10 times and give up with a warning - if $out_of_sync['local_status'] && $out_of_sync['remote_status'] { - $range = range(0, 9) - $range.each |$index| { - warning('Still out of sync...') - if local_status == 'connected' and remote_status == 'connected' { - break + if $agent_local_state != 'connected' and $agent_remote_state != 'active' { + + warning("local and remote state for ${profile::wazuh::agent::wazuh_agent_name} disagree about their state") + + # remove current name from the manager + wazuh::utils::api_remove_agent { "${profile::wazuh_agent::wazuh_agent_name}_supervise": + * => $profile::wazuh_agent::api_hash, + agent_name => $profile::wazuh_agent::wazuh_agent_name, + } + + # reauth + wazuh::utils::local_agent_name { "${profile::wazuh_agent::wazuh_agent_name}_supervise": + * => $profile::wazuh_agent::agent_params_hash, + agent_name => $profile::wazuh_agent::wazuh_agent_name, + require => Wazuh::Utils::Api_remove_agent["${profile::wazuh_agent::wazuh_agent_name}_supervise"], + } } - sleep(5) } } - */ } - diff --git a/manifests/utils/local_agent_name.pp b/manifests/utils/local_agent_name.pp index 37d5021b..da7f8e39 100644 --- a/manifests/utils/local_agent_name.pp +++ b/manifests/utils/local_agent_name.pp @@ -1,31 +1,28 @@ # -# @summary ensure local wazuh agent name +# @summary Ensure local wazuh agent name # # @author Petri Lammi petri.lammi@puppeteers.net # # @example Basic usage # define wazuh::utils::local_agent_name( - $wazuh_register_endpoint, - $wazuh_reporting_endpoint, - $wazuh_enrollment_auth_pass, - $wazuh_enrollment_enabled, - $wazuh_enrollment_port, - $agent_auth_password, - $ossec_port, - $agent_package_version, + Stdlib::Host $wazuh_register_endpoint, + Optional[String] $wazuh_enrollment_auth_pass, + Boolean $wazuh_enrollment_enabled, + Stdlib::Port $wazuh_enrollment_port, + String $agent_auth_password, + Stdlib::Port $ossec_port, + Stdlib::Host $agent_name = $agent_name, + Optional[Stdlib::Host] $wazuh_reporting_endpoint = undef, + Optional[String] $agent_package_version = undef, + Optional[String] $agent_package_revision = undef, - #Stdlib::Host $auth_server_name, - #String $agent_auth_password, - Stdlib::Host $agent_name = $title, - #Stdlib::Port $enrollment_port = 1515, - #Stdlib::Port $communication_port = 1514, ) { with($agent_auth_password, - $wazuh_register_endpoint, - $wazuh_enrollment_port, - $ossec_port) + $wazuh_register_endpoint, + $wazuh_enrollment_port, + $ossec_port) |$auth_password, $auth_server_name, $enrollment_port, $communication_port | { local_agent_name { $agent_name: From 0b80a184ca751a3baddf5b62755789ab230a6c5a Mon Sep 17 00:00:00 2001 From: Kibahop Date: Thu, 16 Mar 2023 23:30:14 +0200 Subject: [PATCH 07/27] Some fixes and rubocop Signed-off-by: Kibahop --- lib/facter/ossec_conf_exists.rb | 3 +- lib/facter/wazuh_facts.rb | 43 ++-- .../functions/wazuh/api_agent_exists.rb | 61 +---- lib/puppet/functions/wazuh/api_agent_id.rb | 21 ++ lib/puppet/functions/wazuh/api_agent_name.rb | 21 ++ .../functions/wazuh/api_agent_remove.rb | 19 ++ lib/puppet/functions/wazuh/api_agent_state.rb | 79 ------- .../functions/wazuh/api_agent_status.rb | 21 ++ .../functions/wazuh/api_remove_agent.rb | 89 -------- lib/puppet/functions/wazuh/apihelper.rb | 216 ++++++++++++++++++ lib/puppet/provider/agent_action/default.rb | 23 +- .../provider/local_agent_name/default.rb | 96 +++++--- lib/puppet/type/agent_action.rb | 20 +- lib/puppet/type/local_agent_name.rb | 37 ++- .../{api_agent_exists.pp => agent_exists.pp} | 25 +- .../{local_agent_name.pp => agent_name.pp} | 25 +- manifests/utils/agent_supervise.pp | 41 ++-- ...pi_remove_agent.pp => api_agent_remove.pp} | 28 +-- manifests/utils/api_agent_state.pp | 56 +++++ 19 files changed, 549 insertions(+), 375 deletions(-) create mode 100755 lib/puppet/functions/wazuh/api_agent_id.rb create mode 100755 lib/puppet/functions/wazuh/api_agent_name.rb create mode 100755 lib/puppet/functions/wazuh/api_agent_remove.rb delete mode 100755 lib/puppet/functions/wazuh/api_agent_state.rb create mode 100755 lib/puppet/functions/wazuh/api_agent_status.rb delete mode 100755 lib/puppet/functions/wazuh/api_remove_agent.rb create mode 100644 lib/puppet/functions/wazuh/apihelper.rb rename manifests/utils/{api_agent_exists.pp => agent_exists.pp} (60%) rename manifests/utils/{local_agent_name.pp => agent_name.pp} (61%) rename manifests/utils/{api_remove_agent.pp => api_agent_remove.pp} (54%) create mode 100644 manifests/utils/api_agent_state.pp diff --git a/lib/facter/ossec_conf_exists.rb b/lib/facter/ossec_conf_exists.rb index 3420a0fa..d55d7501 100644 --- a/lib/facter/ossec_conf_exists.rb +++ b/lib/facter/ossec_conf_exists.rb @@ -1,4 +1,5 @@ -# +# frozen_string_literal: true + # @summary Check if Wazuh configuration file exist # # @author Petri Lammi petri.lammi@puppeteers.net diff --git a/lib/facter/wazuh_facts.rb b/lib/facter/wazuh_facts.rb index 5dfa1bd1..fa5cf2a4 100644 --- a/lib/facter/wazuh_facts.rb +++ b/lib/facter/wazuh_facts.rb @@ -1,4 +1,5 @@ -# +# frozen_string_literal: true + # @summary Produces a structured wazuh fact # # @author Petri Lammi petri.lammi@puppeteers.net @@ -6,17 +7,15 @@ # @example # # notify { $facts['wazuh']['state']['status']: } -# -Facter.add(:wazuh) do - confine :kernel => 'Linux' - confine :ossec_conf_exists => true +Facter.add(:wazuh) do + confine kernel: 'Linux' + confine ossec_conf_exists: true setcode do - @keyfile = String.new('/var/ossec/etc/client.keys') @authd_pass_file = String.new('/var/ossec/etc/authd.pass') - + def wazuh_agent_data(index) if File.exist?(@keyfile) File.read(@keyfile).split[index] @@ -24,12 +23,12 @@ def wazuh_agent_data(index) nil end end - + wazuh_agent_id = wazuh_agent_data(0) wazuh_agent_name = wazuh_agent_data(1) wazuh_agent_ip = wazuh_agent_data(2) wazuh_agent_key = wazuh_agent_data(3) - + def wazuh_agent_version cmd = String.new case Facter.value('osfamily') @@ -42,14 +41,15 @@ def wazuh_agent_version end def password_hash - if File.exists?(@authd_pass_file) + if File.exist?(@authd_pass_file) password = File.read(@authd_pass_file).chomp hash = Digest::SHA256.hexdigest(password) else nil end + hash end - + wazuh_agent_hash = { id: wazuh_agent_id, name: wazuh_agent_name, @@ -58,16 +58,17 @@ def password_hash version: wazuh_agent_version, password_hash: password_hash } - + def get_ossec_conf_value(key) if File.exist?(@keyfile) - IO.readlines('/var/ossec/etc/ossec.conf').grep(/^.*<#{key}>/).map { |line| - line.match(/^.*<#{key}>(.*)<\/#{key}>/)&.captures&.first&.strip + IO.readlines('/var/ossec/etc/ossec.conf').grep(%r{^.*<#{key}>}).map { |line| + line.match(%r{^.*<#{key}>(.*)})&.captures&.first&.strip }.compact.first - else nil + else + nil end end - + def wazuh_server_name get_ossec_conf_value('address') || nil end @@ -75,12 +76,12 @@ def wazuh_server_name def wazuh_server_port get_ossec_conf_value('port').to_i || nil end - + wazuh_server_hash = { name: wazuh_server_name, port: wazuh_server_port } - + wazuh_state_hash = {} state_file_path = '/var/ossec/var/run/wazuh-agentd.state' if File.exist?(state_file_path) @@ -96,17 +97,17 @@ def wazuh_server_port seconds_since_ack = (Time.now - Time.parse(value)).to_i wazuh_state_hash['last_ack_since'] = seconds_since_ack when 'status' - wazuh_state_hash['status'] = value.gsub("'", "") + wazuh_state_hash['status'] = value.gsub("'", '') end end end - + wazuh_hash = { agent: wazuh_agent_hash, server: wazuh_server_hash, state: wazuh_state_hash } - + wazuh_hash end end diff --git a/lib/puppet/functions/wazuh/api_agent_exists.rb b/lib/puppet/functions/wazuh/api_agent_exists.rb index 8cf96810..588a48ca 100755 --- a/lib/puppet/functions/wazuh/api_agent_exists.rb +++ b/lib/puppet/functions/wazuh/api_agent_exists.rb @@ -1,69 +1,20 @@ # frozen_string_literal: true + # -# @summary A custom fact to check the existense of an agent +# @summary A custom function to check the existense of an agent # # @author Kibahop # -Puppet::Functions.create_function(:'wazuh::api_agent_exists') do +require_relative 'apihelper' +Puppet::Functions.create_function(:'wazuh::api_agent_exists') do dispatch :api_agent_exists? do param 'Hash', :config return_type 'Boolean' end -def token_uri(config) - URI("https://#{config['api_host']}:#{config['api_host_port']}/security/user/authenticate") - end - - def agent_id_uri(config) - URI("https://#{config['api_host']}:#{config['api_host_port']}/agents?name=#{config['agent_name']}") - end - - def agent_info_uri(config, id) - URI("https://#{config['api_host']}:#{config['api_host_port']}/agents?agents_list=#{id}&status=#{config['agent_status']}&older_than=0") - end - - def get_token(config) - uri = token_uri(config) - - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - - request = Net::HTTP::Get.new(uri) - request.basic_auth(config['api_username'], config['api_password']) - - begin - response = http.request(request) - rescue StandardError => e - Puppet.err("Failed to retrieve token: #{e.message}") - end - token = JSON.parse(response.body)&.dig('data','token') - end - - def get_agent_id_by_name(config, token) - uri = agent_id_uri(config) - headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{token}" } - - begin - res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| - req = Net::HTTP::Get.new(uri, headers) - http.request(req) - end - rescue StandardError => e - Puppet.err("Failed to retrieve agent id: #{e.message}") - end - - id = JSON.parse(res.body)&.dig('data', 'affected_items', 0, 'id') - if res.code == '200' - return id.nil? ? false : true - end - end - def api_agent_exists?(config) - token = get_token(config) - agent_info = get_agent_id_by_name(config, token) - return agent_info + api_helper = ApiHelper.new(config) + api_helper.agent_exists? end end - diff --git a/lib/puppet/functions/wazuh/api_agent_id.rb b/lib/puppet/functions/wazuh/api_agent_id.rb new file mode 100755 index 00000000..d68cdba7 --- /dev/null +++ b/lib/puppet/functions/wazuh/api_agent_id.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# +# @summary A custom fact to check the status of an agent on the manager side +# +# @author Kibahop +# +require_relative 'apihelper' + +Puppet::Functions.create_function(:'wazuh::api_agent_id') do + dispatch :api_agent_id do + param 'Hash', :config + return_type 'String' + end + + def api_agent_id(config) + Puppet.debug(:config) + api_helper = ApiHelper.new(config) + api_helper.agent_id + end +end diff --git a/lib/puppet/functions/wazuh/api_agent_name.rb b/lib/puppet/functions/wazuh/api_agent_name.rb new file mode 100755 index 00000000..1e5aee47 --- /dev/null +++ b/lib/puppet/functions/wazuh/api_agent_name.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# +# @summary A custom fact to check the status of an agent on the manager side +# +# @author Kibahop +# +require_relative 'apihelper' + +Puppet::Functions.create_function(:'wazuh::api_agent_name') do + dispatch :api_agent_name do + param 'Hash', :config + return_type 'String' + end + + def api_agent_name(config) + Puppet.debug(:config) + api_helper = ApiHelper.new(config) + api_helper.agent_name + end +end diff --git a/lib/puppet/functions/wazuh/api_agent_remove.rb b/lib/puppet/functions/wazuh/api_agent_remove.rb new file mode 100755 index 00000000..30a50916 --- /dev/null +++ b/lib/puppet/functions/wazuh/api_agent_remove.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# +# @summary A custom fact to remove a Wazuh agent via API. +# +# @author Kibahop +# +require_relative 'apihelper' + +Puppet::Functions.create_function(:'wazuh::api_agent_remove') do + dispatch :api_agent_remove do + param 'Hash', :config + end + + def api_agent_remove(config) + api_helper = ApiHelper.new(config) + api_helper.agent_remove + end +end diff --git a/lib/puppet/functions/wazuh/api_agent_state.rb b/lib/puppet/functions/wazuh/api_agent_state.rb deleted file mode 100755 index d489d64b..00000000 --- a/lib/puppet/functions/wazuh/api_agent_state.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true -# -# @summary A custom fact to check the status of an agent on the manager side -# -# @author Kibahop -# -Puppet::Functions.create_function(:'wazuh::api_agent_state') do - - dispatch :api_agent_state do - param 'Hash', :config - return_type 'String' - end - -def token_uri(config) - URI("https://#{config['api_host']}:#{config['api_host_port']}/security/user/authenticate") - end - - def agent_id_uri(config) - URI("https://#{config['api_host']}:#{config['api_host_port']}/agents?name=#{config['agent_name']}") - end - - def agent_info_uri(config, id) - URI("https://#{config['api_host']}:#{config['api_host_port']}/agents?agents_list=#{id}&status=#{config['agent_status']}&older_than=0") - end - - def get_token(config) - uri = token_uri(config) - - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - - request = Net::HTTP::Get.new(uri) - request.basic_auth(config['api_username'], config['api_password']) - - begin - response = http.request(request) - token = JSON.parse(response.body)&.dig('data','token') - rescue StandardError => e - error_message = "Failed to retrieve token: #{e.message}" - Puppet.err(error_message) - fail(error_message) - end - end - - def get_agent_id_by_name(config, token) - uri = agent_id_uri(config) - headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{token}" } - - begin - res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| - req = Net::HTTP::Get.new(uri, headers) - http.request(req) - end - rescue StandardError => e - Puppet.err("Failed to retrieve agent id: #{e.message}") - raise e - end - - status = JSON.parse(res.body)&.dig('data', 'affected_items', 0, 'status') - if res.code == '200' - return status - else - return nil - end - end - - def api_agent_state(config) - begin - token = get_token(config) - agent_state = get_agent_id_by_name(config, token) - return agent_state - rescue StandardError => e - Puppet.err("Failed to get agent state: #{e.message}") - return nil - end - end -end - diff --git a/lib/puppet/functions/wazuh/api_agent_status.rb b/lib/puppet/functions/wazuh/api_agent_status.rb new file mode 100755 index 00000000..6de4ce33 --- /dev/null +++ b/lib/puppet/functions/wazuh/api_agent_status.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# +# @summary A custom fact to check the status of an agent on the manager side +# +# @author Kibahop +# +require_relative 'apihelper' + +Puppet::Functions.create_function(:'wazuh::api_agent_status') do + dispatch :api_agent_status do + param 'Hash', :config + return_type 'String' + end + + def api_agent_status(config) + Puppet.debug(:config) + api_helper = ApiHelper.new(config) + api_helper.agent_status + end +end diff --git a/lib/puppet/functions/wazuh/api_remove_agent.rb b/lib/puppet/functions/wazuh/api_remove_agent.rb deleted file mode 100755 index 2fc0c929..00000000 --- a/lib/puppet/functions/wazuh/api_remove_agent.rb +++ /dev/null @@ -1,89 +0,0 @@ -# frozen_string_literal: true -# -# @summary A custom fact to remove a Wazuh agent via API. Targeted to be run on a Puppet server. -# -# @author Kibahop -# -Puppet::Functions.create_function(:'wazuh::api_remove_agent') do - - dispatch :remove_remote_agent do - param 'Hash', :config - end - - def token_uri(config) - URI("https://#{config['api_host']}:#{config['api_host_port']}/security/user/authenticate") - end - - def agent_id_uri(config) - URI("https://#{config['api_host']}:#{config['api_host_port']}/agents?name=#{config['agent_name']}") - end - - def delete_agent_uri(config, id) - URI("https://#{config['api_host']}:#{config['api_host_port']}/agents?agents_list=#{id}&status=#{config['agent_status']}&older_than=0") - end - - def get_token(config) - uri = token_uri(config) - - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - - request = Net::HTTP::Get.new(uri) - request.basic_auth(config['api_username'], config['api_password']) - - begin - response = http.request(request) - rescue StandardError => e - Puppet.err("Failed to retrieve token: #{e.message}") - end - token = JSON.parse(response.body)&.dig('data','token') - end - - def get_agent_id_by_name(config, token) - uri = agent_id_uri(config) - headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{token}" } - - begin - res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| - req = Net::HTTP::Get.new(uri, headers) - http.request(req) - end - rescue StandardError => e - Puppet.err("Failed to retrieve agent id: #{e.message}") - end - - id = JSON.parse(res.body)&.dig('data', 'affected_items', 0, 'id') - if res.code == '200' - return id.nil? ? nil : id - end - end - - def remove_agent(config, id, token) - uri = delete_agent_uri(config, id) - headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{token}" } - - begin - res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http| - req = Net::HTTP::Delete.new(uri, headers) - http.request(req) - end - rescue StandardError => e - Puppet.err("Error: #{e.message}") - end - - if res.code == '200' - Puppet.notice("Agent #{config['agent_name']}, id #{id} successfully removed from Wazuh server.") - else - Puppet.err("Failed to remove agent #{config['agent_name']}, id #{id} from Wazuh server. HTTP status code: #{res.code}") - end - end - - def remove_remote_agent(config) - token = get_token(config) - agent_id = get_agent_id_by_name(config, token) - unless agent_id.nil? - remove_agent(config, agent_id, token) - end - end -end diff --git a/lib/puppet/functions/wazuh/apihelper.rb b/lib/puppet/functions/wazuh/apihelper.rb new file mode 100644 index 00000000..ed68f43c --- /dev/null +++ b/lib/puppet/functions/wazuh/apihelper.rb @@ -0,0 +1,216 @@ +# frozen_string_literal: true + +# @summary helper class +# +# @author Kibahop +# +# TODO: DRY, shorten methods +class ApiHelper + require 'net/http' + require 'json' + + def initialize(config) + @config = config + @api_host ||= @config['api_host'] + @api_host_port ||= @config['api_host_port'] + @api_username ||= @config['api_username'] + @api_password ||= @config['api_password'] + @api_check_states = sanitize_uri_string(@config['api_check_states']) + @token = retrieve_token + return unless config['api_agent_name'] + + @agent_name = config['api_agent_name'] + end + + attr_reader :agent_name + + def agent_id + @agent_id ||= retrieve_agent_id + end + + def agent_exists? + id = agent_id + if !id.nil? && (id != '000') + true + else + false + end + end + + def agent_status + status = retrieve_agent_status if agent_exists? + end + + def agent_remove + api_agent_remove if agent_exists? + end + + private + + def sanitize_uri_string(string) + if string == 'all' + ['active,pending,never_connected,disconnected'] + else + string.gsub!(/\s+/, "") + [string] + end + end + + def retrieve_token_uri + Puppet.debug("in retrieve_token_uri") + URI("https://#{@api_host}:#{@api_host_port}/security/user/authenticate") + end + + def token_uri + @token_uri ||= retrieve_token_uri + end + + def retrieve_token + Puppet.debug("in retrieve_token") + uri = token_uri + + begin + res = Net::HTTP.start( + uri.host, + uri.port, + use_ssl: true, + verify_mode: OpenSSL::SSL::VERIFY_NONE, + ) do |http| + req = Net::HTTP::Get.new(uri.path.to_s) + req.basic_auth(@api_username, @api_password) + http.request(req) + end + rescue StandardError => e + Puppet.err("WAZUH: Failed to retrieve agent token: #{e.message}") + end + + if !res.code.nil? && (res.code == '200') + begin + data = JSON.parse(res.body) + token = data['data']['token'] + (!token.nil?) ? token : nil + rescue StandardError => e + Puppet.warning("WAZUH: Failed to extract agent token - probably ok: #{e.message}") + end + else + Puppet.err('WAZUH: error communicating with the server') + end + end + + def build_agent_uri(params = {}) + Puppet.debug("in build_agent_uri") + query_params = params.empty? ? { 'name' => @agent_name } : params + + Puppet.err("query_params: #{query_params}") + + uri = URI("https://#{@api_host}:#{@api_host_port}/agents") + uri.query = URI.encode_www_form(query_params) + Puppet.err(uri.query) + Puppet.err("in build_agent_uri: returning: #{uri}") + uri + end + + def retrieve_agent_id + Puppet.err("in retrieve_agent_id") + uri = build_agent_uri + headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{@token}" } + Puppet.err("retrieve_agent_id uri: #{uri}") + begin + res = Net::HTTP.start( + uri.host, + uri.port, + use_ssl: true, + verify_mode: OpenSSL::SSL::VERIFY_NONE, + ) do |http| + req = Net::HTTP::Get.new(uri.request_uri, headers) + http.request(req) + end + rescue StandardError => e + Puppet.err("Wazuh: Error connecting to Wazuh API: #{e.message}") + return nil + end + + if !res.nil? && (res.code == '200') + begin + data = JSON.parse(res.body) + data['data']['affected_items'][0]['id'] + rescue StandardError => e + Puppet.warning("WAZUH: #{@agent_name} doesn't exist on the server #{@api_host} - probably ok: #{e.message}") + nil + end + else + Puppet.err("WAZUH: Unspecific response error from server #{@api_host} for #{@agent_name}") + nil + end + end + + def retrieve_agent_status + Puppet.err("in retrieve_agent_status") + status_params = { + :agents_list => @agent_id, + :older_than => 0 + } + uri = build_agent_uri(params = status_params) + headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{@token}" } + Puppet.err("retrieve_agent_status uri: #{uri}") + begin + res = Net::HTTP.start( + uri.host, + uri.port, + use_ssl: true, + verify_mode: OpenSSL::SSL::VERIFY_NONE, + ) do |http| + req = Net::HTTP::Get.new(uri.request_uri, headers) + http.request(req) + end + rescue StandardError => e + Puppet.err("WAZUH: Failed to retrieve agent status for #{@agent_name}/#{@agent_id}: #{e.message}") + return 'not_found' + end + + if res.code == '200' + begin + data = JSON.parse(res.body) + status = data['data']['affected_items'][0]['status'] + return !status.nil? ? status : nil + rescue StandardError => e + Puppet.err("WAZUH: Failed to extract agent status from data: #{e.message}") + end + end + end + + def api_agent_remove + remove_params = { + :agents_list => @agent_id, + :status => @api_check_states, + :older_than => 0 + } + + uri = build_agent_uri(params = remove_params) + Puppet.err("api_agent_remove: uri: #{uri}") + headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{@token}" } + + begin + res = Net::HTTP.start( + uri.host, + uri.port, + use_ssl: true, + verify_mode: OpenSSL::SSL::VERIFY_NONE, + ) do |http| + req = Net::HTTP::Delete.new(uri.request_uri, headers) + http.request(req) + end + rescue StandardError => e + Puppet.err("WAZUH: Failed to remove agent #{@agent_name} from server #{@api_host}: #{e.message} #{res.body}") + end + + case res.code + when '200' + Puppet.info("WAZUH: agent #{@agent_name}, id #{@agent_id} successfully removed from Wazuh server.") + when '400' + Puppet.err("WAZUH: Failed to remove agent #{@agent_name}, agent_id #{@agent_id} from Wazuh server. HTTP status code: #{res.code}") + else + Puppet.err("WAZUH: Failed to remove agent #{@agen_name}: #{res.body}") + end + end +end diff --git a/lib/puppet/provider/agent_action/default.rb b/lib/puppet/provider/agent_action/default.rb index a94f1689..ac6940fe 100644 --- a/lib/puppet/provider/agent_action/default.rb +++ b/lib/puppet/provider/agent_action/default.rb @@ -1,29 +1,29 @@ # frozen_string_literal: true + # # @summary A Puppet provider for performing actions on the Wazuh agent # # @author Petri Lammi petri.lammi@puppeteers.net # Puppet::Type.type(:agent_action).provide(:ruby) do - - desc "Provider for managing agent actions" + desc 'Provider for managing agent actions' mk_resource_methods - + def refresh=(value) command = generate_command(value) - unless command.nil? - Puppet.debug("Executing command: #{command}") - system(command) - end + return if command.nil? + + Puppet.debug("Executing command: #{command}") + system(command) end def action=(value) command = generate_command(value) - unless command.nil? - Puppet.debug("Executing command: #{command}") - system(command) - end + return if command.nil? + + Puppet.debug("Executing command: #{command}") + system(command) end private @@ -41,6 +41,7 @@ def generate_command(action) if command.nil? raise Puppet::Error, "Invalid action specified: #{action}. Valid values are 'start', 'stop', 'restart', 'enable', and 'disable'." end + command end end diff --git a/lib/puppet/provider/local_agent_name/default.rb b/lib/puppet/provider/local_agent_name/default.rb index bfd74e35..5e206da1 100644 --- a/lib/puppet/provider/local_agent_name/default.rb +++ b/lib/puppet/provider/local_agent_name/default.rb @@ -1,110 +1,130 @@ # frozen_string_literal: true + # # @summary A Puppet provider for changing wazuh agent properties # -# @author Petri Lammi petri.lammi@puppeteers.net +# @author Kibahop petri.lammi@puppeteers.net # Puppet::Type.type(:local_agent_name).provide(:ruby) do - - desc 'Provider for managing agent name' + desc 'Provider for managing agent changes' mk_resource_methods def keyfile - @keyfile = '/var/ossec/etc/client.keys' if File.exists?('/var/ossec/etc/client.keys') + @keyfile = '/var/ossec/etc/client.keys' if File.exist?('/var/ossec/etc/client.keys') end def authdfile @authdfile ||= '/var/ossec/etc/authd.pass' end + def enrollment_port_file + @enrollment_port_file ||= '/var/ossec/etc/.enrollment_port' + end + def ossec_conf_file @ossec_conf_file ||= '/var/ossec/etc/ossec.conf' end def current_auth_password - @current_password ||= File.read(authdfile).chomp if File.exist?(authdfile) + @current_password ||= File.read(authdfile).chomp if File.exist?(authdfile) + end + + def auth_password + @auth_password ||= resource[:auth_password].chomp + if @auth_password != current_auth_password + File.write(authdfile, @auth_password) + end + @auth_password end def current_auth_password_hash - @current_password_hash ||= Digest::SHA256.hexdigest(current_auth_password) + @current_auth_password_hash ||= Digest::SHA256.hexdigest(current_auth_password) + end + + def auth_password_hash + @auth_password_hash ||= Digest::SHA256.hexdigest(auth_password) end def current_agent_name @current_name ||= File.read(keyfile).split[1].chomp if File.exist?(keyfile) end + def agent_name + @agent_name ||= resource[:agent_name].chomp + end + def get_ossec_conf_value(key) if File.exist?(ossec_conf_file) - IO.readlines(ossec_conf_file).grep(/^.*<#{key}>/).map { |line| - line.match(/^.*<#{key}>(.*)<\/#{key}>/)&.captures&.first&.strip - }.compact.first - else nil + values = IO.readlines(ossec_conf_file).grep(%r{^.*<#{key}>}).map { |line| + match = line.match(%r{^.*<#{key}>(.*)}) + match ? match.captures.first.strip : nil + }.compact + values.empty? ? nil : values.first + else + nil end end - + def current_auth_server_name @current_auth_server_name ||= get_ossec_conf_value('address').chomp end - - def agent_name - @agent_name ||= resource[:agent_name].chomp - end def auth_server_name @auth_server_name ||= resource[:auth_server_name].chomp end - def auth_password - @auth_password ||= resource[:auth_password].chomp - if @auth_password != current_auth_password - File.write(@auth_password, authdfile) + def current_enrollment_port + port = 0 + if File.exist?(enrollment_port_file) + port = File.read(enrollment_port_file).chomp end - @auth_password + @enrollment_port = port + @enrollment_port end - def auth_password_hash - @auth_password_hash ||= Digest::SHA256.hexdigest(auth_password) - end - def enrollment_port @enrollment_port ||= resource[:enrollment_port] + unless File.exist?(enrollment_port_file) + File.write(enrollment_port_file, @enrollment_port) + end + @enrollment_port + end + + def current_communication_port + @current_communication_port ||= get_ossec_conf_value('port') end def communication_port @communication_port ||= resource[:communication_port] end - - def current_communication_port - @communication_port ||= get_ossec_conf_value('port') - end def remove_keys_file File.delete(keyfile) end - + def reauthenticate wazuh_agent_auth = '/var/ossec/bin/agent-auth' auth_password agent = agent_name server = auth_server_name - cmd = "#{wazuh_agent_auth} -A #{agent} -m #{server}" - cmd += " -p #{enrollment_port}" unless enrollment_port == 1515 + port = enrollment_port + cmd = "#{wazuh_agent_auth} -A #{agent} -m #{server} -p #{port}" system(cmd) end def exists? - agent_name == current_agent_name && - auth_server_name == current_auth_server_name && - auth_password_hash == current_auth_password_hash && - communication_port == current_communication_port && - enrollment_port == 1515 + (agent_name == current_agent_name) && + (auth_server_name == current_auth_server_name) && + (auth_password_hash == current_auth_password_hash) && + (communication_port == current_communication_port) && + (enrollment_port == current_enrollment_port) end - + def create reauthenticate end - + def destroy pass end diff --git a/lib/puppet/type/agent_action.rb b/lib/puppet/type/agent_action.rb index 9eeeadc9..54108b9c 100644 --- a/lib/puppet/type/agent_action.rb +++ b/lib/puppet/type/agent_action.rb @@ -1,21 +1,21 @@ # frozen_string_literal: true + # # @summary A Puppet type for performing actions on the Wazuh agent # # @author Petri Lammi petri.lammi@puppeteers.net # Puppet::Type.newtype(:agent_action) do - @doc = 'A custom Puppet resource type for agent actions.' - + def pre_run_check - if !File.exist?('/var/ossec/etc/ossec.conf') - raise Puppet::Error, "No Wazuh, no catalog." - end - end - - newparam(:name) do - desc "Just here for itself" + return if File.exist?('/var/ossec/etc/ossec.conf') + + raise Puppet::Error, 'No Wazuh, no catalog.' + end + + newparam(:agent_name) do + desc 'Just here for itself' isnamevar end @@ -24,5 +24,3 @@ def pre_run_check newvalues(:start, :stop, :restart, :disable, :enable) end end - - diff --git a/lib/puppet/type/local_agent_name.rb b/lib/puppet/type/local_agent_name.rb index c5193459..2553b254 100644 --- a/lib/puppet/type/local_agent_name.rb +++ b/lib/puppet/type/local_agent_name.rb @@ -1,65 +1,64 @@ # frozen_string_literal: true + # # @summary A Puppet type for changing wazuh agent properties # # @author Petri Lammi petri.lammi@puppeteers.net # Puppet::Type.newtype(:local_agent_name) do - @doc = 'Custom resource change wazuh agent properties.' ensurable do - - desc 'Create or remove the key and the cert' + desc 'Change wazuh agent properties' newvalue(:present) do provider.create end - + defaultto :present end - + def pre_run_check - if !File.exist?('/var/ossec/etc/ossec.conf') - raise Puppet::ResourceError, 'No ossec configuration file, no changes' - end - end - + return if File.exist?('/var/ossec/etc/ossec.conf') + + raise Puppet::ResourceError, 'No ossec configuration file, no changes' + end + newparam(:name) do - desc 'Just here for itself' + desc 'The name itself' isnamevar end - newparam(:enrollment_port) do + newproperty(:enrollment_port) do desc 'The server enrollment port' defaultto 1515 end - newparam(:communication_port) do + newproperty(:communication_port) do desc 'The server communication port' defaultto 1514 end - + newproperty(:agent_name) do desc 'The name of the agent' validate do |value| - unless value =~ /^[\w\.\-]+$/ + unless value =~ %r{^[\w.-]+$} raise ArgumentError, "#{agent_name} must contain only letters, digits, '_', '-', and '.'" end end end - + newproperty(:auth_server_name) do desc 'Auth server name' validate do |value| - unless value =~ /^[\w\.\-]+$/ + unless value =~ %r{^[\w.-]+$} raise ArgumentError, "#{auth_server_name} must contain only letters, digits, '_', '-', and '.'" end end end - + + # FIXME: disallow empty string newproperty(:auth_password) do desc 'Auth password to authenticate to the server' end end - diff --git a/manifests/utils/api_agent_exists.pp b/manifests/utils/agent_exists.pp similarity index 60% rename from manifests/utils/api_agent_exists.pp rename to manifests/utils/agent_exists.pp index 4a1a2dd0..89f4f83d 100644 --- a/manifests/utils/api_agent_exists.pp +++ b/manifests/utils/agent_exists.pp @@ -6,6 +6,7 @@ # @example Basic usage # # wazuh::utils::agent_exists { 'myagent.example.com': +# api_agent_name => 'my.example.com, # api_username => $api_username, # api_password => $api_password, # api_host => $api_host, @@ -24,26 +25,24 @@ # @param api_host_port # The API port. Default 55000. # -# @param $agent_name -# Agent name to remove. +# @param $api_agent_name +# Agent name to look up # -define wazuh::utils::api_agent_exists( +define wazuh::utils::agent_exists( String $api_username, String $api_password, Stdlib::Host $api_host, - Stdlib::Host $agent_name, - String $agent_status = 'all', + Stdlib::Host $api_agent_name, + String $api_check_states = 'all', Stdlib::Port $api_host_port = 55000, ) { $config_hash = { - 'agent_name' => $agent_name, - 'agent_status' => $agent_status, - 'api_username' => $api_username, - 'api_password' => $api_password, - 'api_host' => $api_host, - 'api_host_port' => $api_host_port + 'api_agent_name' => $api_agent_name, + 'api_check_states' => $api_check_states, + 'api_username' => $api_username, + 'api_password' => $api_password, + 'api_host' => $api_host, + 'api_host_port' => $api_host_port } - - wazuh::api_agent_exists($config_hash) } diff --git a/manifests/utils/local_agent_name.pp b/manifests/utils/agent_name.pp similarity index 61% rename from manifests/utils/local_agent_name.pp rename to manifests/utils/agent_name.pp index da7f8e39..bec8461c 100644 --- a/manifests/utils/local_agent_name.pp +++ b/manifests/utils/agent_name.pp @@ -3,27 +3,36 @@ # # @author Petri Lammi petri.lammi@puppeteers.net # -# @example Basic usage -# -define wazuh::utils::local_agent_name( +define wazuh::utils::agent_name( Stdlib::Host $wazuh_register_endpoint, - Optional[String] $wazuh_enrollment_auth_pass, - Boolean $wazuh_enrollment_enabled, Stdlib::Port $wazuh_enrollment_port, + Optional[String] $wazuh_enrollment_auth_pass, String $agent_auth_password, Stdlib::Port $ossec_port, Stdlib::Host $agent_name = $agent_name, + Optional[Boolean] $wazuh_enrollment_enabled = undef, Optional[Stdlib::Host] $wazuh_reporting_endpoint = undef, Optional[String] $agent_package_version = undef, Optional[String] $agent_package_revision = undef, ) { - with($agent_auth_password, + notify { "before agent_name: ${agent_name}": } + notify { "before auth_password: ${agent_auth_password}": } + notify { "before auth_server_name: ${$wazuh_register_endpoint}": } + notify { "before enrollment_port: ${wazuh_enrollment_port}": } + notify { "before communication_port: ${ossec_port}": } + + with( + $agent_auth_password, $wazuh_register_endpoint, $wazuh_enrollment_port, - $ossec_port) - |$auth_password, $auth_server_name, $enrollment_port, $communication_port | { + $ossec_port + ) + |$auth_password, + $auth_server_name, + $enrollment_port, + $communication_port| { local_agent_name { $agent_name: agent_name => $agent_name, diff --git a/manifests/utils/agent_supervise.pp b/manifests/utils/agent_supervise.pp index 160fa2ca..2ff2221d 100644 --- a/manifests/utils/agent_supervise.pp +++ b/manifests/utils/agent_supervise.pp @@ -8,9 +8,9 @@ # Define a hash $when to decide when # # $when = { -# "last_ack_since" => 300, -# 'last_keepalive_since' => 300, -# 'status' => 'disconnected', +# 'control_last_ack_since' => 300, +# 'control_last_keepalive_since' => 300, +# 'control_status' => 'disconnected', # } # # wazuh::utils::agent_supervisor { 'keep control': @@ -21,18 +21,24 @@ # # $when # A hash with three limits: -# last_ack_since, last_keepalive_since, status +# control_last_ack_since, control_last_keepalive_since, control_status # define wazuh::utils::agent_supervise( Hash $when, ) { + $myfact = $facts['wazuh']['state']['last_keepalive_since'] + $myhash = $when['last_keepalive_since'] + + notify { "fact: ${myfact}": } + notify { "hash: ${myhash}": } + # Restart local agent if any of these conditions are true - if ($facts['wazuh']['state']['status'] == $when['status'] or + if ($facts['wazuh']['state']['status'] == $when['control_status'] or $facts['wazuh']['state']['last_keepalive_since'] > $when['last_keepalive_since'] or $facts['wazuh']['state']['last_ack_since'] > $when['last_ack_since']) { - warning("Agent ${profile::wazuh::agent::wazuh_agent_name} has lost touch with the server, restarting...") + warning("WAZUH: Agent ${profile::wazuh::agent::wazuh_agent_name} has lost touch with the server, restarting...") wazuh::utils::agent_actions { "Agent ${profile::wazuh::agent::wazuh_agent_name} has lost touch with the server, restarting...": action => 'restart', @@ -46,24 +52,27 @@ if $facts.dig('wazuh', 'agent', 'name') == $profile::wazuh_agent::wazuh_agent_name { + $_name = $profile::wazuh_agent::wazuh_agent_name + if ! $profile::wazuh_agent::api_hash.has_key('api_agent_name') { + $local_api_hash = $profile::wazuh_agent::api_hash.merge({ 'api_agent_name' => $_name }) + } + $agent_local_state = assert_type(String[1], $facts.dig('wazuh', 'state', 'status')) - $agent_remote_state = assert_type(String[1], wazuh::api_agent_state($profile::wazuh_agent::api_hash)) + $agent_remote_state = assert_type(String[1], wazuh::api_agent_status($local_api_hash)) - if $agent_local_state != 'connected' and $agent_remote_state != 'active' { + if ($agent_local_state != 'connected') and ($agent_remote_state != 'active') { - warning("local and remote state for ${profile::wazuh::agent::wazuh_agent_name} disagree about their state") + warning("WAZUH: local and remote state for ${profile::wazuh::agent::wazuh_agent_name} disagree about their state") # remove current name from the manager - wazuh::utils::api_remove_agent { "${profile::wazuh_agent::wazuh_agent_name}_supervise": - * => $profile::wazuh_agent::api_hash, - agent_name => $profile::wazuh_agent::wazuh_agent_name, + wazuh::utils::api_agent_remove { "${profile::wazuh_agent::wazuh_agent_name}_supervise_remove": + * => $local_api_hash, } # reauth - wazuh::utils::local_agent_name { "${profile::wazuh_agent::wazuh_agent_name}_supervise": - * => $profile::wazuh_agent::agent_params_hash, - agent_name => $profile::wazuh_agent::wazuh_agent_name, - require => Wazuh::Utils::Api_remove_agent["${profile::wazuh_agent::wazuh_agent_name}_supervise"], + wazuh::utils::agent_name { "${profile::wazuh_agent::wazuh_agent_name}_supervise_reauth": + * => $local_api_hash, + require => Wazuh::Utils::Api_agent_remove["${profile::wazuh_agent::wazuh_agent_name}_supervise_remove"], } } } diff --git a/manifests/utils/api_remove_agent.pp b/manifests/utils/api_agent_remove.pp similarity index 54% rename from manifests/utils/api_remove_agent.pp rename to manifests/utils/api_agent_remove.pp index c414a759..bf045a07 100644 --- a/manifests/utils/api_remove_agent.pp +++ b/manifests/utils/api_agent_remove.pp @@ -1,12 +1,12 @@ # # @summary A define to remove an agent from the manager -# with API and a custom function +# via API with a a custom function # # @author Petri Lammi petri.lammi@puppeteers.net # # @example Basic usage # -# wazuh::utils::api_remove_agent { 'myagent.example.com': +# wazuh::utils::api_agent_remove { 'myagent.example.com': # api_username => $api_username, # api_password => $api_password, # api_host => $api_host, @@ -25,30 +25,30 @@ # @param api_host_port # The API port. Default 55000. # -# @param $agent_name +# @param $api_agent_name # Agent name to remove. # -# @param agent_status +# @param agent_check_statess # Limit removal to agents that have this # status. Default 'all' # -define wazuh::utils::api_remove_agent( +define wazuh::utils::api_agent_remove( String $api_username, String $api_password, Stdlib::Host $api_host, - Stdlib::Host $agent_name, + Stdlib::Host $api_agent_name, Stdlib::Port $api_host_port = 55000, - Enum['active', 'disconnected', 'never_connected', 'all'] $agent_status = 'all', + Enum['active', 'pending', 'disconnected', 'never_connected', 'all'] $api_check_states = 'all', ) { $config_hash = { - 'agent_name' => $agent_name, - 'agent_status' => $agent_status, - 'api_username' => $api_username, - 'api_password' => $api_password, - 'api_host' => $api_host, - 'api_host_port' => $api_host_port + 'api_agent_name' => $api_agent_name, + 'api_check_states' => $api_check_states, + 'api_username' => $api_username, + 'api_password' => $api_password, + 'api_host' => $api_host, + 'api_host_port' => $api_host_port } - wazuh::api_remove_agent($config_hash) + wazuh::api_agent_remove($config_hash) } diff --git a/manifests/utils/api_agent_state.pp b/manifests/utils/api_agent_state.pp new file mode 100644 index 00000000..59077f1c --- /dev/null +++ b/manifests/utils/api_agent_state.pp @@ -0,0 +1,56 @@ +# +# @summary A define to reporet remote agent state +# +# @author Petri Lammi petri.lammi@puppeteers.net +# +# @example Basic usage +# +# wazuh::utils::api_agent_state { 'myagent.example.com': +# api_username => $api_username, +# api_password => $api_password, +# api_host => $api_host, +# api_host_port => $api_host_port +# } +# +# @param api_username, +# username to acces the API. +# +# @param api_password +# password to acces the API. +# +# @param api_host +# The API host. +# +# @param api_host_port +# The API port. Default 55000. +# +# @param $agent_name +# Agent name to remove. +# +define wazuh::utils::api_agent_state( + String $api_username, + String $api_password, + Stdlib::Host $api_host, + Stdlib::Host $agent_name, + String $agent_status = 'all', + Stdlib::Port $api_host_port = 55000, +) { + + if $facts.has_key('wazuh') and $facts['wazuh'].has_key('state') and $facts['wazuh']['state']['status'].length > 0 { + + # if agent name has been changed in the catalog, we cannot yet query it's state + if $profile::wazuh_agent::wazuh_agent_name != $facts['wazuh']['agent']['name'] { + + $config_hash = { + 'api_agent_name' => $agent_name, + 'api_status' => $agent_status, + 'api_username' => $api_username, + 'api_password' => $api_password, + 'api_host' => $api_host, + 'api_host_port' => $api_host_port + } + + wazuh::api_agent_state($config_hash) + } + } +} From 6c07ca5004997af777675e0d31a20650b11886c6 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Fri, 17 Mar 2023 15:55:42 +0200 Subject: [PATCH 08/27] Provider and defs changes Signed-off-by: Kibahop --- .../provider/local_agent_name/default.rb | 2 +- manifests/utils/agent_name.pp | 6 -- manifests/utils/agent_supervise.pp | 87 +++++++++---------- 3 files changed, 44 insertions(+), 51 deletions(-) diff --git a/lib/puppet/provider/local_agent_name/default.rb b/lib/puppet/provider/local_agent_name/default.rb index 5e206da1..a53319cc 100644 --- a/lib/puppet/provider/local_agent_name/default.rb +++ b/lib/puppet/provider/local_agent_name/default.rb @@ -116,7 +116,7 @@ def reauthenticate def exists? (agent_name == current_agent_name) && (auth_server_name == current_auth_server_name) && - (auth_password_hash == current_auth_password_hash) && + (auth_password == current_auth_password) && (communication_port == current_communication_port) && (enrollment_port == current_enrollment_port) end diff --git a/manifests/utils/agent_name.pp b/manifests/utils/agent_name.pp index bec8461c..8055ba62 100644 --- a/manifests/utils/agent_name.pp +++ b/manifests/utils/agent_name.pp @@ -17,12 +17,6 @@ ) { - notify { "before agent_name: ${agent_name}": } - notify { "before auth_password: ${agent_auth_password}": } - notify { "before auth_server_name: ${$wazuh_register_endpoint}": } - notify { "before enrollment_port: ${wazuh_enrollment_port}": } - notify { "before communication_port: ${ossec_port}": } - with( $agent_auth_password, $wazuh_register_endpoint, diff --git a/manifests/utils/agent_supervise.pp b/manifests/utils/agent_supervise.pp index 2ff2221d..8a5cae0e 100644 --- a/manifests/utils/agent_supervise.pp +++ b/manifests/utils/agent_supervise.pp @@ -27,54 +27,53 @@ Hash $when, ) { - $myfact = $facts['wazuh']['state']['last_keepalive_since'] - $myhash = $when['last_keepalive_since'] + # do not supervise if name has been changed + # TODO: cover all meaninfull changes + unless $profile::wazuh_agent::agent_name_changed { - notify { "fact: ${myfact}": } - notify { "hash: ${myhash}": } - - # Restart local agent if any of these conditions are true - if ($facts['wazuh']['state']['status'] == $when['control_status'] or - $facts['wazuh']['state']['last_keepalive_since'] > $when['last_keepalive_since'] or - $facts['wazuh']['state']['last_ack_since'] > $when['last_ack_since']) { - - warning("WAZUH: Agent ${profile::wazuh::agent::wazuh_agent_name} has lost touch with the server, restarting...") - - wazuh::utils::agent_actions { "Agent ${profile::wazuh::agent::wazuh_agent_name} has lost touch with the server, restarting...": - action => 'restart', - } - } - - # - # TODO: replace this with a loop - # - if $facts.dig('wazuh', 'state', 'status') != undef { - - if $facts.dig('wazuh', 'agent', 'name') == $profile::wazuh_agent::wazuh_agent_name { - - $_name = $profile::wazuh_agent::wazuh_agent_name - if ! $profile::wazuh_agent::api_hash.has_key('api_agent_name') { - $local_api_hash = $profile::wazuh_agent::api_hash.merge({ 'api_agent_name' => $_name }) + # Restart local agent if any of these conditions are true + if ($facts['wazuh']['state']['status'] == $when['control_status'] or + $facts['wazuh']['state']['last_keepalive_since'] > $when['last_keepalive_since'] or + $facts['wazuh']['state']['last_ack_since'] > $when['last_ack_since']) { + + warning("WAZUH: Agent ${profile::wazuh::agent::wazuh_agent_name} has lost touch with the server, restarting...") + + wazuh::utils::agent_actions { "Agent ${profile::wazuh::agent::wazuh_agent_name} has lost touch with the server, restarting...": + action => 'restart', } - - $agent_local_state = assert_type(String[1], $facts.dig('wazuh', 'state', 'status')) - $agent_remote_state = assert_type(String[1], wazuh::api_agent_status($local_api_hash)) - - if ($agent_local_state != 'connected') and ($agent_remote_state != 'active') { - - warning("WAZUH: local and remote state for ${profile::wazuh::agent::wazuh_agent_name} disagree about their state") - - # remove current name from the manager - wazuh::utils::api_agent_remove { "${profile::wazuh_agent::wazuh_agent_name}_supervise_remove": - * => $local_api_hash, + } + + # + # TODO: replace this with a loop + # + if $facts.dig('wazuh', 'state', 'status') != undef { + + if $facts.dig('wazuh', 'agent', 'name') == $profile::wazuh_agent::wazuh_agent_name { + + $_name = $profile::wazuh_agent::wazuh_agent_name + if ! $profile::wazuh_agent::api_hash.has_key('api_agent_name') { + $local_api_hash = $profile::wazuh_agent::api_hash.merge({ 'api_agent_name' => $_name }) } - - # reauth - wazuh::utils::agent_name { "${profile::wazuh_agent::wazuh_agent_name}_supervise_reauth": - * => $local_api_hash, - require => Wazuh::Utils::Api_agent_remove["${profile::wazuh_agent::wazuh_agent_name}_supervise_remove"], + + $agent_local_state = assert_type(String[1], $facts.dig('wazuh', 'state', 'status')) + $agent_remote_state = assert_type(String[1], wazuh::api_agent_status($local_api_hash)) + + if ($agent_local_state != 'connected') and ($agent_remote_state != 'active') { + + warning("WAZUH: local and remote state for ${profile::wazuh::agent::wazuh_agent_name} disagree about their state") + + # remove current name from the manager + wazuh::utils::api_agent_remove { "${profile::wazuh_agent::wazuh_agent_name}_supervise_remove": + * => $local_api_hash, + } + + # reauth + wazuh::utils::agent_name { "${profile::wazuh_agent::wazuh_agent_name}_supervise_reauth": + * => $local_api_hash, + require => Wazuh::Utils::Api_agent_remove["${profile::wazuh_agent::wazuh_agent_name}_supervise_remove"], + } } } } - } + } } From 9d0bdcb3a715e52f51c4374c62d1a06c725d03a3 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Sun, 19 Mar 2023 16:33:55 +0200 Subject: [PATCH 09/27] wazuh: fact: remove safe navigator for old ruby, handle empty state values Signed-off-by: Kibahop --- lib/facter/wazuh_facts.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/facter/wazuh_facts.rb b/lib/facter/wazuh_facts.rb index fa5cf2a4..dbeca1ab 100644 --- a/lib/facter/wazuh_facts.rb +++ b/lib/facter/wazuh_facts.rb @@ -62,7 +62,7 @@ def password_hash def get_ossec_conf_value(key) if File.exist?(@keyfile) IO.readlines('/var/ossec/etc/ossec.conf').grep(%r{^.*<#{key}>}).map { |line| - line.match(%r{^.*<#{key}>(.*)})&.captures&.first&.strip + line.match(%r{^.*<#{key}>(.*)}).captures.first.strip }.compact.first else nil @@ -90,11 +90,11 @@ def wazuh_server_port case key when 'last_keepalive' # calculate seconds since last keepalive - seconds_since_keepalive = (Time.now - Time.parse(value)).to_i + seconds_since_keepalive = value.empty? ? 0 : (Time.now - Time.parse(value)).to_i wazuh_state_hash['last_keepalive_since'] = seconds_since_keepalive when 'last_ack' # calculate seconds since last ack - seconds_since_ack = (Time.now - Time.parse(value)).to_i + seconds_since_ack = value.empty? ? 0 : (Time.now - Time.parse(value)).to_i wazuh_state_hash['last_ack_since'] = seconds_since_ack when 'status' wazuh_state_hash['status'] = value.gsub("'", '') From 2c863f58bc7262d96f893cb060d71e5413d89514 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Sun, 19 Mar 2023 17:03:22 +0200 Subject: [PATCH 10/27] handle case string '' as empty in the fact Signed-off-by: Kibahop --- lib/facter/wazuh_facts.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/facter/wazuh_facts.rb b/lib/facter/wazuh_facts.rb index dbeca1ab..57cb0788 100644 --- a/lib/facter/wazuh_facts.rb +++ b/lib/facter/wazuh_facts.rb @@ -90,11 +90,11 @@ def wazuh_server_port case key when 'last_keepalive' # calculate seconds since last keepalive - seconds_since_keepalive = value.empty? ? 0 : (Time.now - Time.parse(value)).to_i + seconds_since_keepalive = value.gsub("'", '').empty? ? 0 : (Time.now - Time.parse(value)).to_i wazuh_state_hash['last_keepalive_since'] = seconds_since_keepalive when 'last_ack' # calculate seconds since last ack - seconds_since_ack = value.empty? ? 0 : (Time.now - Time.parse(value)).to_i + seconds_since_ack = value.gsub("'", '').empty? ? 0 : (Time.now - Time.parse(value)).to_i wazuh_state_hash['last_ack_since'] = seconds_since_ack when 'status' wazuh_state_hash['status'] = value.gsub("'", '') From 697c96b2409a84290c72905019ea49c6db5f5a8e Mon Sep 17 00:00:00 2001 From: Petri Lammi Date: Sun, 19 Mar 2023 21:28:20 +0200 Subject: [PATCH 11/27] wazuh: be spesific about remote server response Signed-off-by: Petri Lammi --- manifests/utils/agent_supervise.pp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/manifests/utils/agent_supervise.pp b/manifests/utils/agent_supervise.pp index 8a5cae0e..4bce1b14 100644 --- a/manifests/utils/agent_supervise.pp +++ b/manifests/utils/agent_supervise.pp @@ -56,8 +56,15 @@ } $agent_local_state = assert_type(String[1], $facts.dig('wazuh', 'state', 'status')) - $agent_remote_state = assert_type(String[1], wazuh::api_agent_status($local_api_hash)) - + # $agent_remote_state = assert_type(String[1], wazuh::api_agent_status($local_api_hash)) + $agent_remote_state = wazuh::api_agent_status($local_api_hash) ? { + 'pending' => 'pending', + 'active' => 'active', + 'never_connected' => 'never_connected', + 'disconnected' => 'disconnected', + default => undef, + } + if ($agent_local_state != 'connected') and ($agent_remote_state != 'active') { warning("WAZUH: local and remote state for ${profile::wazuh::agent::wazuh_agent_name} disagree about their state") From a705ee6126f036c18a7f05d9c76bd8cf8b7b9778 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Sun, 19 Mar 2023 21:37:19 +0200 Subject: [PATCH 12/27] be explicit about remote state Signed-off-by: Kibahop --- manifests/utils/agent_supervise.pp | 1 + 1 file changed, 1 insertion(+) diff --git a/manifests/utils/agent_supervise.pp b/manifests/utils/agent_supervise.pp index 4bce1b14..005a7116 100644 --- a/manifests/utils/agent_supervise.pp +++ b/manifests/utils/agent_supervise.pp @@ -62,6 +62,7 @@ 'active' => 'active', 'never_connected' => 'never_connected', 'disconnected' => 'disconnected', + undef => undef, default => undef, } From a56116be7dbadbace291b8f3df2247c7f347858b Mon Sep 17 00:00:00 2001 From: Kibahop Date: Sun, 19 Mar 2023 21:48:31 +0200 Subject: [PATCH 13/27] When looking up id, return string, not nil if not found Signed-off-by: Kibahop --- lib/puppet/functions/wazuh/apihelper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/functions/wazuh/apihelper.rb b/lib/puppet/functions/wazuh/apihelper.rb index ed68f43c..db5c5386 100644 --- a/lib/puppet/functions/wazuh/apihelper.rb +++ b/lib/puppet/functions/wazuh/apihelper.rb @@ -135,8 +135,8 @@ def retrieve_agent_id data = JSON.parse(res.body) data['data']['affected_items'][0]['id'] rescue StandardError => e - Puppet.warning("WAZUH: #{@agent_name} doesn't exist on the server #{@api_host} - probably ok: #{e.message}") - nil + Puppet.warning("WAZUH: #{@agent_name} doesn't exist on the server #{@api_host} - probably ok: #{res.body}") + return 'not_found' end else Puppet.err("WAZUH: Unspecific response error from server #{@api_host} for #{@agent_name}") From 16c35744da2d9188ced75508f70bb26c66ff3818 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Sun, 19 Mar 2023 21:50:50 +0200 Subject: [PATCH 14/27] Still expect string from remote status Signed-off-by: Kibahop --- manifests/utils/agent_supervise.pp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifests/utils/agent_supervise.pp b/manifests/utils/agent_supervise.pp index 005a7116..1c4d7bc9 100644 --- a/manifests/utils/agent_supervise.pp +++ b/manifests/utils/agent_supervise.pp @@ -62,7 +62,7 @@ 'active' => 'active', 'never_connected' => 'never_connected', 'disconnected' => 'disconnected', - undef => undef, + 'not_found => 'not_found, default => undef, } From 376bc2dd8b3fa19215288797585ff580a17474d3 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Sun, 19 Mar 2023 22:39:08 +0200 Subject: [PATCH 15/27] Fix typo Signed-off-by: Kibahop --- manifests/utils/agent_supervise.pp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifests/utils/agent_supervise.pp b/manifests/utils/agent_supervise.pp index 1c4d7bc9..e00becc8 100644 --- a/manifests/utils/agent_supervise.pp +++ b/manifests/utils/agent_supervise.pp @@ -62,7 +62,7 @@ 'active' => 'active', 'never_connected' => 'never_connected', 'disconnected' => 'disconnected', - 'not_found => 'not_found, + 'not_found' => 'not_found, default => undef, } From e071409575fed1447fac40a0d60b3fb58ec8b468 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Sun, 19 Mar 2023 22:47:06 +0200 Subject: [PATCH 16/27] Fix another typo Signed-off-by: Kibahop --- manifests/utils/agent_supervise.pp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifests/utils/agent_supervise.pp b/manifests/utils/agent_supervise.pp index e00becc8..59667a9d 100644 --- a/manifests/utils/agent_supervise.pp +++ b/manifests/utils/agent_supervise.pp @@ -62,7 +62,7 @@ 'active' => 'active', 'never_connected' => 'never_connected', 'disconnected' => 'disconnected', - 'not_found' => 'not_found, + 'not_found' => 'not_found', default => undef, } From ecc9a38b449e5bd508e5b93c96ed5b6dac529ef2 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Tue, 21 Mar 2023 16:59:52 +0200 Subject: [PATCH 17/27] Logic and bug fixes, cleanups Signed-off-by: Kibahop --- lib/puppet/functions/wazuh/apihelper.rb | 110 ++++++++++++++---------- manifests/utils/agent_exists.pp | 2 +- manifests/utils/agent_supervise.pp | 35 +++----- manifests/utils/api_agent_remove.pp | 5 +- 4 files changed, 81 insertions(+), 71 deletions(-) diff --git a/lib/puppet/functions/wazuh/apihelper.rb b/lib/puppet/functions/wazuh/apihelper.rb index db5c5386..2f060deb 100644 --- a/lib/puppet/functions/wazuh/apihelper.rb +++ b/lib/puppet/functions/wazuh/apihelper.rb @@ -25,22 +25,33 @@ def initialize(config) attr_reader :agent_name def agent_id + Puppet.err("in agent_id") @agent_id ||= retrieve_agent_id + Puppet.err("agent_id: #{@agent_id}") + @agent_id end def agent_exists? id = agent_id - if !id.nil? && (id != '000') + Puppet.err("agent_exists: #{@agent_id}") + if !id.nil? && (id != 000) && (id != 0) true else false end end - + def agent_status - status = retrieve_agent_status if agent_exists? + Puppet.err("agent_status") + if agent_exists? + status = retrieve_agent_status + Puppet.err(status) + status + else + 'non_existent' + end end - + def agent_remove api_agent_remove if agent_exists? end @@ -57,7 +68,7 @@ def sanitize_uri_string(string) end def retrieve_token_uri - Puppet.debug("in retrieve_token_uri") + Puppet.err("in retrieve_token_uri") URI("https://#{@api_host}:#{@api_host_port}/security/user/authenticate") end @@ -82,18 +93,19 @@ def retrieve_token end rescue StandardError => e Puppet.err("WAZUH: Failed to retrieve agent token: #{e.message}") - end - - if !res.code.nil? && (res.code == '200') - begin - data = JSON.parse(res.body) - token = data['data']['token'] - (!token.nil?) ? token : nil - rescue StandardError => e - Puppet.warning("WAZUH: Failed to extract agent token - probably ok: #{e.message}") - end else - Puppet.err('WAZUH: error communicating with the server') + if res.code == '200' + begin + data = JSON.parse(res.body) + token = data['data']['token'] + rescue StandardError => e + Puppet.err("WAZUH: Failed to extract agent token: #{e.message}") + else + !token.nil? ? token : nil + end + else + Puppet.err('WAZUH: error communicating with the server') + end end end @@ -115,6 +127,7 @@ def retrieve_agent_id uri = build_agent_uri headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{@token}" } Puppet.err("retrieve_agent_id uri: #{uri}") + Puppet.err("in retrieve_agent_id, about to begin") begin res = Net::HTTP.start( uri.host, @@ -127,29 +140,36 @@ def retrieve_agent_id end rescue StandardError => e Puppet.err("Wazuh: Error connecting to Wazuh API: #{e.message}") - return nil - end - - if !res.nil? && (res.code == '200') - begin + else + if res.code == '200' data = JSON.parse(res.body) - data['data']['affected_items'][0]['id'] - rescue StandardError => e - Puppet.warning("WAZUH: #{@agent_name} doesn't exist on the server #{@api_host} - probably ok: #{res.body}") - return 'not_found' + Puppet.err(data) + Puppet.err(data['data']['total_affected_items']) + if data['data']['total_affected_items'] == 0 # agent doesn't exist + 0 + elsif + data['data']['total_affected_items'] == 1 # exactly one agent exists + id = data['data']['affected_items'][0]['id'] + id + else + fail('WAZUH: more than one affected_items in the server response') + 0 + end end - else - Puppet.err("WAZUH: Unspecific response error from server #{@api_host} for #{@agent_name}") - nil end end - + def retrieve_agent_status Puppet.err("in retrieve_agent_status") + + return 'non_existent' if @agent_id == 0 + status_params = { :agents_list => @agent_id, + :status => @api_check_states, :older_than => 0 } + uri = build_agent_uri(params = status_params) headers = { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{@token}" } Puppet.err("retrieve_agent_status uri: #{uri}") @@ -165,20 +185,18 @@ def retrieve_agent_status end rescue StandardError => e Puppet.err("WAZUH: Failed to retrieve agent status for #{@agent_name}/#{@agent_id}: #{e.message}") - return 'not_found' - end - - if res.code == '200' - begin + 'non_existent' + else + if res.code == '200' data = JSON.parse(res.body) status = data['data']['affected_items'][0]['status'] - return !status.nil? ? status : nil - rescue StandardError => e - Puppet.err("WAZUH: Failed to extract agent status from data: #{e.message}") + else + Puppet.err("WAZUH: Failed to retrieve agent status for #{@agent_name}/#{@agent_id}: #{e.message}") + 'non_existent' end end end - + def api_agent_remove remove_params = { :agents_list => @agent_id, @@ -202,15 +220,15 @@ def api_agent_remove end rescue StandardError => e Puppet.err("WAZUH: Failed to remove agent #{@agent_name} from server #{@api_host}: #{e.message} #{res.body}") - end - - case res.code - when '200' - Puppet.info("WAZUH: agent #{@agent_name}, id #{@agent_id} successfully removed from Wazuh server.") - when '400' - Puppet.err("WAZUH: Failed to remove agent #{@agent_name}, agent_id #{@agent_id} from Wazuh server. HTTP status code: #{res.code}") else - Puppet.err("WAZUH: Failed to remove agent #{@agen_name}: #{res.body}") + case res.code + when '200' + Puppet.info("WAZUH: agent #{@agent_name}, id #{@agent_id} successfully removed from Wazuh server.") + when '400' + Puppet.err("WAZUH: Failed to remove agent #{@agent_name}, agent_id #{@agent_id} from Wazuh server. HTTP status code: #{res.code}") + else + Puppet.err("WAZUH: Failed to remove agent #{@agen_name}: #{res.body}") + end end end end diff --git a/manifests/utils/agent_exists.pp b/manifests/utils/agent_exists.pp index 89f4f83d..013c5fa8 100644 --- a/manifests/utils/agent_exists.pp +++ b/manifests/utils/agent_exists.pp @@ -1,5 +1,5 @@ # -# @summary A define to check if and agent exists +# @summary A define to check if and agent exists on the server # # @author Petri Lammi petri.lammi@puppeteers.net # diff --git a/manifests/utils/agent_supervise.pp b/manifests/utils/agent_supervise.pp index 59667a9d..9e39b4f7 100644 --- a/manifests/utils/agent_supervise.pp +++ b/manifests/utils/agent_supervise.pp @@ -35,46 +35,37 @@ if ($facts['wazuh']['state']['status'] == $when['control_status'] or $facts['wazuh']['state']['last_keepalive_since'] > $when['last_keepalive_since'] or $facts['wazuh']['state']['last_ack_since'] > $when['last_ack_since']) { - - warning("WAZUH: Agent ${profile::wazuh::agent::wazuh_agent_name} has lost touch with the server, restarting...") - - wazuh::utils::agent_actions { "Agent ${profile::wazuh::agent::wazuh_agent_name} has lost touch with the server, restarting...": + + warning("WAZUH: Agent ${profile::wazuh_agent::wazuh_agent_name} has lost touch with the server, restarting...") + + wazuh::utils::agent_actions { "Agent ${profile::wazuh_agent::wazuh_agent_name} has lost touch with the server, restarting...": action => 'restart', } } - + # - # TODO: replace this with a loop + # TODO: replace this with a loop (function) # if $facts.dig('wazuh', 'state', 'status') != undef { - + if $facts.dig('wazuh', 'agent', 'name') == $profile::wazuh_agent::wazuh_agent_name { - + $_name = $profile::wazuh_agent::wazuh_agent_name if ! $profile::wazuh_agent::api_hash.has_key('api_agent_name') { $local_api_hash = $profile::wazuh_agent::api_hash.merge({ 'api_agent_name' => $_name }) } - + $agent_local_state = assert_type(String[1], $facts.dig('wazuh', 'state', 'status')) - # $agent_remote_state = assert_type(String[1], wazuh::api_agent_status($local_api_hash)) - $agent_remote_state = wazuh::api_agent_status($local_api_hash) ? { - 'pending' => 'pending', - 'active' => 'active', - 'never_connected' => 'never_connected', - 'disconnected' => 'disconnected', - 'not_found' => 'not_found', - default => undef, - } + $agent_remote_state = assert_type(String[1], wazuh::api_agent_status($local_api_hash)) if ($agent_local_state != 'connected') and ($agent_remote_state != 'active') { - - warning("WAZUH: local and remote state for ${profile::wazuh::agent::wazuh_agent_name} disagree about their state") - + + warning("WAZUH: local and remote state for ${profile::wazuh_agent::wazuh_agent_name} disagree about their state") + # remove current name from the manager wazuh::utils::api_agent_remove { "${profile::wazuh_agent::wazuh_agent_name}_supervise_remove": * => $local_api_hash, } - # reauth wazuh::utils::agent_name { "${profile::wazuh_agent::wazuh_agent_name}_supervise_reauth": * => $local_api_hash, diff --git a/manifests/utils/api_agent_remove.pp b/manifests/utils/api_agent_remove.pp index bf045a07..65689191 100644 --- a/manifests/utils/api_agent_remove.pp +++ b/manifests/utils/api_agent_remove.pp @@ -10,7 +10,8 @@ # api_username => $api_username, # api_password => $api_password, # api_host => $api_host, -# api_host_port => $api_host_port +# api_host_port => $api_host_port, +# api_agent_name => 'myagent.example.com' # } # # @param api_username, @@ -28,7 +29,7 @@ # @param $api_agent_name # Agent name to remove. # -# @param agent_check_statess +# @param agent_check_states # Limit removal to agents that have this # status. Default 'all' # From 4b93fc0c9ab4fc29c444f3e4b67c8d54f3b2a6ce Mon Sep 17 00:00:00 2001 From: Kibahop Date: Tue, 21 Mar 2023 20:28:05 +0200 Subject: [PATCH 18/27] Loosen restriction for agent name (might not wanted to be a hostname) Signed-off-by: Kibahop --- manifests/utils/agent_name.pp | 2 +- manifests/utils/api_agent_state.pp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/manifests/utils/agent_name.pp b/manifests/utils/agent_name.pp index 8055ba62..a069d229 100644 --- a/manifests/utils/agent_name.pp +++ b/manifests/utils/agent_name.pp @@ -9,7 +9,7 @@ Optional[String] $wazuh_enrollment_auth_pass, String $agent_auth_password, Stdlib::Port $ossec_port, - Stdlib::Host $agent_name = $agent_name, + String $agent_name = $agent_name, Optional[Boolean] $wazuh_enrollment_enabled = undef, Optional[Stdlib::Host] $wazuh_reporting_endpoint = undef, Optional[String] $agent_package_version = undef, diff --git a/manifests/utils/api_agent_state.pp b/manifests/utils/api_agent_state.pp index 59077f1c..9a342324 100644 --- a/manifests/utils/api_agent_state.pp +++ b/manifests/utils/api_agent_state.pp @@ -31,7 +31,7 @@ String $api_username, String $api_password, Stdlib::Host $api_host, - Stdlib::Host $agent_name, + String $agent_name, String $agent_status = 'all', Stdlib::Port $api_host_port = 55000, ) { From bd55520d4fca50c1a34d958dbb4b0bf350327825 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Tue, 21 Mar 2023 23:30:27 +0200 Subject: [PATCH 19/27] A bit more troubleshooting info for random rare undefined method `[]' for nil:NilClass Signed-off-by: Kibahop --- lib/puppet/functions/wazuh/apihelper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/puppet/functions/wazuh/apihelper.rb b/lib/puppet/functions/wazuh/apihelper.rb index 2f060deb..456fe97c 100644 --- a/lib/puppet/functions/wazuh/apihelper.rb +++ b/lib/puppet/functions/wazuh/apihelper.rb @@ -181,6 +181,7 @@ def retrieve_agent_status verify_mode: OpenSSL::SSL::VERIFY_NONE, ) do |http| req = Net::HTTP::Get.new(uri.request_uri, headers) + Puppet.err(http.request(req)) http.request(req) end rescue StandardError => e @@ -189,6 +190,7 @@ def retrieve_agent_status else if res.code == '200' data = JSON.parse(res.body) + Puppet.err("WAZUH: got 200, data: #{data}") status = data['data']['affected_items'][0]['status'] else Puppet.err("WAZUH: Failed to retrieve agent status for #{@agent_name}/#{@agent_id}: #{e.message}") From bce0f15046e6baf8379002e94f2b1e443af65d5e Mon Sep 17 00:00:00 2001 From: Kibahop Date: Tue, 21 Mar 2023 23:51:29 +0200 Subject: [PATCH 20/27] Still more attempt to catch the random nilclass error Signed-off-by: Kibahop --- lib/puppet/functions/wazuh/apihelper.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/puppet/functions/wazuh/apihelper.rb b/lib/puppet/functions/wazuh/apihelper.rb index 456fe97c..2ce9aa7f 100644 --- a/lib/puppet/functions/wazuh/apihelper.rb +++ b/lib/puppet/functions/wazuh/apihelper.rb @@ -189,9 +189,14 @@ def retrieve_agent_status 'non_existent' else if res.code == '200' - data = JSON.parse(res.body) - Puppet.err("WAZUH: got 200, data: #{data}") - status = data['data']['affected_items'][0]['status'] + begin + data = !JSON.parse(res.body).nil? ? JSON.parse(res.body) : nil + Puppet.err("WAZUH: got 200, data: #{data}") + status = data['data']['affected_items'][0]['status'].nil? unless data.nil? + rescue StandardError => e + Puppet.err("WAZUH: data hash parsing eneded up with: #{e.message}") + 'non_existent' + end else Puppet.err("WAZUH: Failed to retrieve agent status for #{@agent_name}/#{@agent_id}: #{e.message}") 'non_existent' From 362435d0282b9df0700f9e5d10fa5e125e24fbaf Mon Sep 17 00:00:00 2001 From: Petri Lammi Date: Wed, 22 Mar 2023 00:18:53 +0200 Subject: [PATCH 21/27] More troubleshooting Signed-off-by: Petri Lammi --- lib/puppet/functions/wazuh/apihelper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/functions/wazuh/apihelper.rb b/lib/puppet/functions/wazuh/apihelper.rb index 2ce9aa7f..84ef28f4 100644 --- a/lib/puppet/functions/wazuh/apihelper.rb +++ b/lib/puppet/functions/wazuh/apihelper.rb @@ -45,8 +45,8 @@ def agent_status Puppet.err("agent_status") if agent_exists? status = retrieve_agent_status - Puppet.err(status) - status + Puppet.err("got status: #{status}") + !status.nil? ? status : 'unknown' else 'non_existent' end From f268c0e5ba4b83b847b2e1ab1cfe3b6f98430eb9 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Wed, 22 Mar 2023 00:38:21 +0200 Subject: [PATCH 22/27] Still troubleshooting Signed-off-by: Kibahop --- lib/puppet/functions/wazuh/apihelper.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/puppet/functions/wazuh/apihelper.rb b/lib/puppet/functions/wazuh/apihelper.rb index 84ef28f4..dd9a5cce 100644 --- a/lib/puppet/functions/wazuh/apihelper.rb +++ b/lib/puppet/functions/wazuh/apihelper.rb @@ -192,7 +192,8 @@ def retrieve_agent_status begin data = !JSON.parse(res.body).nil? ? JSON.parse(res.body) : nil Puppet.err("WAZUH: got 200, data: #{data}") - status = data['data']['affected_items'][0]['status'].nil? unless data.nil? + status = data['data']['affected_items'][0]['status'] unless data.nil? + !status.nil? ? status.to_s : 'unknown' rescue StandardError => e Puppet.err("WAZUH: data hash parsing eneded up with: #{e.message}") 'non_existent' From 655c6b7e05493c800ffca39bdd37c76a7a6883c2 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Wed, 22 Mar 2023 20:24:30 +0200 Subject: [PATCH 23/27] Also loosen agent_name in utils defined types to String Signed-off-by: Kibahop --- manifests/utils/agent_exists.pp | 2 +- manifests/utils/api_agent_remove.pp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/manifests/utils/agent_exists.pp b/manifests/utils/agent_exists.pp index 013c5fa8..faf128d7 100644 --- a/manifests/utils/agent_exists.pp +++ b/manifests/utils/agent_exists.pp @@ -32,7 +32,7 @@ String $api_username, String $api_password, Stdlib::Host $api_host, - Stdlib::Host $api_agent_name, + String $api_agent_name, String $api_check_states = 'all', Stdlib::Port $api_host_port = 55000, ) { diff --git a/manifests/utils/api_agent_remove.pp b/manifests/utils/api_agent_remove.pp index 65689191..19d2f7e8 100644 --- a/manifests/utils/api_agent_remove.pp +++ b/manifests/utils/api_agent_remove.pp @@ -37,7 +37,7 @@ String $api_username, String $api_password, Stdlib::Host $api_host, - Stdlib::Host $api_agent_name, + String $api_agent_name, Stdlib::Port $api_host_port = 55000, Enum['active', 'pending', 'disconnected', 'never_connected', 'all'] $api_check_states = 'all', ) { From f6f2cf6f49807180547e46ca7d1565580849dadf Mon Sep 17 00:00:00 2001 From: Kibahop Date: Wed, 22 Mar 2023 21:12:04 +0200 Subject: [PATCH 24/27] Fix wrong hash for wazuh::utils::agent_name Signed-off-by: Kibahop --- manifests/utils/agent_supervise.pp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/manifests/utils/agent_supervise.pp b/manifests/utils/agent_supervise.pp index 9e39b4f7..e2e3950d 100644 --- a/manifests/utils/agent_supervise.pp +++ b/manifests/utils/agent_supervise.pp @@ -68,8 +68,9 @@ } # reauth wazuh::utils::agent_name { "${profile::wazuh_agent::wazuh_agent_name}_supervise_reauth": - * => $local_api_hash, - require => Wazuh::Utils::Api_agent_remove["${profile::wazuh_agent::wazuh_agent_name}_supervise_remove"], + * => $profile::wazuh_agent::agent_params_hash, + agent_name => $profile::wazuh_agent::wazuh_agent_name, + require => Wazuh::Utils::Api_agent_remove["${profile::wazuh_agent::wazuh_agent_name}_supervise_remove"], } } } From eaf90a49cea9a3c008019f91bf5f0f0736883012 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Wed, 22 Mar 2023 21:36:12 +0200 Subject: [PATCH 25/27] Fix impossible default for a parameter Signed-off-by: Kibahop --- manifests/utils/agent_name.pp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifests/utils/agent_name.pp b/manifests/utils/agent_name.pp index a069d229..58cf0cfc 100644 --- a/manifests/utils/agent_name.pp +++ b/manifests/utils/agent_name.pp @@ -9,7 +9,7 @@ Optional[String] $wazuh_enrollment_auth_pass, String $agent_auth_password, Stdlib::Port $ossec_port, - String $agent_name = $agent_name, + String $agent_name, Optional[Boolean] $wazuh_enrollment_enabled = undef, Optional[Stdlib::Host] $wazuh_reporting_endpoint = undef, Optional[String] $agent_package_version = undef, From 4376c5a239daff3458a495a1fa31bcd8ccfb70f1 Mon Sep 17 00:00:00 2001 From: Kibahop Date: Wed, 22 Mar 2023 23:43:54 +0200 Subject: [PATCH 26/27] update utils Signed-off-by: Kibahop --- manifests/utils/agent_supervise.pp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/manifests/utils/agent_supervise.pp b/manifests/utils/agent_supervise.pp index e2e3950d..7bea4d8f 100644 --- a/manifests/utils/agent_supervise.pp +++ b/manifests/utils/agent_supervise.pp @@ -28,8 +28,12 @@ ) { # do not supervise if name has been changed - # TODO: cover all meaninfull changes - unless $profile::wazuh_agent::agent_name_changed { + # TODO: cover all meaninful changes + unless ($profile::wazuh_agent::agent_name_changed or + $profile::wazuh_agent::server_name_changed or + $profile::wazuh_agent::password_changed or + $profile::wazuh_agent::server_port_changed) + { # Restart local agent if any of these conditions are true if ($facts['wazuh']['state']['status'] == $when['control_status'] or @@ -38,7 +42,7 @@ warning("WAZUH: Agent ${profile::wazuh_agent::wazuh_agent_name} has lost touch with the server, restarting...") - wazuh::utils::agent_actions { "Agent ${profile::wazuh_agent::wazuh_agent_name} has lost touch with the server, restarting...": + wazuh::utils::agent_actions { "Agent ${profile::wazuh_agent::wazuh_agent_name} restarting...": action => 'restart', } } From 4a3554a488822640fde8b35c3b2aa70e6dc9fade Mon Sep 17 00:00:00 2001 From: Kibahop Date: Thu, 23 Mar 2023 03:08:01 +0200 Subject: [PATCH 27/27] Add option to make remote checking optional and default to false. Signed-off-by: Kibahop --- manifests/utils/agent_supervise.pp | 73 +++++++++++++++--------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/manifests/utils/agent_supervise.pp b/manifests/utils/agent_supervise.pp index 7bea4d8f..b9bf59a1 100644 --- a/manifests/utils/agent_supervise.pp +++ b/manifests/utils/agent_supervise.pp @@ -25,59 +25,58 @@ # define wazuh::utils::agent_supervise( Hash $when, + Boolean $agent_name_changed, + Boolean $server_name_changed, + Boolean $password_changed, + Boolean $server_port_changed, + Boolean $check_remote_state = false, ) { - # do not supervise if name has been changed - # TODO: cover all meaninful changes - unless ($profile::wazuh_agent::agent_name_changed or - $profile::wazuh_agent::server_name_changed or - $profile::wazuh_agent::password_changed or - $profile::wazuh_agent::server_port_changed) - { + # do not supervise if there are changes + unless ($agent_name_changed or $server_name_changed or $password_changed or $server_port_changed) { # Restart local agent if any of these conditions are true if ($facts['wazuh']['state']['status'] == $when['control_status'] or - $facts['wazuh']['state']['last_keepalive_since'] > $when['last_keepalive_since'] or - $facts['wazuh']['state']['last_ack_since'] > $when['last_ack_since']) { + $facts['wazuh']['state']['last_keepalive_since'] > $when['last_keepalive_since'] or + $facts['wazuh']['state']['last_ack_since'] > $when['last_ack_since']) { - warning("WAZUH: Agent ${profile::wazuh_agent::wazuh_agent_name} has lost touch with the server, restarting...") + warning("WAZUH: Agent ${profile::wazuh_agent::wazuh_agent_name} has lost touch with the server, restarting...") - wazuh::utils::agent_actions { "Agent ${profile::wazuh_agent::wazuh_agent_name} restarting...": - action => 'restart', - } - } + wazuh::utils::agent_actions { "Agent ${profile::wazuh_agent::wazuh_agent_name} restarting...": + action => 'restart', + } + } - # - # TODO: replace this with a loop (function) - # - if $facts.dig('wazuh', 'state', 'status') != undef { + if $check_remote_state { - if $facts.dig('wazuh', 'agent', 'name') == $profile::wazuh_agent::wazuh_agent_name { + if $facts.dig('wazuh', 'state', 'status') != undef { - $_name = $profile::wazuh_agent::wazuh_agent_name - if ! $profile::wazuh_agent::api_hash.has_key('api_agent_name') { - $local_api_hash = $profile::wazuh_agent::api_hash.merge({ 'api_agent_name' => $_name }) - } + if $facts.dig('wazuh', 'agent', 'name') == $profile::wazuh_agent::wazuh_agent_name { - $agent_local_state = assert_type(String[1], $facts.dig('wazuh', 'state', 'status')) - $agent_remote_state = assert_type(String[1], wazuh::api_agent_status($local_api_hash)) + $_name = $profile::wazuh_agent::wazuh_agent_name + if ! $profile::wazuh_agent::api_hash.has_key('api_agent_name') { + $local_api_hash = $profile::wazuh_agent::api_hash.merge({ 'api_agent_name' => $_name }) + } - if ($agent_local_state != 'connected') and ($agent_remote_state != 'active') { + $agent_local_state = assert_type(String[1], $facts.dig('wazuh', 'state', 'status')) + $agent_remote_state = assert_type(String[1], wazuh::api_agent_status($local_api_hash)) - warning("WAZUH: local and remote state for ${profile::wazuh_agent::wazuh_agent_name} disagree about their state") + if ($agent_local_state != 'connected') and ($agent_remote_state != 'active') { - # remove current name from the manager - wazuh::utils::api_agent_remove { "${profile::wazuh_agent::wazuh_agent_name}_supervise_remove": - * => $local_api_hash, - } - # reauth - wazuh::utils::agent_name { "${profile::wazuh_agent::wazuh_agent_name}_supervise_reauth": - * => $profile::wazuh_agent::agent_params_hash, - agent_name => $profile::wazuh_agent::wazuh_agent_name, - require => Wazuh::Utils::Api_agent_remove["${profile::wazuh_agent::wazuh_agent_name}_supervise_remove"], - } + warning("WAZUH: local and remote state for ${profile::wazuh_agent::wazuh_agent_name} disagree about their state") + # remove current name from the manager + wazuh::utils::api_agent_remove { "${profile::wazuh_agent::wazuh_agent_name}_supervise_remove": + * => $local_api_hash, + } + # reauth + wazuh::utils::agent_name { "${profile::wazuh_agent::wazuh_agent_name}_supervise_reauth": + * => $profile::wazuh_agent::agent_params_hash, + agent_name => $profile::wazuh_agent::wazuh_agent_name, + require => Wazuh::Utils::Api_agent_remove["${profile::wazuh_agent::wazuh_agent_name}_supervise_remove"], } } } + } + } } }