diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a224433
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,145 @@
+02firstboot = $(CHROOT)/usr/local/bin/02firstboot.start
+cert-renew.sh = $(CHROOT)/etc/ssl/cert-renew.sh
+zabbix-userparameter = $(CHROOT)/var/lib/zabbix/userparameter_mysql.conf
+
+systemd-units: appliance/MySQL-Backup.sh appliance/backup.service appliance/backup.timer appliance/cert-renew.service appliance/cert-renew.timer
+ mkdir -p $(CHROOT)/usr/local/bin
+ cp appliance/MySQL-Backup.sh $(CHROOT)/usr/local/bin/
+ cp appliance/backup.service appliance/backup.timer appliance/cert-renew.service appliance/cert-renew.timer $(CHROOT)/etc/systemd/system/
+
+$(02firstboot): appliance/02firstboot.start
+ mkdir -p $(CHROOT)/usr/local/bin
+ cp $< $@
+ touch $(CHROOT)/02firstboot
+
+$(cert-renew.sh): appliance/cert-renew.sh
+ mkdir -p $(CHROOT)/etc/ssl
+ cp $< $@
+
+$(CHROOT)/var/lib/mysql: mariadb/my.cnf.root
+ # MariaDB-Konfiguration ($$, weil make ein $ entfernt)
+ sed -i "s/^character-set-server.*$$/character-set-server = utf8mb4/" $(CHROOT)/etc/mysql/mariadb.d/50-distro-server.cnf
+ sed -iE 's/^\(log-bin\)/#\1/' $(CHROOT)/etc/mysql/mariadb.d/50-distro-server.cnf
+ echo >> $(CHROOT)/etc/mysql/mariadb.d/50-distro-server.cnf
+ echo "# innodb tuning" >> $(CHROOT)/etc/mysql/mariadb.d/50-distro-server.cnf
+ echo "innodb_buffer_pool_size = 2G" >> $(CHROOT)/etc/mysql/mariadb.d/50-distro-server.cnf
+ echo "innodb_strict_mode = OFF" >> $(CHROOT)/etc/mysql/mariadb.d/50-distro-server.cnf
+ cp mariadb/my.cnf.root $(CHROOT)/root/.my.cnf
+ chmod 0600 $(CHROOT)/root/.my.cnf
+ rm -rf $(CHROOT)/var/lib/mysql/*
+ RUN bash -c 'yes gentoo | emerge --config dev-db/mariadb'
+
+apache-php:
+ 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:.*pcre.jit=.*:pcre.jit=0:' \
+ -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:'
+
+zabbix-services:
+ 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
+
+zabbix-webapp:
+ RUN 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
+ # enable any language
+ sed -i "s:'display' => false]:'display' => true]:" $(CHROOT)/var/www/localhost/htdocs/zabbix/include/locales.inc.php
+ # im Zabbix-Ebuild wird fowners und fperms vor webapp_src_install aufgerufen und deswegen wieder überschrieben
+ RUN chown -R zabbix:zabbix \
+ /etc/zabbix \
+ /var/lib/zabbix \
+ /var/lib/zabbix/home \
+ /var/lib/zabbix/scripts \
+ /var/lib/zabbix/alertscripts \
+ /var/lib/zabbix/externalscripts \
+ /var/log/zabbix
+ chmod 0750 \
+ $(CHROOT)/etc/zabbix \
+ $(CHROOT)/var/lib/zabbix \
+ $(CHROOT)/var/lib/zabbix/home \
+ $(CHROOT)/var/lib/zabbix/scripts \
+ $(CHROOT)/var/lib/zabbix/alertscripts \
+ $(CHROOT)/var/lib/zabbix/externalscripts \
+ $(CHROOT)/var/log/zabbix
+
+$(zabbix-userparameter): zabbix/userparameter_mysql.conf
+ mkdir -p $(CHROOT)/var/lib/zabbix
+ cp $< $@
+
+zabbix-syslog:
+ # 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/Template_syslog.xml $(CHROOT)/etc/zabbix/scripts/Template_syslog.xml
+ 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
+ RUN 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
+
+zabbix-config:
+ sed -i \
+ -e 's:# PidFile=/tmp/zabbix_agent2.pid:PidFile=/run/zabbix/zabbix_agent2.pid:' \
+ -e 's:# LogFile=/tmp/zabbix_agent2.log:LogFile=/var/log/zabbix/zabbix_agent2.log:' \
+ $(CHROOT)/etc/zabbix/zabbix_agent2.conf
+ sed -i \
+ -e 's:LogFile=/tmp/zabbix_proxy.log:LogFile=/var/log/zabbix/zabbix_proxy.log:' \
+ -e 's:# PidFile=/tmp/zabbix_proxy.pid:PidFile=/run/zabbix/zabbix_proxy.pid:' \
+ $(CHROOT)/etc/zabbix/zabbix_proxy.conf
+ sed -i \
+ -e 's:LogFile=/tmp/zabbix_server.log:LogFile=/var/log/zabbix/zabbix_server.log:' \
+ -e 's:# PidFile=/tmp/zabbix_server.pid:PidFile=/run/zabbix/zabbix_server.pid:' \
+ -e 's:# StartPollers=5:StartPollers=15:' \
+ -e 's:# StartIPMIPollers=0:StartIPMIPollers=5:' \
+ -e 's:# StartPollersUnreachable=1:StartPollersUnreachable=15:' \
+ -e 's:# StartPingers=1:StartPingers=25:' \
+ -e 's:# StartDiscoverers=1:StartDiscoverers=5:' \
+ -e 's:# JavaGateway=:JavaGateway=127.0.0.1:' \
+ -e 's:# VMwareCacheSize=8M:VMwareCacheSize=16M:' \
+ -e 's:# VMwareTimeout=10:VMwareTimeout=30:' \
+ -e 's:# SNMPTrapperFile=/tmp/zabbix_traps.tmp:SNMPTrapperFile=/var/log/snmptt/snmptt.log:' \
+ -e 's:# CacheSize=8M:CacheSize=32M:' \
+ -e 's:# HistoryIndexCacheSize=4M:HistoryIndexCacheSize=8M:' \
+ -e 's:Timeout=4:Timeout=10:' \
+ -e 's:^# AlertScriptsPath=.*:AlertScriptsPath=/var/lib/zabbix/alertscripts:' \
+ -e 's:^# ExternalScripts=.*:ExternalScripts=/var/lib/zabbix/externalscripts:' \
+ -e 's:# ProxyConfigFrequency=3600:ProxyConfigFrequency=360:' \
+ -e 's:# ProxyDataFrequency=1:ProxyDataFrequency=60:' \
+ $(CHROOT)/etc/zabbix/zabbix_server.conf
+
+fping-perm:
+ chmod u=rwsx,g=rx,o=rx $(CHROOT)/usr/sbin/fping
+ chmod u=rwsx,g=rx,o=rx $(CHROOT)/usr/sbin/fping6
+
+snmp-conf:
+ cp -f snmp/snmpd.conf $(CHROOT)/etc/snmp/snmpd.conf
+ cp -f snmp/snmptrapd.conf $(CHROOT)/etc/snmp/snmptrapd.conf
+ cp -f snmp/snmptt.conf $(CHROOT)/etc/snmp/snmptt.conf
+ sed -i \
+ -e 's:^mode = .*:mode = standalone:' \
+ -e 's:^net_snmp_perl_enable = .*:net_snmp_perl_enable = 1:' \
+ -e 's:^translate_log_trap_oid = .*:translate_log_trap_oid = 2:' \
+ -e 's:^translate_integers = .*:translate_integers = 0:' \
+ -e 's:^#mibs_environment = .*:mibs_environment = ALL:' \
+ -e 's.^#date_time_format =.date_time_format = %H:%M:%S %Y/%m/%d.' \
+ -e 's:^daemon_uid = .*:daemon_uid = zabbix:' \
+ -e 's:^log_system_enable = .*:log_system_enable = 1:' \
+ -e 's:^unknown_trap_log_enable = .*:unknown_trap_log_enable = 1:' \
+ -e 's:^syslog_level = .*:syslog_level = err:' \
+ -e 's:^syslog_system_level = .*:syslog_system_level = err:' \
+ $(CHROOT)/etc/snmp/snmptt.ini
+ mkdir -p $(CHROOT)/var/log/snmptt
+ chmod 0775 $(CHROOT)/var/log/snmptt
+ RUN chown zabbix:zabbix /var/log/snmptt
+
+preinstall:
+
+postinstall: systemd-units $(02firstboot) $(cert-renew.sh) $(CHROOT)/var/lib/mysql apache-php zabbix-services zabbix-webapp $(zabbix-userparameter) zabbix-syslog zabbix-config fping-perm snmp-conf
diff --git a/appliance/02firstboot.start b/appliance/02firstboot.start
new file mode 100755
index 0000000..82ca7e7
--- /dev/null
+++ b/appliance/02firstboot.start
@@ -0,0 +1,221 @@
+#!/bin/bash
+
+# variables
+LABEL="DATA"
+DATABASE_PASS=$(head -c 300 /dev/urandom | tr -cd 'a-zA-Z0-9' | head -c 16)
+TLD="example.com"
+HOST="zabbix"
+ORGNAME="Zabbix example"
+
+# start
+set -e
+
+[ -e /01firstboot ] && exit 0
+[ -e /02firstboot ] || exit 0
+
+# Select type
+if [ -f "/$LABEL/etc/zabbix/zabbix_server.conf" ]; then
+ zabbixtype="server"
+elif [ -f "/$LABEL/etc/zabbix/zabbix_proxy.conf" ]; then
+ zabbixtype="proxy"
+else
+ echo
+ echo "Should the new appliance be configured as a server or a proxy?"
+ while read -n1 -r -p "choose [s]erver|[p]roxy: "; do
+ case $REPLY in
+ s|S) zabbixtype="server"
+ break
+ ;;
+ p|P) zabbixtype="proxy"
+ break
+ ;;
+ *) echo " (Invalid option, choose again...)"
+ ;;
+ esac
+ done
+ echo
+fi
+
+# Zabbix configuration
+if [ "$zabbixtype" == "server" ]; then
+ if [ ! -L /etc/zabbix/zabbix_server.conf ]; then
+ if [ ! -f "/$LABEL/etc/zabbix/zabbix_server.conf" ]; then
+ echo 'Create Zabbix Server config...'
+ mkdir -p /$LABEL/etc/zabbix
+ cp /etc/zabbix/zabbix_server.conf /$LABEL/etc/zabbix/zabbix_server.conf
+ mv /etc/zabbix/zabbix_server.conf /$LABEL/etc/zabbix/zabbix_server.conf.orig
+ chown -R zabbix:zabbix /$LABEL/etc/zabbix
+ ln -s /$LABEL/etc/zabbix/zabbix_server.conf /etc/zabbix/zabbix_server.conf
+ else
+ echo 'Linking Zabbix Server config...'
+ cp /$LABEL/etc/zabbix/zabbix_server.conf /$LABEL/etc/zabbix/zabbix_server.conf.alt
+ rm -f /$LABEL/etc/zabbix/zabbix_server.conf.orig
+ mv /etc/zabbix/zabbix_server.conf /$LABEL/etc/zabbix/zabbix_server.conf.orig
+ chown -R zabbix:zabbix /$LABEL/etc/zabbix
+ ln -s /$LABEL/etc/zabbix/zabbix_server.conf /etc/zabbix/zabbix_server.conf
+ fi
+ fi
+else
+ if [ ! -L /etc/zabbix/zabbix_proxy.conf ]; then
+ if [ ! -f "/$LABEL/etc/zabbix/zabbix_proxy.conf" ]; then
+ echo 'Create Zabbix Proxy config...'
+ mkdir -p /$LABEL/etc/zabbix
+ chown zabbix:zabbix /$LABEL/etc/zabbix
+ cp /etc/zabbix/zabbix_proxy.conf /$LABEL/etc/zabbix/zabbix_proxy.conf
+ mv /etc/zabbix/zabbix_proxy.conf /$LABEL/etc/zabbix/zabbix_proxy.conf.orig
+ sed -i "s:^DBName=.*:DBName=zabbix:" /$LABEL/etc/zabbix/zabbix_proxy.conf
+ chown -R zabbix:zabbix /$LABEL/etc/zabbix
+ ln -s /$LABEL/etc/zabbix/zabbix_proxy.conf /etc/zabbix/zabbix_proxy.conf
+ else
+ echo 'Linking Zabbix Proxy config...'
+ cp /$LABEL/etc/zabbix/zabbix_proxy.conf /$LABEL/etc/zabbix/zabbix_proxy.conf.alt
+ rm -f /$LABEL/etc/zabbix/zabbix_proxy.conf.orig
+ mv /etc/zabbix/zabbix_proxy.conf /$LABEL/etc/zabbix/zabbix_proxy.conf.orig
+ chown -R zabbix:zabbix /$LABEL/etc/zabbix
+ ln -s /$LABEL/etc/zabbix/zabbix_proxy.conf /etc/zabbix/zabbix_proxy.conf
+ fi
+ fi
+fi
+
+if [ "$zabbixtype" == "server" ]; then
+ if [ ! -L /var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php ]; then
+ if [ ! -f "/$LABEL/var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php" ]; then
+ echo 'Create Zabbix Frontend config...'
+ mkdir -p /$LABEL/var/www/localhost/htdocs/zabbix/conf
+ cp /var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php /$LABEL/var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php
+ mv /var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php /$LABEL/var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php.orig
+ ln -s /$LABEL/var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php /var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php
+ else
+ echo 'Linking Zabbix Frontend config...'
+ cp /$LABEL/var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php /$LABEL/var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php.alt
+ rm -f /$LABEL/var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php.orig
+ mv /var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php /$LABEL/var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php.orig
+ ln -s /$LABEL/var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php /var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php
+ fi
+ fi
+ if [ ! -L /var/lib/net-snmp ]; then
+ echo 'Linking SNMP data directory...'
+ if [ ! -d "/$LABEL/var/lib/net-snmp" ]; then
+ mkdir -p /$LABEL/var/lib/net-snmp
+ cp -a /var/lib/net-snmp/. /$LABEL/var/lib/net-snmp
+ fi
+ rm -rf /var/lib/net-snmp.orig
+ mv /var/lib/net-snmp /var/lib/net-snmp.orig
+ ln -s /$LABEL/var/lib/net-snmp /var/lib/net-snmp
+ fi
+fi
+
+# Zabbix home directory
+if [ ! -L /var/lib/zabbix ]; then
+ echo 'Linking Zabbix home directory...'
+ if [ ! -d "/$LABEL/var/lib/zabbix" ]; then
+ mkdir -p /$LABEL/var/lib/zabbix
+ chown zabbix:zabbix /$LABEL/var/lib/zabbix
+ cp -a /var/lib/zabbix/. /$LABEL/var/lib/zabbix
+ fi
+ rm -rf /var/lib/zabbix.orig
+ mv /var/lib/zabbix /var/lib/zabbix.orig
+ ln -s /$LABEL/var/lib/zabbix /var/lib/zabbix
+fi
+
+
+# Database
+if [ ! -L /var/lib/mysql ]; then
+ systemctl stop mariadb
+ if [ ! -d "/$LABEL/var/lib/mysql/zabbix" ]; then
+ echo 'Initialize MariaDB...'
+ mkdir -p "/$LABEL/var/lib"
+ rm -rf "/$LABEL/var/lib/mysql"
+ rm -rf "/$LABEL/var/lib/mysql.orig"
+ cp -a "/var/lib/mysql" "/$LABEL/var/lib/mysql"
+ mv "/var/lib/mysql" "/$LABEL/var/lib/mysql.orig"
+ ln -s "/$LABEL/var/lib/mysql" "/var/lib/mysql"
+ 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
+ if [ "$zabbixtype" == "server" ]; then
+ mysql -u root zabbix < /usr/share/zabbix/database/mysql/images.sql
+ mysql -u root zabbix < /usr/share/zabbix/database/mysql/data.sql
+ fi
+ else
+ echo 'Start MariaDB...'
+ rm -rf "/$LABEL/var/lib/mysql.orig"
+ mv "/var/lib/mysql" "/$LABEL/var/lib/mysql.orig"
+ ln -s "/$LABEL/var/lib/mysql" "/var/lib/mysql"
+ systemctl start mariadb
+ sleep 5
+ mysql -u root -e "GRANT ALL PRIVILEGES ON zabbix.* TO 'zabbix'@'localhost' IDENTIFIED by '$DATABASE_PASS';"
+ fi
+else
+ echo 'Set new database password...'
+ systemctl restart mariadb
+ sleep 5
+ mysql -u root -e "GRANT ALL PRIVILEGES ON zabbix.* TO 'zabbix'@'localhost' IDENTIFIED by '$DATABASE_PASS';"
+fi
+# update config with new database password
+if [ "$zabbixtype" == "server" ]; then
+ sed -i "s:.*DBPassword=.*:DBPassword=${DATABASE_PASS}:" /$LABEL/etc/zabbix/zabbix_server.conf
+ sed -i "s:\$DB\['PASSWORD'\].*:\$DB\['PASSWORD'\] = '${DATABASE_PASS}';:" /$LABEL/var/www/localhost/htdocs/zabbix/conf/zabbix.conf.php
+else
+ sed -i "s:.*DBPassword=.*:DBPassword=${DATABASE_PASS}:" /$LABEL/etc/zabbix/zabbix_proxy.conf
+fi
+
+# Certificate
+if [ "$zabbixtype" == "server" ]; then
+ if [ -x "/$LABEL/etc/ssl/cert-renew.sh" ]; then
+ # angepaßtes Zertifikat vorhanden (kein example)
+ if [ ! -L /etc/ssl/cert-renew.sh ]; then
+ rm -f "/$LABEL/etc/ssl/cert-renew.sh.orig"
+ mv "/etc/ssl/cert-renew.sh" "/$LABEL/etc/ssl/cert-renew.sh.orig"
+ else
+ rm -f "/etc/ssl/cert-renew.sh"
+ fi
+ ln -s "/$LABEL/etc/ssl/cert-renew.sh" "/etc/ssl/cert-renew.sh"
+ else
+ echo 'Create example certificate...'
+ mkdir -p "/$LABEL/CERTS/KEYS/"
+ mkdir -p "/$LABEL/CERTS/$HOST.$TLD"
+ echo "FQDN = $HOST.$TLD" > "/$LABEL/CERTS/$HOST.$TLD/$HOST.$TLD.cnf"
+ echo "ORGNAME = $ORGNAME" >> "/$LABEL/CERTS/$HOST.$TLD/$HOST.$TLD.cnf"
+ echo "ALTNAMES = DNS:$HOST.$TLD , DNS:$TLD" >> "/$LABEL/CERTS/$HOST.$TLD/$HOST.$TLD.cnf"
+ echo -e "\n[ req ]\ndefault_bits = 4096\ndefault_md = sha256\nprompt = no\nencrypt_key = no\ndistinguished_name = dn\nreq_extensions = req_ext\ndefault_keyfile = ../KEYS/\$FQDN-key.pem\n" >> "/$LABEL/CERTS/$HOST.$TLD/$HOST.$TLD.cnf"
+ echo -e "\n[ dn ]\nC = DE\nO = \$ORGNAME\nCN = \$FQDN\n" >> "/$LABEL/CERTS/$HOST.$TLD/$HOST.$TLD.cnf"
+ echo -e "\n[ req_ext ]\nsubjectAltName = \$ALTNAMES" >> "/$LABEL/CERTS/$HOST.$TLD/$HOST.$TLD.cnf"
+ openssl req -x509 -new -config "/$LABEL/CERTS/$HOST.$TLD/$HOST.$TLD.cnf" -out "/$LABEL/CERTS/$HOST.$TLD/$HOST.$TLD-cert.pem" -keyout "/$LABEL/CERTS/KEYS/$HOST.$TLD-key.pem"
+ cp "/$LABEL/CERTS/$HOST.$TLD/$HOST.$TLD-cert.pem" "/$LABEL/CERTS/$HOST.$TLD/$HOST.$TLD-fullchain.pem"
+ touch "/$LABEL/CERTS/$HOST.$TLD/$HOST.$TLD-chain.pem"
+ fi
+
+ rm -rf /etc/ssl/apache2
+ mkdir -p /etc/ssl
+ ln -sf "/$LABEL/etc/ssl/apache2" "/etc/ssl/apache2"
+
+ /etc/ssl/cert-renew.sh
+fi
+
+systemctl enable mariadb
+systemctl enable zabbix-agent2
+if [ "$zabbixtype" == "server" ]; then
+ systemctl enable zabbix-server
+ systemctl enable apache2
+else
+ systemctl enable zabbix-proxy
+fi
+
+systemctl restart zabbix-agent2
+if [ "$zabbixtype" == "server" ]; then
+ systemctl restart zabbix-server
+ systemctl restart apache2
+else
+ systemctl restart zabbix-proxy
+fi
+
+rm /02firstboot
diff --git a/appliance/MySQL-Backup.sh b/appliance/MySQL-Backup.sh
new file mode 100755
index 0000000..19ccfc9
--- /dev/null
+++ b/appliance/MySQL-Backup.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
+
+DIR="/DATA/Backup/MySQL"
+
+if [ -z $1 ]; then
+ echo "database name missing! use --all for all db's"
+ exit 1;
+elif [ $1 = '--all' ]; then
+ echo "full backup"
+ for i in `mysqlshow --defaults-file=/root/.my.cnf | awk '{print $2}' | grep -v Databases`; do
+ if [ "$i" != "information_schema" ] && [ "$i" != "performance_schema" ]; then
+ if test -f ${DIR}/${i}.sql; then
+ echo "Move ${DIR}/${i}.sql to ${DIR}/${i}.sql.1"
+ mv ${DIR}/${i}.sql ${DIR}/${i}.sql.1
+ fi
+ echo "dump ${i} to ${DIR}/${i}.sgl"
+ mysqldump --defaults-file=/root/.my.cnf --single-transaction --events --opt -QF -r${DIR}/${i}.sql $i
+ chmod 600 ${DIR}/${i}.sql
+ fi
+ done;
+elif [ -n $1 ]; then
+ echo "Starting backup of $1"
+ if test -f $DIR/$1.sql; then
+ echo "Move $DIR/$1.sql to $DIR/$1.sql.1"
+ mv ${DIR}/${1}.sql ${DIR}/${1}.sql.1
+ fi
+ mysqldump --defaults-file=/root/.my.cnf --single-transaction --opt -QF -r${DIR}/${1}.sql $1
+ chmod 600 ${DIR}/${1}.sql
+fi
+echo "Done"
+exit 0;
diff --git a/appliance/backup.service b/appliance/backup.service
new file mode 100644
index 0000000..13ca921
--- /dev/null
+++ b/appliance/backup.service
@@ -0,0 +1,8 @@
+[Unit]
+Description=execute backup tasks
+RefuseManualStart=no
+RefuseManualStop=yes
+
+[Service]
+Type=oneshot
+ExecStart=/usr/local/bin/MySQL-Backup.sh --all
diff --git a/appliance/backup.timer b/appliance/backup.timer
new file mode 100644
index 0000000..ec59929
--- /dev/null
+++ b/appliance/backup.timer
@@ -0,0 +1,12 @@
+[Unit]
+Description=execute backup tasks
+RefuseManualStart=no
+RefuseManualStop=no
+
+[Timer]
+Persistent=false
+OnCalendar=Sun *-*-* 02:19:00
+Unit=backup.service
+
+[Install]
+WantedBy=default.target
diff --git a/appliance/cert-renew.service b/appliance/cert-renew.service
new file mode 100644
index 0000000..59ec86d
--- /dev/null
+++ b/appliance/cert-renew.service
@@ -0,0 +1,8 @@
+[Unit]
+Description=renew certificates from git store
+RefuseManualStart=no
+RefuseManualStop=yes
+
+[Service]
+Type=oneshot
+ExecStart=/etc/ssl/cert-renew.sh
diff --git a/appliance/cert-renew.sh b/appliance/cert-renew.sh
new file mode 100755
index 0000000..07747a4
--- /dev/null
+++ b/appliance/cert-renew.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+HOST="zabbix"
+TLD="example.com"
+FQDN="$HOST.$TLD"
+LABEL="DATA"
+
+CERT_DIR=/$LABEL/CERTS
+CERT_APACHE=/$LABEL/etc/ssl/apache2
+GETREPO=""
+GETUSER=""
+GETPASS=""
+
+function getCurrentVersion() {
+# Get hash from latest revision
+ git log --format=format:%H -1
+}
+
+cd $CERT_DIR
+
+if [ -z "$GETREPO" ]; then
+ GIT_REVISION=0
+ GIT_NEW_REVISION=1
+ cd $FQDN
+elif [ ! -d "$FQDN" ]; then
+ GIT_REVISION=0
+ git clone "https://$GETUSER:$GETPASS@$GETREPO"
+ cd $FQDN
+ GIT_NEW_REVISION=$(getCurrentVersion)
+else
+ cd $FQDN
+ GIT_REVISION=$(getCurrentVersion)
+ git commit -m "CRON: auto commit"
+ git fetch
+ git merge origin/master -m "Auto Merge"
+ GIT_NEW_REVISION=$(getCurrentVersion)
+fi
+
+echo "old: $GIT_REVISION"
+echo "new: $GIT_NEW_REVISION"
+
+if [ $GIT_REVISION != $GIT_NEW_REVISION ]
+then
+ echo "Update Apache certificate..."
+ mkdir -p $CERT_APACHE
+ cp $CERT_DIR/$FQDN/$FQDN-fullchain.pem $CERT_APACHE/server.crt
+ cp $CERT_DIR/KEYS/$FQDN-key.pem $CERT_APACHE/server.key
+ echo "Restarting Apache..."
+ systemctl is-active --quiet apache2 && systemctl restart apache2
+fi
+
+exit 0
diff --git a/appliance/cert-renew.timer b/appliance/cert-renew.timer
new file mode 100644
index 0000000..fa2ee54
--- /dev/null
+++ b/appliance/cert-renew.timer
@@ -0,0 +1,12 @@
+[Unit]
+Description=renew certificates from git store
+RefuseManualStart=no
+RefuseManualStop=no
+
+[Timer]
+Persistent=false
+OnCalendar=Sun *-*-* 04:03:00
+Unit=cert-renew.service
+
+[Install]
+WantedBy=default.target
diff --git a/mariadb/my.cnf.root b/mariadb/my.cnf.root
new file mode 100644
index 0000000..b5ac578
--- /dev/null
+++ b/mariadb/my.cnf.root
@@ -0,0 +1,11 @@
+[mysqladmin]
+user = root
+password = gentoo
+
+[mysql]
+user = root
+password = gentoo
+
+[client]
+user = root
+password = gentoo
diff --git a/package.accept_keywords b/package.accept_keywords
new file mode 100644
index 0000000..ca7f274
--- /dev/null
+++ b/package.accept_keywords
@@ -0,0 +1,2 @@
+# Zabbix
+net-analyzer/snmptt
diff --git a/package.mask b/package.mask
new file mode 100644
index 0000000..bb0343a
--- /dev/null
+++ b/package.mask
@@ -0,0 +1,2 @@
+# nur LTS-Versionen 6.0.x bauen
+>=net-analyzer/zabbix-6.1
diff --git a/package.use b/package.use
new file mode 100644
index 0000000..d82b983
--- /dev/null
+++ b/package.use
@@ -0,0 +1,12 @@
+# Zabbix
+app-admin/rsyslog dbi mysql openssl snmp systemd
+app-eselect/eselect-php apache2
+app-text/poppler -introspection -jpeg -jpeg2k
+dev-java/openjdk-bin headless-awt
+dev-lang/php apache2 bcmath curl gd ldap mysql mysqli odbc sockets sysvipc truetype xmlreader xmlwriter
+media-libs/gd jpeg png
+net-analyzer/zabbix -agent agent2 curl frontend java ldap libxml2 mysql odbc openipmi -postgres proxy server snmp ssh xmpp
+net-analyzer/zabbix-headers -agent agent2 curl frontend java ldap libxml2 mysql odbc openipmi -postgres proxy server snmp ssh xmpp
+net-analyzer/net-snmp perl
+net-analyzer/fping suid
+net-print/cups-filters -foomatic -postscript
diff --git a/snmp/snmpd.conf b/snmp/snmpd.conf
new file mode 100644
index 0000000..4c2263b
--- /dev/null
+++ b/snmp/snmpd.conf
@@ -0,0 +1,2 @@
+#mibs +SOME-OTHER-SPIFFY-MIB
+mibs +ALL
diff --git a/snmp/snmptrapd.conf b/snmp/snmptrapd.conf
new file mode 100644
index 0000000..c91bbcd
--- /dev/null
+++ b/snmp/snmptrapd.conf
@@ -0,0 +1,2 @@
+traphandle default /usr/sbin/snmptt
+disableAuthorization yes
diff --git a/snmp/snmptt.conf b/snmp/snmptt.conf
new file mode 100644
index 0000000..c06e586
--- /dev/null
+++ b/snmp/snmptt.conf
@@ -0,0 +1,2 @@
+EVENT general .* "General event" Normal
+FORMAT ZBXTRAP $aA $ar severity:$s $Fn$+*
diff --git a/va-zabbix.cfg b/va-zabbix.cfg
new file mode 100644
index 0000000..f4f8641
--- /dev/null
+++ b/va-zabbix.cfg
@@ -0,0 +1,2 @@
+REPO_NAMES += unitas-zabbix
+REPO_URI_unitas-zabbix = https://git.unitas-network.de/Gentoo/unitas-zabbix.git
diff --git a/world b/world
new file mode 100644
index 0000000..99c51f8
--- /dev/null
+++ b/world
@@ -0,0 +1,20 @@
+app-admin/rsyslog
+dev-db/mariadb
+dev-db/phpmyadmin
+dev-java/openjdk-bin:11
+dev-perl/Config-General
+dev-perl/JSON-RPC
+dev-perl/JSON-XS
+dev-perl/libwww-perl
+dev-tcltk/expect
+net-analyzer/snmp-lldp
+net-analyzer/net-snmp
+net-analyzer/nmap
+net-analyzer/snmptt
+net-analyzer/traceroute
+net-dns/bind-tools
+net-im/sendxmpp
+net-misc/netkit-telnetd
+sys-apps/ipmitool
+sys-libs/openipmi
+sys-process/lsof
diff --git a/zabbix/userparameter_mysql.conf b/zabbix/userparameter_mysql.conf
new file mode 100644
index 0000000..099ed1c
--- /dev/null
+++ b/zabbix/userparameter_mysql.conf
@@ -0,0 +1 @@
+UserParameter=mysql.status[*],echo "show global status where Variable_name='$1';" | HOME=/etc/ mysql -N | awk '{print $$2}'
\ No newline at end of file
diff --git a/zabbix/zabbix-syslog/70-zabbix_rsyslog.conf b/zabbix/zabbix-syslog/70-zabbix_rsyslog.conf
new file mode 100644
index 0000000..08afc2c
--- /dev/null
+++ b/zabbix/zabbix-syslog/70-zabbix_rsyslog.conf
@@ -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
+}
diff --git a/zabbix/zabbix-syslog/Template_syslog.xml b/zabbix/zabbix-syslog/Template_syslog.xml
new file mode 100644
index 0000000..00c76fd
--- /dev/null
+++ b/zabbix/zabbix-syslog/Template_syslog.xml
@@ -0,0 +1,128 @@
+
+
+ 2.0
+ 2015-03-13T14:27:56Z
+
+
+ Templates
+
+
+
+
+ Template_Syslog
+ Template_Syslog
+
+
+
+ Templates
+
+
+
+
+ Log
+
+
+
+ -
+ Syslog
+ 2
+
+ 0
+
+ syslog
+ 0
+ 3
+ 365
+ 0
+ 2
+
+
+ 0
+
+
+ 0
+ 0
+
+ 0
+
+ 1
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ 0
+
+
+ Log
+
+
+
+ yyyyxMMxddxhhxmmxssxxxxxx
+
+
+
+
+
+
+
+
+
+
+ ({Template_Syslog:syslog.str(.alert)}=1)and({Template_Syslog:syslog.nodata(900)}=0)
+ [SYSLOG] Alert message received
+
+ 0
+ 4
+
+ 0
+
+
+
+ ({Template_Syslog:syslog.str(.crit)}=1)and({Template_Syslog:syslog.nodata(900)}=0)
+ [SYSLOG] Critical message received
+
+ 0
+ 3
+
+ 0
+
+
+
+ ({Template_Syslog:syslog.str(.emerg)}=1)and({Template_Syslog:syslog.nodata(900)}=0)
+ [SYSLOG] Emergency message received
+
+ 0
+ 5
+
+ 0
+
+
+
+ ({Template_Syslog:syslog.str(.err)}=1)and({Template_Syslog:syslog.nodata(900)}=0)
+ [SYSLOG] Error received
+
+ 0
+ 2
+
+ 0
+
+
+
+ ({Template_Syslog:syslog.str(.warning)}=1)and({Template_Syslog:syslog.nodata(900)}=0)
+ [SYSLOG] Warning received
+
+ 0
+ 1
+
+ 0
+
+
+
+
diff --git a/zabbix/zabbix-syslog/lib/ZabbixAPI.pm b/zabbix/zabbix-syslog/lib/ZabbixAPI.pm
new file mode 100644
index 0000000..2c41751
--- /dev/null
+++ b/zabbix/zabbix-syslog/lib/ZabbixAPI.pm
@@ -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;
\ No newline at end of file
diff --git a/zabbix/zabbix-syslog/zabbix_syslog.cfg b/zabbix/zabbix-syslog/zabbix_syslog.cfg
new file mode 100644
index 0000000..39d6a4f
--- /dev/null
+++ b/zabbix/zabbix-syslog/zabbix_syslog.cfg
@@ -0,0 +1,5 @@
+url = http://localhost/zabbix/api_jsonrpc.php
+user = Admin
+password = zabbix
+server = localhost
+debug = 0
\ No newline at end of file
diff --git a/zabbix/zabbix-syslog/zabbix_syslog_create_urls.pl b/zabbix/zabbix-syslog/zabbix_syslog_create_urls.pl
new file mode 100644
index 0000000..00826f8
--- /dev/null
+++ b/zabbix/zabbix-syslog/zabbix_syslog_create_urls.pl
@@ -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 = 4.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 $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;
+}
\ No newline at end of file
diff --git a/zabbix/zabbix-syslog/zabbix_syslog_lkp_host.pl b/zabbix/zabbix-syslog/zabbix_syslog_lkp_host.pl
new file mode 100644
index 0000000..d5f89bf
--- /dev/null
+++ b/zabbix/zabbix-syslog/zabbix_syslog_lkp_host.pl
@@ -0,0 +1,274 @@
+#!/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 = 4.0;
+
+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
+ "\n%s\n%s\n%s\n\n",
+ encode_base64($hostname), encode_base64($item), encode_base64($data);
+ my $packet = "ZBXD\1" . pack('V', length($request)) . "\0\0\0\0" . $request;
+
+ 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($packet);
+
+ 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;
+
+}
\ No newline at end of file