wip omnishell, command executor and directories

This commit is contained in:
fbsanchez 2019-11-08 17:07:14 +01:00
parent c78e3f4184
commit 2daf473ba9
12 changed files with 293 additions and 2 deletions

View File

@ -36,6 +36,8 @@ mkdir -p temp_package/usr/bin/
mkdir -p temp_package/usr/sbin/
mkdir -p temp_package/etc/pandora/plugins
mkdir -p temp_package/etc/pandora/collections
mkdir -p temp_package/etc/pandora/trans
mkdir -p temp_package/etc/pandora/commands
mkdir -p temp_package/etc/init.d/
mkdir -p temp_package/var/log/pandora/
mkdir -p temp_package/usr/share/man/man1/

View File

@ -324,6 +324,11 @@ install () {
cp -r collections $PANDORA_BASE$PANDORA_HOME
chmod -R 700 $PANDORA_BASE$PANDORA_HOME/collections
ln -s $PANDORA_BASE$PANDORA_HOME/collections $PANDORA_BASE$PANDORA_CFG
echo "Copying Pandora FMS Agent commands to $PANDORA_BASE$PANDORA_HOME/commands..."
cp -r commands $PANDORA_BASE$PANDORA_HOME
chmod -R 700 $PANDORA_BASE$PANDORA_HOME/commands
ln -s $PANDORA_BASE$PANDORA_HOME/commands $PANDORA_BASE$PANDORA_CFG
echo "Copying tentacle server to $PANDORA_BASE$TENTACLE_SERVER"
cp tentacle_server $PANDORA_BASE$TENTACLE_SERVER

View File

@ -36,6 +36,8 @@ mkdir -p temp_package/usr/bin/
mkdir -p temp_package/usr/sbin/
mkdir -p temp_package/etc/pandora/plugins
mkdir -p temp_package/etc/pandora/collections
mkdir -p temp_package/etc/pandora/trans
mkdir -p temp_package/etc/pandora/commands
mkdir -p temp_package/etc/init.d/
mkdir -p temp_package/lib/systemd/system/
mkdir -p temp_package/var/log/pandora/

View File

@ -32,6 +32,18 @@ use IO::Socket;
use Sys::Syslog;
use Time::Local;
my $YAML = 0;
# Dynamic load. Avoid unwanted behaviour.
eval {
eval 'require YAML::Tiny;1' or die('YAML::Tiny lib not found, commands feature won\'t be available');
};
if ($@) {
$YAML = 0;
print STDERR $@;
} else {
$YAML = 1;
}
# Agent XML data
my $Xml;
@ -187,6 +199,7 @@ my %DefaultConf = (
'custom_id' => '',
'url_address' => '',
'standby' => 0,
'cmd_file' => undef,
);
my %Conf = %DefaultConf;
@ -284,6 +297,28 @@ sub valid_regexp ($) {
return 1;
}
################################################################################
# Reads a file and returns entire content or undef if error.
################################################################################
sub read_file {
my $path = shift;
my $_FILE;
if( !open($_FILE, "<".$path) ) {
print ">> Failed to open $path ".`whoami`."\n";
# failed to open, return undef
return undef;
}
# Slurp configuration file content.
my $content = do { local $/; <$_FILE> };
# Close file
close($_FILE);
return $content;
}
################################################################################
# Recursively delete files and directories.
################################################################################
@ -682,6 +717,13 @@ sub parse_conf_modules($) {
# Macros
} elsif ($line =~ /^\s*module_macro(\S+)\s+(.*)\s*$/) {
$module->{'macros'}{$1} = $2;
# Commands
} elsif ($line =~ /^\s*cmd_file\s+(.+)$/) {
if (ref ($Conf{'commands'}) ne "HASH") {
$Conf{'commands'} = {};
}
# Initialize empty command hash.
$Conf{'commands'}->{$1} = {};
}
}
return;
@ -1362,6 +1404,185 @@ sub check_collections () {
}
}
################################################################################
# Check for remote commands defined.
################################################################################
sub prepare_remote_commands {
if ($YAML == 0) {
log_message(
'error',
'Cannot use commands without YAML dependency, please install it.'
);
return;
}
foreach my $ref (keys %{$Conf{'commands'}}) {
my $file_content;
my $download = 0;
my $rcmd_file = $ConfDir.'/commands/'.$ref.'.rcmd';
# Check for local .rcmd.done files
if (-e $Conf{'temporal'}.'/'.$ref.'.rcmd.done') {
# Ignore.
delete $Conf{'commands'}->{$ref};
next;
}
# Search for local .rcmd file
if (-e $rcmd_file) {
my $remote_md5_file = $Conf{'temporal'}.'/'.$ref.'.md5';
$file_content = read_file($rcmd_file);
if (recv_file($ref.'.md5', $remote_md5_file) != 0) {
# Remote file could not be retrieved, skip.
delete $Conf{'commands'}->{$ref};
next;
}
my $local_md5 = md5($file_content);
my $remote_md5 = md5(read_file($remote_md5_file));
if ($local_md5 ne $remote_md5) {
# Must be downloaded again.
$download = 1;
}
} else {
$download = 1;
}
# Search for remote .rcmd file
if ($download == 1) {
# Download .rcmd file
if (recv_file($ref.'.rcmd') != 0) {
# Remote file could not be retrieved, skip.
delete $Conf{'commands'}->{$ref};
next;
} else {
# Success
move($Conf{'temporal'}.'/'.$ref.'.rcmd', $rcmd_file);
}
}
# Parse and prepare in memory skel.
eval {
$Conf{'commands'}->{$ref} = YAML::Tiny->read($rcmd_file);
};
if ($@) {
# Failed.
log_message('error', 'Failed to decode command. ' . $@);
delete $Conf{'commands'}->{$ref};
next;
}
}
}
################################################################################
# Command report.
################################################################################
sub report_command {
my ($ref, $err_level) = @_;
# Retrieve content from .stdout and .stderr
my $stdout_file = $Conf{'temporal'}.'/'.$ref.'.stdout';
my $stderr_file = $Conf{'temporal'}.'/'.$ref.'.stderr';
my $return;
eval {
$return = {
'error_level' => $err_level,
'stdout' => read_file($stdout_file),
'stderr' => read_file($stderr_file),
};
$return->{'name'} = $Conf{'commands'}->{$ref}->[0]->{'name'};
};
if ($@) {
log_message('error', 'Failed to report command output. ' . $@);
}
# Cleanup
unlink($stdout_file) if (-e $stdout_file);
unlink($stderr_file) if (-e $stderr_file);
# Mark command as done.
open (my $R_FILE, '> '.$ConfDir.'/commands/'.$ref.'.rcmd.done');
print $R_FILE $err_level;
close($R_FILE);
return $return;
}
################################################################################
# Executes a block of commands, returns error level, leaves output in
# redirection set by $std_files. E.g:
# $std_files = ' >> /tmp/stdout 2>> /tmp/stderr
################################################################################
sub execute_command_block {
my ($commands, $std_files) = @_;
$std_files = '' unless defined ($std_files);
return 0 if ref($commands) ne "ARRAY";
my $err_level;
foreach my $comm (@{$commands}) {
`($comm) $std_files`;
$err_level = $?>>8;
last unless ($err_level == 0);
}
return $err_level;
}
################################################################################
# Evalate given command.
################################################################################
sub evaluate_command {
my ($ref) = @_;
# Not found.
return unless defined $Conf{'commands'}->{$ref};
# Already completed.
return if (-e $ConfDir.'/commands/'.$ref.'.rcmd.done');
# [0] because how library works.
my $cmd = $Conf{'commands'}->{$ref}->[0];
my $std_files = ' >> '.$Conf{'temporal'}.'/'.$ref.'.stdout ';
$std_files .= ' 2>> '.$Conf{'temporal'}.'/'.$ref.'.stderr ';
# Check preconditions
my $err_level;
$err_level = execute_command_block(
$cmd->{'preconditions'},
$std_files
);
# Precondition not satisfied.
return report_command($ref, $err_level) unless ($err_level == 0);
# Main run.
$err_level = execute_command_block(
$cmd->{'script'},
$std_files
);
# Script not success.
return report_command($ref, $err_level) unless ($err_level == 0);
# Check postconditions
$err_level = execute_command_block(
$cmd->{'postconditions'},
$std_files
);
# Return results.
return report_command($ref, $err_level);
}
################################################################################
# Sleep function
################################################################################
@ -2999,6 +3220,25 @@ while (1) {
# Check file collections
check_collections () unless ($Conf{'debug'} eq '1');
if ($Conf{'debug'} ne '1') {
# Check remote commands
prepare_remote_commands ();
# Cleanup old .rcmd.done files.
my %registered = map { $_.'.rcmd.done' => 1 } keys %{$Conf{'commands'}};
if(opendir(my $dir, $ConfDir.'/commands/')) {
while (my $item = readdir($dir)) {
# Skip other files.
next if ($item !~ /\.rcmd\.done$/);
# Clean .rcmd.done file if its command is not referenced in conf.
if (!defined($registered{$item})) {
unlink ($item);
}
}
}
}
# Launch broker agents
@BrokerPid = ();
my @broker_agents = read_config ('broker_agent');
@ -3128,6 +3368,23 @@ while (1) {
}
}
if (ref ($Conf{'commands'}) eq "HASH") {
foreach my $command (keys %{$Conf{'commands'}}) {
my $result = evaluate_command($command);
if (ref($result) eq "HASH") {
# Process command result.
$Xml .= "<cmd_report>\n";
$Xml .= " <cmd_response>\n";
$Xml .= " <cmd_name><![CDATA[".$result->{'name'}."]]></cmd_name>\n";
$Xml .= " <cmd_errorlevel><![CDATA[".$result->{'error_level'}."]]></cmd_errorlevel>\n";
$Xml .= " <cmd_stdout><![CDATA[".$result->{'stdout'}."]]></cmd_stdout>\n";
$Xml .= " <cmd_stderr><![CDATA[".$result->{'stderr'}."]]></cmd_sterr>\n";
$Xml .= " </cmd_response>\n";
$Xml .= "</cmd_report>\n";
}
}
}
# Wait for all the threads
$ThreadSem->down ($Conf{'agent_threads'}) if (defined ($ThreadSem) && $Conf{'agent_threads'} > 1);
$ThreadSem->up ($Conf{'agent_threads'}) if (defined ($ThreadSem) && $Conf{'agent_threads'} > 1);

