Automatisch lichten aanzetten met behulp van een API

Ooit, een hele tijd terug heb ik een APC rack pdu (ap7921) in huis gehaald om 2 dingen te doen. Het meten van het totale verbruik van de mediahoek én het in/uitschakelen van lichten of andere zaken. Dat kan prima met deze pdu, want er zit netwerk op en dus ook via SNMP aan te sturen. Via een php webpagina (die ik met mijn mobile device kan openen) kan ik al heel lang de belangrijkste outlets schakelen en ik heb ook al een tijd een cronjob op een server draaien die de lichten schakelen. Ja, ook de kerstboom hoewel het stekkerblok op die outlet nu onder de bank ligt voor algemeen gebruik. De subwoofer was voor het gemak, tijdens een film licht uit, sub aan en geniet.

De pagina stuurt in feite ook via een ‘snmpset’ commando een 1 of 2 naar de juiste poort. Daarbij is de kleur groen ‘aan’ en rood is ‘uit’ puur voor de opmaak.

Het script in cronjob is een paar keer herschreven en kwijt geraakt door een crash van de OrangePi thuis en dus weer opnieuw gemaakt. Voor nu is het een ‘fancy’ versie want het kan uiteraard ook gewoon met praktisch 8 of zo regels. Maar dat is het mooie, je wil leren en je script verder ontwikkelen, verharden. Een geval van “good practice” dus.

#!/bin/bash#
# Switching on or off light at home
# bartjan@pc-mania.nl - 08 Mar 2018

snmp_exec="/usr/bin/snmpset"

function check_reqs {
        if [ ! -f $snmp_exec ]; then
                echo "Error: snmpset executable not found.."
                echo ""
                exit 1
        fi
}

function show_help {
        echo "Error: incorrect syntax.."
        echo "Usage: ./lights.sh [on|off]"
        echo ""
}

function switch {
        # 1st arg is outlet nr (integer)
        # 2nd arg options: on / off
        if [ $2 == "on" ]; then
                ${snmp_exec} -v2c -cpublic 192.168.1.251 1.3.6.1.4.1.318.1.1.4.4.2.1.3.${1} i 1 1>/dev/null 2>&1
        elif [ $2 == "off" ]; then
                ${snmp_exec} -v2c -cpublic 192.168.1.251 1.3.6.1.4.1.318.1.1.4.4.2.1.3.${1} i 2 1>/dev/null 2>&1
fi
}

# Start the program

check_reqs

if [ ! $1 ]; then
        show_help
        exit 1
fi


if [ $1 == "on" ]; then
        echo "Iluminate"
        switch 1 on
        switch 8 on
elif [ $1 == "off" ]; then
        echo "Deluminate"
        switch 1 off
        switch 8 off
else
        show_help
        exit 1
fi

De output op de cli is vanzelf sprekend. De cronjob zelf voert de output naar dev/null.

Nu kwam het zo naar boven dat in de herfst de tijden van de job in cron verschuiven. Elke keer dit aanpassen is suf en dat kan slimmer. Ik ben al een ruime tijd veel meer met Python bezig en dat bevalt erg goed. Ik heb een script geschreven die tijden ophaalt via een API call bij een publieke dienst aan de hand van de fysieke locatie. Hiermee kan ik de crontab van de user ‘root’ op de OrangePi weer aanpassen. Deze gebruikt deze dus de handige module ‘python-crontab‘.

#!/usr/bin/python
#
# @author bartjan@pc-mania.nl
# Oct-6,2018

import requests
import json
import smtplib
import sys
import time
from crontab import CronTab

link = 'https://api.sunrise-sunset.org/json?lat=51.840449&lng=-4.972762?&formatted=0'
localtime = time.localtime()

def time_of(input):
    # Get the JSON output from the API
    try:
        data = requests.get(link).text
    except Exception:
        sendmail("Failed to connect to API")
        sys.exit()
    data = json.loads(data)

    # Fetch the wanted time
    try:
        time = str(data['results'][input]).split('T')[1].split('+')[0]
    except Exception:
        sendmail("No usable data is returned, keeping old time")
        sys.exit()

    return time

def reset_crontab(minute,hour):
    cron = CronTab(user='root')

    # Check and remove the old job
    try:
        for job in cron.find_command('lights.sh on'):
            job.delete()
    except Exception:
        sendmail("Old cron job not found, stopping update")
        sys.exit()

    # Set the new job at the given time
    job = cron.new(command='/bin/bash /root/scripts/lights.sh on >/dev/null 2>&1')
    job.minute.on(minute)
    job.hour.on(hour)

    # Save job
    try:
        cron.write()
    except Exception:
        sendmail("Writing new cron failed")
        sys.exit()

def sendmail(msg):
    msg = "Subject: sunset tool warning" + "\n\n" + msg
    server = smtplib.SMTP('smtp.yourisp.org',25)
    server.sendmail('you@domain.net','johndoe@somedomain.com',msg)

###

# Get new time and split in hour/minute
time = time_of('nautical_twilight_end')
hour = time.split(':')[0]
if not localtime.tm_isdst:
    # Correct daylight savings
    hour = hour - 1
