Zabbix: erste Version

This commit is contained in:
Joerg Deckert 2018-09-08 18:46:45 +02:00
parent 5672dd82e1
commit d00aa2ecc5
23 changed files with 4287 additions and 0 deletions

22
zabbix/00-eth0.network Normal file
View File

@ -0,0 +1,22 @@
# Beispiel für feste IP-Adreß-Konfiguration:
# Anpassen und als 00-eth0.network nach /etc/systemd/network verschieben
# (s. "man systemd.network", "man systemd-resolved")
#
# NICHT VERGESSEN: entsprechende Einträge in /etc/hosts hinzufügen
# <IPv4> <FQDN> <Hostname>
# <IPv6> <FQDN> <Hostname>
[Match]
Name=eth0
[Network]
Description=1. Netzwerk-Port
Address=192.168.1.2/24
Address=fdb5:78b:64cc:0:f8c0::2/64
Gateway=192.168.1.1
Gateway=fdb5:78b:64cc:0:f8c0::1
DNS=192.168.1.3
DNS=fdb5:78b:64cc:0:f8c0::3
NTP=192.168.1.4
NTP=fdb5:78b:64cc:0:f8c0::4
Domains=privacyidea.de

95
zabbix/Makefile Normal file
View File

@ -0,0 +1,95 @@
preinstall:
# hardcoded users and groups
$(inroot) useradd --system --comment="created from appliance building - zabbix user" --home-dir="/var/lib/zabbix/home" --shell="/sbin/nologin" --no-create-home --uid 600 --user-group zabbix
# switch to hardened, build hardened toolchain, rebuild everything
mkdir -p $(CHROOT)/etc/portage/profile
echo "-hardened" >> $(CHROOT)/etc/portage/profile/use.mask
$(inroot) $(EMERGE) $(USEPKG) --oneshot gcc
$(inroot) $(EMERGE) $(USEPKG) --oneshot binutils virtual/libc
-$(gcc_config)
$(inroot) $(EMERGE) $(USEPKG) --emptytree @world
$(inroot) bash -c 'yes YES | etc-update --automode -9'
# Unitas-Portage-Overlay einbinden
$(inroot) $(EMERGE) -n $(USEPKG) app-portage/layman
sed -i 's/check_official : Yes/check_official : No/' $(CHROOT)/etc/layman/layman.cfg
wget -P $(CHROOT)/etc/layman/overlays http://dev.unitas-network.de/raw/Gentoo/Unitas.git/master/unitas-overlays.xml
$(inroot) layman -l | grep -q unitas || $(inroot) layman -La unitas
postinstall: timesyncd.conf firstboot.start
# Konfigurationen anpassen
cp timesyncd.conf $(CHROOT)/etc/systemd/timesyncd.conf
mkdir -p $(CHROOT)/etc/local.d
cp firstboot.start $(CHROOT)/etc/local.d/firstboot.start
touch $(CHROOT)/firstboot
sed -i 's/# %wheel ALL=(ALL) ALL/%wheel ALL=(ALL) ALL/' $(CHROOT)/etc/sudoers
$(inroot) useradd -m -G users,wheel -s /bin/bash admin
$(inroot) passwd -d admin; $(inroot) passwd -e admin
$(inroot) systemctl enable screen@adm.service
# Beispiel feste IP-Adresse
cp 00-eth0.network $(CHROOT)/00-eth0.network.example
# MariaDB-Konfiguration
cp mariadb/my.cnf $(CHROOT)/etc/mysql/my.cnf
cp mariadb/my.cnf.root $(CHROOT)/root/.my.cnf
chmod 0600 $(CHROOT)/root/.my.cnf
rm -rf $(CHROOT)/var/lib/mysql/*
$(inroot) bash -c 'yes gentoo | emerge --config dev-db/mariadb'
# Apache-/PHP-Konfiguration
sed -i 's:APACHE2_OPTS=\":APACHE2_OPTS=\"-D PHP :' $(CHROOT)/etc/conf.d/apache2
find $(CHROOT)/etc/php/apache2-*/ -iname php.ini -print | xargs \sed -i \
-e 's:.*date.timezone =.*:date.timezone = Europe/Berlin:' \
-e 's:.*max_execution_time =.*:max_execution_time = 300:' \
-e 's:.*max_input_time =.*:max_input_time = 300:' \
-e 's:.*post_max_size =.*:post_max_size = 16M:' \
-e 's:.*always_populate_raw_post_data =.*:always_populate_raw_post_data = -1:'
$(inroot) systemctl enable apache2
# Add zabbix service definitions
echo "zabbix-agent 10050/tcp Zabbix Agent" >> $(CHROOT)/etc/services
echo "zabbix-agent 10050/udp Zabbix Agent" >> $(CHROOT)/etc/services
echo "zabbix-trapper 10051/tcp Zabbix Trapper" >> $(CHROOT)/etc/services
echo "zabbix-trapper 10051/udp Zabbix Trapper" >> $(CHROOT)/etc/services
# Install Zabbix webapp
$(inroot)webapp-config -h localhost -d zabbix -I zabbix `ls $(CHROOT)/usr/share/webapps/zabbix`
cp $(CHROOT)/var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php.example $(CHROOT)/var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php
# Zabbix Agent
cp zabbix/userparameter_mysql.conf $(CHROOT)/var/lib/zabbix/userparameter_mysql.conf
# Zabbix Syslog (https://github.com/v-zhuravlev/zabbix-syslog)
mkdir -p $(CHROOT)/etc/zabbix/scripts/lib
cp zabbix/zabbix-syslog/zabbix_syslog_create_urls.pl $(CHROOT)/etc/zabbix/scripts/zabbix_syslog_create_urls.pl
cp zabbix/zabbix-syslog/zabbix_syslog_lkp_host.pl $(CHROOT)/etc/zabbix/scripts/zabbix_syslog_lkp_host.pl
cp zabbix/zabbix-syslog/zabbix_syslog.cfg $(CHROOT)/etc/zabbix/zabbix_syslog.cfg
cp zabbix/zabbix-syslog/lib/ZabbixAPI.pm $(CHROOT)/etc/zabbix/scripts/lib/ZabbixAPI.pm
cp zabbix/zabbix-syslog/70-zabbix_rsyslog.conf $(CHROOT)/etc/rsyslog.d/70-zabbix_rsyslog.conf
$(inroot)chown -R zabbix:zabbix /etc/zabbix/scripts
chmod +x $(CHROOT)/etc/zabbix/scripts/zabbix_syslog_create_urls.pl
chmod +x $(CHROOT)/etc/zabbix/scripts/zabbix_syslog_lkp_host.pl
# FPing
$(inroot)chmod u=rwsx,g=rx,o=rx /usr/sbin/fping
$(inroot)chmod u=rwsx,g=rx,o=rx /usr/sbin/fping6
# SNMP
cp snmp/snmpd.conf $(CHROOT)/etc/snmp/snmpd.conf
cp snmp/snmptrapd.conf $(CHROOT)/etc/snmp/snmptrapd.conf
cp snmp/snmptt.conf $(CHROOT)/etc/snmp/snmptt.conf
sed -i \
-e 's:net_snmp_perl_enable = 0:net_snmp_perl_enable = 1:' \
-e 's:translate_integers = 1:translate_integers = 0:' \
-e 's:#mibs_environment = ALL:mibs_environment = ALL:' \
-e 's.#date_time_format =.date_time_format = %H:%M:%S %Y/%m/%d.' \
-e 's:daemon_uid = snmptt:daemon_uid = zabbix:' \
-e 's:log_system_enable = 0:log_system_enable = 1:' \
-e 's:unknown_trap_log_enable = 0:unknown_trap_log_enable = 1:' \
$(CHROOT)/etc/snmp/snmptt.ini
mkdir -p $(CHROOT)/var/log/snmptt
$(inroot)chmod 0775 /var/log/snmptt
$(inroot)chown zabbix:zabbix /var/log/snmptt
clean:

