Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • devel
  • hruska-feature-#6799-filter-keys
  • hruska-feature-5066-duplicateIdeaID
  • hruska-feature-clients-api
  • malostik-#5066-deduplicate-idea-ids
  • master
  • warden-postgresql-port
  • warden-client-1.1.0
  • warden-client-1.2.0
  • warden-client-2.0
  • warden-client-2.0.0-beta1
  • warden-client-2.0.0-beta2
  • warden-client-2.1
  • warden-client-2.1-beta
  • warden-client-2.2
  • warden-client-2.2-final
  • warden-client-3.0-beta0
  • warden-client-3.0-beta1
  • warden-client-3.0-beta2
  • warden-client-3.0-beta3
  • warden-server-0.1.0
  • warden-server-2.0
  • warden-server-2.0.0-beta1
  • warden-server-2.1
  • warden-server-2.1-aplha1
  • warden-server-2.1-beta1
  • warden-server-2.1-beta2
  • warden-server-2.1-beta3
  • warden-server-2.1-beta4
  • warden-server-2.1-beta5
  • warden-server-2.1-beta6
  • warden-server-2.1-patch1
  • warden-server-2.2
  • warden-server-2.2-final
  • warden-server-2.2-patch1
  • warden-server-2.2-patch3
  • warden-server-3.0-beta0
  • warden-server-3.0-beta1
  • warden-server-3.0-beta2
  • warden-server-3.0-beta3
40 results

Target

Select target project
  • Pavel.Valach/warden
1 result
Select Git revision
  • devel
  • hruska-feature-#6799-filter-keys
  • hruska-feature-5066-duplicateIdeaID
  • hruska-feature-clients-api
  • malostik-#5066-deduplicate-idea-ids
  • master
  • warden-postgresql-port
  • warden-client-1.1.0
  • warden-client-1.2.0
  • warden-client-2.0
  • warden-client-2.0.0-beta1
  • warden-client-2.0.0-beta2
  • warden-client-2.1
  • warden-client-2.1-beta
  • warden-client-2.2
  • warden-client-2.2-final
  • warden-client-3.0-beta0
  • warden-client-3.0-beta1
  • warden-client-3.0-beta2
  • warden-client-3.0-beta3
  • warden-server-0.1.0
  • warden-server-2.0
  • warden-server-2.0.0-beta1
  • warden-server-2.1
  • warden-server-2.1-aplha1
  • warden-server-2.1-beta1
  • warden-server-2.1-beta2
  • warden-server-2.1-beta3
  • warden-server-2.1-beta4
  • warden-server-2.1-beta5
  • warden-server-2.1-beta6
  • warden-server-2.1-patch1
  • warden-server-2.2
  • warden-server-2.2-final
  • warden-server-2.2-patch1
  • warden-server-2.2-patch3
  • warden-server-3.0-beta0
  • warden-server-3.0-beta1
  • warden-server-3.0-beta2
  • warden-server-3.0-beta3