minute = time.split(':')[1]

# Update (so reset) the cronjob
reset_crontab(minute,hour)

Dit script draait sowieso ook in de crontab van dezelfde user ‘root’. Dit alleen 1x per dag om 06.00, om te job voor diezelfde dag goed te zetten. De gewenste tijd is aan te passen door de parameter “time” te veranderen. De API call geeft namelijk al deze velden terug (formatted):

{
"results":
    {
        "sunrise":"6:30:51 AM",
        "sunset":"5:44:52 PM",
        "solar_noon":"12:07:51 PM",
        "day_length":"11:14:01",
        "civil_twilight_begin":"5:56:38 AM",
        "civil_twilight_end":"6:19:05 PM",
        "nautical_twilight_begin":"5:16:49 AM",
        "nautical_twilight_end":"6:58:54 PM",
        "astronomical_twilight_begin":"4:36:17 AM",
        "astronomical_twilight_end":"7:39:26 PM"
    },
    "status":"OK"
}

Of zonder format, de toevoeging ?&formatted=0 in de url zoals ik nu gebruik (scheelt een extra stapel stappen om naar 24-uurs tijd om te zetten):

{
"results":
    {
        "sunrise":"2018-10-08T06:34:22+00:00",
        "sunset":"2018-10-08T17:40:13+00:00",
        "solar_noon":"2018-10-08T12:07:17+00:00",
        "day_length":39951,
        "civil_twilight_begin":"2018-10-08T06:00:05+00:00",
        "civil_twilight_end":"2018-10-08T18:14:30+00:00",
        "nautical_twilight_begin":"2018-10-08T05:20:18+00:00",
        "nautical_twilight_end":"2018-10-08T18:54:17+00:00",
        "astronomical_twilight_begin":"2018-10-08T04:39:57+00:00",
        "astronomical_twilight_end":"2018-10-08T19:34:38+00:00"
    },
"status":"OK"
}

Dus, nu past de cronjob zichzelf aan en zorgt dat de 2 lichten rondom het tv-meubel aangaan als het begint te schemeren en ik hoef niet meer in te loggen via SSH en crontab -e te doen en de tijd per x-dagen aan te passen.

De ‘nautical_twilight’ is een andere benadering van wanneer het donker word. Lees anders even ook deze pagina voor meer informatie.

Ik zie 2 IPv6 adressen en maar 1 geconfigureerd?

Ja, het kan. Ze zal vast eens zien, dat wanneer je op een Windows machine (in dit geval Windows 2008 R2) een vast IPv6 adres configureert toch nog een 2e adres ziet via een ‘ipconfig’. Nee, dit is niet het adres beginnend met “fe80:: ”

Dit is mede door de configuratie op het netwerk waar de machine op aangesloten zit. Het adres komt van de ‘Router Discovery’ af, welke in een soort DHCP manier van werken prima adressen uit deelt aan clients.

Echter, in een strakke omgeving waar hosting sprake is, is 1 vast adres voor zowel inkomend maar meer ook uitgaand erg praktisch. Dit is uit te zetten:

netsh int ipv6 set int "Local Area Connection" routerdiscovery=disabled

Waarbij ‘Local Area Connection’ uiteraard de juiste naam moet zijn.

Na in het invoeren is het 2e adres ook direct weg.

Cacti 1.0.4 op Centos7 met PHP7 en Percona-server

Als je een server hebt met Centos7 gecombineerd met PHP7.x en dus ook Percona Server 5.x geinstalleerd, dan is het installeren van Cacti wel een beetje een domper.

Want, de vereisten van Cacti op hun website zijn als volgt (belangrijkste items):

  • httpd,php,php-mysql,php-snmp
  • php-xml
  • mysql,mysql-server

Dat zou zeggen, de installatie is zo gedaan. Kwestie van yum install en klaar. Maar helaas;

Error: Package: 1:mariadb-5.5.52-1.el7.x86_64 (base)
           Requires: mariadb-libs(x86-64) = 1:5.5.52-1.el7
           Available: 1:mariadb-libs-5.5.52-1.el7.x86_64 (base)
               mariadb-libs(x86-64) = 1:5.5.52-1.el7
Error: Package: php-ldap-5.4.16-42.el7.x86_64 (base)
           Requires: php-common(x86-64) = 5.4.16-42.el7
           Installed: php-common-7.0.17-1.el7.remi.x86_64 (@remi-php70)
               php-common(x86-64) = 7.0.17-1.el7.remi
           Available: php-common-5.4.16-42.el7.x86_64 (base)
               php-common(x86-64) = 5.4.16-42.el7
Error: Package: php-snmp-5.4.16-42.el7.x86_64 (base)
           Requires: php-common(x86-64) = 5.4.16-42.el7
           Installed: php-common-7.0.17-1.el7.remi.x86_64 (@remi-php70)
               php-common(x86-64) = 7.0.17-1.el7.remi
           Available: php-common-5.4.16-42.el7.x86_64 (base)
               php-common(x86-64) = 5.4.16-42.el7