9
zabbix/README.md Normal file
View File

@ -0,0 +1,9 @@
Erstkonfiguration
=================
- für variable Daten (MySQL/MariaDB, Konfiguration) muß eine mit ext4 formatierte Datenpartition mit dem Label "DATA" vorhanden sein. Diese wird nach /DATA gemountet.
- feste IP-Adresse und /etc/hosts konfigurieren
- evtl. Apache-Zertifikat neu erzeugen
- unter VMware evtl. open-vm-tools aktivieren
- Evt. /etc/mysql/my.cnf anpassen (~80% vom RAM): innodb_buffer_pool_size = 2G

73
zabbix/firstboot.start Executable file
View File

@ -0,0 +1,73 @@
#!/bin/bash
# base settings
set -e
[ -e /firstboot ] || exit 0
echo 'Setting defaults...'
localectl --no-convert set-keymap de-latin1-nodeadkeys
echo 'Activate services...'
timedatectl set-ntp true
# variables
LABEL="DATA"
DATABASE_PASS="Di1sgMySQLPwd."
# Data partition
echo 'Mount data partition...'
mkdir -p /$LABEL
if [ ! -L "/dev/disk/by-label/$LABEL" ]; then
echo 'ERROR: Data partition not found!'
echo "Please create a data partition with ext4 filesystem and label \"$LABEL\":"
echo "# cfdisk /dev/<disk> (use GPT label, create linux partition)"
echo "# mkfs.ext4 -L $LABEL /dev/<partition>"
exit 1
fi
if ! grep -Fq "LABEL=$LABEL" /etc/fstab; then
echo "LABEL=$LABEL /$LABEL ext4 noatime 0 1" >> /etc/fstab
fi
mount -a
if ! mount | grep /$LABEL > /dev/null; then
echo "ERROR: Could not mount data partition!"
exit 1
fi
if [ ! -d "/$LABEL/var/lib/mysql/zabbix" ]; then
echo 'Initialize MariaDB...'
systemctl stop mariadb
mkdir -p /$LABEL/var/lib/mysql
rm -rf /$LABEL/var/lib/mysql/*
cp -a /var/lib/mysql/. /$LABEL/var/lib/mysql
sed -i "s:^datadir.*:datadir = /$LABEL/var/lib/mysql:" /etc/mysql/my.cnf
systemctl start mariadb
sleep 5
echo 'Create Zabbix database...'
mysql -u root -e "CREATE USER 'zabbix'@'localhost' IDENTIFIED BY '$DATABASE_PASS'"
mysql -u root -e "CREATE DATABASE zabbix DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;"
mysql -u root -e "GRANT ALL PRIVILEGES ON zabbix.* TO 'zabbix'@'localhost' IDENTIFIED by '$DATABASE_PASS';"
mysql -u root -e "FLUSH PRIVILEGES;"
echo 'Import Zabbix MySQL data'
mysql -u root zabbix < /usr/share/zabbix/database/mysql/schema.sql
mysql -u root zabbix < /usr/share/zabbix/database/mysql/images.sql
mysql -u root zabbix < /usr/share/zabbix/database/mysql/data.sql
else
echo 'Start MariaDB...'
sed -i "s:^datadir.*:datadir = /$LABEL/var/lib/mysql:" /etc/mysql/my.cnf
systemctl start mariadb
fi
echo 'Enable database...'
systemctl enable mariadb
echo 'Start Zabbix...'
sed -i "s:# DBPassword=:DBPassword=${DATABASE_PASS}:" /etc/zabbix/zabbix_server.conf
sed -i "s:\$DB\['PASSWORD'\].*:\$DB\['PASSWORD'\] = '${DATABASE_PASS}';:" /var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php
systemctl start zabbix-server
systemctl enable zabbix-server
systemctl start zabbix-agentd
systemctl enable zabbix-agentd
rm /firstboot

2754
zabbix/kernel.config Normal file

File diff suppressed because it is too large Load Diff

6
zabbix/make.conf Normal file
View File

@ -0,0 +1,6 @@
CFLAGS="-O2 -pipe"
CXXFLAGS="-O2 -pipe"
USE="hardened justify pie ssp urandom xattr -fortran -jit -orc -pch -pic -prelink -profile -tcc"
MAKEOPTS="-j5"
PYTHON_TARGETS="python2_7 python3_6"
PHP_TARGETS="php7-0 php7-1"

139
zabbix/mariadb/my.cnf Normal file
View File

@ -0,0 +1,139 @@
# /etc/mysql/my.cnf: The global mysql configuration file.
# $Id$
# The following options will be passed to all MySQL clients
[client]
#password = your_password
port = 3306
socket = /var/run/mysqld/mysqld.sock
[mysql]
character-sets-dir=/usr/share/mysql/charsets
default-character-set=utf8
[mysqladmin]
character-sets-dir=/usr/share/mysql/charsets
default-character-set=utf8
[mysqlcheck]
character-sets-dir=/usr/share/mysql/charsets
default-character-set=utf8
[mysqldump]
character-sets-dir=/usr/share/mysql/charsets
default-character-set=utf8
[mysqlimport]
character-sets-dir=/usr/share/mysql/charsets
default-character-set=utf8
[mysqlshow]
character-sets-dir=/usr/share/mysql/charsets
default-character-set=utf8
[myisamchk]
character-sets-dir=/usr/share/mysql/charsets
[myisampack]
character-sets-dir=/usr/share/mysql/charsets
# add a section [mysqld-4.1] or [mysqld-5.0] for specific configurations
[mysqld]
character-set-server = utf8
user = mysql
port = 3306
socket = /var/run/mysqld/mysqld.sock
pid-file = /var/run/mysqld/mysqld.pid
log-error = /var/log/mysql/mysqld.err
basedir = /usr
datadir = /var/lib/mysql
skip-external-locking
key_buffer_size = 16M
max_allowed_packet = 4M
table_open_cache = 400
sort_buffer_size = 512K
net_buffer_length = 16K
read_buffer_size = 256K
read_rnd_buffer_size = 512K
myisam_sort_buffer_size = 8M
lc_messages_dir = /usr/share/mysql
#Set this to your desired error message language
lc_messages = en_US
# security:
# using "localhost" in connects uses sockets by default
# skip-networking
bind-address = 127.0.0.1
##log-bin
server-id = 1
# point the following paths to different dedicated disks
tmpdir = /tmp/
#log-update = /path-to-dedicated-directory/hostname
# you need the debug USE flag enabled to use the following directives,
# if needed, uncomment them, start the server and issue
# #tail -f /tmp/mysqld.sql /tmp/mysqld.trace
# this will show you *exactly* what's happening in your server ;)
#log = /tmp/mysqld.sql
#gdb
#debug = d:t:i:o,/tmp/mysqld.trace
#one-thread
# the rest of the innodb config follows:
# don't eat too much memory, we're trying to be safe on 64Mb boxes
# you might want to bump this up a bit on boxes with more RAM
innodb_buffer_pool_size = 2G
#
# i'd like to use /var/lib/mysql/innodb, but that is seen as a database :-(
# and upstream wants things to be under /var/lib/mysql/, so that's the route
# we have to take for the moment
#innodb_data_home_dir = /var/lib/mysql/
#innodb_log_arch_dir = /var/lib/mysql/
#innodb_log_group_home_dir = /var/lib/mysql/
# you may wish to change this size to be more suitable for your system
# the max is there to avoid run-away growth on your machine
innodb_data_file_path = ibdata1:10M:autoextend
# we keep this at around 25% of of innodb_buffer_pool_size
# sensible values range from 1MB to (1/innodb_log_files_in_group*innodb_buffer_pool_size)
innodb_log_file_size = 48M
# this is the default, increase it if you have very large transactions going on
innodb_log_buffer_size = 8M
# this is the default and won't hurt you
# you shouldn't need to tweak it
innodb_log_files_in_group=2
# see the innodb config docs, the other options are not always safe
innodb_flush_log_at_trx_commit = 1
innodb_lock_wait_timeout = 50
innodb_file_per_table
# Uncomment this to get FEDERATED engine support
#plugin-load=federated=ha_federated.so
##loose-federated
[mysqldump]
quick
max_allowed_packet = 16M
[mysql]
# uncomment the next directive if you are not familiar with SQL
#safe-updates
[isamchk]
key_buffer_size = 20M
sort_buffer_size = 20M
read_buffer = 2M
write_buffer = 2M
[myisamchk]
key_buffer_size = 20M
sort_buffer_size = 20M
read_buffer_size = 2M
write_buffer_size = 2M
[mysqlhotcopy]
interactive-timeout
[mariadb]

View File

@ -0,0 +1,7 @@
[mysqladmin]
user = root
password = gentoo
[mysql]
user = root
password = gentoo

8
zabbix/package.keywords Normal file
View File

@ -0,0 +1,8 @@
# Grundsystem
app-emulation/open-vm-tools ~amd64 ~x86
sys-auth/pam_ssh_agent_auth ~amd64 ~x86
# Zabbix
##dev-db/mariadb ~amd64 ~x86
net-analyzer/zabbix ~amd64 ~x86
net-analyzer/snmptt ~amd64 ~x86

2
zabbix/package.mask Normal file
View File

@ -0,0 +1,2 @@
# nur LTS-Versionen 3.0.x bauen
>=net-analyzer/zabbix-3.1

25
zabbix/package.use Normal file
View File

@ -0,0 +1,25 @@
app-admin/sudo -sendmail
app-editors/nano ncurses
app-emulation/open-vm-tools pic -modules
app-misc/mc -slang
dev-lang/python ssl threads xml
dev-libs/libpcre cxx jit
dev-libs/libpcre2 jit
dev-util/pkgconfig internal-glib
net-misc/openssh ssl
net-misc/wget ssl
sys-apps/hwids udev
sys-apps/kmod tools
sys-apps/net-tools hostname
sys-apps/portage ipc
sys-auth/pambase nullok sha512
sys-devel/gcc cxx nptl
sys-kernel/gentoo-sources symlink
# Zabbix
app-admin/rsyslog dbi mysql snmp systemd
app-eselect/eselect-php apache2
dev-lang/php apache2 bcmath gd ldap mysql mysqli sockets sysvipc truetype xmlreader xmlwriter
media-libs/gd jpeg png
net-analyzer/zabbix curl frontend ldap libxml2 mysql openipmi proxy server snmp ssh xmpp
net-analyzer/net-snmp perl

1
zabbix/snmp/snmpd.conf Normal file
View File

@ -0,0 +1 @@
mibs +ALL

View File

@ -0,0 +1,2 @@
traphandle default /usr/sbin/snmptt
disableAuthorization yes

2
zabbix/snmp/snmptt.conf Normal file
View File

@ -0,0 +1,2 @@
EVENT general .* "General event" Normal
FORMAT ZBXTRAP $aA $ar severity:$s $Fn$+*

12
zabbix/timesyncd.conf Normal file
View File

@ -0,0 +1,12 @@
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# See timesyncd.conf(5) for details.
[Time]
NTP=0.de.pool.ntp.org 1.de.pool.ntp.org 2.de.pool.ntp.org 3.de.pool.ntp.org
FallbackNTP=0.gentoo.pool.ntp.org 1.gentoo.pool.ntp.org 2.gentoo.pool.ntp.org 3.gentoo.pool.ntp.org

21
zabbix/world Normal file
View File

@ -0,0 +1,21 @@
app-admin/logrotate
app-admin/sudo
app-emulation/open-vm-tools
app-misc/mc
app-misc/screenservice
net-analyzer/zabbix
sys-auth/pam_ssh_agent_auth
sys-power/acpid
app-admin/rsyslog
dev-db/mariadb
dev-db/phpmyadmin
dev-perl/Config-General
dev-perl/JSON-XS
dev-perl/libwww-perl
dev-tcltk/expect
net-analyzer/net-snmp
net-analyzer/nmap
net-analyzer/snmptt
net-dns/bind-tools
net-misc/netkit-telnetd
sys-process/lsof

18
zabbix/zabbix.cfg Normal file
View File

@ -0,0 +1,18 @@
##HOSTNAME = $(APPLIANCE)
##TIMEZONE = UTC
DISK_SIZE = 8.0G
##SWAP_SIZE = 30
##SWAP_FILE = $(CHROOT)/.swap
##ARCH = amd64-hardened
##MAKEOPTS = -j10 -l10
##PRUNE_CRITICAL = NO
##CHANGE_PASSWORD = YES
##HEADLESS = NO
##SOFTWARE = 1
##PKGLIST = 0
##RSYNC_MIRROR = rsync://rsync15.de.gentoo.org/gentoo/
##KERNEL_PKG = gentoo-sources
KERNEL_CONFIG = appliances/$(APPLIANCE)/kernel.config
ENABLE_SSHD = YES
TIMEZONE=Europe/Berlin
LOCALE=de_DE.utf8

View File

@ -0,0 +1 @@
UserParameter=mysql.status[*],echo "show global status where Variable_name='$1';" | HOME=/etc/ mysql -N | awk '{print $$2}'

View File

@ -0,0 +1,19 @@
# provides UDP syslog reception
$ModLoad imudp
$UDPServerRun 514
#enables omrpog module
$ModLoad omprog
$template RFC3164fmt,"<%PRI%>%TIMESTAMP% %HOSTNAME% %syslogtag%%msg%"
$template network-fmt,"%TIMESTAMP:::date-rfc3339% [%fromhost-ip%] %pri-text% %syslogtag%%msg%\n"
#exclude unwanted messages(examples):
:msg, contains, "Child connection from" stop
:msg, contains, "exit after auth (ubnt): Disconnect received" stop
:msg, contains, "password auth succeeded for 'ubnt' from" stop
:msg, contains, "exit before auth: Exited normally" stop
if $fromhost-ip != '127.0.0.1' then {
action(type="omprog" binary="/etc/zabbix/scripts/zabbix_syslog_lkp_host.pl" template="network-fmt")
stop
}

View File

@ -0,0 +1,587 @@
package ZabbixAPI;
use Data::Dumper;
use LWP;
use JSON::XS;
use MIME::Base64 qw(encode_base64);
my @no_auth_methods = ('user.login','apiinfo.version');
sub new {
my $class = shift;
my $args = shift;
my $api_url = $args->{api_url}
|| 'http://localhost/zabbix/api_jsonrpc.php';
my $username = $args->{username} || 'Admin';
my $password = $args->{password} || 'zabbix';
my $self = bless {
api_url => $api_url,
username => $username,
password => $password,
ua => LWP::UserAgent->new(),
auth => undef,
id => 1,
}, $class;
$self->{req} = HTTP::Request->new( POST => $self->{api_url} );
$self->{req}->content_type('application/json-rpc');
return $self;
}
sub id {
my $self = shift;
return $self->{id}++;
}
sub prepare_auth {
my $self = shift;
my $method = shift;
if (grep /$method/,@no_auth_methods) {
return undef;
}
else
{
return $self->{auth};
}
};
sub do {
my $self = shift;
my $method = shift;
my $params = shift;
my $json = JSON::XS->new->utf8->encode(
{
jsonrpc => '2.0',
method => $method,
params => $params,
id => $self->id,
auth => $self->prepare_auth($method)
}
);
$self->{req}->content($json);
# Pass request to the user agent and get a response back
my $res = $self->{ua}->request( $self->{req} );
# Check the outcome of the response
if ( $res->is_success ) {
my $return = JSON::XS->new->utf8->decode( $res->content );
die $return->{error}->{data}."\n" if $return->{error};
return $return->{result};
}
else {
die $res->status_line, "\n";
}
}
sub do_raw {
my $self = shift;
my $method = shift;
my $params = shift;
my $json = JSON::XS->new->utf8->encode(
{
jsonrpc => '2.0',
method => $method,
params => JSON::XS->new->utf8->decode($params),
id => $self->id,
auth => $self->prepare_auth($method)
}
);
$self->{req}->content($json);
# Pass request to the user agent and get a response back
my $res = $self->{ua}->request( $self->{req} );
# Check the outcome of the response
if ( $res->is_success ) {
my $return = JSON::XS->new->utf8->decode( $res->content );
die $return->{error}->{data}."\n" if $return->{error};
return $return->{result};
}
else {
die $res->status_line, "\n";
}
}
sub import_configuration_from_file {
my $self = shift;
my $file = shift;
my $configuration;
{
local $/ = undef;
open(my $fh,'<:encoding(UTF-8)', $file) or die "Error opening $file: $!";
$configuration = <$fh>;
close $fh;
}
my $json = <<'END_PARAMS';
{
"format": "xml",
"rules": {
"groups": {
"createMissing": true
},
"hosts": {
"createMissing": true,
"updateExisting": true
},
"templates": {
"createMissing": true,
"updateExisting": true
},
"templateLinkage": {
"createMissing": true
},
"templateScreens": {
"createMissing": true,
"updateExisting": true,
"deleteMissing": true
},
"applications": {
"createMissing": true,
"deleteMissing": true
},
"discoveryRules": {
"createMissing": true,
"updateExisting": true,
"deleteMissing": true
},
"items": {
"createMissing": true,
"updateExisting": true,
"deleteMissing": true
},
"triggers": {
"createMissing": true,
"updateExisting": true,
"deleteMissing": true
},
"graphs": {
"createMissing": true,
"updateExisting": true,
"deleteMissing": true
},
"screens": {
"createMissing": true,
"updateExisting": true
},
"maps": {
"createMissing": true,
"updateExisting": true
},
"images": {
"createMissing": true,
"updateExisting": true
},
"valueMaps": {
"createMissing": true,
"updateExisting": true
}
},
"source": ""
}
END_PARAMS
my $params = JSON::XS->new->utf8->decode($json);
$params->{source}=$configuration;
$self->do('configuration.import', $params);
}
sub login {
my $self = shift;
my $params = {
user => $self->{username},
password => $self->{password}
};
my $content = $self->do("user.login",$params);
$self->{auth}=$content;
}
sub logout {
my $self = shift;
$self->do( 'user.logout', {} );
}
sub create_or_update_mediatype {
my $self = shift;
my $params = shift;
my $result;
eval { #try to create JSON
$result = $self->do('mediatype.create', $params);
};
if ($@) {
if($@ =~ /already exists/) {
warn "WARN: $params->{description} already exists. Updating instead..."."\n";
#get mediatypeid
$json = { output => ['mediatypeid'], filter =>{description=>[$params->{description}]}};
my $id = $self->do('mediatype.get',$json);
$params->{mediatypeid}= $id->[0]->{mediatypeid};
#update instead of creating....
$result = $self->do('mediatype.update', $params);
return $result;
}
else {
die $@;
}
}
else {
return $result;
}
}
sub create_or_update_user {
my $self = shift;
my $params = shift;
my $result;
eval { #try to create JSON
$result = $self->do('user.create', $params);
};
if ($@) {
if($@ =~ /already exists/) {
warn "WARN: $params->{alias} already exists. Updating instead..."."\n";
#get mediatypeid
$json = { output => ['userid'], filter =>{alias=>[$params->{alias}]}};
my $id = $self->do('user.get',$json);
$params->{userid}= $id->[0]->{userid};
#update instead of creating....
my $medias = $params->{user_medias}->[0];
delete $params->{user_medias}; # remove user_medias, not possible in 'user.update' call
$result = $self->do('user.update', $params);
my $result_media = $self->do('user.updatemedia',{users => [ {userid=>$params->{userid}} ],
medias => $medias
});
return $result;
}
else {
die $@;
}
}
else {
return $result;
}
}
sub create_or_update_action {
my $self = shift;
my $params = shift;
my $result;
eval { #try to create JSON
$result = $self->do('action.create', $params);
};
if ($@) {
if($@ =~ /already exists/) {
warn "WARN: $params->{name} already exists. Updating instead..."."\n";
#get mediatypeid
$json = { output => ['actionid'], filter =>{name=>[$params->{name}]}};
my $id = $self->do('action.get',$json);
$params->{actionid}= $id->[0]->{actionid};
#update instead of creating....
delete $params->{eventsource}; #cannot be update, must be removed
$result = $self->do('action.update', $params);
return $result;
}
else {
die $@;
}
}
else {
return $result;
}
}
sub create_or_update_drule {
my $self = shift;
my $params = shift;
my $result;
eval { #try to create JSON
$result = $self->do('drule.create', $params);
};
if ($@) {
if($@ =~ /already exists/) {
warn "WARN: $params->{name} already exists. Updating instead..."."\n";
$json = { output => ['druleid'], filter =>{name=>[$params->{name}]}};
my $id = $self->do('drule.get',$json);
$params->{druleid}= $id->[0]->{druleid};
#update instead of creating....
$result = $self->do('drule.update', $params);
return $result;
}
else {
die $@;
}
}
else {
return $result;
}
}
sub get_template_id {
my $self = shift;
my $template_name = shift;
my $json = { output => ['host','templateid'], filter =>{host=>[$template_name]}};
my $result = $self->do('template.get',$json);
return $result->[0]->{templateid};
}
sub get_hostgroup_id {
my $self = shift;
my $hgroup_name = shift;
my $json = { output => ['groupid'], filter =>{name=>[$hgroup_name]}};
my $result = $self->do('hostgroup.get',$json);
return $result->[0]->{groupid};
}
sub get_host_id {
my $self = shift;
my $host_name = shift;
my $json = { output => ['hostid'], filter =>{host=>[$host_name]}};
my $result = $self->do('host.get',$json);
return $result->[0]->{hostid};
}
sub get_host_by_name {
my $self = shift;
my $host_name = shift;
my $json = {
output => ['hostid'],
filter =>{host=>[$host_name]},
selectGroups => ['groupid','name'],
selectParentTemplates => ['templateid','name'],
selectMacros => ['macro','value']
};
my $result = $self->do('host.get',$json);
return $result->[0];
}
sub create_or_merge_host {
my $self = shift;
my $host_name = shift;
my $params = shift;
my $result;
my $hostid = $self->get_host_id($host_name);
if ($hostid) {
print "WARN: Cannot create host $host_name ... going to merge instead\n";
#print $hostid."\n";
#update (merge mode currently)
$params->{hostid}=$hostid;
my $host = $self->get_host_by_name($host_name);
if ($params->{templates}) {
#merge with already existed
my @templates;
foreach my $template (@{$params->{templates}}){
push @templates,$template->{templateid};
}
foreach my $template (@{$host->{parentTemplates}}){
push @templates,$template->{templateid};
}
my %seen = (); # see http://perldoc.perl.org/perlfaq4.html#How-can-I-remove-duplicate-elements-from-a-list-or-array%3f
@templates = grep { ! $seen{ $_ }++ } @templates;
my $i=0;
delete $params->{templates};
foreach my $templateid (@templates) {
$params->{templates}->[$i]->{templateid} = $templateid;
$i++;
}
}
if ($params->{groups}) {
#merge with already existed
my @groups;
foreach my $group (@{$params->{groups}}){
push @groups,$group->{groupid};
}
foreach my $group (@{$host->{groups}}){
push @groups,$group->{groupid};
}
my %seen = (); # see http://perldoc.perl.org/perlfaq4.html#How-can-I-remove-duplicate-elements-from-a-list-or-array%3f
@groups = grep { ! $seen{ $_ }++ } @groups;
my $i=0;
delete $params->{groups};
foreach my $groupid (@groups) {
$params->{groups}->[$i]->{groupid} = $groupid;
$i++;
}
}
if ($params->{macros}) {
#merge with already existed
my @macros;
foreach my $macro (@{$params->{macros}}){
push @macros,
{
macro => $macro->{macro},
value => $macro->{value}
};
}
foreach my $macro (@{$host->{macros}}){
push @macros,
{
macro => $macro->{macro},
value => $macro->{value}
};
}
my %seen = (); # see http://perldoc.perl.org/perlfaq4.html#How-can-I-remove-duplicate-elements-from-a-list-or-array%3f
@macros = grep { ! $seen{ $_->{macro} }++ } @macros;
my $i=0;
delete $params->{macros};
foreach my $macro (@macros) {
$params->{macros}->[$i] = $macro;
$i++;
}
}
if ($params->{interfaces}) {
#merge with already existed
#warn "WARN: host interfaces merge is not supported yet... skipping interfaces part\n";
delete $params->{interfaces};
}
#print Dumper $params;
$result = $self->do('host.update', $params);
}
else {
#create
$params->{host}=$host_name;
#print Dumper $params;
$result = $self->do('host.create', $params);
}
return $result;
}
sub import_image_from_file {
my $self = shift;
my $file = shift;
my $imagename = shift || $file;
my $imagetype = shift || 1; # 1 - icon , 2 - background
my $image;
my $result;
my $json;
{
local $/ = undef;
open(my $fh,, $file) or die "Error opening $file: $!";
$image = <$fh>;
close $fh;
}
my $params = {name => $imagename, imagetype => 1, image => encode_base64($image)};
eval {#try to import image
$result = $self->do('image.create', $params);
};
if ($@) {
if($@ =~ /already exists/) {
warn "WARN: $params->{name} already exists. Updating instead..."."\n";
#get imageid and update instead
$json = { output => ['imageid'], filter =>{name=>[$params->{name}]}};
my $id = $self->do('image.get',$json);
$params->{imageid}= $id->[0]->{imageid};
delete($params->{imagetype});
#update instead of creating....
$result = $self->do('image.update', $params);
return $result;
}
else {
die $@;
}
}
else {
return $result;
}
}
1;

View File

@ -0,0 +1,5 @@
url = http://localhost/zabbix/api_jsonrpc.php
user = Admin
password = zabbix
server = localhost
debug = 0

View File

@ -0,0 +1,207 @@
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
use FindBin qw($Bin);
use lib "$Bin/lib";
use Data::Dumper;
use Config::General;
use ZabbixAPI;
our $VERSION = 3.1;
my $conf;
$conf = eval {Config::General->new('/usr/local/etc/zabbix_syslog.cfg')};
if ($@) {
eval {$conf = Config::General->new('/etc/zabbix/zabbix_syslog.cfg')};
if ($@) {die "Please check that config file is available as /usr/local/etc/zabbix_syslog.cfg or /etc/zabbix/zabbix_syslog.cfg\n";}
}
my %Config = $conf->getall;
#Authenticate yourself
my $url = $Config{'url'} || die "URL is missing in zabbix_syslog.cfg\n";
my $user = $Config{'user'} || die "API user is missing in zabbix_syslog.cfg\n";
my $password = $Config{'password'} || die "API user password is missing in zabbix_syslog.cfg\n";
my $server = $Config{'server'} || die "server hostname is missing in zabbix_syslog.cfg\n";
my $debug = $Config{'debug'};
my ( $authID, $response, $json );
my $zbx = ZabbixAPI->new( { api_url => $url, username => $user, password => $password } );
$zbx->login();
my $syslog_url_base = 'history.php?action=showvalues';
my @selements;
foreach my $map ( @{ map_get_extended() } ) {
my $mapid=$map->{sysmapid};
#put all map elements into array @selements (so you can update map later!)
@selements = @{ $map->{selements} };
print "INFO: Checking map with mapid $map->{sysmapid}\n";
foreach my $selement (@selements) {
my $syslog_button_exists = 0;
if ( $debug > 0 ) {
print 'Object ID: '
. $selement->{selementid}
. ' Type: '
. $selement->{elementtype}."\n";
}
# elementtype=0 hosts
if ( $selement->{elementtype} == 0 ) {
my $hostid;
#Zabbix API 3.4+
if (exists($selement->{elements}->[0]->{hostid})) {
$hostid = $selement->{elements}->[0]->{hostid};
}
#Zabbix API before 3.4
elsif (exists($selement->{elementid})) {
$hostid = $selement->{elementid};
}
else {
die "Cannot get hostid of selement $selement->{selementid}\n";
}
my $itemid = get_syslogid_by_hostid($hostid);
if ($itemid) {
#and add urls:
my $syslog_exists = 0;
foreach my $syslog_url ( @{ $selement->{urls} } ) {
$syslog_exists = 0;
if ( $syslog_url->{name} =~ 'Syslog' ) {
$syslog_exists = 1;
$syslog_url->{'name'} = 'Syslog';
$syslog_url->{'url'} =
$syslog_url_base
. '&itemids['
. $itemid . ']='
. $itemid;
}
}
if ( $syslog_exists == 0 ) {
#syslog item doesn't exist... add it
push @{ $selement->{urls} },
{
'name' => 'Syslog',
'url' => $syslog_url_base
. '&itemids['
. $itemid . ']='
. $itemid
};
}
}
}
}
map_update($mapid,\@selements);
}
$zbx->logout();
#______SUBS
sub get_syslogid_by_hostid {
my $hostid = shift;
my $params = {
output => ['itemid'],
hostids => $hostid,
filter => {'key_' => 'syslog' },
limit => 1,
};
my $result = $zbx->do('item.get',$params);
# Check if response was successful
if ( !$result ) {
$zbx->logout();
die "item.get failed\n";
}
#return itemid of syslog key (trapper type)
return ${ $result }[0]->{itemid};
}
sub map_get {
#retrieve all maps
my $params = {
output => ['sysmapid']
};
my $result = $zbx->do('map.get',$params);
# Check if response was successful
if ( !$result ) {
$zbx->logout();
die "map.get failed\n";
}
if ( $debug > 1 ) { print Dumper $result; }
return $result;
}
sub map_get_extended {
my $params = {
selectSelements => 'extend',
#sysmapids => $map,
};
my $result = $zbx->do('map.get',$params);
# Check if response was successful
if ( !$result ) {
$zbx->logout();
die "map.get failed\n";
}
if ( $debug > 1 ) {
print Dumper $result;
}
return $result;
}
sub map_update {
my $mapid = shift;
my $selements_ref = shift;
my $params = {
selements => [@{$selements_ref}],
sysmapid => $mapid,
};
my $result;
eval {$result=$zbx->do('map.update',$params);};
if($@){
warn "Failed to update map with mapid $mapid, check for write permissions for this map\n";
}
else {
if ( $debug > 0 ) {
print "About to map.update this\n:";
print Dumper $params;
}
if ( $debug > 0 ) {
print Dumper $result;
}
}
return;
}

View File

@ -0,0 +1,272 @@
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
use FindBin qw($Bin);
use lib "$Bin/lib";
use Data::Dumper;
use Config::General;
use ZabbixAPI;
use English '-no_match_vars';
use MIME::Base64 qw(encode_base64);
use IO::Socket::INET;
use Storable qw(lock_store lock_retrieve);
our $VERSION = 3.1;
my $CACHE_TIMEOUT = 600;
my $CACHE_DIR = '/tmp/zabbix_syslog_cache_n';
die "No argumets required anymore since script version 3.0\n" if @ARGV > 0;
my $conf;
$conf = eval {Config::General->new('/usr/local/etc/zabbix_syslog.cfg')};
if ($@) {
eval {$conf = Config::General->new('/etc/zabbix/zabbix_syslog.cfg')};
if ($@) {die "Please check that config file is available as /usr/local/etc/zabbix_syslog.cfg or /etc/zabbix/zabbix_syslog.cfg\n";}
}
my %Config = $conf->getall;
#Authenticate yourself
my $url = $Config{'url'} || die "URL is missing in zabbix_syslog.cfg\n";
my $user = $Config{'user'} || die "API user is missing in zabbix_syslog.cfg\n";
my $password = $Config{'password'} || die "API user password is missing in zabbix_syslog.cfg\n";
my $server = $Config{'server'} || die "server hostname is missing in zabbix_syslog.cfg\n";
my $zbx;
my $debug = $Config{'debug'};
my ( $authID, $response, $json );
#IP regex patter part
my $ipv4_octet = q/(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/;
#rsyslog omprog loop
#http://www.rsyslog.com/doc/master/configuration/modules/omprog.html
while (defined(my $message = <>)) {
chomp($message);
#get ip from message
my $ip;
if ( $message =~ / \[ ((?:$ipv4_octet[.]){3}${ipv4_octet}) \]/msx ) {
$ip = $1;
}
else {
warn "No IP in square brackets found in '$message', cannot continue\n";
next;
}
my $hostname = ${retrieve_from_store($ip)}->{'hostname'};
if ( !defined $hostname ) {
my $result;
$zbx = ZabbixAPI->new( { api_url => $url, username => $user, password => $password } );
$zbx->login();
my @hosts_found;
my $hostid;
my @hostinterfaces;
eval {@hostinterfaces=hostinterface_get($ip)};
if($@){
warn "Failed to retrieve any host interface with IP = $ip. Unable to bind message to item, skipping\n";
next;
}
foreach my $host (@hostinterfaces) {
$hostid = $host->{'hostid'};
if ( grep { /$hostid/msx } @hosts_found ) {
next;
}#check if $hostid already is in array then skip(next)
else { push @hosts_found, $hostid; }
#now get hostname
if ( get_zbx_trapper_syslogid_by_hostid($hostid) ) {
my $result = host_get($hostid);
#return hostname if possible
if ( $result->{'host'} ) {
if ( $result->{'proxy_hostid'} == 0 ) #check if host monitored directly or via proxy
{
#lease $server as is
}
else {
#assume that rsyslogd and zabbix_proxy are on the same server
$server = 'localhost';
}
$hostname = $result->{'host'};
}
last;
}
}
$zbx->logout();
store_message( $ip, $hostname );
}
zabbix_send( $server, $hostname, 'syslog', $message );
}
#______SUBS
sub hostinterface_get {
my $ip = shift;
my $params = {
output => [ 'ip', 'hostid' ],
filter => { ip => $ip, }
};
my $result = $zbx->do('hostinterface.get',$params);
if ( $debug > 0 ) { print Dumper $result; }
# Check if response was successful (not empty array in result)
if ( !@{ $result } ) {
$zbx->logout();
die "hostinterface.get failed\n";
}
return @{ $result };
}
sub get_zbx_trapper_syslogid_by_hostid {
my $hostid = shift;
my $params = {
output => ['itemid'],
hostids => $hostid,
search => {
'key_' => 'syslog',
type => 2, #type => 2 is zabbix_trapper
status => 0,
},
limit => 1,
};
my $result = $zbx->do('item.get',$params);
if ( $debug > 0 ) { print Dumper $result; }
# Check if response was successful
if ( !@{ $result } ) {
warn "item.get failed\n";
}
#return itemid of syslog key (trapper type)
return ${ $result }[0]->{itemid};
}
sub host_get {
my $hostid = shift;
my $params = {
hostids => [$hostid],
output => [ 'host', 'proxy_hostid', 'status' ],
filter => { status => 0, }, # only use hosts enabled
limit => 1,
};
my $result = $zbx->do('host.get',$params);
if ( $debug > 0 ) { print Dumper $result; }
# Check if response was successful
if ( !$result ) {
$zbx->logout();
die "host.get failed\n";
}
return ${ $result }[0]; #return result
}
sub zabbix_send {
my $zabbixserver = shift;
my $hostname = shift;
my $item = shift;
my $data = shift;
my $SOCK_TIMEOUT = 10;
my $SOCK_RECV_LENGTH = 1024;
my $result;
my $request =
sprintf
"<req>\n<host>%s</host>\n<key>%s</key>\n<data>%s</data>\n</req>\n",
encode_base64($hostname), encode_base64($item), encode_base64($data);
my $sock = IO::Socket::INET->new(
PeerAddr => $zabbixserver,
PeerPort => '10051',
Proto => 'tcp',
Timeout => $SOCK_TIMEOUT
);
die "Could not create socket: $ERRNO\n" unless $sock;
$sock->send($request);
my @handles = IO::Select->new($sock)->can_read($SOCK_TIMEOUT);
if ( $debug > 0 ) { print "host - $hostname, item - $item, data - $data\n"; }
if ( scalar(@handles) > 0 ) {
$sock->recv( $result, $SOCK_RECV_LENGTH );
if ( $debug > 0 ) {
print "answer from zabbix server $zabbixserver: $result\n";
}
}
else {
if ( $debug > 0 ) { print "no answer from zabbix server\n"; }
}
$sock->close();
return;
}
#helpers
sub store_message {
my $ip = shift;
my $hostname = shift;
my $storage_file = $CACHE_DIR;
my ( $stored, $to_store );
$to_store->{$ip} = {
hostname => $hostname,
created => time()
};
if ( -f $storage_file ) {
$stored = lock_retrieve $storage_file;
lock_store { %{$stored}, %{$to_store} }, $storage_file;
}
else {
#first time file creation, apply proper file permissions and store only single event
lock_store $to_store, $storage_file;
chmod 0666, $storage_file;
}
}
sub retrieve_from_store {
my $ip = shift;
my $storage_file = $CACHE_DIR;
my $stored;
my $message_to_retrieve;
if ( -f $storage_file ) {
$stored = lock_retrieve $storage_file;
#remove expired from cache
if (defined($stored->{$ip})){
if (time() - $stored->{$ip}->{created} > $CACHE_TIMEOUT){
delete $stored->{$ip};
lock_store $stored, $storage_file;
}
else {
$message_to_retrieve = $stored->{$ip};
}
}
}
return \$message_to_retrieve;
}