40 results
Show changes
Showing
with 1361 additions and 2927 deletions
+------------------------------+
| README - Warden Server 0.1.0 |
+------------------------------+
Content
A. Overall Information
B. Installation Dependencies
C. Installation
D. Miscellaneous
E. Registration of Clients
F. Status Info
G. Authors
--------------------------------------------------------------------------------
A. Overall Information
1. About Warden System
Warden is a client-server architecture service designed to share detected
security events (issues) among CSIRT and CERT teams in a simple and fast way.
This package contains the Warden server.
2. Version
0.1.0 (2012-03-02)
3. Package structure
warden-server/
bin/
create_tables.sh
getClients.pl
getStatus.pl
registerReceiver.pl
registerSender.pl
unregisterClients.pl
warden-alive
wardend
warden-server.pl
doc/
CHANGELOG
INSTALL
LICENSE
README
etc/
warden-client.conf
warden-server.conf
package_version
lib/
WardenConf.pm
WardenReg.pm
WardenStatus.pm
var/
warden.db
uninstall.sh
--------------------------------------------------------------------------------
B. Installation Dependencies
1. Applications:
Perl >= 5.10.1
SQLite >= 3.7.3
2. Perl modules:
SOAP::Lite >= 0.712
SOAP::Transport::TCP >= 0.712
File::Pid >= 1.01
POSIX >= 1.17
DBI >= 1.612
DBD::SQLite >= 1.29
Format::Human::Bytes >= 0.05
Sys::Syslog >= 0.27
File::Basename >= 2.77
FindBin >= 1.50
Net::CIDR::Lite >= 0.21
DateTime >= 0.61
Getopt::Std >= 1.06
Switch >= 2.14
IO::Socket::SSL >= 1.33
--------------------------------------------------------------------------------
C. Installation
1. Check SHA1 checksum of the Warden server package archive.
$ sha1sum -c warden-server-0.1.0.tar.gz.sig
2. Untar it.
$ tar xzvf warden-server-0.1.0.tar.gz
3. Run install.sh.
Default destination directory is /opt/warden-server/
For more information about install.sh options run install.sh -h
You must be root for running this script.
4. Configuration files
You are advised to check configuration file warden-server.conf and
warden-client.conf in warden-server/etc/ directory after installation.
Although this is the Warden server package it also contains several
functions (for administration and maintenance) that are strictly
client-side in a way the Warden system handles functions. Therefore you have
to check both server and client config files to make sure your installation
of the Warden server was successful and complete.
SOAP protocol is used for handling communication between server and clients.
Therefore, correct URI of Warden server must be set.
Authentication of clients and server is performed using client and server
SSL certificates. Both clients and server must have valid certificate.
Configuration files contain following parameters:
a) warden-client.conf:
URI - URI Warden server
e.g. 'https://warden.server.com:443/Warden'
SSL_KEY_FILE - path to a host key file,
e.g. '/etc/ssl/private/warden.server.com.key'
SSL_CERT_FILE - path to a host certificate file,
e.g. '/etc/ssl/certs/warden.server.com.pem'
SSL_CA_FILE - path to a CA file
e.g. '/etc/ssl/certs/tcs-ca-bundle.pem'
b) warden-server.conf
The Warden server configuration file contains:
ADDRESS - IP address/domain name of the Warden server
e.g. warden.server.com
PORT - used TCP port for the Warden server
e.g. 443
BASEDIR - base directory of the Warden server
e.g. /opt/warden-server/
VARDIR - var directory
e.g. $BASEDIR/var/
LOGDIR - logging directory
e.g. /var/log/
PIDDIR - Process ID directory
e.g. /var/run/
SSL_KEY_FILE - path to the server SSL certificate key file
e.g. /etc/ssl/private/warden.server.com.key
SSL_CERT_FILE - path to the server SSL certificate file
e.g. /etc/ssl/certs/warden.server.com.pem
SSL_CA_FILE - path to a CA certificate file
e.g. /etc/ssl/certs/tcs-ca-bundle.pem
FACILITY - syslog facility
e.g. local7
5. Usage of install.sh
Usage: $ ./install.sh [-d <directory>] [-k <ssl_key_file>]
[-c <ssl_cert_file>] [-a <ssl_ca_file>] [-hV]"
-d <directory> installation directory (default: /opt)
-k <ssl_key_file> SSL certificate key file path
-c <ssl_cert_file> SSL certificate file path
-a <ssl_ca_file> CA certificate file path
-h print this help
-V print script version number and exit
Example: $ ./install.sh -d /opt -k /etc/ssl/private/server.key
-c /etc/ssl/certs/server.pem
-a /etc/ssl/certs/bundle.pem
6. Usage of uninstall.sh
Usage: $ ./uninstall.sh [-d <directory>] [-hV]
-d <directory> uninstallation directory (default: /opt)
-h print this help
-V print script version number and exit
Example: # ./uninstall.sh -d /opt
--------------------------------------------------------------------------------
D. Miscellaneous
1. Error Messages
Error messages of the server functions are sent via Syslog to log file,
default is /var/log/messages.
During installation, until Syslog reporting is initiated, error
messages may be reported to the standard error output instead.
2. Init Script
Usage: /etc/init.d/wardend [start|stop|status|restart|force-stop]
The PID of the Warden server process is stored in
/var/run/warden-server.pl.pid.
The lock file is placed in /var/lock/warden-server.
To start the Warden server after rebooting, set init script
/etc/init.d/wardend to runlevels configuration system.
3. Nagios Integration
Nagios check is available via Nagios plugin located in
/opt/warden-server/bin/warden-alive.
4. Firewall Settings
Make sure that the TCP port listed in warden-server.conf ($PORT variable)
is allowed on your firewall.
5. Privileges
The Warden server runs only under root privileges.
6. Known Issues
Benchmarking of the Warden system showed that the system is capable of
sending/receiving about 4 events per second.
--------------------------------------------------------------------------------
E. Registration of Clients
The Warden server administrator is responsible for registering new clients or
removing those already registered. Both registration or unregistration scripts
are provided in the Warden server package. Those scripts should be run from
localhost (the same machine the Warden server is installed and running on).
Members of Warden community who would like to have their client registered must
contact the Warden server administrator with the requirement. This is usually
done via secured e-mail. Requestor should provide all important data to the
Warden server administrator so that the client can be successfully registered.
1. Register Sender
New sender clients are registered in Warden system via registerSender.pl.
Following attributes must be provided in order to register new client
successfully:
hostname - hostname of the client,
requestor - organization or authorized person who demands new
client registration,
service - name of the service of a new registered client,
description_tags - tags describing the nature of the service,
ip_net_client - CIDR the client is only allowed to communicate from.
One can run registerSender.pl with -h argument to see a help.
2. Register Receiver
New receiver clients are registered in Warden system via
registerReceiver.pl.
Following attributes must be provided in order to register new client
successfully:
hostname - hostname of the client,
requestor - organization or authorized person who demands new
client registration,
type - the type of events the client wish to receive (only
this and nothing else),
receive_own_events - boolean value describing if events originating from
the same CIDR will be sent to the client,
ip_net_client - CIDR the client is only allowed to communicate from.
One can run registerReceiver.pl with -h argument to see a help.
3. Unregister Client
In the Warden system, already registered clients can be removed
(unregistered) via unregisterClient.pl.
Following attribute must be provided in order to unregister existing client
successfully:
client_id - ID of the client that should be removed (unregistered).
One can run unregisterClient.pl with -h argument to see a help.
The process of unregistration deletes this client from clients table in DB.
But all messages stored by this client (considering "sender" client) are not
deleted, they are merely set 'invalid' in DB table events.
--------------------------------------------------------------------------------
F. Status Info
Functions in this section show status of the Warden server and active
(registered) clients to the Warden system administrator.
Similarly to (un)registration, these functions should be run from
localhost (e. g. from the same machine the Warden server is installed and
running on).
1. Get Status
Function getStatus is accessible via getStatus.pl. Function has no input
parameters and returns info about the Warden server and its DB status.
2. Get Clients
Function getClients is accessible via getClients.pl. Function has no input
parameters and returns detailed information about all registered clients.
--------------------------------------------------------------------------------
G. Authors
Development: Tomas PLESNIK <plesnik@ics.muni.cz>
Jan SOUKAL <soukal@ics.muni.cz>
Copyright (C) 2011-2012 Cesnet z.s.p.o
Special thanks go to Martin Drasar from CSIRT-MU for his help and support
in the development of Warden system.
Strucny technicky navod pro preklopeni Warden serveru pod Apache a mod_perl
===========================================================================
INSTALACE
=========
1) Instalace Apache a MySQL DB
aptitude install apache2 mysql-server
2) Povoleni mod_ssl
an2enmod ssl
3) Instalace knihovny mod_perl
libapache2-mod-perl2
4) Instalace podpory metody prefork pro Apache
apache2-mpm-prefork
5) Instalace nove pridanych modulu
aptitude install libcrypt-x509-perl libmime-base64-perl
KONFIGURACE
===========
1) Nastaveni APACHE
a) /etc/apache2/sites-enables/default
- konfigurace sekce <VirtualHost *:443>
- includovani potrebnych parametru ze souboru {warden-server}/etc/warden-apache.conf
Include /opt/warden-server/etc/warden-apache.conf
b) Nastaveni vykonovych parametru Apache (/etc/apache2/apache2.conf)
- modul prefork (nastavujte dle vykonu vaseho serveru)
= pro 12C, 16GB RAM funguje dobre
<IfModule mpm_prefork_module>
StartServers 2
MinSpareServers 4
MaxSpareServers 8
ServerLimit 700
MaxClients 700
MaxRequestsPerChild 0
</IfModule>
- parametry spojeni
Timeout 10
KeepAlive Off
c) restartovani Apache po kazde zmene Warden.pm (serverova cast)
2) Nastaveni DB
a) (volitelne) Vytvoreni noveho uzivatele
b) Vytvoreni databazove struktury
mysql -u uzivatel -p heslo < {warden-server}/doc/warden.mysql
3) Nastaveni warden-server.conf, warden-client.conf, {warden-server}/etc/warden-apache.conf
a) Zkontrolovat spravnost IP adres, portu a hlavne cest k certifikatum + nove udaje pro pripojeni do DB
b) Pro klienta a server na jednom stroji jsou zrejme treba 2 ruzne certifikaty (me to jinak nejde, zkuste;))
Tato chyba se vypisuje pri problemu s certifikaty (chybna adresa serveru, chybne cesty pro certifikat, stejny certifikat pro klienta a server)
DEBUG: .../IO/Socket/SSL.pm:420: fatal SSL error: SSL connect attempt failed with unknown errorerror:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed not well-formed (invalid token) at line 1, column 3, byte 3 at /usr/lib/perl5/XML/Parser.pm line 187
SLEDOVATKO
==========
Pro sledovani stavu Apache, poctu prijatych udalosti, poctu klientu lze pouzit sledovatko
{warden-server}/bin/getWebStatus.sh
-- MySQL dump 10.11
--
-- Host: localhost Database: warden
-- ------------------------------------------------------
-- Server version 5.0.51a-24+lenny3
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Current Database: `warden`
--
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `warden` /*!40100 DEFAULT CHARACTER SET latin1 */;
USE `warden`;
--
-- Table structure for table `clients`
--
DROP TABLE IF EXISTS `clients`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `clients` (
`client_id` int(11) NOT NULL auto_increment,
`hostname` varchar(256) default NULL,
`registered` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
`requestor` varchar(256) default NULL,
`service` varchar(64) default NULL,
`client_type` varchar(1) default NULL,
`type` varchar(64) default NULL,
`receive_own_events` varchar(1) default NULL,
`description_tags` varchar(256) default NULL,
`ip_net_client` varchar(256) default NULL,
PRIMARY KEY (`client_id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
SET character_set_client = @saved_cs_client;
--
-- Table structure for table `events`
--
DROP TABLE IF EXISTS `events`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `events` (
`id` int(11) NOT NULL auto_increment,
`hostname` varchar(256) default NULL,
`service` varchar(64) default NULL,
`detected` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
`received` timestamp NOT NULL default '0000-00-00 00:00:00',
`type` varchar(64) default NULL,
`source_type` varchar(64) default NULL,
`source` varchar(256) default NULL,
`target_proto` varchar(16) default NULL,
`target_port` int(2) default NULL,
`attack_scale` int(4) default NULL,
`note` text,
`priority` int(1) default NULL,
`timeout` int(2) default NULL,
`valid` varchar(1) default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
SET character_set_client = @saved_cs_client;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2012-03-22 15:29:35
warden-server-0.1.0
#DumpIOInput On
#DumpIOOutput On
#DumpIOLogLevel warn
SSLEngine on
SSLVerifyDepth 3
SSLVerifyClient require
SSLOptions +StdEnvVars +ExportCertData
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
SSLCertificateFile server-cert.pem
SSLCertificateKeyFile server-key.pem
SSLCACertificateFile ca-cert.pem
#Mozno umistit jeste pred samotny <VirtualHost>
#http://perl.apache.org/docs/2.0/user/config/config.html#C_Parent_
#Create a new parent Perl interpreter for the given VirtualHost and give it its own interpreter pool
PerlOptions +Parent
PerlSwitches -I/opt/warden-server/lib
<Location /Warden>
SetHandler perl-script
PerlHandler Warden::ApacheDispatch
SSLOptions +StdEnvVars
</Location>
#
# warden-client.conf - configuration file for registration and status clients
#
#-------------------------------------------------------------------------------
# URI - URI address of Warden server
#-------------------------------------------------------------------------------
$URI = "https://warden-dev.cesnet.cz:443/Warden";
#-------------------------------------------------------------------------------
# SSL_KEY_FILE - path to server SSL certificate key file
#-------------------------------------------------------------------------------
$SSL_KEY_FILE = "/etc/ssl/private/warden-dev.cesnet.cz.key";
#-------------------------------------------------------------------------------
# SSL_CERT_FILE - path toserver SSL certificate file
#-------------------------------------------------------------------------------
$SSL_CERT_FILE = "/etc/ssl/certs/warden-dev.cesnet.cz.pem";
#-------------------------------------------------------------------------------
# SSL_CA_FILE - path to CA certificate file
#-------------------------------------------------------------------------------
$SSL_CA_FILE = "/etc/ssl/certs/tcs-ca-bundle.pem";
#
# warden-server.conf - configuration file for Warden server
#
#-------------------------------------------------------------------------------
# ADDRESS - IP address of warden server
#-------------------------------------------------------------------------------
$ADDRESS = "warden-dev.cesnet.cz";
#-------------------------------------------------------------------------------
# PORT - used TCP port for Warden server
#-------------------------------------------------------------------------------
$PORT = "443";
#-------------------------------------------------------------------------------
# BASEDIR - base directory of Warden server
#-------------------------------------------------------------------------------
$BASEDIR = "/opt/warden-server";
#-------------------------------------------------------------------------------
# VARDIR - var directory
#-------------------------------------------------------------------------------
$VARDIR = "$BASEDIR/var/";
#-------------------------------------------------------------------------------
# LOGDIR - logging directory
#-------------------------------------------------------------------------------
$LOGDIR = "/var/log/";
#-------------------------------------------------------------------------------
# PIDDIR - process ID directory
#-------------------------------------------------------------------------------
$PIDDIR = "/var/run/";
#-------------------------------------------------------------------------------
# SSL_KEY_FILE - path to server SSL certificate key file
#-------------------------------------------------------------------------------
$SSL_KEY_FILE = "/etc/ssl/private/warden-dev.cesnet.cz.key";
#-------------------------------------------------------------------------------
# SSL_CERT_FILE - path to server SSL certificate file
#-------------------------------------------------------------------------------
$SSL_CERT_FILE = "/etc/ssl/certs/warden-dev.cesnet.cz.pem";
#-------------------------------------------------------------------------------
# SSL_CA_FILE - path to CA ceritificate file
#-------------------------------------------------------------------------------
$SSL_CA_FILE = "/etc/ssl/certs/tcs-ca-bundle.pem";
#-------------------------------------------------------------------------------
# FACILITY - syslog facility
#-------------------------------------------------------------------------------
$FACILITY = "local7";
#!/usr/bin/perl -w
#
# warden-server.pl
#
# Copyright (C) 2011-2012 Cesnet z.s.p.o
# Author(s): Tomas PLESNIK <plesnik@ics.muni.cz>
# Jan SOUKAL <soukal@ics.muni.cz>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name of the Cesnet z.s.p.o nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# This software is provided ``as is'', and any express or implied
# warranties, including, but not limited to, the implied warranties of
# merchantability and fitness for a particular purpose are disclaimed.
# In no event shall the Cesnet z.s.p.o or contributors be liable for
# any direct, indirect, incidental, special, exemplary, or consequential
# damages (including, but not limited to, procurement of substitute
# goods or services; loss of use, data, or profits; or business
# interruption) however caused and on any theory of liability, whether
# in contract, strict liability, or tort (including negligence or
# otherwise) arising in any way out of the use of this software, even
# if advised of the possibility of such damage.
package Warden;
use strict;
#use SOAP::Lite;
#use SOAP::Transport::TCP;
#use SOAP::Transport::HTTP;
#use File::Pid;
#use POSIX;
use DBI;
use DBD::mysql;
use Format::Human::Bytes;
use Sys::Syslog qw(:DEFAULT setlogsock);
Sys::Syslog::setlogsock('unix');
use File::Basename;
#use FindBin;
#use Data::Dumper;
use Net::CIDR::Lite;
use DateTime;
#use Crypt::OpenSSL::X509;
use MIME::Base64;
use Crypt::X509;
our $VERSION = "0.1";
################################################################################
# CONFIG FILE VARIABLES
################################################################################
#my $script_name = $FindBin::Script;
my $conf_file = "/opt/warden-server/etc/warden-server.conf";
#
## first declaration of globa variables from config file
#our $ADDRESS = undef;
#our $PORT = undef;
#our $LOGDIR = undef;
#our $PIDDIR = undef;
#our $VARDIR = undef;
#our $SSL_KEY_FILE = undef;
#our $SSL_CERT_FILE = undef;
#our $SSL_CA_FILE = undef;
our $FACILITY = undef;
#DB options
our $DB_NAME = undef;
our $DB_USER = undef;
our $DB_PASS = undef;
our $DB_HOST = undef;
#
#$ADDRESS = "147.228.52.72";
##
###-------------------------------------------------------------------------------
### PORT - used TCP port for Warden server
###-------------------------------------------------------------------------------
#$PORT = "8889";
##
###-------------------------------------------------------------------------------
### BASEDIR - base directory of Warden server
###-------------------------------------------------------------------------------
#our $BASEDIR = "/opt/warden-server/";
##
###-------------------------------------------------------------------------------
### VARDIR - var directory
###-------------------------------------------------------------------------------
#$VARDIR = "$BASEDIR/var/";
##
###-------------------------------------------------------------------------------
### LOGDIR - logging directory
###-------------------------------------------------------------------------------
#$LOGDIR = "/var/log/";
##
###-------------------------------------------------------------------------------
### PIDDIR - process ID directory
###-------------------------------------------------------------------------------
#$PIDDIR = "/var/run/";
##
##
##
## read config file
if ( ! open( TMP, $conf_file) ) {
die errMsg("Can't read config file '$conf_file': $!\n");
}
close TMP;
# load set variables by user
if ( !do $conf_file ) {
die errMsg("Errors in config file '$conf_file': $@");
}
#
################################################################################
# VARIABLES
################################################################################
#my $die_now = 0;
# PID path
#my $pid_file = $PIDDIR . $script_name . ".pid";
# DB file
#my $db_file = "warden.db";
#my $db = $VARDIR . $db_file;
# connect to DB - DBH is GLOBAL variable
#my $dbargs = {AutoCommit => 0, PrintError => 1};
#our $DBH = DBI->connect("dbi:SQLite:dbname=$db","","",$dbargs) or die errMsg("Can't connect to DB: $!");
our $DBH = DBI->connect("DBI:mysql:database=$DB_NAME;host=$DB_HOST", $DB_USER, $DB_PASS, {RaiseError => 1, mysql_auto_reconnect => 0}) || die "Could not connect to database: $DBI::errstr";
#our $DBH;
################################################################################
# LOCAL FUNCTIONS
################################################################################
#-------------------------------------------------------------------------------
# errMsg - print error message and die
#-------------------------------------------------------------------------------
sub errMsg
{
my $msg = shift;
$msg = trim($msg);
print $msg . "\n";
exit 1;
} # End of errMsg
#-------------------------------------------------------------------------------
# trim - remove whitespace from the start and end of the string
#-------------------------------------------------------------------------------
sub trim
{
my $string = shift;
$string =~ s/^\s+//;
$string =~ s/\s+$//;
return $string;
} # End of trim
#-------------------------------------------------------------------------------
# write2log - writing message to syslog
#-------------------------------------------------------------------------------
sub write2log
{
my $priority = shift;
my $msg = shift;
my $filename = File::Basename::basename($0);
Sys::Syslog::openlog($filename, "cons,pid", $FACILITY);
Sys::Syslog::syslog("$priority", "$msg");
Sys::Syslog::closelog();
} # End of write2log
#-------------------------------------------------------------------------------
# signalHandler - catch signals and end the program if one is caught.
#-------------------------------------------------------------------------------
#sub signalHandler
#{
# $die_now = 1; # this will cause the "infinite loop" to exit
#} # End of signalHandler
#
#
#-------------------------------------------------------------------------------
# sslErrorHandler - handle errors in SSL negitiation
#-------------------------------------------------------------------------------
#sub sslErrorHandler
#{
# my $socket = shift;
# my $msg = shift;
#
# my $ip = $socket->peerhost;
# print $socket $msg;
# $socket->close;
# write2log ("err", "Caught SSL handshake error from $ip: $msg");
# return 1;
#} # End of sslErrorHandler
#
#
#-------------------------------------------------------------------------------
# altNamesFilter - parse hostnames from subjectAltNames array for SQL
# IN operator in database query
#-------------------------------------------------------------------------------
sub altNamesFilter
{
# my $alt_names_array_ref = shift;
# my @alt_names_array = @$alt_names_array_ref;
#our $CN;
my $CN = $ENV{'SSL_CLIENT_S_DN_CN'};
my @an_array;
#push @an_array, $DBH->quote($CN);
# my $i = 1;
# while ($i <= scalar @alt_names_array) {
# push @an_array, $DBH->quote($alt_names_array[$i]);
# $i+=2;
# }
# my $an_filter = join(',', @an_array);
# return $an_filter;
push @an_array, "'$CN'";
my @a = split("\n", $ENV{'SSL_CLIENT_CERT'});
pop @a;
shift @a;
my $der = decode_base64(join("", @a));
my $decoded= Crypt::X509->new(cert => $der);
foreach my $tmp (@{$decoded->SubjectAltName}){
if($tmp =~ s/dNSName=//){
push @an_array, "'$tmp'";
}
}
my $an_filter = join(',', @an_array);
return $an_filter;
}
################################################################################
# SOAP Functions
################################################################################
#-----------------------------------------------------------------------------
# saveNewEvent - save new received event into database
#-----------------------------------------------------------------------------
sub saveNewEvent
{
my ($class, $data) = @_;
my ($sth, $cidr_list);
# variables defined by server
#our $IP; # IP address of sender
#our $CN; # common name of sender
#our $AN_FILTER; # alternate names of sender
# my @alt_names_array = $socket->peer_certificate("subjectAltNames");
my $CN = $ENV{'SSL_CLIENT_S_DN_CN'};
my $AN_FILTER = altNamesFilter(undef);
my $IP = $ENV{'REMOTE_ADDR'};
my $LOCAL_IP = $ENV{'SERVER_ADDR'};
my $cn_db = $DBH->quote($CN);
# variables defined by server
my $client_type = "s"; # incoming client MUST be sender
my $client_type_db = $DBH->quote($client_type);
my $valid = "t"; # registered sender has valid events
my $valid_db = $DBH->quote($valid);
my $received = DateTime->now; # time of event delivery (UTC)
my $received_db = $DBH->quote($received);
## parse object (event) parameters
my $service = $data->{'SERVICE'};
my $service_db = $DBH->quote($service);
my $detected = $data->{'DETECTED'};
my $detected_db = $DBH->quote($detected);
my $type = $data->{'TYPE'};
my $type_db = $DBH->quote($type);
my $source_type = $data->{'SOURCE_TYPE'};
my $source_type_db = $DBH->quote($source_type);
my $source = $data->{'SOURCE'};
my $source_db = $DBH->quote($source);
my $target_proto = $data->{'TARGET_PROTO'};
my $target_proto_db = $DBH->quote($target_proto);
my $target_port = $data->{'TARGET_PORT'};
my $target_port_db = $DBH->quote($target_port);
my $attack_scale = $data->{'ATTACK_SCALE'};
my $attack_scale_db = $DBH->quote($attack_scale);
my $note = $data->{'NOTE'};
my $note_db = $DBH->quote($note);
my $priority = $data->{'PRIORITY'};
my $priority_db = $DBH->quote($priority);
my $timeout = $data->{'TIMEOUT'};
my $timeout_db = $DBH->quote($timeout);
# Authorization of incomming client
#-----------------------------------------------------------------------------
# obtain cidr based on rigth common name and alternate names, service and client_type
$sth = $DBH->prepare("SELECT hostname, ip_net_client FROM clients WHERE hostname IN ($AN_FILTER) AND service = $service_db AND client_type = $client_type_db LIMIT 1;");
if ( !defined $sth ) {die("Cannot prepare authorization statement in saveNewEvent: $DBI::errstr\n")}
$sth->execute;
my ($an, $cidr) = $sth->fetchrow();
# check if client is registered
if (!defined $cidr) {
write2log ("err", "Unauthorized access to saveNewEvent from: $IP (CN: $CN; AN: $an) - client is not registered");
die("Access denied - client is not registered at warden server!");
} else {
$cidr_list = Net::CIDR::Lite
-> new
-> add($cidr);
}
# check if client has IP from registered CIDR
if (!$cidr_list->bin_find($IP)) {
write2log ("err", "Unauthorized access to saveNewEvent from: $IP (CN: $CN; AN: $an) - access from bad subnet: $cidr");
die("Access denied - access from bad subnet!");
} else {
# insert new event
$DBH->do("INSERT INTO events VALUES (null,$cn_db,$service_db,$detected_db,$received_db,$type_db,$source_type_db,$source_db,$target_proto_db,$target_port_db,$attack_scale_db,$note_db,$priority_db,$timeout_db,$valid_db);");
if ($DBH->err()) {die("Cannot do insert statement in saveNewEvent: $DBI::errstr\n")}
#$DBH->commit();
# log last inserted ID
$sth = $DBH->prepare("SELECT last_insert_id()");
if ( !defined $sth ) {die("Cannot prepare last ID statement in saveNewEvent: $DBI::errstr\n")}
$sth->execute;
my $id= $sth->fetchrow();
write2log ("info", "Stored new event (#$id) from $IP (CN: $CN; AN: )");
if (! defined $id) {
write2log ("err", "Event from $IP ($CN) was not save: INSERT INTO events VALUES (null,$cn_db,$service_db,$detected_db,$received_db,$type_db,$source_type_db,$source_db,$target_proto_db,$target_port_db,$attack_scale_db,$note_db,$priority_db,$timeout_db,$valid_db);");
die("Event was not save at warden server - database return empty ID!");
return 0;
} else {
return 1;
}
}
#$DBH->disconnect();
} # END of saveNewEvent
#-----------------------------------------------------------------------------
# getNewEvents - get new events from the DB greater than received ID
#-----------------------------------------------------------------------------
sub getNewEvents
{
my ($class, $data) = @_;
my ($sth, @events, $event, @ids);
my ($id, $hostname, $service, $detected, $type, $source_type, $source, $target_proto, $target_port, $attack_scale, $note, $priority, $timeout);
# variables defined by server
#our $IP; # IP address of receiver
#our $CN; # common name of receiver
#our $AN_FILTER; # alternate name of receiver
my $CN = $ENV{'SSL_CLIENT_S_DN_CN'};
my $AN_FILTER = altNamesFilter(undef);
my $IP = $ENV{'REMOTE_ADDR'};
my $cn_db = $DBH->quote($CN);
my $client_type = "r"; # incoming client MUST be sender
my $client_type_db = $DBH->quote($client_type);
my $cidr_list;
# parse SOAP data object
my $requested_type = $data->{'REQUESTED_TYPE'};
my $requested_type_db = $DBH->quote($requested_type);
my $last_id = $data->{'LAST_ID'};
my $last_id_db = $DBH->quote($last_id);
# Authorization of incomming client
#-----------------------------------------------------------------------------
# obtain cidr based on rigth common name, service and client_type
$sth = $DBH->prepare("SELECT hostname, receive_own_events, ip_net_client FROM clients WHERE hostname IN ($AN_FILTER) AND type = $requested_type_db AND client_type = $client_type_db limit 1;");
if ( !defined $sth ) {die("Cannot prepare authorization statement in getNewEvents: $DBI::errstr\n")}
$sth->execute;
my ($an, $receive_own_events, $cidr) = $sth->fetchrow();
# check if client is registered
if (!defined $cidr) {
write2log ("err", "Unauthorized access to getNewEvents from: $IP (CN: $CN; AN: $an) - client is not registered");
die("Access denied - client is not registered at warden server!");
} else {
$cidr_list = Net::CIDR::Lite
-> new
-> add($cidr);
}
# check if client has IP from registered CIDR
if (!$cidr_list->bin_find($IP)) {
write2log ("err", "Unauthorized access to getNewEvents from: $IP (CN: $CN; AN: $an) - access from bad subnet: $cidr");
die("Access denied - access from bad subnet!");
} else {
# check if client want your own events or not
if ($receive_own_events eq 't') {
write2log("info", "SELECT * FROM events WHERE type != 'test' AND id > $last_id_db AND type = $requested_type_db AND valid = 't' ORDER BY id ASC;");
$sth = $DBH->prepare("SELECT * FROM events WHERE type != 'test' AND id > $last_id_db AND type = $requested_type_db AND valid = 't' ORDER BY id ASC;");
} else {
my ($domain) = $CN =~ /([^\.]+\.[^\.]+)$/;
my $domain_db = $DBH->quote("%$domain");
write2log("info", "SELECT * FROM events WHERE type != 'test' AND id > $last_id_db AND type = $requested_type_db AND valid = 't' AND hostname NOT LIKE $domain_db ORDER BY id ASC;");
$sth = $DBH->prepare("SELECT * FROM events WHERE type != 'test' AND id > $last_id_db AND type = $requested_type_db AND valid = 't' AND hostname NOT LIKE $domain_db ORDER BY id ASC;");
}
if ( !defined $sth ) { die("Cannot prepare statement in getNewEvents: $DBI::errstr\n") }
$sth->execute;
# parse items of events stored in DB
while (my @result = $sth->fetchrow()) {
$id = $result[0];
$hostname = $result[1];
$service = $result[2];
$detected = $result[3];
$type = $result[5];
$source_type = $result[6];
$source = $result[7];
$target_proto = $result[8];
$target_port = $result[9];
$attack_scale = $result[10];
$note = $result[11];
$priority = $result[12];
$timeout = $result[13];
# create SOAP data object
$event = SOAP::Data->name(event => \SOAP::Data->value(
SOAP::Data->name(ID => $id),
SOAP::Data->name(HOSTNAME => $hostname),
SOAP::Data->name(SERVICE => $service),
SOAP::Data->name(DETECTED => $detected),
SOAP::Data->name(TYPE => $type),
SOAP::Data->name(SOURCE_TYPE => $source_type),
SOAP::Data->name(SOURCE => $source),
SOAP::Data->name(TARGET_PROTO => $target_proto),
SOAP::Data->name(TARGET_PORT => $target_port),
SOAP::Data->name(ATTACK_SCALE => $attack_scale),
SOAP::Data->name(NOTE => $note),
SOAP::Data->name(PRIORITY => $priority),
SOAP::Data->name(TIMEOUT => $timeout)
));
push(@events, $event);
push(@ids, $id);
}
# log sent ID of events
if (scalar(@events) != 0) {
write2log("info", "Sent events with ID: [@ids] to $IP (CN: $CN; AN: $an)");
}
return @events;
}
} # END of getNewEvents
#
##-----------------------------------------------------------------------------
## getLastId - get lastest saved event ID
##-----------------------------------------------------------------------------
sub getLastId
{
my ($class, $arg) = @_;
my $sth = $DBH->prepare("SELECT max(id) FROM events;");
if ( !defined $sth ) { die("Cannot prepare statement in getLastId: $DBI::errstr\n") }
$sth->execute;
my $result = $sth->fetchrow();
return $result;
} # END of getLastID
##-----------------------------------------------------------------------------
## registerSender - register new sender
##-----------------------------------------------------------------------------
sub registerSender
{
my ($class, $data) = @_;
my $sth;
#our $IP;
#our $LOCAL_IP;
#our $CN;
my $CN = $ENV{'SSL_CLIENT_S_DN_CN'};
my $IP = $ENV{'REMOTE_ADDR'};
my $LOCAL_IP = $ENV{'SERVER_ADDR'};
if ($LOCAL_IP ne $IP) {
write2log ("err", "Unauthorized access to registerSender from: $IP ($CN) - access allowed only from localhost");
die("Access denied - access allowed only from localhost!");
} else {
# defined variables by server
my $client_type = "s";
my $client_type_db = $DBH->quote($client_type);
my $registered = DateTime->now;
my $registered_db = $DBH->quote($registered);
my $type = "null";
my $type_db = $DBH->quote($type);
my $receive_own_events = "null";
my $receive_own_events_db = $DBH->quote($receive_own_events);
# parse SOAP data oject
my $hostname = $data->{'HOSTNAME'};
my $hostname_db = $DBH->quote($hostname);
my $requestor = $data->{'REQUESTOR'};
my $requestor_db = $DBH->quote($requestor);
my $service = $data->{'SERVICE'};
my $service_db = $DBH->quote($service);
my $description_tags = $data->{'DESCRIPTION_TAGS'};
my $description_tags_db = $DBH->quote($description_tags);
my $ip_net_client = $data->{'IP_NET_CLIENT'};
my $ip_net_client_db = $DBH->quote($ip_net_client);
# check if sender has been already registered
$sth = $DBH->prepare("SELECT registered FROM clients WHERE hostname = $hostname_db AND requestor = $requestor_db AND service = $service_db AND client_type = $client_type_db AND type = $type_db AND receive_own_events = $receive_own_events_db AND description_tags = $description_tags_db AND ip_net_client = $ip_net_client_db;");
if ( !defined $sth ) {die("Cannot prepare check statement in registerSender: $DBI::errstr\n")}
$sth->execute;
my $result = $sth->fetchrow();
# register new sender
if (defined $result) {
write2log ("err", "Attempt to re-register the sender");
die("Error - sender has already been registered at $result");
} else {
$DBH->do("INSERT INTO clients VALUES (null,$hostname_db,$registered_db,$requestor_db,$service_db,$client_type_db,$type_db,$receive_own_events_db,$description_tags_db,$ip_net_client_db);");
if ($DBH->err()) {die("Cannot do statement in registerSender: $DBI::errstr\n")}
#$DBH->commit();
write2log("info", "New sender $hostname (service: $service, cidr: $ip_net_client) was registered");
return 1;
}
}
} # END of registerSender
##-----------------------------------------------------------------------------
## registerReceiver - register new receiver
##-----------------------------------------------------------------------------
sub registerReceiver
{
my ($class, $data) = @_;
my $sth;
#our $IP;
#our $LOCAL_IP;
#our $CN;
my $CN = $ENV{'SSL_CLIENT_S_DN_CN'};
my $IP = $ENV{'REMOTE_ADDR'};
my $LOCAL_IP = $ENV{'SERVER_ADDR'};
if ($LOCAL_IP ne $IP) {
write2log ("err", "Unauthorized access to registerReceiver from: $IP ($CN) - access allowed only from localhost");
die("Access denied - access allowed only from localhost!");
} else {
# variables defined by server
my $client_type = "r";
my $client_type_db = $DBH->quote($client_type);
my $registered = DateTime->now;
my $registered_db = $DBH->quote($registered);
my $service = "null";
my $service_db = $DBH->quote($service);
my $description_tags = "null";
my $description_tags_db = $DBH->quote($description_tags);
# parse SOAP data oject
my $hostname = $data->{'HOSTNAME'};
my $hostname_db = $DBH->quote($hostname);
my $requestor = $data->{'REQUESTOR'};
my $requestor_db = $DBH->quote($requestor);
my $type = $data->{'TYPE'};
my $type_db = $DBH->quote($type);
my $receive_own_events = $data->{'RECEIVE_OWN_EVENTS'};
my $receive_own_events_db = $DBH->quote($receive_own_events);
my $ip_net_client = $data->{'IP_NET_CLIENT'};
my $ip_net_client_db = $DBH->quote($ip_net_client);
# check if receiver has been already registered
$sth = $DBH->prepare("SELECT registered FROM clients WHERE hostname = $hostname_db AND requestor = $requestor_db AND service = $service_db AND client_type = $client_type_db AND type = $type_db AND receive_own_events = $receive_own_events_db AND description_tags = $description_tags_db AND ip_net_client = $ip_net_client_db;");
if ( !defined $sth ) {die("Cannot prepare check statement in registerReceiver: $DBI::errstr\n")}
$sth->execute;
my $result = $sth->fetchrow();
# register new receiver
if (defined $result) {
write2log ("err", "Attempt to re-register the receiver");
die("Error - receiver has already been registered at $result");
} else {
$DBH->do("INSERT INTO clients VALUES (null,$hostname_db,$registered_db,$requestor_db,$service_db,$client_type_db,$type_db,$receive_own_events_db,$description_tags_db,$ip_net_client_db);");
if ($DBH->err()) {die("Cannot do statement in registerReceiver: $DBI::errstr\n")}
#$DBH->commit();
write2log("info", "New receiver $hostname (type: $type, cidr: $ip_net_client: receive_own_events: $receive_own_events) was registered");
return 1;
}
}
} # END of registerReceiver
#-----------------------------------------------------------------------------
# unregisterClient - unregister client
#-----------------------------------------------------------------------------
sub unregisterClient
{
my ($class, $data) = @_;
my $sth;
#our $IP;
#our $LOCAL_IP;
#our $CN;
my $CN = $ENV{'SSL_CLIENT_S_DN_CN'};
my $IP = $ENV{'REMOTE_ADDR'};
my $LOCAL_IP = $ENV{'SERVER_ADDR'};
if ($LOCAL_IP ne $IP) {
write2log ("err", "Unauthorized access to unregisterClients from: $IP ($CN) - access allowed only from localhost");
die("Access denied - access allowed only from localhost!");
} else {
# parse SOAP data oject
my $client_id = $data->{'CLIENT_ID'};
my $client_id_db = $DBH->quote($client_id);
# check if receiver has been already registered
$sth = $DBH->prepare("SELECT client_id, hostname, service, client_type FROM clients WHERE client_id = $client_id_db;");
if ( !defined $sth ) {die("Cannot prepare check statement in unregisterClient: $DBI::errstr\n")}
$sth->execute;
my ($id, $hostname, $service, $client_type) = $sth->fetchrow();
my $hostname_db = $DBH->quote($hostname);
my $service_db = $DBH->quote($service);
# delete registered client
if (!defined $id) {
write2log ("err", "Attempt to delete unregister client");
die("Error - client (#$client_id) is not registered");
} else {
if ($client_type eq 's') {
$DBH->do("DELETE FROM clients WHERE client_id = $client_id_db;");
if ($DBH->err()) {die("Cannot do delete statement of sender in unregisterClient: $DBI::errstr\n")}
#$DBH->commit();
$DBH->do("UPDATE events SET valid = 'f' where hostname = $hostname_db AND service = $service_db;");
if ($DBH->err()) {die("Cannot do unvalidation statement in unregisterClient: $DBI::errstr\n")}
#$DBH->commit();
write2log("info", "Sender $hostname (client_id: $client_id, service: $service) was deleted and its data were invalidated" );
return 1;
} else {
$DBH->do("DELETE FROM clients WHERE client_id = $client_id_db;");
if ($DBH->err()) {die("Cannot do delete statement of receiver in unregisterClient: $DBI::errstr\n")}
#$DBH->commit();
write2log("info", "Receiver $hostname (client_id: $client_id) was deleted" );
return 1;
}
}
}
} # END of unregisterClient
#-----------------------------------------------------------------------------
# getClients - get list of clients which were registered at warden server
#-----------------------------------------------------------------------------
sub getClients
{
my ($class, $arg) = @_;
#our $IP;
#our $LOCAL_IP;
#our $CN;
my $CN = $ENV{'SSL_CLIENT_S_DN_CN'};
my $IP = $ENV{'REMOTE_ADDR'};
my $LOCAL_IP = $ENV{'SERVER_ADDR'};
if ($LOCAL_IP ne $IP) {
write2log ("err", "Unauthorized access to getClients from: $IP ($CN) - access allowed only from localhost");
die("Access denied - access allowed only from localhost!");
} else {
my (@clients, $client);
my ($client_id, $hostname, $registered, $requestor, $service, $client_type, $type, $receive_own_events, $description_tags, $ip_net_client);
my $sth = $DBH->prepare("SELECT * FROM clients;");
if (!defined $sth) { die("Cannot prepare statement in getClients: $DBI::errstr\n") }
$sth->execute;
while ( my @result = $sth->fetchrow() ) {
$client_id = $result[0];
$hostname = $result[1];
$registered = $result[2];
$requestor = $result[3];
$service = $result[4];
$client_type = $result[5];
$type = $result[6];
$receive_own_events = $result[7];
$description_tags = $result[8];
$ip_net_client = $result[9];
$client = SOAP::Data->name(client => \SOAP::Data->value(
SOAP::Data->name(CLIENT_ID => $client_id),
SOAP::Data->name(HOSTNAME => $hostname),
SOAP::Data->name(REGISTERED => $registered),
SOAP::Data->name(REQUESTOR => $requestor),
SOAP::Data->name(SERVICE => $service),
SOAP::Data->name(CLIENT_TYPE => $client_type),
SOAP::Data->name(TYPE => $type),
SOAP::Data->name(RECEIVE_OWN_EVENTS => $receive_own_events),
SOAP::Data->name(DESCRIPTION_TAGS => $description_tags),
SOAP::Data->name(IP_NET_CLIENT => $ip_net_client),
));
push(@clients, $client);
}
my $sum = scalar @clients;
write2log("info", "Sending information about $sum registered clients");
return @clients;
}
} # END of getClients
#-----------------------------------------------------------------------------
# getStatus - get list of status items of warden server
#-----------------------------------------------------------------------------
sub getStatus
{
my ($class, $arg) = @_;
#our $IP;
#our $LOCAL_IP;
#our $CN;
my $CN = $ENV{'SSL_CLIENT_S_DN_CN'};
my $IP = $ENV{'REMOTE_ADDR'};
my $LOCAL_IP = $ENV{'SERVER_ADDR'};
if ($LOCAL_IP ne $IP) {
write2log ("err", "Unauthorized access to getStatus from: $IP ($CN) - access allowed only from localhost");
die("Access denied - access allowed only from localhost!");
} else {
my ($sth, @status);
# size of database events
#my $db_size = Format::Human::Bytes::base10(-s $db);
$sth = $DBH->prepare("SELECT data_length + index_length FROM information_schema.TABLES WHERE table_schema = ? AND TABLE_NAME = ?");
$sth->execute("warden", "events");
my $db_size_db = $sth->fetchrow();
my $db_size = (defined $db_size_db ? Format::Human::Bytes::base10($db_size_db) : "none");
# sum of records in table events
$sth = $DBH->prepare("SELECT count(*) FROM events WHERE valid = 't';");
if (!defined $sth) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") }
$sth->execute;
my $events_sum = $sth->fetchrow();
if (!defined $events_sum) { $events_sum = "none" }
# id of last record in table events
$sth = $DBH->prepare("SELECT max(id) FROM events;");
if (!defined $sth) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") }
$sth->execute;
my $events_last_id = $sth->fetchrow();
if (!defined $events_last_id) { $events_last_id = "none" }
# timestamp of first record in table events
$sth = $DBH->prepare("SELECT received FROM events WHERE id = (SELECT min(id) FROM events);");
if (!defined $sth) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") }
$sth->execute;
my $events_first_timestamp = $sth->fetchrow();
if (!defined $events_first_timestamp) { $events_first_timestamp = "none" }
# timestamp of last record in table events
$sth = $DBH->prepare("SELECT received FROM events WHERE id = (SELECT max(id) FROM events);");
if (!defined $sth) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") }
$sth->execute;
my $events_last_timestamp = $sth->fetchrow();
if (!defined $events_last_timestamp) { $events_last_timestamp = "none" }
# sum of records in table clients
$sth = $DBH->prepare("SELECT count(*) FROM clients;");
if (!defined $sth) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") }
$sth->execute;
my $clients_sum = $sth->fetchrow();
if (!defined $clients_sum) { $clients_sum = "none" }
my $server_status = SOAP::Data->name(server_status => \SOAP::Data->value(
SOAP::Data->name(VERSION => $VERSION),
#SOAP::Data->name(ADDRESS => $ADDRESS),
#SOAP::Data->name(PORT => $PORT),
#SOAP::Data->name(LOGDIR => $LOGDIR),
#SOAP::Data->name(PIDDIR => $PIDDIR),
#SOAP::Data->name(VARDIR => $VARDIR),
#SOAP::Data->name(SSL_KEY_FILE => $SSL_KEY_FILE),
#SOAP::Data->name(SSL_CERT_FILE => $SSL_CERT_FILE),
#SOAP::Data->name(SSL_CA_FILE => $SSL_CA_FILE),
SOAP::Data->name(FACILITY => $FACILITY),
SOAP::Data->name(DB_SIZE => $db_size),
SOAP::Data->name(EVENTS_SUM => $events_sum),
SOAP::Data->name(EVENTS_LAST_ID => $events_last_id),
SOAP::Data->name(EVENTS_FIRST_TIMESTAMP => $events_first_timestamp),
SOAP::Data->name(EVENTS_LAST_TIMESTAMP => $events_last_timestamp),
SOAP::Data->name(CLIENTS_SUM => $clients_sum)
));
push(@status, $server_status);
# statistics of senders
if ($clients_sum != 0) {
$sth = $DBH->prepare("SELECT client_id, hostname, service FROM clients WHERE client_type = 's';");
if (!defined $sth) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") }
$sth->execute;
my ($client_id, $hostname, $service);
my $client_status;
while(($client_id, $hostname, $service) = $sth->fetchrow()) {
my $hostname_db = $DBH->quote($hostname);
my $service_db = $DBH->quote($service);
my $sth2;
# sum of stored events
$sth2 = $DBH->prepare("SELECT count(*) FROM events WHERE hostname = $hostname_db AND service = $service_db;");
if ( !defined $sth2 ) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") }
$sth2->execute;
my $count = $sth2->fetchrow();
if (!defined $count) {$count = "none"}
# timestamp of last stored event
$sth2 = $DBH->prepare("SELECT max(received) FROM events WHERE hostname = $hostname_db AND service = $service_db;");
if ( !defined $sth2 ) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") }
$sth2->execute;
my $timestamp = $sth2->fetchrow();
if (!defined $timestamp) { $timestamp = "none" }
# create SOAP data object
$client_status = SOAP::Data->name(client_status => \SOAP::Data->value(
SOAP::Data->name(CLIENT_ID => $client_id),
SOAP::Data->name(HOSTNAME => $hostname),
SOAP::Data->name(SERVICE => $service),
SOAP::Data->name(COUNT => $count),
SOAP::Data->name(TIMESTAMP => $timestamp),
));
push(@status, $client_status);
}
}
write2log("info", "Sent of warden server status info");
return @status;
}
} # END of getStatus
##
##
##################################################################################
### MAIN warden-server
##################################################################################
##
###-------------------------------------------------------------------------------
### Superuser controle
###-------------------------------------------------------------------------------
##my $UID = $<;
##if ($UID != 0) {
## die errMsg("You must be root for running this script!")
##}
##
###-------------------------------------------------------------------------------
### Daemonize section
###-------------------------------------------------------------------------------
##use POSIX qw(setsid);
##chdir '/';
##umask 0;
### all STDERR messages are printed on terminal
##open STDIN, '/dev/null' or die errMsg("Can't read /dev/null: $!");
##open STDOUT, '/dev/null' or die errMsg("Can't write to /dev/null: $!");
##defined( my $pid = fork ) or die errMsg("Can't fork: $!");
##exit if $pid;
##
###-------------------------------------------------------------------------------
### Dissociate this process from the controlling terminal
### that started it and stop being part of whatever
### process group this process was a part of.
###------------------------------------------------------------------------------
##POSIX::setsid() or die errMsg("Can't start a new session.");
##
###-------------------------------------------------------------------------------
### Callback signal handler for signals.
###-------------------------------------------------------------------------------
##$SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&signalHandler;
##$SIG{PIPE} = 'ignore';
##
###-------------------------------------------------------------------------------
### Create pid file in /var/run/
###-------------------------------------------------------------------------------
##my $pfh = File::Pid->new( { file => $pid_file, } );
##$pfh->write or die errMsg("Can't write PID file $pid_file: $!");
##my $pid_number = $pfh->pid;
##
###-------------------------------------------------------------------------------
### Starting of Warden server
###-------------------------------------------------------------------------------
##write2log("info", "Starting WARDEN server daemon with pid $pid_number");
##
### log of warden database size
##my $db_size_human = Format::Human::Bytes::base10(-s $db);
##write2log("info", "Size of DB file ($db_file) is: $db_size_human");
##
### start TCP server
##my $server = SOAP::Transport::TCP::Server
## ->new(
## Listen => 5,
## LocalAddr => $ADDRESS,
## LocalPort => $PORT,
## Proto => "tcp",
## ReuseAddr => 1,
## SSL_verify_mode => 0x02,
## SSL_use_cert => 1,
## SSL_server => 1,
## SSL_key_file => $SSL_KEY_FILE,
## SSL_cert_file => $SSL_CERT_FILE,
## SSL_ca_file => $SSL_CA_FILE,
## SSL_error_trap =>\&sslErrorHandler,
## );
##
### check if socket exist
##$server or die errMsg("Socket error: $!");
##
### start SOAP server
##my $soap = SOAP::Server
## ->new()
## ->dispatch_to('Warden');
##
##
###-------------------------------------------------------------------------------
### Process of incoming client's requests and send response
###-------------------------------------------------------------------------------
##write2log("info", "Starting TCP and SOAP server at $ADDRESS:$PORT");
##while ($die_now != 1)
##{
## my $socket = $server->accept();
## next if (!$socket);
##
# our $CN = $ENV{'SSL_CLIENT_S_DN_CN'};
# my @alt_names_array = $socket->peer_certificate("subjectAltNames");
# our $AN_FILTER = altNamesFilter(undef);
# our $IP = $ENV{'REMOTE_ADDR'};
# our $LOCAL_IP = $ENV{'SERVER_ADDR'};
##
## # read input serialized SOAP envelope and data
## my ($request, $buf);
## while (defined($buf = <$socket>))
## {
## $request .= $buf;
## }
##
## # handle of called server function from client and send response to client
## my $response = $soap->handle($request);
## print $socket $response;
##
## $socket->close;
## undef($socket);
## undef($CN);
## undef($AN_FILTER);
## undef($IP);
## undef($LOCAL_IP);
##}
##
##
##
##################################################################################
### Cleanup section
##################################################################################
##END {
## if ($die_now == 1)
## {
## my $pid = trim(`cat $pid_file`);
## write2log("info", "Stopping WARDEN server daemon with pid $pid");
##
## # close connection to DB
## $DBH->disconnect();
##
## # remove pid file
## $pfh->remove if defined $pfh;
## }
##}
#
1;
package Warden::ApacheDispatch;
use SOAP::Transport::HTTP;
my $server = SOAP::Transport::HTTP::Apache->dispatch_to('.','Warden');
sub handler {
$server->handler(@_)
}
1;
#!/usr/bin/perl -w
#
# WardenConf.pm
#
# Copyright (C) 2011-2012 Cesnet z.s.p.o
# Author(s): Tomas PLESNIK <plesnik@ics.muni.cz>
# Jan SOUKAL <soukal@ics.muni.cz>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name of the Cesnet z.s.p.o nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# This software is provided ``as is'', and any express or implied
# warranties, including, but not limited to, the implied warranties of
# merchantability and fitness for a particular purpose are disclaimed.
# In no event shall the Cesnet z.s.p.o or contributors be liable for
# any direct, indirect, incidental, special, exemplary, or consequential
# damages (including, but not limited to, procurement of substitute
# goods or services; loss of use, data, or profits; or business
# interruption) however caused and on any theory of liability, whether
# in contract, strict liability, or tort (including negligence or
# otherwise) arising in any way out of the use of this software, even
# if advised of the possibility of such damage.
#
package WardenConf;
use strict;
our $VERSION = "0.1";
#-------------------------------------------------------------------------------
# loadConf - load variables from configuration file
#-------------------------------------------------------------------------------
sub loadConf
{
my $conf_file = shift;
# preset of default variables
our $URI = undef;
our $SSL_KEY_FILE = undef;
our $SSL_CERT_FILE = undef;
our $SSL_CA_FILE = undef;
# read config file
if ( ! open( TMP, $conf_file) ) {
die "Can't read config file '$conf_file': $!\n";
}
close TMP;
# load set variables by user
if ( !do $conf_file ) {
die("Errors in config file '$conf_file': $@");
}
return ($URI, $SSL_KEY_FILE, $SSL_CERT_FILE, $SSL_CA_FILE);
} # End of loadConf
1;
#!/usr/bin/perl -w
#
# WardenReg.pm
#
# Copyright (C) 2011-2012 Cesnet z.s.p.o
# Author(s): Tomas PLESNIK <plesnik@ics.muni.cz>
# Jan SOUKAL <soukal@ics.muni.cz>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name of the Cesnet z.s.p.o nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# This software is provided ``as is'', and any express or implied
# warranties, including, but not limited to, the implied warranties of
# merchantability and fitness for a particular purpose are disclaimed.
# In no event shall the Cesnet z.s.p.o or contributors be liable for
# any direct, indirect, incidental, special, exemplary, or consequential
# damages (including, but not limited to, procurement of substitute
# goods or services; loss of use, data, or profits; or business
# interruption) however caused and on any theory of liability, whether
# in contract, strict liability, or tort (including negligence or
# otherwise) arising in any way out of the use of this software, even
# if advised of the possibility of such damage.
package WardenReg;
use strict;
use SOAP::Lite;
use IO::Socket::SSL qw(debug1);
#use SOAP::Transport::TCP;
use SOAP::Transport::HTTP;
our $VERSION = "0.1";
#-------------------------------------------------------------------------------
# errMsg - print error message and die
#-------------------------------------------------------------------------------
sub errMsg
{
my $msg = shift;
die($msg . "\n");
} # End of errMsg
#-------------------------------------------------------------------------------
# c2s - connect to server, send request and receive response
#-------------------------------------------------------------------------------
#sub c2s
#{
# my $uri = shift;
# my $ssl_key_file = shift;
# my $ssl_cert_file = shift;
# my $ssl_ca_file = shift;
# my $method = shift;
# my $data = shift;
#
# my $client;
# my ($server, $port, $service) = $uri =~ /https:\/\/(.+)\:(\d+)\/(.+)/;
# if (!($client = SOAP::Transport::TCP::Client->new(
# PeerAddr => $server,
# PeerPort => $port,
# Proto => 'tcp',
# SSL_use_cert => 1,
# SSL_verify_mode => 0x02,
# SSL_key_file => $ssl_key_file,
# SSL_cert_file => $ssl_cert_file,
# SSL_ca_file => $ssl_ca_file,
# ))) {errMsg("Sorry, unable to create socket: " . &SOAP::Transport::TCP::Client::errstr)}
#
# # setting of URI and serialize SOAP envelope and data object
# my $soap = SOAP::Lite->uri($uri);
# my $envelope = $soap->serializer->envelope(method => $method, $data);
#
# # setting of TCP URI and send serialized SOAP envelope and data
# my $tcp_uri = "tcp://$server:$port/$service";
# my $result = $client->send_receive(envelope => $envelope, endpoint => $tcp_uri);
#
# # check server response
# if (!defined $result) {
# errMsg("Error: server returned empty response." . "\n" . "Problem with used SSL ceritificates or Warden server at $server:$port is down.");
# } else {
# # deserialized response from server -> create SOAP envelope and data object
# my $response = $soap->deserializer->deserialize($result);
# # check SOAP fault status
# $response->fault ? errMsg("Server sent error message:: " . $response->faultstring) : return 1;
# }
#}
#
sub c2s
{
my $uri = shift;
my $ssl_key_file = shift;
my $ssl_cert_file = shift;
my $ssl_ca_file = shift;
my $method = shift;
my $data = shift;
my $client;
my ($server, $port, $service) = $uri =~ /https:\/\/(.+)\:(\d+)\/(.+)/;
if (!($client = SOAP::Transport::HTTP::Client->new(
# ssl_opts => {
# verify_hostname => 0,
# SSL_use_cert => 1,
# SSL_verify_mode => 0x02,
# SSL_key_file => $ssl_key_file,
# SSL_cert_file => $ssl_cert_file,
# SSL_ca_file => $ssl_ca_file
#}
))) {errMsg("Sorry, unable to create socket: " . &SOAP::Transport::HTTP::Client::errstr)}
$client->ssl_opts( verify_hostname => 1,
SSL_use_cert => 1,
SSL_verify_mode => 0x02,
SSL_key_file => $ssl_key_file,
SSL_cert_file => $ssl_cert_file,
SSL_ca_file => $ssl_ca_file);
# setting of URI and serialize SOAP envelope and data object
my $soap = SOAP::Lite->uri($service)->proxy($uri);
my $envelope = $soap->serializer->envelope(method => $method, $data);
# setting of TCP URI and send serialized SOAP envelope and data
my $tcp_uri = "https://$server:$port/$service";
my $result = $client->send_receive(envelope => $envelope, endpoint => $tcp_uri);
# check server response
if (!defined $result) {
errMsg("Error: server returned empty response." . "\n" . "Problem with used SSL ceritificates or Warden server at $server:$port is down.");
} else {
# deserialized response from server -> create SOAP envelope and data object
my $response = $soap->deserializer->deserialize($result);
# check SOAP fault status
$response->fault ? errMsg("Server sent error message:: " . $response->faultstring) : return 1;
}
}
#-------------------------------------------------------------------------------
# registerSender - register new warden sender
#-------------------------------------------------------------------------------
sub registerSender
{
my $warden_path = shift;
my $hostname = shift;
my $requestor = shift;
my $service = shift;
my $description_tags = shift;
my $ip_net_client = shift;
my $etcdir = $warden_path . "/etc/";
my $libdir = $warden_path . "/lib/";
# read the config file
require $libdir . "WardenConf.pm";
my $conf_file = $etcdir . "warden-client.conf";
my ($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file) = WardenConf::loadConf($conf_file);
# create SOAP data obejct
my $request_data = SOAP::Data->name(client => \SOAP::Data->value(
SOAP::Data->name(HOSTNAME => $hostname),
SOAP::Data->name(REQUESTOR => $requestor),
SOAP::Data->name(SERVICE => $service),
SOAP::Data->name(DESCRIPTION_TAGS => $description_tags),
SOAP::Data->name(IP_NET_CLIENT => $ip_net_client)
));
my $result = c2s($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file, "registerSender", $request_data);
$result ? return 1 : return 0;
} # End of registerSender
#-------------------------------------------------------------------------------
# registerReceiver - register new warden receiver
#-------------------------------------------------------------------------------
sub registerReceiver
{
my $warden_path = shift;
my $hostname = shift;
my $requestor = shift;
my $type = shift;
my $receive_own_events = shift;
my $ip_net_client = shift;
my $etcdir = $warden_path . "/etc/";
my $libdir = $warden_path . "/lib/";
# read the config file
require $libdir . "WardenConf.pm";
my $conf_file = $etcdir . "warden-client.conf";
my ($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file) = WardenConf::loadConf($conf_file);
# create SOAP data obejct
my $request_data = SOAP::Data->name(client => \SOAP::Data->value(
SOAP::Data->name(HOSTNAME => $hostname),
SOAP::Data->name(REQUESTOR => $requestor),
SOAP::Data->name(TYPE => $type),
SOAP::Data->name(RECEIVE_OWN_EVENTS => $receive_own_events),
SOAP::Data->name(IP_NET_CLIENT => $ip_net_client)
));
my $result = c2s($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file, "registerReceiver", $request_data);
$result ? return 1 : return 0;
} # End of registerReceiver
#-------------------------------------------------------------------------------
# unregisterClient - unregister client from warden server
#-------------------------------------------------------------------------------
sub unregisterClient
{
my $warden_path = shift;
my $client_id = shift;
my $etcdir = $warden_path . "/etc/";
my $libdir = $warden_path . "/lib/";
# read the config file
require $libdir . "WardenConf.pm";
my $conf_file = $etcdir . "warden-client.conf";
my ($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file) = WardenConf::loadConf($conf_file);
# create SOAP data obejct
my $request_data = SOAP::Data->name(client => \SOAP::Data->value(
SOAP::Data->name(CLIENT_ID => $client_id)
));
my $result = c2s($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file, "unregisterClient", $request_data);
$result ? return 1 : return 0;
} # End of unregisterClient
1;
#!/usr/bin/perl -w
#
# WardenStatus.pm
#
# Copyright (C) 2011-2012 Cesnet z.s.p.o
# Author(s): Tomas PLESNIK <plesnik@ics.muni.cz>
# Jan SOUKAL <soukal@ics.muni.cz>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name of the Cesnet z.s.p.o nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# This software is provided ``as is'', and any express or implied
# warranties, including, but not limited to, the implied warranties of
# merchantability and fitness for a particular purpose are disclaimed.
# In no event shall the Cesnet z.s.p.o or contributors be liable for
# any direct, indirect, incidental, special, exemplary, or consequential
# damages (including, but not limited to, procurement of substitute
# goods or services; loss of use, data, or profits; or business
# interruption) however caused and on any theory of liability, whether
# in contract, strict liability, or tort (including negligence or
# otherwise) arising in any way out of the use of this software, even
# if advised of the possibility of such damage.
package WardenStatus;
use strict;
use SOAP::Lite;
use IO::Socket::SSL qw(debug1);
#use SOAP::Transport::TCP;
use SOAP::Transport::HTTP;
#use Data::Dumper;
our $VERSION = "0.1";
#-------------------------------------------------------------------------------
# errMsg - print error message and die
#-------------------------------------------------------------------------------
sub errMsg
{
my $msg = shift;
die($msg . "\n");
} # End of errMsg
#-------------------------------------------------------------------------------
# c2s - connect to server, send request and receive response
#-------------------------------------------------------------------------------
#sub c2s
#{
# my $uri = shift;
# my $ssl_key_file = shift;
# my $ssl_cert_file = shift;
# my $ssl_ca_file = shift;
# my $method = shift;
#
# my $client;
# my ($server, $port, $service) = $uri =~ /https:\/\/(.+)\:(\d+)\/(.+)/;
# if (!($client = SOAP::Transport::TCP::Client->new(
# PeerAddr => $server,
# PeerPort => $port,
# Proto => 'tcp',
# SSL_use_cert => 1,
# SSL_verify_mode => 0x02,
# SSL_key_file => $ssl_key_file,
# SSL_cert_file => $ssl_cert_file,
# SSL_ca_file => $ssl_ca_file,
# ))) {errMsg("Sorry, unable to create socket: " . &SOAP::Transport::TCP::Client::errstr)}
#
# # setting of URI and serialize SOAP envelope and data object
# my $soap = SOAP::Lite->uri($uri);
# my $envelope = $soap->serializer->envelope(method => $method);
#
# # setting of TCP URI and send serialized SOAP envelope and data
# my $tcp_uri = "tcp://$server:$port/$service";
# my $result = $client->send_receive(envelope => $envelope, endpoint => $tcp_uri);
#
# # check server response
# if (!defined $result) {
# errMsg("Error: server returned empty response." . "\n" . "Problem with used SSL ceritificates or Warden server at $server:$port is down.");
# } else {
# # deserialized response from server -> create SOAP envelope and data object
# my $response = $soap->deserializer->deserialize($result);
# # check SOAP fault status
# $response->fault ? errMsg("Server sent error message:: " . $response->faultstring) : return $response;
# }
#}
sub c2s
{
my $uri = shift;
my $ssl_key_file = shift;
my $ssl_cert_file = shift;
my $ssl_ca_file = shift;
my $method = shift;
my $data = shift;
my $client;
my ($server, $port, $service) = $uri =~ /https:\/\/(.+)\:(\d+)\/(.+)/;
if (!($client = SOAP::Transport::HTTP::Client->new(
# ssl_opts => {
# verify_hostname => 0,
# SSL_use_cert => 1,
# SSL_verify_mode => 0x02,
# SSL_key_file => $ssl_key_file,
# SSL_cert_file => $ssl_cert_file,
# SSL_ca_file => $ssl_ca_file
#}
))) {errMsg("Sorry, unable to create socket: " . &SOAP::Transport::HTTP::Client::errstr)}
$client->ssl_opts( verify_hostname => 1,
SSL_use_cert => 1,
SSL_verify_mode => 0x02,
SSL_key_file => $ssl_key_file,
SSL_cert_file => $ssl_cert_file,
SSL_ca_file => $ssl_ca_file);
# setting of URI and serialize SOAP envelope and data object
my $soap = SOAP::Lite->uri($service)->proxy($uri);
my $envelope = $soap->serializer->envelope(method => $method);
# setting of TCP URI and send serialized SOAP envelope and data
my $tcp_uri = "https://$server:$port/$service";
my $result = $client->send_receive(envelope => $envelope, endpoint => $tcp_uri);
# check server response
if (!defined $result) {
errMsg("Error: server returned empty response." . "\n" . "Problem with used SSL ceritificates or Warden server at $server:$port is down.");
} else {
# deserialized response from server -> create SOAP envelope and data object
my $response = $soap->deserializer->deserialize($result);
# check SOAP fault status
$response->fault ? errMsg("Server sent error message:: " . $response->faultstring) : return $response;
}
}
#-------------------------------------------------------------------------------
# getClients - get list of registered clients
#-------------------------------------------------------------------------------
sub getClients
{
my $warden_path = shift;
my $etcdir = $warden_path . "/etc/";
my $libdir = $warden_path . "/lib/";
# read the config file
require $libdir . "WardenConf.pm";
my $conf_file = $etcdir . "warden-client.conf";
my ($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file) = WardenConf::loadConf($conf_file);
my $response = c2s($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file, "getClients");
# match getClients functions response
$response->match('/Envelope/Body/getClientsResponse/');
my $i = 1;
my ($client_id, $hostname, $registered, $requestor, $service, $client_type, $type, $receive_own_events, $description_tags, $ip_net_client);
my @clients;
my $response_data = $response->valueof("[$i]");
# parse returned data object
while (defined $response_data) {
# inicialization of temporal client array
my @client;
# parse items of one client
$client_id = $response_data->{'CLIENT_ID'} ;
$hostname = $response_data->{'HOSTNAME'};
$registered = $response_data->{'REGISTERED'};
$requestor = $response_data->{'REQUESTOR'};
$service = $response_data->{'SERVICE'};
$client_type = $response_data->{'CLIENT_TYPE'};
$type = defined $response_data->{'TYPE'} ? $response_data->{'TYPE'} : "";
$receive_own_events = defined $response_data->{'RECEIVE_OWN_EVENTS'} ? $response_data->{'RECEIVE_OWN_EVENTS'} : "";
$description_tags = defined $response_data->{'DESCRIPTION_TAGS'} ? $response_data->{'DESCRIPTION_TAGS'} : "";
$ip_net_client = $response_data->{'IP_NET_CLIENT'};
# push received clients from warden server into @clients which is returned
@client = ("$client_id", "$hostname", "$registered", "$requestor", "$service", "$client_type", "$type", "$receive_own_events", "$description_tags", "$ip_net_client");
push (@clients,\@client);
# go to the next received client
$i++;
$response_data = $response->valueof("[$i]");
}
return @clients;
} # End of getClients
#-------------------------------------------------------------------------------
# getStatus - get warden server status
#-------------------------------------------------------------------------------
sub getStatus
{
my $warden_path = shift;
my $etcdir = $warden_path . "/etc/";
my $libdir = $warden_path . "/lib/";
# read the config file
require $libdir . "WardenConf.pm";
my $conf_file = $etcdir . "warden-client.conf";
my ($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file) = WardenConf::loadConf($conf_file);
my $response = c2s($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file, "getStatus");
# match getStatus functions response
$response->match('/Envelope/Body/getStatusResponse/');
my @status;
my $i = 1;
# get first value from SOAP data object
my $response_data = $response->valueof("[$i]");
# parse items of server status
my $version = $response_data->{'VERSION'};
#my $address = $response_data->{'ADDRESS'};
#my $port = $response_data->{'PORT'};
my $logdir = $response_data->{'LOGDIR'};
#my $piddir = $response_data->{'PIDDIR'};
my $vardir = $response_data->{'VARDIR'};
#my $ssl_key_file_server = $response_data->{'SSL_KEY_FILE'};
#my $ssl_cert_file_server = $response_data->{'SSL_CERT_FILE'};
#my $ssl_ca_file_server = $response_data->{'SSL_CA_FILE'};
my $facility = $response_data->{'FACILITY'};
my $db_size = $response_data->{'DB_SIZE'};
my $events_sum = $response_data->{'EVENTS_SUM'};
my $events_last_id = $response_data->{'EVENTS_LAST_ID'};
my $events_first_timestamp = $response_data->{'EVENTS_FIRST_TIMESTAMP'};
my $events_last_timestamp = $response_data->{'EVENTS_LAST_TIMESTAMP'};
my $clients_sum = $response_data->{'CLIENTS_SUM'};
#my @server_status = ("$version", "$address", "$port", "$logdir", "$piddir", "$vardir", "$ssl_key_file_server", "$ssl_cert_file_server", "$ssl_ca_file_server", "$facility", "$db_size", "$events_sum", "$events_last_id", "$events_first_timestamp", "$events_last_timestamp", "$clients_sum");
my @server_status = ("$version", undef, undef, undef, undef, undef, undef, undef, undef, "$facility", "$db_size", "$events_sum", "$events_last_id", "$events_first_timestamp", "$events_last_timestamp", "$clients_sum");
push(@status, \@server_status);
$i++;
$response_data = $response->valueof("[$i]");
while (defined $response_data) {
my @client_status;
# parse SOAP data object
my $client_id = $response_data->{'CLIENT_ID'};
my $hostname = $response_data->{'HOSTNAME'};
my $service = $response_data->{'SERVICE'};
my $count = $response_data->{'COUNT'};
my $timestamp = $response_data->{'TIMESTAMP'};
@client_status = ("$client_id", "$hostname", "$service", "$count", "$timestamp");
push(@status, \@client_status);
$i++;
$response_data = $response->valueof("[$i]");
}
return @status;
} # End of getStatus
1;
#!/bin/bash
# Pri pouziti Apache + mod_perl se tento soubor nepouziva
#
# create_table.sh
#
# Copyright (C) 2011-2012 Cesnet z.s.p.o
# Author(s): Tomas PLESNIK <plesnik@ics.muni.cz>
# Jan SOUKAL <soukal@ics.muni.cz>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name of the Cesnet z.s.p.o nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# This software is provided ``as is'', and any express or implied
# warranties, including, but not limited to, the implied warranties of
# merchantability and fitness for a particular purpose are disclaimed.
# In no event shall the Cesnet z.s.p.o or contributors be liable for
# any direct, indirect, incidental, special, exemplary, or consequential
# damages (including, but not limited to, procurement of substitute
# goods or services; loss of use, data, or profits; or business
# interruption) however caused and on any theory of liability, whether
# in contract, strict liability, or tort (including negligence or
# otherwise) arising in any way out of the use of this software, even
# if advised of the possibility of such damage.
VERSION="0.1"
sqlite=`which sqlite3`
db_file=$1
# create table events
$sqlite $db_file "CREATE TABLE events (id INTEGER PRIMARY KEY, hostname VARCHAR(256), service VARCHAR(64), detected TIMESTAMP, received TIMESTAMP, type VARCHAR(64), source_type VARCHAR(64), source VARCHAR(256), target_proto VARCHAR(16), target_port INT(2), attack_scale INT(4), note TEXT, priority INT(1), timeout INT(2), valid VARCHAR(1));"
# create table clients
$sqlite $db_file "CREATE TABLE clients (client_id INTEGER PRIMARY KEY, hostname VARCHAR(256), registered TIMESTAMP, requestor VARCHAR(256), service VARCHAR(64), client_type VARCHAR(1), type VARCHAR(64), receive_own_events VARCHAR(1), description_tags VARCHAR(256), ip_net_client VARCHAR(256));"
exit 0
#!/bin/bash
#
# install.sh
#
# Copyright (C) 2011-2012 Cesnet z.s.p.o
# Author(s): Tomas PLESNIK <plesnik@ics.muni.cz>
# Jan SOUKAL <soukal@ics.muni.cz>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name of the Cesnet z.s.p.o nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# This software is provided ``as is'', and any express or implied
# warranties, including, but not limited to, the implied warranties of
# merchantability and fitness for a particular purpose are disclaimed.
# In no event shall the Cesnet z.s.p.o or contributors be liable for
# any direct, indirect, incidental, special, exemplary, or consequential
# damages (including, but not limited to, procurement of substitute
# goods or services; loss of use, data, or profits; or business
# interruption) however caused and on any theory of liability, whether
# in contract, strict liability, or tort (including negligence or
# otherwise) arising in any way out of the use of this software, even
# if advised of the possibility of such damage.
VERSION="0.1"
#-------------------------------------------------------------------------------
# FUNCTIONS
#-------------------------------------------------------------------------------
usage()
{
echo "Usage: `basename $0` [-d <directory>] [-k <ssl_key_file>] [-c <ssl_cert_file>] [-a <ssl_ca_file>] [-hV]"
echo "-d <directory> installation directory (default: /opt)"
echo "-k <ssl_key_file> path to SSL certificate key file"
echo "-c <ssl_cert_file> path to SSL certificate file"
echo "-a <ssl_ca_file> path to CA certificate file"
echo "-h print this help"
echo "-V print script version number and exit"
echo
echo "Example: # ./`basename $0` -d /opt -k /etc/ssl/private/server.key -c /etc/ssl/certs/server.pem -a /etc/ssl/certs/tcs-ca-bundle.pem"
echo
echo "Note: You must be root for running this script."
echo " For more information about installation process, see README file (section Installation)."
echo
exit 0
}
version()
{
echo "`basename ${0}` - current version is $VERSION"
exit 0
}
err()
{
echo "FAILED!"
cat $err
rm -rf $err
echo
echo "Installation of $package_version package FAILED!!!"
exit 1
}
err_clean()
{
echo "FAILED!"
echo " -> Uninstalling server package ... OK"
rm -rf $server_path > /dev/null 2>&1
cat $err
rm -rf $err
echo
echo "Installation of $package_version package FAILED!!!"
exit 1
}
os_chck()
{
OS=`uname`
if [ "$OS" != "Linux" ]; then
echo "Sorry, unsupported operating system detected - \"$OS\"!"
exit 1
fi
}
shell_chck()
{
SHELL=`echo $SHELL`
if [ "$SHELL" != "/bin/bash" ]; then
echo "Sorry, this script is usable in Bourne Again Shell (bash) only!"
exit 1
fi
}
root_chck()
{
if [ $UID -ne 0 ]; then
echo "You must be root for running this script!"
exit 1
fi
}
params_chck()
{
if [ -z $prefix ]; then
prefix=/opt
echo "Warning: parameter -d <directory> is not set - default installation directory is $prefix!"
fi
if [ -z $key ]; then
echo "Parameter -k <ssl_key_file> is not set!"
exit 1
fi
if [ -z $cert ]; then
echo "Parameter -c <ssl_cert_file> is not set!"
exit 1
fi
if [ -z $ca_file ]; then
echo "Parameter -a <ssl_ca_file> is not set!"
exit 1
fi
}
old_package_chck()
{
old_package_version_file="$etc/package_version"
if [ -f $old_package_version_file ]; then
old_package_version=`cat $old_package_version_file`
echo "Sorry, but $old_package_version package is installed!"
echo "For update of warden server package please use update.sh"
exit 1
fi
}
perl_chck()
{
echo -n "Checking Perl interpreter ... "
if which perl 1> /dev/null; then
echo "OK"
else
echo "FAILED!"
echo "Error: Perl interpreter is not installed!"
exit 1
fi
}
modules_chck()
{
for module in ${modules[@]};
do
echo -n "Checking $module module ... "
if perl -e "use $module" 2> $err; then
echo "OK"
else
err
fi
done
}
make_warden_dir()
{
echo -n "Creating warden server directory ... "
test -d ${prefix} || mkdir -p ${prefix}
if cp -R $dirname/warden-server $prefix 2> $err; then
echo "OK"
else
err_clean
fi
files=(CHANGELOG INSTALL LICENSE README)
for file in ${files[@]};
do
cp $dirname/$file $server_path/doc
done
cp $dirname/uninstall.sh $server_path
}
make_client_conf()
{
echo -n "Creating client configuration file ... "
echo "#
# warden-client.conf - configuration file for the warden sender/receiver client
#
#-------------------------------------------------------------------------------
# URI - URI address of Warden server
#-------------------------------------------------------------------------------
\$URI = \"https://${hostname}:443/Warden\";
#-------------------------------------------------------------------------------
# SSL_KEY_FILE - path to client SSL certificate key file
#-------------------------------------------------------------------------------
\$SSL_KEY_FILE = \"${key}\";
#-------------------------------------------------------------------------------
# SSL_CERT_FILE - path to client SSL certificate file
#-------------------------------------------------------------------------------
\$SSL_CERT_FILE = \"${cert}\";
#-------------------------------------------------------------------------------
# SSL_CA_FILE - path to CA certificate file
#-------------------------------------------------------------------------------
\$SSL_CA_FILE = \"${ca_file}\";
" > $client_conf 2> $err; ret_val=`echo $?`
if [ $ret_val -eq 0 ]; then
echo "OK"
else
err_clean
fi
}
make_server_conf()
{
echo -n "Creating server configuration file ... "
echo "#
# warden-server.conf - configuration file for Warden server
#
#-------------------------------------------------------------------------------
# BASEDIR - base directory of Warden server
#-------------------------------------------------------------------------------
\$BASEDIR = \"${server_path}\";
#-------------------------------------------------------------------------------
# FACILITY - syslog facility
#-------------------------------------------------------------------------------
\$FACILITY = \"local7\";
\$DB_NAME = \"warden\";
\$DB_USER = \"username\";
\$DB_PASS = \"\";
\$DB_HOST = \"localhost\";
" > $server_conf 2> $err; ret_val=`echo $?`
if [ $ret_val -eq 0 ]; then
echo "OK"
else
err_clean
fi
}
changeServerPath()
{
echo "Update server path ...";
for file in `ls -1 $bin | grep -v warden-alive | grep -v create_tables.sh | grep -v wardend`
do
echo "- update server path: ${bin}/$file"
perl -pi -e "s#/opt#${prefix}#" ${bin}/$file
done
echo "- update server path: ${apache_conf}"
perl -pi -e "s#/opt#${prefix}#" ${apache_conf}
echo "- update server path: ${lib}/Warden.pm"
perl -pi -e "s#/opt#${prefix}#" ${lib}/Warden.pm
}
updateCertsPath()
{
echo "- update certs path: ${apache_conf}"
perl -pi -e "s#server-cert.pem#${cert}#" ${apache_conf}
perl -pi -e "s#server-key.pem#${key}#" ${apache_conf}
perl -pi -e "s#ca-cert.pem#${ca_file}#" ${apache_conf}
}
#create_db()
#{
# echo -n "Creating warden server database ... "
# $create_tables $db_file 2> $err || err_clean
# if chmod 600 $db_file 2> $err; then
# echo "OK"
# else
# err_clean
# fi
#}
create_symlinks()
{
echo "Creating symbolic links ..."
for file in `ls -1 $bin | grep -v warden-alive | grep -v create_tables.sh | grep -v wardend`
do
echo "- making symlink: ${local_bin}/$file -> ${bin}/$file"
ln -s ${bin}/$file ${local_bin}/$file
done
# echo "- making symlink: ${bin}/wardend -> $init"
# ln -s ${bin}/wardend $init
}
#-------------------------------------------------------------------------------
# MAIN
#-------------------------------------------------------------------------------
# list of used Perl modules
#modules=(SOAP::Lite SOAP::Transport::TCP File::Pid POSIX DBI DBD::SQLite Format::Human::Bytes Sys::Syslog File::Basename FindBin Net::CIDR::Lite DateTime Getopt::Std Switch IO::Socket::SSL)
modules=(DBI DBD::mysql Format::Human::Bytes Sys::Syslog File::Basename FindBin Net::CIDR::Lite DateTime Getopt::Std Switch IO::Socket::SSL MIME::Base64 Crypt::X509)
# OS test
os_chck
# Shell test
shell_chck
# read input
while getopts "d:k:c:a:Vh" options; do
case $options in
d ) prefix=$OPTARG;;
k ) key=$OPTARG;;
c ) cert=$OPTARG;;
a ) ca_file=$OPTARG;;
h ) usage;;
V ) version;;
* ) usage;;
esac
done
# root test
root_chck
# params test
params_chck
# create variables
dirname=`dirname $0`
hostname=`hostname`
key_file=`basename $key`
cert_file=`basename $cert`
package_version=`cat ${dirname}/warden-server/etc/package_version`
create_tables="${dirname}/warden-server/bin/create_tables.sh"
[[ $prefix == */ ]] && prefix="${prefix%?}" # remove last char (slash) from prefix
server_path="${prefix}/warden-server"
bin="${server_path}/bin"
local_bin="/usr/local/bin"
etc="${server_path}/etc"
client_conf="${etc}/warden-client.conf"
server_conf="${etc}/warden-server.conf"
apache_conf="${etc}/warden-apache.conf"
var="${server_path}/var"
lib="${server_path}/lib"
db_file="${var}/warden.db"
err="/tmp/warden-err"
init="/etc/init.d/wardend"
# check if warden-server is installed
old_package_chck
echo
echo "------------------------- Dependencies check-in -------------------------"
# Perl interpreter test
perl_chck
## SQLite database engine test
#sqlite_chck
# Perl modules test
modules_chck
echo
echo "------------------------- Installation process --------------------------"
# make warden client directory
make_warden_dir
# create client configuration file
make_client_conf
# create server configuration file
make_server_conf
## create warden server database
#create_db
#update paths in utilities
changeServerPath
#update paths in apachefile
updateCertsPath
# crate symlinks from warden server bin directory to /usr/local/bin
create_symlinks
echo
echo "Please check client configuration file in $client_conf!"
echo "Please check server configuration file in $server_conf!"
echo
echo "Warden server directory: $server_path"
#echo "Warden server daemon: $init [start|stop|status|restart|force-stop]"
echo
echo "Installation of $package_version package was SUCCESSFUL!!!"
# cleanup section
rm -rf $err
exit 0
#!/bin/bash
#
# uninstall.sh
#
# Copyright (C) 2011-2012 Cesnet z.s.p.o
# Author(s): Tomas PLESNIK <plesnik@ics.muni.cz>
# Jan SOUKAL <soukal@ics.muni.cz>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name of the Cesnet z.s.p.o nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# This software is provided ``as is'', and any express or implied
# warranties, including, but not limited to, the implied warranties of
# merchantability and fitness for a particular purpose are disclaimed.
# In no event shall the Cesnet z.s.p.o or contributors be liable for
# any direct, indirect, incidental, special, exemplary, or consequential
# damages (including, but not limited to, procurement of substitute
# goods or services; loss of use, data, or profits; or business
# interruption) however caused and on any theory of liability, whether
# in contract, strict liability, or tort (including negligence or
# otherwise) arising in any way out of the use of this software, even
# if advised of the possibility of such damage.
VERSION="0.1"
#-------------------------------------------------------------------------------
# FUNCTIONS
#-------------------------------------------------------------------------------
usage()
{
echo "Usage: `basename $0` [-d <directory>] [-hV]"
echo "-d <directory> uninstallation directory (default: /opt)"
echo "-h print this help"
echo "-V print script version number and exit"
echo
echo "Example: # ./`basename $0` -d /opt"
echo
echo "Note: You must be root for running this script."
echo " For more information about uninstallation process, see README file (section Uninstallation)."
echo
exit 0
}
version()
{
echo "`basename ${0}` - current version is $VERSION"
exit 0
}
err()
{
echo "FAILED!"
cat $err
rm -rf $err $backup_dir
echo
echo "Uninstallation of $package_version package FAILED!!!"
exit 1
}
err_clean()
{
echo "FAILED!"
echo " -> Reverting changes of warden server package ... OK"
rm -rf $server_path/* > /dev/null 2>&1 # delete new version
cp -R $backup_dir/* $server_path # copy old backuped server
chmod 600 $db_file # change permission to DB file
for file in `ls -1 $bin | grep -v warden-alive | grep -v create_tables.sh | grep -v wardend`
do
ln -s ${bin}/$file ${local_bin}/$file # create symlinks to /usr/local/bin
done
ln -s ${bin}/wardend $init # create symlink to /etc/init.d/
$init start # start server
cat $err
rm -rf $err $backup_dir
echo
echo "Uninstallation of $package_version package FAILED!!!"
exit 1
}
os_chck()
{
OS=`uname`
if [ "$OS" != "Linux" ]; then
echo "Sorry, unsupported operating system detected - \"$OS\"!"
exit 1
fi
}
shell_chck()
{
SHELL=`echo $SHELL`
if [ "$SHELL" != "/bin/bash" ]; then
echo "Sorry, this script is usable in Bourne Again Shell (bash) only!"
exit 1
fi
}
root_chck()
{
if [ $UID -ne 0 ]; then
echo "You must be root for running this script!"
exit 1
fi
}
params_chck()
{
if [ -z $prefix ]; then
prefix=/opt
echo "Warning: parameter -d <directory> is not set - default uninstallation directory is $prefix!"
fi
}
obtain_package_version()
{
if [ -f $old_package_version_file ]; then
package_version=`cat $old_package_version_file`
else
package_version="unknown"
fi
}
warden_dir_chck()
{
echo -n "Checking warden server directory ... "
if [ ! -d $server_path ]; then
echo "FAILED!"
ls $server_path
exit 1
else
echo "OK"
fi
}
stop_warden_server()
{
echo "Stopping warden server ... "
$init force-stop 1>/dev/null 2>&1
}
backup()
{
echo -n "Backing-up warden server directory ... "
mkdir $backup_dir
if cp -R $server_path $backup_dir 2> $err; then
echo "OK"
else
err
fi
}
delete_symlinks()
{
echo -n "Deleting symlinks from /usr/local/bin ..."
for file in `ls -1 $bin | grep -v warden-alive | grep -v wardend | grep -v create_tables.sh`
do
rm -rf /usr/local/bin/$file 2> /dev/null
done
echo "OK"
echo -n "Deleting symlink from $init ..."
rm -rf $init 2> /dev/null
echo "OK"
}
uninstall_warden_server()
{
echo -n "Uninstalling $package_version package ... "
if rm -rf $server_path 2> $err; then
echo "OK"
else
err_clean
fi
}
#-------------------------------------------------------------------------------
# MAIN
#-------------------------------------------------------------------------------
# OS test
os_chck
# Shell test
shell_chck
# read input
while getopts "d:Vh" options; do
case $options in
d ) prefix=$OPTARG;;
h ) usage;;
V ) version;;
* ) usage;;
esac
done
# root test
root_chck
# params test
params_chck
# create variables
[[ $prefix == */ ]] && prefix="${prefix%?}" # remove last char (slash) from prefix
server_path="${prefix}/warden-server"
bin="${server_path}/bin"
local_bin="/usr/local/bin"
etc="${server_path}/etc"
var="${server_path}/etc"
db_file="${var}/warden.db"
old_package_version_file="${etc}/package_version"
err="/tmp/warden-err"
backup_dir="/tmp/warden-backup"
init="/etc/init.d/wardend"
# obtain version of installed warden-server package
obtain_package_version
echo
echo "------------------------- Uninstallation process --------------------------------"
# check if $prefix/warden-server directory exist
warden_dir_chck
# stop running warden server
stop_warden_server
# make backup of currently installed warden-server package
backup
# delete symbolic links
delete_symlinks
# do uninstallation
uninstall_warden_server
echo
echo "Uninstallation of $package_version package was SUCCESSFUL!!!"
# cleanup section
rm -rf $err $backup_dir
exit 0
BSD License
Copyright © 2011-2015 Cesnet z.s.p.o
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the Cesnet z.s.p.o nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE Cesnet z.s.p.o BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------+
| Warden Client Library 3.0-beta3 |
+---------------------------------+
Content
A. Introduction
B. Quick start (TL;DR)
C. Concepts
D. HTTP/JSON API
E. Python API
------------------------------------------------------------------------------
A. Introduction
The main goal of Warden 3 is to address the shortcomings, which emerged
during several years of Warden 2.X operation. Warden 3 uses flexible and
descriptive event format, based on JSON. Warden 3 protocol is based on plain
HTTPS queries with help of JSON (Warden 2 SOAP is heavyweight, outdated and
draws in many dependencies). Clients can be multilanguage, unlike SOAP/HTTPS,
plain HTTPS and JSON is mature in many mainstream programming languages.
Server is written in Python - mature language with consistent and coherent
libraries and many skilled developers.
------------------------------------------------------------------------------
B. Quick start (TL;DR)
* Obtain X509 key/cert, corresponding with DNS name of you machine.
* Obtain X509 CA chain for server validation.
* Choose client name ("reverse DNS", like org.example.warden.client, but
it does not necessary need to correspond to your machine's DNS name).
* Ask Warden server admins for registration. They will want to know at least
client name and dns name, and short description of the (planned) client
and its purpose. Work with them. They may request some changes or
clarifications, offer you useful guidelines, provide you with alternative
sandbox URL, etc.
If succesful, you will receive authentication secret.
* Use warden_curl_test.sh to check you are able to talk to server.
* See warden_client_examples.py on how to integrate sending/receiving
into your Python application.
* Alternatively, check 'contrib' directory in Warden GIT for various
ready to use tools or recipes. You may find senders for various
honeypots, or warden_filer may get handy if you do not want to delve
into Python at all.
* Welcome! Thanks for participating in in the data exchange to improve
network security awareness.
------------------------------------------------------------------------------
C. Concepts
C.1. Event description format
IDEA - Intrusion Detection Extensible Alert, flexible extensible format
for security events, see:
https://idea.cesnet.cz/
C.2. Event serial ID
Each received event gets assigned integer serial number. These numbers are
sequential, so each recipient can keep track of the last event "id" it
received and next time ask only for following events.
C.3. Authentication
In Warden 2, clients get authenticated by server certificate, however
server certificate is usually same for the whole machine, so individual
clients are differentiated only by telling their own name. However, client name
is widely known, so this allows for client impersonation within one machine.
Warden 3 slightly improves this schema by replacing client name in
authentication phase by "secret", random string, shared among particular
client and main server, which makes it harder to forge client identity (be it
by mistake or intentional).
However, best solution for these cases is of course specific certificate
for each particular client (which is also fully supported).
Client also has to have server CA certificate (or chain) at its disposal
to be able to verify server authenticity.
C.4. Client name
Unlike Warden 2, client names in Warden 3 have hierarchy. Modelled after
Java class names, client name is dot separated list of labels, with
significance from left to right – leftmost denoting largest containing realm,
rightmost denoting single entity.
Country.organisation.suborganizations.machine.local realm scheme akin to
"org.example.csirt.northwest.honeypot.jabberwock" is strongly recommended.
Label case is significant, label can contain only letters, numbers or
underscore and must not start with number.
The reason is the possibility to filter incoming events based not only on
particular client, or (for some recipients flawed) notion of "own" messages,
but based on wider units.
------------------------------------------------------------------------------
D. HTTP/JSON API
Client must know the base URL of the Warden server. Warden 3 accepts
queries on paths under base URL (which correspond to called method), with
usual query string variable=data pairs separated by ampersand as arguments.
Multivalues are specified by repeating same variable with each value several
times.
https://warden.example.org/warden3/getEvents?secret=PwD&cat=Abusive.Spam&cat=Fraud.Phishing
\________________ _______________/ \___ ___/ \____ ___/ \______ _______/ \________ _______/
V V V V V
Base URL --' | | | |
Called method ------------------------' | | |
Key/value pair -----------------------------------' | |
Multivalue ------------------------------------------------'------------------'
Method may expect bulk data (events to save, for example) - query then
must be POST, with POST JSON data, formed appropriately as documented in
particular method.
If HTTPS call succeeds (200 OK), method returns JSON object containing
requested data.
D.1. Error handling
Should the call fail, server returns HTTP status code, together with JSON
object, describing the errors (there may be multiple ones, especially when
sending events). The keys of the object, which may be available, are:
* method - name of the method called
* req_id - unique identifier or the request (for troubleshooting, Warden
administrator can uniquely identify related log lines)
* errors - always present list of JSON objects, which contain:
* error - HTTP status code
* message - human readable error description
* Other context dependent fields may appear, see particular method
description.
Client errors (4xx) are considered permanent - client must not try to send
same event again as it will always get rejected - client administrator
will need to inspect logs and rectify the cause.
Server errors (5xx) may be considered by client as temporary and client is
advised to try again after reasonable recess.
D.2. Common arguments
* secret - shared secret, assigned to client during registration
* client - client name, optional, can be used to mimic Warden 2
authentication behavior if explicitly allowed for this client by server
administrator
= getEvents =
Fetches events from server.
Arguments:
* count - number of requested events
* id - starting serial number requested, id of all received events will
be greater
* cat, nocat - selects only events with categories, which are/are not
present in the event Category field (mutually exclusive)
* group, nogroup - selects only events originated/not originated from
this realms and/or client names, as denoted in the event Node.Name field
(mutually exclusive)
* tag, notag - selects only events with/without this client description
tags, as denoted in the event Node.Type field (mutually exclusive)
Returns:
* lastid - serial number of the last received event
* events - array of Idea events
Example:
$ curl \
--key key.pem \
--cert cert.pem \
--cacert ca.pem \
--connect-timeout 3 \
--request POST \
\
"https://warden.example.org/getEvents?\
secret=SeCrEt\
&count=1\
&nogroup=org.example\
&cat=Abusive.Spam\
&cat=Fraud.Phishing"
{"lastid": 581,
"events": [{
"Format": "IDEA0",
"DetectTime": "2015-02-03T09:55:21.563638Z",
"Target": [{"URL": ["http://example.com/kocHq"]}],
"Category": ["Fraud.Phishing"],
"Note": "Example event"}]}
= sendEvents =
Uploads events to server.
Arguments:
* POST data - JSON array of Idea events
Returns:
Returns object with number of saved messages in "saved" attribute.
In case of error, multiple errors may be returned in "errors" list (see
[[Warden3#Error-handling|Error handling]] section). Each of the error objects
may contain "events" key with list of indexes of events affected by this
particular error. If there is error object without "events" key, caller
must consider all events affected.
Should the call fail because of errors in just couple of events, error
message will contain JSON object in "detail.errors" section. The keys of the
object are indexes into POST data array, values are error messages for each
particular failed Idea event.
Example:
$ eventid=$RANDOM$RANDOM$RANDOM$RANDOM$RANDOM
$ detecttime=$(date --rfc-3339=seconds|tr " " "T")
$ client="cz.example.warden.test"
$ printf '
[
{
"Format": "IDEA0",
"ID": "%s",
"DetectTime": "%s",
"Category": ["Test"],
"Node": [{"Name": "%s"}]
}
]' $eventid $detecttime $client |\
curl \
--key $keyfile \
--cert $certfile \
--cacert $cafile \
--connect-timeout 3 \
--request POST \
--data-binary "@-" \
"https://warden.example.org/sendEvents?client=$client&secret=SeCrEt"
{"saved":1}
(However note that this is not the best way to generate Idea messages. :) )
Example with error:
$ curl \
--key $keyfile \
--cert $certfile \
--cacert $cafile \
--connect-timeout 3 \
--request POST \
--data-binary '[{"Format": "IDEA0","ID":"ASDF","Category":[],"DetectTime":"asdf"}]' \
"https://warden.example.org/sendEvents?client=cz.example.warden.test&secret=SeCrEt"
{"errors":
[
{"message": "Validation error: key \"DetectTime\", value \"asdf\", expected - RFC3339 timestamp.",
"events": [0],
"error": 460
}
],
"method": "sendEvents",
"req_id": 3726454025
}
= getInfo =
Returns basic server information.
Returns:
* version - Warden server version string
* description - server greeting
* send_events_limit - sendEvents will be rejected if client sends more
events in one call
* get_events_limit - getEvents will return at most that much events
Example:
$ curl \
--key key.pem \
--cert cert.pem \
--cacert ca.pem \
--connect-timeout 3 \
--request POST \
"https://warden.example.org/getInfo?secret=SeCrEt"
{"version": "3.0",
"send_events_limit": 500,
"get_events_limit": 1000,
"description": "Warden 3 server"}
E. Python API
Python API tries to abstract from raw HTTPS/URL/JSON details. User
instantiates Client class with necessary settings (certificates, secret,
client name, logging, limits, ...) and then uses its method to access server.
= Client constructor =
wclient = warden.Client(
url,
certfile=None,
keyfile=None,
cafile=None,
timeout=60,
retry=3,
pause=5,
get_events_limit=6000,
send_events_limit=500,
errlog={},
syslog=None,
filelog=None,
idstore=None,
name="org.example.warden_client",
secret=None)
* url - Warden server base URL
* certfile, keyfile, cafile - paths to X509 material
* timeout - network timeout value in seconds
* retry - number retries on transitional errors during sending events
* pause - wait time in seconds between transitional error retries
* get_events_limit - maximum number of events to receive (note that server
may have its own notion)
* send_events_limit - when sending, event lists will be split and sent by
chunks of at most this size (note that used value will get adjusted according
to the limit reported by server)
* errlog - stderr logging configuration dict
* level - most verbose loglevel to log
* syslog - syslog logging configuration dict
* level - most verbose loglevel to log
* socket - syslog socket path (defaults to "/dev/log")
* facility - syslog facility (defaults to "local7")
* filelog - file logging configuration dict
* level - most verbose loglevel to log
* file - path to log file
* idstore - path to simple text file, in which last received event ID gets
stored. If None, server notion is used
* name - client name
* secret - authentication secret
= Configuration file helper =
warden.read_cfg(cfgfile)
Warden object can get initialized from JSON like configuration file. It's
essentially JSON, but full line comments, starting with "#" or "//", are
allowed. read_cfg reads the configuration file and returns dict suitable
for passing as Client constructor arguments.
Usage example:
wclient = warden.Client(**warden.read_cfg("warden_client.cfg"))
= warden.Client.getEvents =
wclient.getEvents(
id=None,
idstore=None,
count=1,
cat=None, nocat=None,
tag=None, notag=None,
group=None, nogroup=None)
* id - can be used to explicitly override value from idstore file
* idstore - can be used to explicitly override idstore for this request
* count - number of requested events
* cat, nocat - selects only events with categories, which are/are not
present in the event Category field (mutually exclusive)
* group, nogroup - selects only events originated/not originated from
this realms and/or client names, as denoted in the event Node.Name field
(mutually exclusive)
* tag, notag - selects only events with/without this client description
tags, as denoted in the event Node.Type field (mutually exclusive)
Returns:
* list of Idea events
= warden.Client.sendEvents =
wclient.sendEvents(self, events=[], retry=None, pause=None):
* events - list of Idea events to be sent to server
* retry - use this retry value just for this call instead from value from
constructor
* pause - use this pause value just for this call instead from value from
constructor
Returns:
* dict with number of sent events under "saved" key
Note:
events list length is limited only by available resources, sendEvents
will split it and send separately in at most send_events_limit long chunks
(however note that sendEvents will also need additional memory for its
internal data structures).
Server errors (5xx) are considered transitional and sendEvents will do
retry number of attempts to deliver corresponding events, delayed by
pause seconds.
Should the call fail because of errors, particular errors may contain
"events" list. Values of the list are then indexes into POST data array. If
no "events" list is present, all events attempted to send must be
considered as failed (with this particular error). See also
[[Warden3#Error-handling|Error handling]] section.
Errors may also contain event IDs from Idea messages in "events_id" list.
This is primarily for logging - client administrator may identify offending
messages by stable identifiers.
= warden.Client.getInfo =
wclient.getInfo()
Returns dictionary of information from getInfo Warden call.
= Error class =
Error(
message,
logger=None,
error=None,
prio="error",
method=None,
req_id=None,
detail=None,
exc=None)
Class, which gets returned in case of client or server error. Caller can
test whether it received data or error by checking:
isinstance(res, Error).
However if he does not want to deal with errors altogether, this error
object also returns False value if used in Bool context and acts as an
empty iterator - in following examples do_stuff() is not evaluated:
if res:
do_stuff(res)
for e in res:
do_stuff(e)
str(Error_Instance) outputs formatted error, info_str() and
debug_str() output increasingly more detailed info.
------------------------------------------------------------------------------
Copyright (C) 2011-2022 Cesnet z.s.p.o
{
"url": "https://warden-hub.example.org/warden3",
"certfile": "cert.pem",
"keyfile": "key.pem",
"filelog": {"level": "debug"},
"name": "org.example.warden_client",
"secret": "ToP_SeCrEt"
}
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011-2015 Cesnet z.s.p.o
# Use of this source is governed by a 3-clause BSD-style license, see LICENSE file.
import hashlib # Some Python/ssl versions incorrectly initialize hashes, this helps
from sys import stderr, exc_info, version_info
import json, ssl, socket, logging, logging.handlers, time
from traceback import format_tb
from os import path
from operator import itemgetter
fix_logging_filename = str if version_info<(2, 7) else lambda x: x
if version_info[0] >= 3:
import http.client as httplib
from urllib.parse import urlparse
from urllib.parse import urlencode
basestring = str
else:
import httplib
from urlparse import urlparse
from urllib import urlencode
VERSION = "3.0-beta3"
DEFAULT_CA_STORES = [
"/etc/ssl/certs/ca-certificates.crt", # Deb
"/etc/pki/tls/certs/ca-bundle.crt", # RH
"/var/lib/ca-certificates/ca-bundle.pem" # SuSE
]
class HTTPSConnection(httplib.HTTPSConnection):
'''
Overridden to allow peer certificate validation, configuration
of SSL/ TLS version and cipher selection. See:
http://hg.python.org/cpython/file/c1c45755397b/Lib/httplib.py#l1144
and `ssl.wrap_socket()`
Used only if ssl.SSLContext is not available (Python version < 2.7.9)
'''
def __init__(self, host, **kwargs):
self.ciphers = kwargs.pop('ciphers', None)
self.ca_certs = kwargs.pop('ca_certs', None)
self.ssl_version = kwargs.pop('ssl_version', getattr(ssl, "PROTOCOL_TLS", ssl.PROTOCOL_SSLv23))
httplib.HTTPSConnection.__init__(self,host,**kwargs)
def connect(self):
sock = socket.create_connection( (self.host, self.port), self.timeout)
if self._tunnel_host:
self.sock = sock
self._tunnel()
self.sock = ssl.wrap_socket(
sock,
keyfile = self.key_file,
certfile = self.cert_file,
ca_certs = self.ca_certs,
cert_reqs = ssl.CERT_REQUIRED if self.ca_certs else ssl.CERT_NONE,
ssl_version = self.ssl_version)
class Error(Exception):
""" Object for returning error messages to calling application.
Caller can test whether it received data or error by checking
isinstance(res, Error).
However if he does not want to deal with errors altogether,
this error object also returns False value if used in Bool
context (e.g. in "if res: print res" print is not evaluated),
and also acts as empty iterator (e.g. in "for e in res: print e"
print is also not evaluated).
Also, it can be raised as an exception.
"""
def __init__(self, method=None, req_id=None, errors=None, **kwargs):
self.errors = []
if errors:
self.extend(method, req_id, errors)
if kwargs:
self.append(method, req_id, **kwargs)
def append(self, method=None, req_id=None, **kwargs):
# We shift method and req_id into each and every error, because
# we want to be able to simply merge more Error arrays (for
# returning errors from more Warden calls at once
if method and not "method" in kwargs:
kwargs["method"] = method
if req_id and not "req_id" in kwargs:
kwargs["req_id"] = req_id
# Ugly, but be paranoid, don't rely on server reply to be well formed
try:
kwargs["error"] = int(kwargs["error"])
except Exception:
kwargs["error"] = 0
if "events" in kwargs:
evlist = kwargs["events"]
try:
evlist_new = []
for ev in evlist:
try:
evlist_new.append(int(ev))
except Exception:
pass
kwargs["events"] = evlist_new
except Exception:
kwargs["events"] = []
if "events_id" in kwargs:
try:
dummy = iter(kwargs["events_id"])
except TypeError:
kwargs["events_id"] = [None]*len(kwargs["events"])
if "send_events_limit" in kwargs:
try:
kwargs["send_events_limit"] = int(kwargs["send_events_limit"])
except Exception:
del kwargs["send_events_limit"]
if "exc" in kwargs:
# Traceback objects cause reference loops, so memory may be not
# correctly free'd. We only need traceback to log it in str_debug(),
# so let's get the string representation now and forget the
# traceback object, thus preventing the loop.
exctype, excvalue, tb = kwargs["exc"]
tb = format_tb(tb)
kwargs["exc"] = exctype, excvalue, tb
self.errors.append(kwargs)
def extend(self, method=None, req_id=None, iterable=[]):
try:
dummy = iter(iterable)
except TypeError:
iterable = [] # Bad joke from server
for e in iterable:
try:
args = dict(e)
except TypeError:
args = {} # Not funny!
self.append(method, req_id, **args)
def __len__ (self):
""" In list or iterable context we're empty """
return 0
def __iter__(self):
""" We are the iterator """
return self
def next(self):
""" In list or iterable context we're empty """
raise StopIteration
__next__ = next
def __bool__(self):
""" In boolean context we're never True """
return False
def __str__(self):
out = []
for e in self.errors:
out.append(self.str_err(e))
out.append(self.str_info(e))
return "\n".join(out)
def log(self, logger=None, prio=logging.ERROR):
if not logger:
logger = logging.getLogger()
for e in self.errors:
logger.log(prio, self.str_err(e))
info = self.str_info(e)
if info:
logger.info(info)
debug = self.str_debug(e)
if debug:
logger.debug(debug)
def str_preamble(self, e):
return "%08x/%s" % (e.get("req_id", 0), e.get("method", "?"))
def str_err(self, e):
out = []
out.append(self.str_preamble(e))
out.append(" Error(%s) %s " % (e.get("error", 0), e.get("message", "Unknown error")))
if "exc" in e and e["exc"]:
out.append("(cause was %s: %s)" % (e["exc"][0].__name__, str(e["exc"][1])))
return "".join(out)
def str_info(self, e):
ecopy = dict(e) # shallow copy
ecopy.pop("req_id", None)
ecopy.pop("method", None)
ecopy.pop("error", None)
ecopy.pop("message", None)
ecopy.pop("exc", None)
if ecopy:
out = "%s Detail: %s" % (self.str_preamble(e), json.dumps(ecopy, default=lambda v: str(v)))
else:
out = ""
return out
def str_debug(self, e):
out = []
out.append(self.str_preamble(e))
if not "exc" in e or not e["exc"]:
return ""
exc_tb = e["exc"][2] # exc_tb is string repr. of traceback object
if exc_tb:
out.append("Traceback:\n")
out.extend(exc_tb)
return "".join(out)
class Client(object):
def __init__(self,
url,
certfile=None,
keyfile=None,
cafile=None,
timeout=60,
retry=20,
pause=10,
get_events_limit=6000,
send_events_limit=500,
errlog={},
syslog=None,
filelog=None,
idstore=None,
name="org.example.warden.test",
secret=None):
self.name = name
self.secret = secret
# Init logging as soon as possible and make sure we don't
# spit out exceptions but just log or return Error objects
self.init_log(errlog, syslog, filelog)
self.url = urlparse(url, allow_fragments=False)
self.conn = None
base = path.join(path.dirname(__file__))
self.certfile = self.get_readable_file(certfile or "cert.pem", base)
self.keyfile = self.get_readable_file(keyfile or "key.pem", base)
self.cafile = self.get_readable_file(cafile if cafile is not None else DEFAULT_CA_STORES, base)
self.timeout = int(timeout)
self.get_events_limit = int(get_events_limit)
self.idstore = path.join(base, idstore) if idstore is not None else None
self.send_events_limit = int(send_events_limit)
self.retry = int(retry)
self.pause = int(pause)
self.ciphers = None
self.sslversion = getattr(ssl, "PROTOCOL_TLS", ssl.PROTOCOL_SSLv23)
# If Python is new enough to have SSLContext, use it for SSL settings,
# otherwise our own class derived from httplib.HTTPSConnection is used
# later in connect().
if hasattr(ssl, 'SSLContext'):
self.sslcontext = ssl.SSLContext(self.sslversion)
self.sslcontext.load_cert_chain(self.certfile, self.keyfile)
if self.cafile:
self.sslcontext.load_verify_locations(self.cafile)
self.sslcontext.verify_mode = ssl.CERT_REQUIRED
else:
self.sslcontext.verify_mode = ssl.CERT_NONE
else:
self.sslcontext = None
self.getInfo() # Call to align limits with server opinion
def get_readable_file(self, name, base):
names = [name] if isinstance(name, basestring) else name
names = [path.join(base, n) for n in names]
errors = []
for n in names:
try:
open(n, "r").close()
self.logger.debug("Using %s" % n)
return n
except IOError as e:
errors.append(e)
for e in errors:
self.logger.error(str(e))
return names[0] if names else None
def init_log(self, errlog, syslog, filelog):
def loglevel(lev):
try:
return int(getattr(logging, lev.upper()))
except (AttributeError, ValueError):
self.logger.warning("Unknown loglevel \"%s\", using \"debug\"" % lev)
return logging.DEBUG
def facility(fac):
try:
return int(getattr(logging.handlers.SysLogHandler, "LOG_" + fac.upper()))
except (AttributeError, ValueError):
self.logger.warning("Unknown syslog facility \"%s\", using \"local7\"" % fac)
return logging.handlers.SysLogHandler.LOG_LOCAL7
form = "%(filename)s[%(process)d]: %(name)s (%(levelname)s) %(message)s"
format_notime = logging.Formatter(form)
format_time = logging.Formatter('%(asctime)s ' + form)
self.logger = logging.getLogger(self.name)
self.logger.propagate = False # Don't bubble up to root logger
self.logger.setLevel(logging.DEBUG)
if errlog is not None:
el = logging.StreamHandler(stderr)
el.setFormatter(format_time)
el.setLevel(loglevel(errlog.get("level", "info")))
self.logger.addHandler(el)
if filelog is not None:
try:
fl = logging.FileHandler(
filename=path.join(
path.dirname(__file__),
filelog.get("file", "%s.log" % self.name)),
encoding="utf-8")
fl.setLevel(loglevel(filelog.get("level", "debug")))
fl.setFormatter(format_time)
self.logger.addHandler(fl)
except Exception as e:
Error(message="Unable to setup file logging", exc=exc_info()).log(self.logger)
if syslog is not None:
try:
sl = logging.handlers.SysLogHandler(
address=fix_logging_filename(syslog.get("socket", "/dev/log")),
facility=facility(syslog.get("facility", "local7")))
sl.setLevel(loglevel(syslog.get("level", "debug")))
sl.setFormatter(format_notime)
self.logger.addHandler(sl)
except Exception as e:
Error(message="Unable to setup syslog logging", exc=exc_info()).log(self.logger)
if not (errlog or filelog or syslog):
# User wants explicitly no logging, so let him shoot his socks off.
# This silences complaining of logging module about no suitable
# handler.
self.logger.addHandler(logging.NullHandler())
def log_err(self, err, prio=logging.ERROR):
if isinstance(err, Error):
err.log(self.logger, prio)
return err
def connect(self):
try:
if self.url.scheme=="https":
if self.sslcontext:
conn = httplib.HTTPSConnection(
self.url.netloc,
timeout = self.timeout,
context = self.sslcontext)
else:
conn = HTTPSConnection(
self.url.netloc,
key_file = self.keyfile,
cert_file = self.certfile,
timeout = self.timeout,
ciphers = self.ciphers,
ca_certs = self.cafile,
ssl_version = self.sslversion)
elif self.url.scheme=="http":
conn = httplib.HTTPConnection(
self.url.netloc,
timeout = self.timeout)
else:
return Error(message="Don't know how to connect to \"%s\"" % self.url.scheme,
url=self.url.geturl())
except Exception:
return Error(message="HTTP(S) connection failed", exc=exc_info(),
url=self.url.geturl(),
timeout=self.timeout,
key_file=self.keyfile,
cert_file=self.certfile,
cafile=self.cafile,
ciphers=self.ciphers,
ssl_version=self.sslversion)
return conn
def sendRequest(self, func="", payload=None, **kwargs):
if self.secret is None:
kwargs["client"] = self.name
else:
kwargs["secret"] = self.secret
if kwargs:
for k in list(kwargs.keys()):
if kwargs[k] is None:
del kwargs[k]
argurl = "?" + urlencode(kwargs, doseq=True)
else:
argurl = ""
self.headers = {"Accept": "application/json"}
data = None
if payload is None:
method = "GET"
else:
method = "POST"
try:
data = json.dumps(payload)
except:
return Error(message="Serialization to JSON failed",
exc=exc_info(), method=func, payload=payload)
self.headers.update({
"Content-Type": "application/json",
"Content-Length": str(len(data))
})
# HTTP(S)Connection is oneshot object (and we don't speak "pipelining")
conn = self.connect()
if not conn:
return conn # either False of Error instance
loc = '%s/%s%s' % (self.url.path, func, argurl)
try:
conn.request(method, loc, data, self.headers)
except:
conn.close()
return Error(message="Sending of request to server failed",
exc=exc_info(), method=func, log=loc, headers=self.headers, data=data)
try:
res = conn.getresponse()
except:
conn.close()
return Error(method=func, message="HTTP reply failed",
exc=exc_info(), loc=loc, headers=self.headers, data=data)
try:
response_data = res.read()
except:
conn.close()
return Error(method=func, message="Fetching HTTP data from server failed",
exc=exc_info(), loc=loc, headers=self.headers, data=data)
conn.close()
if res.status==httplib.OK:
try:
data = json.loads(response_data.decode("utf-8"))
except:
data = Error(method=func, message="JSON message parsing failed",
exc=exc_info(), response=response_data)
else:
try:
data = json.loads(response_data.decode("utf-8"))
data["errors"] # trigger exception if not dict or no error key
except:
data = Error(method=func, message="Generic server HTTP error",
error=res.status, exc=exc_info(), response=response_data)
else:
data = Error(
method=data.get("method", None),
req_id=data.get("req_id", None),
errors=data.get("errors", []))
return data
def _saveID(self, id, idstore=None):
idf = idstore or self.idstore
if not idf:
return False
try:
with open(idf, "w+") as f:
f.write(str(id))
except (ValueError, IOError) as e:
# Use Error instance just for proper logging
Error(message="Writing id file \"%s\" failed" % idf, exc=exc_info(),
idstore=idf).log(self.logger, logging.INFO)
return id
def _loadID(self, idstore=None):
idf = idstore or self.idstore
if not idf:
return None
try:
with open(idf, "r") as f:
id = int(f.read())
except (ValueError, IOError) as e:
Error(message="Reading id file \"%s\" failed, relying on server" % idf,
exc=exc_info(), idstore=idf).log(self.logger, logging.INFO)
id = None
return id
def getDebug(self):
return self.log_err(self.sendRequest("getDebug"))
def getInfo(self):
res = self.sendRequest("getInfo")
if isinstance(res, Error):
res.log(self.logger)
else:
try:
self.send_events_limit = min(res["send_events_limit"], self.send_events_limit)
self.get_events_limit = min(res["get_events_limit"], self.get_events_limit)
except (AttributeError, TypeError, KeyError):
pass
return res
def send_events_raw(self, events=[]):
return self.sendRequest("sendEvents", payload=events)
def send_events_chunked(self, events=[]):
""" Split potentially long "events" list to send_events_limit
long chunks to avoid slap from server.
"""
count = len(events)
err = Error()
send_events_limit = self.send_events_limit # object stored value can change during sending
for offset in range(0, count, send_events_limit):
res = self.send_events_raw(events[offset:min(offset+send_events_limit, count)])
if isinstance(res, Error):
# Shift all error indices by offset to correspond with 'events' list
for e in res.errors:
evlist = e.get("events", [])
# Update sending limit advice, if present in error
srv_limit = e.get("send_events_limit")
if srv_limit:
self.send_events_limit = min(self.send_events_limit, srv_limit)
for i in range(len(evlist)):
evlist[i] += offset
err.errors.extend(res.errors)
return err if err.errors else {}
def sendEvents(self, events=[], retry=None, pause=None):
""" Send out "events" list to server, retrying on server errors.
"""
ev = events
idx_xlat = list(range(len(ev)))
err = Error()
retry = retry or self.retry
attempt = retry
while ev and attempt:
if attempt<retry:
self.logger.info("%d transient errors, retrying (%d to go)" % (len(ev), attempt))
time.sleep(pause or self.pause)
res = self.send_events_chunked(ev)
attempt -= 1
next_ev = []
next_idx_xlat = []
if isinstance(res, Error):
# Sort to process fatal errors first
res.errors.sort(key=itemgetter("error"))
for e in res.errors:
errno = e["error"]
evlist = e.get("events", list(range(len(ev)))) # none means all
if errno < 500 or not attempt:
# Fatal error or last try, translate indices
# to original and prepare for returning to caller
for i in range(len(evlist)):
evlist[i] = idx_xlat[evlist[i]]
err.errors.append(e)
else:
# Maybe transient error, prepare to try again
for evlist_i in evlist:
next_ev.append(ev[evlist_i])
next_idx_xlat.append(idx_xlat[evlist_i])
ev = next_ev
idx_xlat = next_idx_xlat
return self.log_err(err) if err.errors else {"saved": len(events)}
def getEvents(self, id=None, idstore=None, count=None,
cat=None, nocat=None,
tag=None, notag=None,
group=None, nogroup=None):
if id is None:
id = self._loadID(idstore)
res = self.sendRequest(
"getEvents", id=id, count=self.get_events_limit if count is None else count, cat=cat,
nocat=nocat, tag=tag, notag=notag, group=group, nogroup=nogroup)
if res:
try:
events = res["events"]
newid = res["lastid"]
except KeyError:
events = Error(method="getEvents", message="Server returned bogus reply",
exc=exc_info(), response=res)
self._saveID(newid)
else:
events = res
return self.log_err(events)
def close(self):
if hasattr(self, "conn") and hasattr(self.conn, "close"):
self.conn.close()
__del__ = close
def format_timestamp(epoch=None, utc=True, utcoffset=None):
if utcoffset is None:
utcoffset = -(time.altzone if time.daylight else time.timezone)
if epoch is None:
epoch = time.time()
if utc:
epoch += utcoffset
us = int(epoch % 1 * 1000000 + 0.5)
return format_time(*time.gmtime(epoch)[:6], microsec=us, utcoffset=utcoffset)
def format_time(year, month, day, hour, minute, second, microsec=0, utcoffset=None):
if utcoffset is None:
utcoffset = -(time.altzone if time.daylight else time.timezone)
tstr = "%04d-%02d-%02dT%02d:%02d:%02d" % (year, month, day, hour, minute, second)
usstr = "." + str(microsec).rstrip("0") if microsec else ""
offsstr = ("%+03d:%02d" % divmod((utcoffset+30)//60, 60)) if utcoffset else "Z"
return tstr + usstr + offsstr
def read_cfg(cfgfile):
with open(cfgfile, "r") as f:
stripcomments = "\n".join((l for l in f if not l.lstrip().startswith(("#", "//"))))
return json.loads(stripcomments)
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011-2015 Cesnet z.s.p.o
# Use of this source is governed by a 3-clause BSD-style license, see LICENSE file.
import string
from time import time
from uuid import uuid4
from pprint import pprint
from random import randint, randrange, choice, random
from base64 import b64encode
from warden_client import Client, Error, read_cfg, format_timestamp
def gen_min_idea():
return {
"Format": "IDEA0",
"ID": str(uuid4()),
"DetectTime": format_timestamp(),
"Category": ["Test"],
}
def gen_random_idea(client_name="cz.example.warden.test"):
def geniprange(gen):
def iprange():
u = v = 0
while u==v:
u, v = gen(), gen()
u, v = min(u, v), max(u, v)
return "%s-%s" % (u, v)
return iprange
def rand4ip():
return "%s%d" % ('192.0.2.', randint(1, 254))
def rand4cidr():
return "%s%d/%d" % ('192.0.2.', randint(1, 254), randint(24, 31))
def randip4():
return [rand4ip, geniprange(rand4ip), rand4cidr][randint(0, 2)]()
def rand6ip():
return "2001:DB8:%s" % ":".join("%x" % randint(0, 65535) for i in range(6))
def rand6cidr():
m = randint(0, 5)
return "2001:DB8%s%s::/%d" % (":" if m else "", ":".join("%x" % randint(0, 65535) for i in range(m)), (m+2)*16)
def randip6():
return [rand6ip, geniprange(rand6ip), rand6cidr][randint(0, 2)]()
def randstr(charlist=string.ascii_letters, maxlen=32, minlen=1):
return ''.join(choice(charlist) for i in range(randint(minlen, maxlen)))
event = {
"Format": "IDEA0",
"ID": str(uuid4()),
"CreateTime": format_timestamp(),
"DetectTime": format_timestamp(),
"WinStartTime": format_timestamp(),
"WinEndTime": format_timestamp(),
"EventTime": format_timestamp(),
"CeaseTime": format_timestamp(),
#"Category": ["Abusive.Spam","Abusive.Harassment","Malware","Fraud.Copyright","Test","Fraud.Phishing","Fraud.Scam"],
# "Category": ["Abusive.Spam","Fraud.Copyright"],
"Category": [choice(["Abusive.Spam","Abusive.Harassment","Malware","Fraud.Copyright","Test","Fraud.Phishing","Fraud.Scam"]) for dummy in range(randint(1, 3))],
"Ref": ["cve:CVE-%s-%s" % (randstr(string.digits, 4), randstr()), "http://www.example.com/%s" % randstr()],
"Confidence": random(),
"Note": "Random event",
"ConnCount": randint(0, 65535),
# "ConnCount": choice([randint(0, 65535), "asdf"]), # Send wrong event sometimes
"Source": [
{
"Type": ["Phishing"],
"IP4": [randip4() for i in range(randrange(1, 5))],
"IP6": [randip6() for i in range(randrange(1, 5))],
"Hostname": ["example.com"],
"Port": [randint(1, 65535) for i in range(randrange(1, 3))],
"AttachHand": ["att1"],
"Netname": ["arin:TEST-NET-1"]
}
],
"Target": [
{
"IP4": [randip4() for i in range(randrange(1, 5))],
"IP6": [randip6() for i in range(randrange(1, 5))],
"URL": ["http://example.com/%s" % randstr()],
"Proto": ["tcp", "http"],
"Netname": ["arin:TEST-NET-1"]
}
],
"Attach": [
{
"Handle": "att1",
"FileName": [randstr()],
"Type": ["Malware"],
"ContentType": "application/octet-stream",
"Hash": ["sha1:%s" % randstr(string.hexdigits, 24)],
"Size": 46,
"Ref": ["cve:CVE-%s-%s" % (randstr(string.digits, 4), randstr())],
"ContentEncoding": "base64",
"Content": b64encode(randstr().encode('ascii')).decode("ascii")
}
],
"Node": [
{
"Name": client_name,
"Type": [choice(["Data", "Protocol", "Honeypot", "Heuristic", "Log"]) for dummy in range(randint(1, 3))],
"SW": ["Kippo"],
"AggrWin": "00:05:00"
},
{
"Name": "org.example.warden.client",
"Type": [choice(["Connection", "Datagram"]) for dummy in range(randint(1, 2))],
}
]
}
return event
def main():
wclient = Client(**read_cfg("warden_client.cfg"))
# Also inline arguments are possible:
# wclient = Client(
# url = 'https://warden.example.com/warden3',
# keyfile = '/opt/warden3/etc/key.pem',
# certfile = '/opt/warden3/etc/cert.pem',
# cafile = '/opt/warden3/etc/tcs-ca-bundle.pem',
# timeout=10,
# errlog={"level": "debug"},
# filelog={"level": "debug"},
# idstore="MyClient.id",
# name="cz.example.warden.test")
print("=== Debug ===")
info = wclient.getDebug()
pprint(info)
# All methods return something.
# If you want to catch possible errors (for example implement some
# form of persistent retry, or save failed events for later, you may
# check for Error instance and act based on contained info.
# If you want just to be informed, this is not necessary, just
# configure logging correctly and check logs.
if isinstance(info, Error):
print(info)
print("=== Server info ===")
info = wclient.getInfo()
print("=== Sending 10 event(s) ===")
start = time()
ret = wclient.sendEvents([gen_random_idea(client_name=wclient.name) for i in range(10)])
print(ret)
print("Time: %f" % (time()-start))
print("=== Getting 10 events ===")
start = time()
# cat = ['Availability', 'Abusive.Spam','Attempt.Login']
# cat = ['Attempt', 'Information','Fraud.Scam','Malware.Virus']
# cat = ['Fraud', 'Abusive.Spam']
# nocat = ['Availability', 'Information', 'Fraud.Scam']
cat = []
nocat = []
#tag = ['Log', 'Data']
#notag = ['Flow', 'Datagram']
tag = []
notag = []
#group = ['cz.tul.ward.kippo','cz.vsb.buldog.kippo']
#nogroup = ['cz.zcu.civ.afrodita','cz.vutbr.net.bee.hpscan']
group = []
nogroup = []
ret = wclient.getEvents(count=10, cat=cat, nocat=nocat, tag=tag, notag=notag, group=group, nogroup=nogroup)
print("Time: %f" % (time()-start))
print("Got %i events" % len(ret))
for e in ret:
print(e.get("Category"), e.get("Node")[0].get("Type"), e.get("Node")[0].get("Name"))
if __name__ == "__main__":
main()