Error: php-mysql conflicts with php-mysqlnd-7.0.17-1.el7.remi.x86_64
Error: Package: php-mbstring-5.4.16-42.el7.x86_64 (base)
           Requires: php-common(x86-64) = 5.4.16-42.el7
           Installed: php-common-7.0.17-1.el7.remi.x86_64 (@remi-php70)
               php-common(x86-64) = 7.0.17-1.el7.remi
           Available: php-common-5.4.16-42.el7.x86_64 (base)
               php-common(x86-64) = 5.4.16-42.el7
Error: Package: cacti-1.0.4-1.el7.noarch (epel)
           Requires: php-mysql
           Available: php-mysqlnd-5.4.16-42.el7.x86_64 (base)
               php-mysql = 5.4.16-42.el7
           Installed: php-mysqlnd-7.0.17-1.el7.remi.x86_64 (@remi-php70)
               Not found
Error: Package: php-xml-5.4.16-42.el7.x86_64 (base)
           Requires: php-common(x86-64) = 5.4.16-42.el7
           Installed: php-common-7.0.17-1.el7.remi.x86_64 (@remi-php70)
               php-common(x86-64) = 7.0.17-1.el7.remi
           Available: php-common-5.4.16-42.el7.x86_64 (base)
               php-common(x86-64) = 5.4.16-42.el7
Error: Package: php-mysql-5.4.16-42.el7.x86_64 (base)
           Requires: php-pdo(x86-64) = 5.4.16-42.el7
           Installed: php-pdo-7.0.17-1.el7.remi.x86_64 (@remi-php70)
               php-pdo(x86-64) = 7.0.17-1.el7.remi
           Available: php-pdo-5.4.16-42.el7.x86_64 (base)
               php-pdo(x86-64) = 5.4.16-42.el7
 You could try using --skip-broken to work around the problem
 You could try running: rpm -Va --nofiles --nodigest

De optie –enablerepo=remi-php70 geeft wel een deel opgelost terug maar niet alles. Jammer genoeg.

Error: Package: cacti-1.0.4-1.el7.noarch (epel)
           Requires: php-mysql
           Available: php-mysql-5.4.16-42.el7.x86_64 (base)
               php-mysql = 5.4.16-42.el7
           Available: php-mysqlnd-5.4.16-42.el7.x86_64 (base)
               php-mysql = 5.4.16-42.el7
           Available: php-pecl-mysql-1.0.0-0.8.20151007git294ce3b.el7.remi.7.0.x86_64 (remi-php70)
               php-mysql = 1:1.0.0
           Available: php-pecl-mysql-1.0.0-0.9.20151007git294ce3b.el7.remi.7.0.x86_64 (remi-php70)
               php-mysql = 1:1.0.0
           Installed: php-mysqlnd-7.0.17-1.el7.remi.x86_64 (@remi-php70)
               Not found
           Available: php-mysqlnd-7.0.16-1.el7.remi.x86_64 (remi-php70)
               Not found
Error: Package: 1:mariadb-5.5.52-1.el7.x86_64 (base)
           Requires: mariadb-libs(x86-64) = 1:5.5.52-1.el7
           Available: 1:mariadb-libs-5.5.52-1.el7.x86_64 (base)
               mariadb-libs(x86-64) = 1:5.5.52-1.el7
 You could try using --skip-broken to work around the problem
 You could try running: rpm -Va --nofiles --nodigest

De optie –skip-broken geeft helemaal niet het gewenste resultaat. Oftewel we zijn weer terug bij af.

Er is een weg dat we het wel aan de praat krijgen middels de source RPM.

We maken allereerst een extra user aan “rpmbuild” en installeren de package rpmbuild. Onder de user rpmbuild downloaden we de source RPM voor cacti versie 1.0.4 en pakken deze uit:

useradd rpmbuild
yum install -y rpm-build
su - rpmbuild

wget https://dl.fedoraproject.org/pub/epel/7/SRPMS/c/cacti-1.0.4-1.el7.src.rpm

rpm -i cacti-1.0.4-1.el7.src.rpm

Er zijn dan een tweetal directories aangemaakt in de map “rpmbuild”. We open de directory “SPECS” en stellen de originele even veilig waarna we de “cacti.spec” openen met een text editor zoals VIM:cd

cd rpmbuild/

ls -l
total 0
drwxr-xr-x. 2 rpmbuild rpmbuild 124 Mar 31 13:46 SOURCES
drwxr-xr-x. 2 rpmbuild rpmbuild  24 Mar 31 13:46 SPECS

cd SPECS
cp cacti.spec cacti.orig

vim cacti.spec

Op regel nummer 13 en 14 vind je de vereisten php (en componenten) en mariadb terug. Deze kan je uitzetten door een ‘#’ er voor te zetten:

#Requires: php, php-ldap, php-mbstring, php-mysql, php-pdo, php-snmp, php-xml
#Requires: mariadb

