Added files:
ossec-batch-manager.pl
Log message:
Description: ossec-batch-manager.pl started as a quick hack to script the
functionality available in the manage_agents binary. After a
bit of looking into it, it wasn't very hard to clone the
functionality completely in a sane way using perl.
Features:
- Add / Remove / List / Extract Key / Import agent
- Makes it much easier to script ossec from the server and
agent side of things on a mass scale.
- perl is sexy
Examples:
./ossec-batch-manager.pl -l # List all available agents
# Add a new agent. The key and id will autogenerate unless
# they are specified with the --id and --key options
./ossec-batch-manager.pl -a --name agent02 --ip 10.75.25.42
./ossec-batch-manager.pl -a --name agent02 --ip 10.75.25.42 --key '5a832efb8f93660857ce2acf8eec66a19fd9d4fa58e3221bbd2927ca8a0b40c3' --id 001
./ossec-batch-manager.pl -e 001 # Extract the key of 001
# Import a key extracted from the server onto an agent
./ossec-batch-manager.pl -m -n agent02 -p 10.75.25.42 -i 10.75.25.42 -k 'NWE4MzJlZmI4ZjkzNjYwODU3Y2UyYWNmOGVlYzY2YTE5ZmQ5ZDRmYTU4ZTMyMjFiYmQyOTI3Y2E4YTBiNDBjMw=='
./ossec-batch-manager.pl -r 001 # Remove agent 001
Contact jeffsc...@computer.org with any problems or feature requests.
-jeff schroeder (SEJeff on irc)
Reviewed by: Daniel Cid
--- NEW FILE: ossec-batch-manager.pl ---
#!/usr/bin/perl
# vim:shiftwidth=2:tabstop=2:expandtab:textwidth=80:softtabstop=2:ai:
#########################################################
# Written Aug 4, 2007 and released under the GNU/GPLv2 ##
# by Jeff Schroeder (jeffsc...@computer.org) # #
######################################################### #
# # #
# ossec-batch-manager.pl - Add and extract agents from # #
# the ossec client.keys file non-interactively. This # #
# started as a hack to properly script manage_agents. # #
# # #
##########################################################
#$Id: ossec-batch-manager.pl,v 1.1 2007/08/08 00:03:18 jeff Exp $
use strict;
use warnings;
require 5.8.2; # Time::HiRes is standard from this version forth
#use diagnostics;
use MIME::Base64;
use Digest::MD5 qw(md5_hex);
use Getopt::Long;
use constant AUTH_KEY_FILE => "/var/ossec/etc/client.keys";
my ($key, $add, $remove, $extract, $import, $listagents);
my ($agentid, $agentname, $ipaddress);
GetOptions(
'k|key=s' => \$key, # Unencoded ssh key
'a|add' => \$add, # Add a new agent
'r|remove=s' => \$remove, # Remove an agent
'e|extract=s' => \$extract, # Extract a key
'm|import' => \$import, # Import a key
'l|list' => \$listagents, # List all agents
'i|id=s' => \$agentid, # Unique agent id
'n|name=s' => \$agentname, # Agent name. 32 char max
'p|ip=s' => \$ipaddress # IP Address in "dotted quad" notation
);
# Spit out a list of available agents, their names, and ip information
if ($listagents) {
print "Available agents:\n";
list_agents();
}
# Decode and extract the key for $agentid
elsif ($extract) {
$agentid = $extract;
if ($agentid) {
extract_key($agentid);
}
else {
usage();
}
}
# Adding a new agent
elsif ($add) {
if ($agentname && $ipaddress && $ipaddress =~
m/(1?\d\d?|2[0-4]\d|25[0-5])(\.(1?\d\d?|2[0-4]\d|25[0-5])){3}/ &&
# ossec doesn't like agent names > 32 characters.
length($agentname) <= 32) {
# Autogenerate an id incremented 1 from the last in a sorted list of
# all current ones if it isn't specified from the command line.
if (!$agentid) {
my @used_agent_ids = ();
# Make a list of all of the used agentids and then sort it.
open (FH, "<", AUTH_KEY_FILE) or die "Error: $!\n";
while (<FH>) {
my ($id, $name, $ip, $key) = split;
push(@used_agent_ids, $id);
}
close(FH);
if (@used_agent_ids) {
@used_agent_ids = sort(@used_agent_ids);
$agentid = sprintf("%03d", $used_agent_ids[-1] + 1);
}
else {
# If the client.keys is empty, create the first entry
$agentid = sprintf("%03d", 001);
}
}
# Autogenerate a key unless one was specified on the command line
if (!$key) {
use Time::HiRes; # Standard with perl >= 5.8.2
my $rand_str1 = time() . $agentname . rand(10000);
my $rand_str2 = Time::HiRes::time . $ipaddress . $agentid . rand(10000);
$key = md5_hex($rand_str1) . md5_hex($rand_str2);
}
add_agent($agentid, $agentname, $ipaddress, $key);
}
else {
warn "Error: adding agents requires: --name and --ip options.\n";
usage();
}
}
elsif ($remove) {
if ($agentid) {
remove_agent($agentid);
}
else {
remove_agent($remove)
}
}
elsif ($import) {
# Every option needs to be specified and NOT autogenerated because what
# is autogenerated on the server and the agent will likely be different
if (!$agentid || !$agentname || !$ipaddress || !$key) {
warn "Error: importing requires: --id, --name, --ip, and --key\n";
usage();
}
else {
# The key extracted from the server needs to be decoded before being put
# into the client.keys
$key = MIME::Base64::decode($key);
add_agent($agentid, $agentname, $ipaddress, $key);
}
}
else {
warn "Error: no options specified!\n";
usage();
}
sub usage {
warn "Usage: $0 [OPERATION] [OPTIONS]\n";
warn " [operations]\n";
warn " -a or --add = Add a new agent\n";
warn " -r or --remove [id] = Remove agent\n";
warn " -e or --extract [id] = Extract key\n";
warn " -m or --import [keydata] = Import key\n";
warn " -l or --list = List available agents\n";
warn " [options]\n";
warn " -k or --key [keydata] = Key data\n";
warn " -n or --name [name] = Agent name (32 character max)\n";
warn " -i or --id [id] = Agent identification (integer)\n";
warn " -p or --ip [ip] = IP address\n\n";
exit 1;
}
sub list_agents {
if (-r AUTH_KEY_FILE) {
open (FH, "<", AUTH_KEY_FILE);
}
else {
die "No ".AUTH_KEY_FILE."!\n";
}
print "ID", " " x (25 - length('ID')),
"NAME", " " x (25 - length('NAME')),
"IP", " " x (25 - length('IP'));
print "\n";
while (<FH>) {
chomp;
my ($id, $name, $ip, $key) = split;
print "$id", " " x (25 - length($id)),
"$name", " " x (25 - length($name)),
"$ip", " " x (25 - length($ip)) . "\n";
}
close(FH);
exit 0;
}
sub extract_key {
my $extractid = shift;
my ($encoded, $decoded);
if (-r AUTH_KEY_FILE) {
open (FH, "<", AUTH_KEY_FILE);
}
else {
die "No ".AUTH_KEY_FILE."!\n";
}
while (<FH>) {
chomp;
my ($id, $name, $ip, $key) = split;
if ($id == $extractid) {
# Newlines are valid base64 characters so use '' instead for \n
$decoded = MIME::Base64::encode($key, '');
print "$decoded\n";
exit 0;
}
}
warn "Error: Agent ID $extractid doesn't exist!\n";
}
sub add_agent {
my $id = shift;
my $name = shift;
my $ip = shift;
my $agentkey = shift;
if ($name && $ip && $agentkey && -e AUTH_KEY_FILE) {
# Valid example key:
# 5a832efb8f93660857ce2acf8eec66a19fd9d4fa58e3221bbd2927ca8a0b40c3
if ($agentkey !~ m/[a-z0-9]{64}/) {
warn "Error: invalid keydata! Let this script autogenerate it.\n";
usage();
}
my @newagent = ($id, $name, $ip, $agentkey);
my $exists = check_if_exists(\@newagent);
if ($exists == 0) {
open (FH, ">>", AUTH_KEY_FILE) or die "Error: $!\n";
print FH join(' ', @newagent), "\n";
close(FH);
}
elsif ($exists == 1) {
warn "ID: $id already in ".AUTH_KEY_FILE."!\n";
}
elsif ($exists == 2) {
warn "Agent: $name already in ".AUTH_KEY_FILE."!\n";
}
elsif ($exists == 3) {
warn "IP: $ip already in ".AUTH_KEY_FILE."!\n";
}
}
else {
warn "Missing options to add agent or problem with ".AUTH_KEY_FILE."!\n";
usage();
}
}
sub remove_agent {
my $removeid = shift;
my @agent_array;
open (FH, "<", AUTH_KEY_FILE) if -e AUTH_KEY_FILE or die "No ".AUTH_KEY_FILE."!\n";
while (<FH>) {
push(@agent_array, $_);
}
close(FH);
open (FHRW, ">", AUTH_KEY_FILE) if -e AUTH_KEY_FILE or die "No ".AUTH_KEY_FILE."!\n";
foreach my $line (@agent_array) {
if ($line !~ $removeid) {
print FHRW "$line";
}
}
close(FHRW);
exit 0;
}
sub import_key {
my $keydata = shift;
warn "Importing keys not implemented yet!\n";
exit 0;
}
sub check_if_exists {
my $agentlist_ref = shift;
my ($newid, $newname, $newip);
my $rval = 0;
$newid = $agentlist_ref->[0];
$newname = $agentlist_ref->[1];
$newip = $agentlist_ref->[2];
open (FH, AUTH_KEY_FILE) if -e AUTH_KEY_FILE or die "No ".AUTH_KEY_FILE."!\n";
while (<FH>) {
chomp;
my ($id, $name, $ip, $key) = split;
$rval = 1 if ($id == $newid && $rval == 0);
$rval = 2 if ($name eq $newname && $rval == 0);
$rval = 3 if ($ip eq $newip && $rval == 0);
}
close(FH);
return $rval;
}
Modified files:
ossec-batch-manager.pl
Log message:
Changes:
- Removed useless import_key sub, --import just calls add_agent anyways
- Changed a few open( statements to multiline where printing the error
message causes the code to be > 80 characters wide
- Fixed some whitespace / tab issues
Bugs fixed:
- When adding an agent and the client.keys doesn't exist, create a
new file instead of complaining it doesn't exist and exiting.
- Change a few areas that open the client.keys to work better when
it is empty, or doesn't exist
Diffstat:
ossec-batch-manager.pl | 95 +++++++++++++++++++++++++++----------------------
1 file changed, 54 insertions(+), 41 deletions(-)
Index: ossec-batch-manager.pl
===================================================================
RCS file: /usr/cvsroot/ossec-hids/contrib/ossec-batch-manager.pl,v
diff -u -r1.1 -r1.2
--- ossec-batch-manager.pl 8 Aug 2007 00:03:18 -0000 1.1
+++ ossec-batch-manager.pl 8 Aug 2007 22:34:47 -0000 1.2
@@ -32,7 +32,7 @@
'a|add' => \$add, # Add a new agent
'r|remove=s' => \$remove, # Remove an agent
'e|extract=s' => \$extract, # Extract a key
- 'm|import' => \$import, # Import a key
+ 'm|import' => \$import, # Import a key
'l|list' => \$listagents, # List all agents
'i|id=s' => \$agentid, # Unique agent id
'n|name=s' => \$agentname, # Agent name. 32 char max
@@ -41,7 +41,6 @@
# Spit out a list of available agents, their names, and ip information
if ($listagents) {
- print "Available agents:\n";
list_agents();
}
# Decode and extract the key for $agentid
@@ -64,25 +63,25 @@
# Autogenerate an id incremented 1 from the last in a sorted list of
# all current ones if it isn't specified from the command line.
if (!$agentid) {
- my @used_agent_ids = ();
# Make a list of all of the used agentids and then sort it.
- open (FH, "<", AUTH_KEY_FILE) or die "Error: $!\n";
- while (<FH>) {
- my ($id, $name, $ip, $key) = split;
- push(@used_agent_ids, $id);
+ if (-r AUTH_KEY_FILE) {
+ my @used_agent_ids = ();
+ open (FH, "<", AUTH_KEY_FILE);
+ while (<FH>) {
+ my ($id, $name, $ip, $key) = split;
+ push(@used_agent_ids, $id);
+ }
+ close(FH);
+
+ if (@used_agent_ids) {
+ @used_agent_ids = sort(@used_agent_ids);
+ $agentid = sprintf("%03d", $used_agent_ids[-1] + 1);
+ }
+ }
+ # If the client.keys is empty or doesn't exist set the id to 001
+ $agentid = sprintf("%03d", 001) if (!$agentid);
}
- close(FH);
-
- if (@used_agent_ids) {
- @used_agent_ids = sort(@used_agent_ids);
- $agentid = sprintf("%03d", $used_agent_ids[-1] + 1);
- }
- else {
- # If the client.keys is empty, create the first entry
- $agentid = sprintf("%03d", 001);
- }
- }
# Autogenerate a key unless one was specified on the command line
if (!$key) {
@@ -149,11 +148,12 @@
open (FH, "<", AUTH_KEY_FILE);
}
else {
- die "No ".AUTH_KEY_FILE."!\n";
+ die "Error reading ".AUTH_KEY_FILE.": $!\n";
}
- print "ID", " " x (25 - length('ID')),
- "NAME", " " x (25 - length('NAME')),
- "IP", " " x (25 - length('IP'));
+ print "Available Agents:\n";
+ print "ID", " " x (25 - length('ID')),
+ "NAME", " " x (25 - length('NAME')),
+ "IP", " " x (25 - length('IP'));
print "\n";
while (<FH>) {
chomp;
@@ -195,7 +195,7 @@
my $ip = shift;
my $agentkey = shift;
- if ($name && $ip && $agentkey && -e AUTH_KEY_FILE) {
+ if ($name && $ip && $agentkey) {
# Valid example key:
# 5a832efb8f93660857ce2acf8eec66a19fd9d4fa58e3221bbd2927ca8a0b40c3
if ($agentkey !~ m/[a-z0-9]{64}/) {
@@ -207,7 +207,13 @@
my $exists = check_if_exists(\@newagent);
if ($exists == 0) {
- open (FH, ">>", AUTH_KEY_FILE) or die "Error: $!\n";
+ # Append if client.keys exists and create it if it doesn't
+ if (-e AUTH_KEY_FILE) {
+ open(FH, ">>", AUTH_KEY_FILE) or die AUTH_KEY_FILE." error: $!\n";
+ }
+ else {
+ open(FH, ">", AUTH_KEY_FILE) or die AUTH_KEY_FILE." error: $!\n";
+ }
print FH join(' ', @newagent), "\n";
close(FH);
}
@@ -222,7 +228,7 @@
}
}
else {
- warn "Missing options to add agent or problem with ".AUTH_KEY_FILE."!\n";
+ warn "Missing options to --add or problem with ".AUTH_KEY_FILE.": $!\n";
usage();
}
}
@@ -231,13 +237,23 @@
my $removeid = shift;
my @agent_array;
- open (FH, "<", AUTH_KEY_FILE) if -e AUTH_KEY_FILE or die "No ".AUTH_KEY_FILE."!\n";
+ if (-r AUTH_KEY_FILE) {
+ open (FH, "<", AUTH_KEY_FILE);
+ }
+ else {
+ die "Error: with ".AUTH_KEY_FILE.": $!\n";
+ }
while (<FH>) {
push(@agent_array, $_);
}
close(FH);
- open (FHRW, ">", AUTH_KEY_FILE) if -e AUTH_KEY_FILE or die "No ".AUTH_KEY_FILE."!\n";
+ if (-w AUTH_KEY_FILE) {
+ open (FHRW, ">", AUTH_KEY_FILE);
+ }
+ else {
+ die "Error writing ".AUTH_KEY_FILE.": $!\n";
+ }
foreach my $line (@agent_array) {
if ($line !~ $removeid) {
print FHRW "$line";
@@ -247,12 +263,6 @@
exit 0;
}
-sub import_key {
- my $keydata = shift;
- warn "Importing keys not implemented yet!\n";
- exit 0;
-}
-
sub check_if_exists {
my $agentlist_ref = shift;
my ($newid, $newname, $newip);
@@ -262,14 +272,17 @@
$newname = $agentlist_ref->[1];
$newip = $agentlist_ref->[2];
- open (FH, AUTH_KEY_FILE) if -e AUTH_KEY_FILE or die "No ".AUTH_KEY_FILE."!\n";
- while (<FH>) {
- chomp;
- my ($id, $name, $ip, $key) = split;
- $rval = 1 if ($id == $newid && $rval == 0);
- $rval = 2 if ($name eq $newname && $rval == 0);
- $rval = 3 if ($ip eq $newip && $rval == 0);
+ # If the file isn't readable, the id probably isn't already in it
+ if (-r AUTH_KEY_FILE) {
+ open (FH, "<", AUTH_KEY_FILE);
+ while (<FH>) {
+ chomp;
+ my ($id, $name, $ip, $key) = split;
+ $rval = 1 if ($id == $newid && $rval == 0);
+ $rval = 2 if ($name eq $newname && $rval == 0);
+ $rval = 3 if ($ip eq $newip && $rval == 0);
+ }
+ close(FH);
}
- close(FH);
return $rval;
}
Modified files:
ossec-batch-manager.pl
Log message:
This should be the last of the stupid whitespace changes. I started coding
using normal tabs and then used the magic #vim: line and changed it to spaces.
This made things looks inconsistent when viewed in non-vim editors sometimes.
Index: ossec-batch-manager.pl
===================================================================
RCS file: /usr/cvsroot/ossec-hids/contrib/ossec-batch-manager.pl,v
diff -u -r1.2 -r1.3
--- ossec-batch-manager.pl 8 Aug 2007 22:34:47 -0000 1.2
+++ ossec-batch-manager.pl 8 Aug 2007 22:48:33 -0000 1.3
@@ -69,8 +69,8 @@
my @used_agent_ids = ();
open (FH, "<", AUTH_KEY_FILE);
while (<FH>) {
- my ($id, $name, $ip, $key) = split;
- push(@used_agent_ids, $id);
+ my ($id, $name, $ip, $key) = split;
+ push(@used_agent_ids, $id);
}
close(FH);
@@ -145,7 +145,7 @@
sub list_agents {
if (-r AUTH_KEY_FILE) {
- open (FH, "<", AUTH_KEY_FILE);
+ open (FH, "<", AUTH_KEY_FILE);
}
else {
die "Error reading ".AUTH_KEY_FILE.": $!\n";
@@ -155,36 +155,36 @@
"NAME", " " x (25 - length('NAME')),
"IP", " " x (25 - length('IP'));
print "\n";
- while (<FH>) {
- chomp;
- my ($id, $name, $ip, $key) = split;
+ while (<FH>) {
+ chomp;
+ my ($id, $name, $ip, $key) = split;
print "$id", " " x (25 - length($id)),
"$name", " " x (25 - length($name)),
"$ip", " " x (25 - length($ip)) . "\n";
- }
+ }
close(FH);
exit 0;
}
sub extract_key {
- my $extractid = shift;
- my ($encoded, $decoded);
+ my $extractid = shift;
+ my ($encoded, $decoded);
if (-r AUTH_KEY_FILE) {
- open (FH, "<", AUTH_KEY_FILE);
+ open (FH, "<", AUTH_KEY_FILE);
}
else {
die "No ".AUTH_KEY_FILE."!\n";
}
- while (<FH>) {
- chomp;
- my ($id, $name, $ip, $key) = split;
- if ($id == $extractid) {
- # Newlines are valid base64 characters so use '' instead for \n
- $decoded = MIME::Base64::encode($key, '');
- print "$decoded\n";
+ while (<FH>) {
+ chomp;
+ my ($id, $name, $ip, $key) = split;
+ if ($id == $extractid) {
+ # Newlines are valid base64 characters so use '' instead for \n
+ $decoded = MIME::Base64::encode($key, '');
+ print "$decoded\n";
exit 0;
- }
+ }
}
warn "Error: Agent ID $extractid doesn't exist!\n";
}
@@ -238,7 +238,7 @@
my @agent_array;
if (-r AUTH_KEY_FILE) {
- open (FH, "<", AUTH_KEY_FILE);
+ open (FH, "<", AUTH_KEY_FILE);
}
else {
die "Error: with ".AUTH_KEY_FILE.": $!\n";
@@ -249,7 +249,7 @@
close(FH);
if (-w AUTH_KEY_FILE) {
- open (FHRW, ">", AUTH_KEY_FILE);
+ open (FHRW, ">", AUTH_KEY_FILE);
}
else {
die "Error writing ".AUTH_KEY_FILE.": $!\n";
@@ -264,7 +264,7 @@
}
sub check_if_exists {
- my $agentlist_ref = shift;
+ my $agentlist_ref = shift;
my ($newid, $newname, $newip);
my $rval = 0;
@@ -274,15 +274,15 @@
# If the file isn't readable, the id probably isn't already in it
if (-r AUTH_KEY_FILE) {
- open (FH, "<", AUTH_KEY_FILE);
- while (<FH>) {
+ open (FH, "<", AUTH_KEY_FILE);
+ while (<FH>) {
chomp;
my ($id, $name, $ip, $key) = split;
$rval = 1 if ($id == $newid && $rval == 0);
$rval = 2 if ($name eq $newname && $rval == 0);
$rval = 3 if ($ip eq $newip && $rval == 0);
}
- close(FH);
+ close(FH);
}
return $rval;
}
Modified files:
ossec-batch-manager.pl
Log message:
Description: Add a TODO comment for when I get a chance to add these features
Reviewed by: Jeff Schroeder
Bug: N/A
Index: ossec-batch-manager.pl
===================================================================
RCS file: /usr/cvsroot/ossec-hids/contrib/ossec-batch-manager.pl,v
diff -u -r1.3 -r1.4
--- ossec-batch-manager.pl 8 Aug 2007 22:48:33 -0000 1.3
+++ ossec-batch-manager.pl 5 Nov 2007 21:05:39 -0000 1.4
@@ -13,6 +13,10 @@
##########################################################
#$Id$
+# TODO:
+# - Add check for ossec 1.4 and support longer agent names
+# - Add in eval so that older version of perl without
+# Time::HiRes still can use this script.
use strict;
use warnings;