View File

@ -99,6 +99,11 @@ if [ ! -e /etc/pandora/collections ]; then
ln -s /usr/share/pandora_agent/collections /etc/pandora
fi
if [ ! -e /etc/pandora/commands ]; then
mkdir -p /usr/share/pandora_agent/commands
ln -s /usr/share/pandora_agent/commands /etc/pandora
fi
mkdir -p /var/spool/pandora/data_out
if [ ! -d /var/log/pandora ]; then
mkdir -p /var/log/pandora

View File

@ -90,6 +90,11 @@ fi
if [ ! -e /etc/pandora/collections ]; then
mkdir /etc/pandora/collections
fi
if [ ! -e /etc/pandora/commands ]; then
mkdir /etc/pandora/commands
fi
cp -aRf /usr/share/pandora_agent/pandora_agent_logrotate /etc/logrotate.d/pandora_agent
# Enable the service on SystemD

View File

@ -400,6 +400,11 @@ install () {
mkdir -p $PANDORA_BASE$PANDORA_HOME/collections
chmod -R 700 $PANDORA_BASE$PANDORA_HOME/collections
ln -s $PANDORA_BASE_REAL$PANDORA_HOME/collections $PANDORA_BASE$PANDORA_CFG
echo "Creating the commands directory in $PANDORA_BASE$PANDORA_HOME/commands..."
mkdir -p $PANDORA_BASE$PANDORA_HOME/commands
chmod -R 700 $PANDORA_BASE$PANDORA_HOME/commands
ln -s $PANDORA_BASE_REAL$PANDORA_HOME/commands $PANDORA_BASE$PANDORA_CFG
if [ $WITHOUT_TENTACLE_SERVER -eq 0 ]
then

View File

@ -80,7 +80,9 @@ then
mkdir -p temp_package/var/spool/pandora/data_in/netflow
chmod 770 temp_package/var/spool/pandora/data_in/netflow
mkdir -p temp_package/var/spool/pandora/data_in/trans
chmod 770 temp_package/var/spool/pandora/data_in/trans
chmod 770 temp_package/var/spool/pandora/data_in/trans
mkdir -p temp_package/var/spool/pandora/data_in/commands
chmod 770 temp_package/var/spool/pandora/data_in/commands
mkdir -p temp_package/var/log/pandora/
chmod 754 temp_package/var/log/pandora/
mkdir -p temp_package/usr/share/pandora_server/conf/

View File

@ -21,7 +21,7 @@ daemon 1
# insecure 0
# Filters (regexp:dir;regexp:dir...)
filters .*\.conf:conf;.*\.md5:md5;.*\.zip:collections;.*\.lock:trans
filters .*\.conf:conf;.*\.md5:md5;.*\.zip:collections;.*\.lock:trans;.*\.rcmd:commands
# [-m] Maximum file size allowed by the server in bytes
#max_size 2000000

View File

@ -54,6 +54,8 @@ mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/spool/pandora/data_in/conf
mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/spool/pandora/data_in/md5
mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/spool/pandora/data_in/collections
mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/spool/pandora/data_in/netflow
mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/spool/pandora/data_in/trans
mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/spool/pandora/data_in/commands
mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/log/pandora/
mkdir -p $RPM_BUILD_ROOT%{prefix}/pandora_server/conf/
mkdir -p $RPM_BUILD_ROOT%{_mandir}/man1/
@ -195,3 +197,5 @@ exit 0
%{_localstatedir}/spool/pandora/data_in/collections
%{_localstatedir}/spool/pandora/data_in/conf
%{_localstatedir}/spool/pandora/data_in/netflow
%{_localstatedir}/spool/pandora/data_in/trans
%{_localstatedir}/spool/pandora/data_in/commands

View File

@ -61,6 +61,7 @@ mkdir -p $RPM_BUILD_ROOT/var/spool/pandora/data_in/md5
mkdir -p $RPM_BUILD_ROOT/var/spool/pandora/data_in/collections
mkdir -p $RPM_BUILD_ROOT/var/spool/pandora/data_in/netflow
mkdir -p $RPM_BUILD_ROOT/var/spool/pandora/data_in/trans
mkdir -p $RPM_BUILD_ROOT/var/spool/pandora/data_in/commands
mkdir -p $RPM_BUILD_ROOT/var/log/pandora/
mkdir -p $RPM_BUILD_ROOT%{prefix}/pandora_server/conf/
mkdir -p $RPM_BUILD_ROOT%{prefix}/tentacle/conf/
@ -198,6 +199,7 @@ rm -Rf /usr/share/man/man1/tentacle_server.1.gz
/var/spool/pandora/data_in/netflow
/var/spool/pandora/data_in/conf
/var/spool/pandora/data_in/trans
/var/spool/pandora/data_in/commands
%defattr(-,pandora,root,750)
/etc/pandora

View File

@ -315,6 +315,8 @@ install () {
chmod 2770 $DESTDIR$PANDORA_SPOOL/data_in/netflow
mkdir $DESTDIR$PANDORA_SPOOL/data_in/trans 2> /dev/null
chmod 2770 $DESTDIR$PANDORA_SPOOL/data_in/trans
mkdir $DESTDIR$PANDORA_SPOOL/data_in/commands 2> /dev/null
chmod 2770 $DESTDIR$PANDORA_SPOOL/data_in/commands
mkdir -p $DESTDIR$PANDORA_LOG 2> /dev/null
chown -R pandora $DESTDIR$PANDORA_LOG 2> /dev/null
chmod 2774 $DESTDIR$PANDORA_LOG 2> /dev/null