Hierna moeten nog 2 regels uitgezet worden door een comment (‘#’) te plaatsen op regel 46 en 71:

#%{__install} -d -m 0755 %{buildroot}/%{_pkgdocdir}
#%{__cp} -a docs/ %{buildroot}/%{_pkgdocdir}

Daarna, zet in de regel 3 iets extra’s neer om een eigen release naam aan te duiden, zodat duidelijk is dat deze niet origineel is:

Release: 1%{?dist}
word:
Release: 1%{?dist}.bj

Nu is alles in place om de RPM weer terug te bouwen:

rpmbuild -ba cacti.spec

Er is zijn nu diverse files gemaakt waarmee je verder kan. Je kan ook de nieuwe Source RPM kopieren om weer extra aanpassingen elders te maken. Dat doen we nu niet, we gaan de gecompileerde RPM installeren. Deze is gemaakt in de directory rpmbuild/RPMS/. Uiteraard moeten we weer uit het user account ‘rpmbuild’ gaan en root access hebben. De installatie kan door middel van:

yum install /home/rpmbuild/rpmbuild/RPMS/noarch/cacti-1.0.4-1.el7.centos.bj.noarch.rpm

En daarmee is Cacti door Yum geinstalleerd op een CentOS7 machine met PHP7.0 en Percona-Server 5.7.

Het beste is dan te starten vanaf stap 2:

http://docs.cacti.net/manual:088:1_installation.1_install_unix.5_install_and_configure_cacti

In mijn geval n.a.v. de nieuwe server is het volgende eerst inregelen, en dan op stap 2 verder:

mv /etc/httpd/conf.d/cacti.conf /etc/httpd/conf.d/cacti.conf.old
ln -s /usr/share/cacti /var/www/cacti

vim /etc/httpd/conf.d/cacti.pc-mania.nl.conf:
<VirtualHost *:80>
 DocumentRoot /var/www/cacti/
 ServerName cacti.pc-mania.nl
 <Directory /var/www/cacti/>
      AllowOverride all
      Options MultiViews IncludesNoExec FollowSymLinks
 </Directory>
</VirtualHost>

<VirtualHost *:443>
 DocumentRoot /var/www/cacti
 ServerName cacti.pc-mania.nl
 SSLEngine on
 SSLCertificateFile /etc/letsencrypt/live/cacti.pc-mania.nl/cert.pem
 SSLCertificateKeyFile /etc/letsencrypt/live/cacti.pc-mania.nl/privkey.pem
 SSLCertificateChainFile /etc/letsencrypt/live/cacti.pc-mania.nl/chain.pem

<Directory /var/www/cacti/>
       <IfModule mod_authz_core.c>
               # httpd 2.4
               Require all granted
       </IfModule>
       <IfModule !mod_authz_core.c>
               # httpd 2.2
               Order allow,deny
               Allow from all
       </IfModule>
      AllowOverride all
      Options MultiViews IncludesNoExec FollowSymLinks
</Directory>

<Directory /var/www/cacti/install>
        # mod_security overrides.
        # Uncomment these if you use mod_security.
        # allow POST of application/x-www-form-urlencoded during install
        #SecRuleRemoveById 960010
        # permit the specification of the rrdtool paths during install
        #SecRuleRemoveById 900011
</Directory>


# These sections marked "Require all denied" (or "Deny from all")
# should not be modified.
# These are in place in order to harden Cacti.
<Directory /var/www/cacti/log>
        <IfModule mod_authz_core.c>
                Require all denied
        </IfModule>
        <IfModule !mod_authz_core.c>
                Order deny,allow
                Deny from all
        </IfModule>
</Directory>
<Directory /var/www/cacti/rra>
        <IfModule mod_authz_core.c>
                Require all denied
        </IfModule>
        <IfModule !mod_authz_core.c>
                Order deny,allow
                Deny from all
        </IfModule>
</Directory>

</VirtualHost>

 

Een nieuwe Centos7 server opzetten

Inleiding

Er is een tijd van komen en gaan, dat zeggen ze wel eens. Om even beeld te geven: ik heb diverse virtuele machines in mijn eigen Vmware cloud. Daarvan zijn er 2 met publieke functies te weten “uxx-001” en “uxx-002” met respectievelijk Centos6.8 en Centos5.11. De uxx-002 is de oudste in het ‘serverpark’. Waarom 002 ? Ik weet eerlijk niet meer wat de eerste 001 was.

De uxx-002 server is na ruim 5 jaar definitief uitgezet en verwijderd. Het OS is ouderwets en de software erop niet meer van deze tijd. Dat brengt mij op deze post.

Ik heb daarom ook ruim een jaar terug redelijk wat inhoud van websites uit Centos5.11 met PHP5.3 en functionaliteit verhuisd naar de uxx-001. Daar is tenminste PHP5.6 actief, MySQL (MariaDB) 5.6, Postfix i.p.v Sendmail en meer.

Echter; ik heb nog 2 service VM’s. Deze “services-001” en “services-002” doen bijzaken voor het gehele netwerk, dus ook de via VPN bereikbare thuis netwerken. Deze doen: DNS caching (met Adblock), SMTP (met TLS), LDAP, Tacacs en NTP. Het zijn relatief simpele en platte VM met 1 cpu, 1GB mem en 10GB disk, ook Centos6.8.

Het idee is dus nu ontstaan om deze service VM’s samen te voegen op de bestaande uxx-001 en de nieuwe uxx-002. En uiteindelijk de websites te verhuizen.

De nieuwe server

  • Centos7 minimal install, x86_64
    • default packages na installatie:
      wget mailx mlocate open-vm-tools vim epel-release bind-utils ftp
    • firewalld vervangen voor iptables
    • httpd
    • php7
    • functionaliteit vanuit services-00x
      • ntpd
      • dnsmasq
      • ldap
      • tacacs
    • functionaliteit vanuit uxx-001
      • mysql
      • postfix voor pc-mania.nl domain
      • rsyslog vervangen voor syslog-ng
      • vsftpd
      • cacti en snmpd
      • phpmyadmin

Redelijk eenvoudig zou je zeggen.

Na de basis installatie:

yum install -y wget mailx mlocate open-vm-tools vim epel-release bind-utils ftp
systemctl enable vmtoolsd
systemctl start vmtoolsd
yum update -y && reboot

Na de reboot is de basis er en kunnen we aan de slag. Het eerste is de firewalld vervangen voor iptables. Dit is puur gemak, firewalld werkt ook prima.

yum install -y iptables-services
systemctl stop firewalld
systemctl start iptables
systemctl start ip6tables
systemctl enable iptables
systemctl enable ip6tables
systemctl disable firewalld
yum -y remove firewalld

Dan, kunnen we beginnen aan de httpd en php setup. Hiervoor moeten we een extra Repo installeren. Dit is bij de voorkeur REMI. De webtatic en IUS repo’s zijn het over het algemeen nét niet.

wget -4 http://rpms.remirepo.net/enterprise/remi-release-7.rpm
rpm -Uvh remi-release-7.rpm

De installatie van httpd en php is dan als volgt

yum install -y httpd mod_ssl
yum install -y --enablerepo=remi-php70 php php70-php-mysqlnd php-mysqli php70-php-ldap php70-php-gd

Dan moet er, vanwege een externe Repo wat recht gezet gaan worden.

echo "extension=gd.so" > /etc/php.d/30-gd.ini
echo "extension=ldap.so" > /etc/php.d/30-ldap.ini
echo "date.timezone = Europe/Amsterdam" /etc/php.ini
mv /usr/lib64/php/modules /usr/lib64/php/modules.old
ln -s /opt/remi/php70/root/usr/lib64/php/modules /usr/lib64/php/modules
systemctl restart httpd

Nu dat gebeurd is kan ook de MySQL server geïnstalleerd. Hier is de voorkeur Percona 5.7 ten opzichte van MariaDB.

yum install -y http://www.percona.com/downloads/percona-release/redhat/0.1-3/percona-release-0.1-3.noarch.rpm
yum install -y Percona-Server-server-57

En niet te vergeten:

systemctl enable httpd
systemctl enable mysqld
systemctl start httpd
systemctl start mysqld

Gefeliciteerd! Er is een LAMP-stack actief. Goed, de firewall rules voor poort 80/443 moeten er nog in maar dat ter zijde.

SELinux?

Er is, wat dat moet opgevallen zijn, sprake van een “enforcing” SELinux policy. Vele zetten dit direct in het begin uit met “setenforce 0” maar dat is relatief laf. Daarom alles met Enforcing. Hier zijn later dus ook wat trucs voor. De belangrijkste nu omdat we zover zijn is:

setsebool -P httpd_can_network_connect_db 1

Je leest het goed. Anders werkt je website gewoon niet met MySQL samen.

Netwerk statisch maken

Er is vanuit de basis installatie middels DHCP een adres en via de IPV6_ND een IPv6 adres verkregen. Dit is voor de basis installatie prima geweest maar we veranderen dit nu. Hier is de VMware console voor nodig.

rm -f /var/lib/NetworkManager/dhclient*
systemctl stop NetworkManager
systemctl disable NetworkManager

Daarna kan je het ifcfg-eth0 bestand keurig invullen naar wens:

TYPE="Ethernet"
BOOTPROTO="none"
DEFROUTE="yes"
IPADDR="192.168.190.52"
PREFIX="24"
GATEWAY="192.168.190.254"
DNS1="192.168.190.21"
DOMAIN="192.168.190.22"
IPV4_FAILURE_FATAL="yes"
IPV6INIT="yes"
IPV6ADDR="2001:9a0:5004:1900::52/64"
IPV6_DEFAULTGW="2001:9a0:5004:1900::1"
IPV6_AUTOCONF="no"
IPV6_DEFROUTE="yes"
IPV6_FAILURE_FATAL="no"
IPV6_FORWARDING="no"
IPV6_ADDR_GEN_MODE="stable-privacy"
NAME="eth0"
# This is system specific and can be created using 'uuidgen eth0' command #
UUID="c27bbe86-3299-440e-88bc-9ed5901b2142"
DEVICE="ens192"
ONBOOT="yes"
NM_CONTROLLED=no

Het commando in de comment is mogelijk nog nodig om te draaien op de prompt. Let hier op. Na een extra ‘systemctl restart network’ moet je weer verbonden zijn en kan je het verifiëren met ‘ip a’ of ‘ifconfig’.
De laatste regel is met name van belang zodat deze file gebruikt word voor configuratie van het ‘eth0’ interface, in plaats dat de NetworkManager dit overneemt.

Syslog-NG met MySQL Logparser (Netlog)

Vanuit de Uxx-001 server heb ik een Syslog systeem wat dermate handig en simplistisch is in gebruik maar wel goed oogt dat dit ook mee moet. Hier is in de basis Syslog-NG voor nodig, als mede MySQL en een stukje PHP software. De laatst genoemde functionaliteit is mogelijk, dus nu Syslog-NG nog. Vanuit de CentOS community is er gekozen voor RSyslog en deze zal verwijderd moeten worden.

wget https://copr.fedorainfracloud.org/coprs/czanik/syslog-ng37/repo/epel-7/czanik-syslog-ng37-epel-7.repo
mv czanik-syslog-ng37-epel-7.repo /etc/yum.repos.d/syslog-ng37-epel-7.repo
systemctl stop rsyslog
systemctl disable rsyslog
yum -y remove rsyslog
yum install -y syslog-ng
systemctl start syslog-ng
systemctl enable syslog-ng

Meer is het niet, gelukkig en dat scheelt. Nu de meer interessante stukken. Eerst zal de basis van MySQL nog even goed gezet moeten worden.

Configureer in /root een bestand ‘.my.cnf’ . Zoek hiervoor het root password op via “cat /var/log/mysqld.log | grep root@”

[client]
user=root
pass=<wat je vond>

Nu kan je inloggen in de MySQL server. Hier zullen diverse changes doorgevoerd moeten worden als basis. Het eerste is je password opnieuw invoeren.

ALTER USER 'root'@'localhost' IDENTIFIED BY '<je password>';

Er zullen een aantal databases en tabellen aangemaakt moeten worden. Door onderstaand in te voeren is dat geregeld (voer indien gewenst een ander wachtwoord in op regels 5 en 6:

CREATE DATABASE `syslog`;
CREATE DATABASE `netlogconfig`;
 
GRANT ALL ON syslog.* TO 'syslog'@'localhost' IDENTIFIED BY 'WonFaznu$(s#3nCi';
GRANT ALL ON netlogconfig.* TO 'syslog'@'localhost' IDENTIFIED BY 'WonFaznu$(s#3nCi';
 
USE syslog;
CREATE TABLE `template` (
  `id` int(15) unsigned NOT NULL AUTO_INCREMENT,
  `HOST` varchar(39) NOT NULL,
  `FAC` varchar(255) NOT NULL,
  `PRIO` varchar(255) NOT NULL,
  `LVL` varchar(255) NOT NULL,
  `TAG` varchar(255) NOT NULL,
  `DAY` varchar(10) NOT NULL,
  `TIME` varchar(8) NOT NULL,
  `PROG` varchar(255) NOT NULL,
  `MSG` text NOT NULL,
  PRIMARY KEY (`id`),
  KEY `HOST` (`HOST`),
  KEY `DAY` (`DAY`),
  KEY `TIME` (`TIME`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
 
USE netlogconfig;
CREATE TABLE `hostnames` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `hostip` text NOT NULL,
  `hostname` text NOT NULL,
  `hosttype` int(10) unsigned NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
 
CREATE TABLE `hosttype` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `name` text NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM;
 
CREATE TABLE `lograte` (
  `id` int(10) NOT NULL auto_increment,
  `hostnameid` int(10) NOT NULL,
  `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `1min` float default NULL,
  `5min` float default NULL,
  `10min` float default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM;
 
CREATE TABLE `lograteconf` (
  `hostnameid` int(10) NOT NULL,
  `samplerate` int(10) default NULL
) ENGINE=MyISAM;

CREATE TABLE `logscavenger` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `keyword` varchar(100) DEFAULT NULL,
  `dateadded` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `active` int(1) NOT NULL,
  `datedeleted` timestamp NULL DEFAULT NULL,
  `emailrcpt` varchar(255) DEFAULT NULL,
  `emailgroup` int(2) DEFAULT NULL,
  KEY `id` (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

CREATE TABLE `emailgroups` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `groupname` varchar(40) NOT NULL,
  `recepients` text NOT NULL,
  `active` int(1) DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

CREATE TABLE `logcache` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `host` varchar(39) NOT NULL,
  `msg` text NOT NULL,
  PRIMARY KEY (`id`),
  KEY `host` (`host`),
  KEY `timestamp` (`timestamp`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

Vervolgens kan de basis van Netlog geinstalleerd worden. Download de installatie files van de Netlog applicatie:

wget https://www.pc-mania.nl/netlog_v2.01.tgz

Dan kan het bestand uitgepakt worden, bij voorkeur in de /tmp of /root directory en de  installatie uitgevoerd:

tar xvf netlog_v2.01.tgz
cd netlog_v2.01
./install.sh

Deze zal de bestanden verplaatsen naar de juiste directories. Na deze stap kan het nodige aan instellingen rechtgezet worden indien je bijvoorbeeld het wachtwoord van een of beide databases hebt aangepast. De 2 bestanden voor wachtwoorden zijn, zoals je zag in de install.sh output duidelijk te vinden.

 Nu de belangrijkste 2 directory’s en bestanden bestaan is het tijd om deze ‘logparser’ als daemon (service) mee te laten draaien. Dit kan via de systemd systematiek binnen Centos7. De oude ‘/etc/inittab’ met runlevels (3 bijv.) zijn niet meer  namelijk.
Maak een bestand aan met de naam ‘/usr/lib/systemd/system/logparser.service‘ en vul deze met:

[Unit]
Description=System Logger Parser Daemon
After=mysqld.service
 
[Service]
ExecStart=/usr/bin/php /usr/share/syslog-ng/php/logparser.php
Restart=on-failure
 
[Install]
WantedBy=multi-user.target
Alias=logparser.service

Dan kan het systeem herladen worden en de service gestart.

systemctl daemon-reload
systemctl enable logparser.service
systemctl status logparser

De service word nog niet  gestart! Dat is met reden dat de Syslog-NG backend nog verteld moet worden wat deze moet doen en hoe deze inkomende berichten moet verwerken.

In het bestand ‘/etc/syslog-ng/syslog-ng.conf’ moeten er 2 sources, 1 destination en 2 log methodes voor toegevoegd worden.

#voer in onder de stanza van 'source s_sys'
 
source s_network_udp {
    network(
        ip(0.0.0.0)
        port(514)
        transport("udp")
    );
};
source s_network_tcp {
    network(
        ip(0.0.0.0)
        port(514)
        transport("tcp")
    );
};
 
#voer in onder de stanza van 'destination d_mlal'
 
destination d_mysql {
    pipe("/var/log/syslog.fifo"
    template("HOST_:_$SOURCEIP _,_ FAC_:_$FACILITY _,_ PRIO_:_$PRI _,_ LVL_:_$LEVEL _,_ TAG_:_$TAG _,_ DAY_:_$YEAR-$MONTH-$DAY _,_ TIME_:_$HOUR:$MIN:$SEC _,_ PROG_:_$PROGRAM _,_ MSG_:_$MSG\n")
    template-escape(yes));
};
 
#voer in onder de laatste stanza van 'log { source(s_sys)'
 
log { source(s_network_udp); destination(d_mysql); };
log { source(s_network_tcp); destination(d_mysql); };

Sla het werk op en herstart de Syslog-NG daemon via “systemctl restart syslog-ng“. Voer desgewenst een check uit met “systemctl status syslog-ng” en “ss -ln|grep 514”:

udp    UNCONN     0      0         *:514                   *:*
tcp    LISTEN     0      128       *:514                   *:*

Nu Syslog-NG weet wat er moet gebeuren met de inkomende berichten en deze dus met een specifieke opmaak naar het FiFo bestand in ‘/var/log/syslog.fifo‘ moet schrijven kunnen we de logparser service starten.

systemctl start logparser
systemctl status logparser
● logparser.service - System Logger Parser Daemon
   Loaded: loaded (/usr/lib/systemd/system/logparser.service; enabled; vendor preset: disabled)
   Active: active (running) since Wed 2017-03-29 13:33:37 CEST; 4s ago
 Main PID: 3155 (php)
   CGroup: /system.slice/logparser.service
           └─3155 /usr/bin/php /usr/share/syslog-ng/php/logparser.php

Mar 29 13:33:37 uxx-002.pc-mania.nl systemd[1]: Started System Logger Parser Daemon.
Mar 29 13:33:37 uxx-002.pc-mania.nl systemd[1]: Starting System Logger Parser Daemon...

 Voila. De Netlog parser service draait en als alles goed verlopen is zal ook de website werken. Browse naar http://192.168.190.52/ en de website moet getoond worden. Uiteraard zonder informatie, hosts of categorieen.

Vergeet niet de jobs in ‘/etc/crontab’ actief te zetten. Let op; de laatste regel met “prunelog.php” is alleen noodzakelijk bij een archiver situatie. In dit geval dus even niet.

Test door middel van een extra host of netwerk device of je logging werkt. Een check kan als volgt uitgevoerd worden:

iptables -L -nv
Chain INPUT (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
...
   96 17444 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0            state NEW udp dpt:514


mysqlshow --count syslog
Database: syslog
+-------------------------------------+----------+------------+
|               Tables                | Columns  | Total Rows |
+-------------------------------------+----------+------------+
| HST_192_168_190_254_DATE_2017_03_29 |       10 |        235 |
| template                            |       10 |          0 |
+-------------------------------------+----------+------------+
2 rows in set.

Voor de lograte pagina moet er een correctie gedaan worden. Vanwege PHP7 is GD nog niet volledig up to date. Bewerk het bestand ‘/usr/share/php/jpgraph/gd_image.inc.php’ op regel 110 en zet de “//” voor deze regel

  //JpGraphError::RaiseL(25128);//(......

Om de pagina direct beter te laten werken (er zijn namelijk wat functies missend in de settings.php) moet er handmatig even wat via MySQL gedaan worden:

use netlogconfig;
insert into hosttype values (1,"Firewall")
insert into hostnames (hostip,hostname,hosttype) values ("192.168.190.254","dc-firewall",1);
insert into lograteconf values (1,1);
exit;

Ga naar de logout knop op de pagina om de sessie te resetten. En dan zal de logging getoond moeten worden.

Webserver en TLS/SSL

De standaard inrichting van httpd en php zijn nog van toepassing. Ook de zojuist geïnstalleerde Netlog applicatie draait, jawel, in de standaard ‘/var/www/html’ directory. Geen probleem. Er zal wat aangepast moeten worden.

#Listen 80
Listen *:80

#ServerAdmin root@localhost
ServerAdmin bartjan [ at ] pc-mania [dot] nl

#ServerName www.example.com:80
ServerName uxx-002.pc-mania.nl

Maak een default site aan, zodat alle request daar op binnen komen in de configuratie ‘/etc/httpd/conf.d/_default-site.conf

<VirtualHost *:80>
 DocumentRoot /var/www/html
 ServerName uxx-002.pc-mania.nl
 <Directory /var/www/html>
      AllowOverride all
      Options MultiViews IncludesNoExec FollowSymLinks
 </Directory>
</VirtualHost>

Test de configuratie met het commando ‘httpd -t‘ en eventueel met de output van ‘httpd -S’ zoals hieronder (let op, het voorheen gebruikte ‘apachectl -S’ word niet meer gebruikt):

httpd -S
VirtualHost configuration:
*:443                  uxx-002.pc-mania.nl (/etc/httpd/conf.d/ssl.conf:56)
*:80                   is a NameVirtualHost
         default server uxx-002.pc-mania.nl (/etc/httpd/conf.d/_default-site.conf:1)
         port 80 namevhost uxx-002.pc-mania.nl (/etc/httpd/conf.d/_default-site.conf:1)

Nu kunnen er meer websites toegevoegd worden, door elke in zijn eigen .conf  bestand te plaatsen. Verplaats de inhoud van de map ‘/var/www/html’ naar een nieuwe map ‘/var/www/syslog’ en voeg deze configuratie toe in ‘/etc/httpd/conf.d/syslog.pc-mania.nl.conf’

<VirtualHost *:80>
 DocumentRoot /var/www/syslog
 ServerName syslog.pc-mania.nl
 <Directory /var/www/syslog>
      AllowOverride all
      Options MultiViews IncludesNoExec FollowSymLinks
 </Directory>
</VirtualHost>

Voer een herstart uit met ‘apachectl restart’ of ‘systemctl restart httpd’ en controleer of de output van ‘httpd -S’ nu uitgebreid is.

TLS/SSL hardening toepassen

Voordat we TLS (of SSL) gaan toepassen moeten er wat regels gezet worden wat de beveiliging omhoog brengt. Dit voorkomt mogelijke man-in-the-middle aanvallen of andere meeluister praktijken. Dat zal niet hard lopen met de hobby matige informatie maar toch.

Maak een bestand aan ‘/etc/httpd/conf.d/security.conf en voeg onderstaande in.

ServerTokens Prod
ServerSignature Off
TraceEnable Off
Header set X-Content-Type-Options: "nosniff"
Header set X-Frame-Options: "sameorigin"
Header unset ETag
FileETag None

Nadat deze aangemaakt is moet ook de ‘/etc/httpd/conf.d/ssl.conf’ er aan geloven. Open deze en voeg het volgende toe onderaan het bestand:

SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:AES256+ED
SSLHonorCipherOrder on

##
## Header context
##
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always edit Set-Cookie ^(.*)$ $1;secure
Header always set X-Frame-Options "DENY"
Header always set X-XSS-Protection "1; mode=block"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Permitted-Cross-Domain-Policies "none"

Verwijderd het gehele stuk van de ‘virtualhost _default_:443’ en voeg dit toe aan de ‘/etc/httpd/conf.d/_default-site.conf:

<VirtualHost *:443>
 DocumentRoot /var/www/html
 ServerName uxx-002.pc-mania.nl
 SSLEngine on
 SSLCertificateFile /etc/pki/tls/certs/localhost.crt
 SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
 <Directory /var/www/html>
      AllowOverride all
      Options MultiViews IncludesNoExec FollowSymLinks
 </Directory>
</VirtualHost>

En ook dat werkt nu, op het certificaat na, goed. Dat is eenvoudig toe te voegen door LetsEncrypt.

yum install -y python-certbot-apache

Zorg wel dat de server uiteraard bereikbaar is op DNS en IP adres, zowel poort 80 en 443.

certbot --apache certonly -m bartjan [ at ]  pc-mania [dot] nl -d uxx-002.pc-mania.nl

Hierna zijn er diverse files gegenereerd en kan je de configuratie in de ‘/etc/httpd/conf.d/_default-site.conf’ aanpassen:

 SSLCertificateFile /etc/letsencrypt/live/uxx-002.pc-mania.nl/cert.pem
 SSLCertificateKeyFile /etc/letsencrypt/live/uxx-002.pc-mania.nl/privkey.pem
 SSLCertificateChainFile /etc/letsencrypt/live/uxx-002.pc-mania.nl/chain.pem